Home | History | Annotate | Download | only in timer
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.deskclock.timer;
     18 
     19 import android.content.Context;
     20 import android.content.res.Resources;
     21 import android.graphics.Canvas;
     22 import android.graphics.Paint;
     23 import android.graphics.Typeface;
     24 import android.text.TextUtils;
     25 import android.util.AttributeSet;
     26 import android.view.MotionEvent;
     27 import android.view.View;
     28 import android.view.accessibility.AccessibilityManager;
     29 import android.widget.TextView;
     30 
     31 import com.android.deskclock.Log;
     32 import com.android.deskclock.R;
     33 import com.android.deskclock.Utils;
     34 
     35 
     36 public class CountingTimerView extends View {
     37     private static final String TWO_DIGITS = "%02d";
     38     private static final String ONE_DIGIT = "%01d";
     39     private static final String NEG_TWO_DIGITS = "-%02d";
     40     private static final String NEG_ONE_DIGIT = "-%01d";
     41     private static final float TEXT_SIZE_TO_WIDTH_RATIO = 0.75f;
     42     // This is the ratio of the font typeface we need to offset the font by vertically to align it
     43     // vertically center.
     44     private static final float FONT_VERTICAL_OFFSET = 0.14f;
     45 
     46     private String mHours, mMinutes, mSeconds, mHundredths;
     47 
     48     private boolean mShowTimeStr = true;
     49     private final Typeface mAndroidClockMonoThin, mAndroidClockMonoBold, mAndroidClockMonoLight;
     50     private final Typeface mRobotoLabel;
     51     private final Paint mPaintBig = new Paint();
     52     private final Paint mPaintBigThin = new Paint();
     53     private final Paint mPaintMed = new Paint();
     54     private final Paint mPaintLabel = new Paint();
     55     private final float mBigFontSize, mSmallFontSize;
     56     private final SignedTime mBigHours, mBigMinutes;
     57     private final UnsignedTime mBigThinSeconds;
     58     private final Hundredths mMedHundredths;
     59     private float mTextHeight = 0;
     60     private float mTotalTextWidth;
     61     private static final String HUNDREDTH_SEPERATOR = ".";
     62     private boolean mRemeasureText = true;
     63 
     64     private int mDefaultColor;
     65     private final int mPressedColor;
     66     private final int mWhiteColor;
     67     private final int mRedColor;
     68     private TextView mStopStartTextView;
     69     private final AccessibilityManager mAccessibilityManager;
     70 
     71     // Fields for the text serving as a virtual button.
     72     private boolean mVirtualButtonEnabled = false;
     73     private boolean mVirtualButtonPressedOn = false;
     74 
     75     Runnable mBlinkThread = new Runnable() {
     76         private boolean mVisible = true;
     77         @Override
     78         public void run() {
     79             mVisible = !mVisible;
     80             CountingTimerView.this.showTime(mVisible);
     81             postDelayed(mBlinkThread, 500);
     82         }
     83 
     84     };
     85 
     86     class UnsignedTime {
     87         protected Paint mPaint;
     88         protected float mEm;
     89         protected float mWidth = 0;
     90         private final String mWidest;
     91         protected String mLabel;
     92         private float mLabelWidth = 0;
     93 
     94         public UnsignedTime(Paint paint, final String label, String allDigits) {
     95             mPaint = paint;
     96             mLabel = label;
     97 
     98             if (TextUtils.isEmpty(allDigits)) {
     99                 Log.wtf("Locale digits missing - using English");
    100                 allDigits = "0123456789";
    101             }
    102 
    103             float widths[] = new float[allDigits.length()];
    104             int ll = mPaint.getTextWidths(allDigits, widths);
    105             int largest = 0;
    106             for (int ii = 1; ii < ll; ii++) {
    107                 if (widths[ii] > widths[largest]) {
    108                     largest = ii;
    109                 }
    110             }
    111 
    112             mEm = widths[largest];
    113             mWidest = allDigits.substring(largest, largest + 1);
    114         }
    115 
    116         public UnsignedTime(UnsignedTime unsignedTime, final String label) {
    117             this.mPaint = unsignedTime.mPaint;
    118             this.mEm = unsignedTime.mEm;
    119             this.mWidth = unsignedTime.mWidth;
    120             this.mWidest = unsignedTime.mWidest;
    121             this.mLabel = label;
    122         }
    123 
    124         protected void updateWidth(final String time) {
    125             mEm = mPaint.measureText(mWidest);
    126             mLabelWidth = mLabel == null ? 0 : mPaintLabel.measureText(mLabel);
    127             mWidth = time.length() * mEm;
    128         }
    129 
    130         protected void resetWidth() {
    131             mWidth = mLabelWidth = 0;
    132         }
    133 
    134         public float calcTotalWidth(final String time) {
    135             if (time != null) {
    136                 updateWidth(time);
    137                 return mWidth + mLabelWidth;
    138             } else {
    139                 resetWidth();
    140                 return 0;
    141             }
    142         }
    143 
    144         public float getWidth() {
    145             return mWidth;
    146         }
    147 
    148         public float getLabelWidth() {
    149             return mLabelWidth;
    150         }
    151 
    152         protected float drawTime(Canvas canvas, final String time, int ii, float x, float y) {
    153             float textEm  = mEm / 2f;
    154             while (ii < time.length()) {
    155                 x += textEm;
    156                 canvas.drawText(time.substring(ii, ii + 1), x, y, mPaint);
    157                 x += textEm;
    158                 ii++;
    159             }
    160             return x;
    161         }
    162 
    163         public float draw(Canvas canvas, final String time, float x, float y, float yLabel) {
    164             x = drawTime(canvas, time, 0, x, y);
    165             if (mLabel != null ) {
    166                 canvas.drawText(mLabel, x, yLabel, mPaintLabel);
    167             }
    168             return x + getLabelWidth();
    169         }
    170     }
    171 
    172     class Hundredths extends UnsignedTime {
    173         public Hundredths(Paint paint, final String label, final String allDigits) {
    174             super(paint, label, allDigits);
    175         }
    176 
    177         @Override
    178         public float draw(Canvas canvas, final String time, float x, float y, float yLabel) {
    179             if (mLabel != null) {
    180                 canvas.drawText(mLabel, x, yLabel, mPaintLabel);
    181             }
    182             return drawTime(canvas, time, 0, x + getLabelWidth(), y);
    183         }
    184     }
    185 
    186     class SignedTime extends UnsignedTime {
    187         private float mMinusWidth = 0;
    188 
    189         public SignedTime(Paint paint, final String label, final String allDigits) {
    190             super(paint, label, allDigits);
    191         }
    192 
    193         public SignedTime (SignedTime signedTime, final String label) {
    194             super(signedTime, label);
    195         }
    196 
    197         @Override
    198         protected void updateWidth(final String time) {
    199             super.updateWidth(time);
    200             if (time.contains("-")) {
    201                 mMinusWidth = mPaint.measureText("-");
    202                 mWidth += (mMinusWidth - mEm);
    203             } else {
    204                 mMinusWidth = 0;
    205             }
    206         }
    207 
    208         @Override
    209         protected void resetWidth() {
    210             super.resetWidth();
    211             mMinusWidth = 0;
    212         }
    213 
    214         @Override
    215         public float draw(Canvas canvas, final String time, float x, float y, float yLabel) {
    216             int ii = 0;
    217             if (mMinusWidth != 0f) {
    218                 float minusWidth = mMinusWidth / 2;
    219                 x += minusWidth;
    220                 canvas.drawText(time.substring(ii, ii + 1), x, y, mPaint);
    221                 x += minusWidth;
    222                 ii++;
    223             }
    224             x = drawTime(canvas, time, ii, x, y);
    225             if (mLabel != null) {
    226                 canvas.drawText(mLabel, x, yLabel, mPaintLabel);
    227             }
    228             return x + getLabelWidth();
    229         }
    230     }
    231 
    232     public CountingTimerView(Context context) {
    233         this(context, null);
    234     }
    235 
    236     public CountingTimerView(Context context, AttributeSet attrs) {
    237         super(context, attrs);
    238         mAndroidClockMonoThin = Typeface.createFromAsset(
    239                 context.getAssets(), "fonts/AndroidClockMono-Thin.ttf");
    240         mAndroidClockMonoBold = Typeface.createFromAsset(
    241                 context.getAssets(), "fonts/AndroidClockMono-Bold.ttf");
    242         mAndroidClockMonoLight = Typeface.createFromAsset(
    243                 context.getAssets(), "fonts/AndroidClockMono-Light.ttf");
    244         mAccessibilityManager =
    245                 (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
    246         mRobotoLabel= Typeface.create("sans-serif-condensed", Typeface.BOLD);
    247         Resources r = context.getResources();
    248         mWhiteColor = r.getColor(R.color.clock_white);
    249         mDefaultColor = mWhiteColor;
    250         mPressedColor = r.getColor(Utils.getPressedColorId());
    251         mRedColor = r.getColor(R.color.clock_red);
    252 
    253         mPaintBig.setAntiAlias(true);
    254         mPaintBig.setStyle(Paint.Style.STROKE);
    255         mPaintBig.setTextAlign(Paint.Align.CENTER);
    256         mPaintBig.setTypeface(mAndroidClockMonoBold);
    257         mBigFontSize = r.getDimension(R.dimen.big_font_size);
    258         mSmallFontSize = r.getDimension(R.dimen.small_font_size);
    259 
    260         mPaintBigThin.setAntiAlias(true);
    261         mPaintBigThin.setStyle(Paint.Style.STROKE);
    262         mPaintBigThin.setTextAlign(Paint.Align.CENTER);
    263         mPaintBigThin.setTypeface(mAndroidClockMonoThin);
    264 
    265         mPaintMed.setAntiAlias(true);
    266         mPaintMed.setStyle(Paint.Style.STROKE);
    267         mPaintMed.setTextAlign(Paint.Align.CENTER);
    268         mPaintMed.setTypeface(mAndroidClockMonoLight);
    269 
    270         mPaintLabel.setAntiAlias(true);
    271         mPaintLabel.setStyle(Paint.Style.STROKE);
    272         mPaintLabel.setTextAlign(Paint.Align.LEFT);
    273         mPaintLabel.setTypeface(mRobotoLabel);
    274         mPaintLabel.setTextSize(r.getDimension(R.dimen.label_font_size));
    275 
    276         resetTextSize();
    277         setTextColor(mDefaultColor);
    278 
    279         // allDigits will contain ten digits: "0123456789" in the default locale
    280         final String allDigits = String.format("%010d", 123456789);
    281         mBigHours = new SignedTime(mPaintBig,
    282                 r.getString(R.string.hours_label).toUpperCase(), allDigits);
    283         mBigMinutes = new SignedTime(mBigHours,
    284                 r.getString(R.string.minutes_label).toUpperCase());
    285         mBigThinSeconds = new UnsignedTime(mPaintBigThin,
    286                 r.getString(R.string.seconds_label).toUpperCase(), allDigits);
    287         mMedHundredths = new Hundredths(mPaintMed, HUNDREDTH_SEPERATOR, allDigits);
    288     }
    289 
    290     protected void resetTextSize() {
    291         mPaintBig.setTextSize(mBigFontSize);
    292         mTextHeight = mBigFontSize;
    293         mPaintBigThin.setTextSize(mBigFontSize);
    294         mPaintMed.setTextSize(mSmallFontSize);
    295     }
    296 
    297     protected void setTextColor(int textColor) {
    298         mPaintBig.setColor(textColor);
    299         mPaintBigThin.setColor(textColor);
    300         mPaintMed.setColor(textColor);
    301         mPaintLabel.setColor(textColor);
    302     }
    303 
    304     public void setTime(long time, boolean showHundredths, boolean update) {
    305         boolean neg = false, showNeg = false;
    306         String format = null;
    307         if (time < 0) {
    308             time = -time;
    309             neg = showNeg = true;
    310         }
    311         long hundreds, seconds, minutes, hours;
    312         seconds = time / 1000;
    313         hundreds = (time - seconds * 1000) / 10;
    314         minutes = seconds / 60;
    315         seconds = seconds - minutes * 60;
    316         hours = minutes / 60;
    317         minutes = minutes - hours * 60;
    318         if (hours > 999) {
    319             hours = 0;
    320         }
    321         // time may less than a second below zero, since we do not show fractions of seconds
    322         // when counting down, do not show the minus sign.
    323         if (hours ==0 && minutes == 0 && seconds == 0) {
    324             showNeg = false;
    325         }
    326 
    327         if (!showHundredths) {
    328             if (!neg && hundreds != 0) {
    329                 seconds++;
    330                 if (seconds == 60) {
    331                     seconds = 0;
    332                     minutes++;
    333                     if (minutes == 60) {
    334                         minutes = 0;
    335                         hours++;
    336                     }
    337                 }
    338             }
    339             if (hundreds < 10 || hundreds > 90) {
    340                 update = true;
    341             }
    342         }
    343 
    344         int oldLength = getDigitsLength();
    345 
    346         if (hours >= 10) {
    347             format = showNeg ? NEG_TWO_DIGITS : TWO_DIGITS;
    348             mHours = String.format(format, hours);
    349         } else if (hours > 0) {
    350             format = showNeg ? NEG_ONE_DIGIT : ONE_DIGIT;
    351             mHours = String.format(format, hours);
    352         } else {
    353             mHours = null;
    354         }
    355 
    356         if (minutes >= 10 || hours > 0) {
    357             format = (showNeg && hours == 0) ? NEG_TWO_DIGITS : TWO_DIGITS;
    358             mMinutes = String.format(format, minutes);
    359         } else {
    360             format = (showNeg && hours == 0) ? NEG_ONE_DIGIT : ONE_DIGIT;
    361             mMinutes = String.format(format, minutes);
    362         }
    363 
    364         mSeconds = String.format(TWO_DIGITS, seconds);
    365         if (showHundredths) {
    366             mHundredths = String.format(TWO_DIGITS, hundreds);
    367         } else {
    368             mHundredths = null;
    369         }
    370 
    371         int newLength = getDigitsLength();
    372         if (oldLength != newLength) {
    373             if (oldLength > newLength) {
    374                 resetTextSize();
    375             }
    376             mRemeasureText = true;
    377         }
    378 
    379         if (update) {
    380             setContentDescription(getTimeStringForAccessibility((int) hours, (int) minutes,
    381                     (int) seconds, showNeg, getResources()));
    382             invalidate();
    383         }
    384     }
    385 
    386     private int getDigitsLength() {
    387         return ((mHours == null) ? 0 : mHours.length())
    388                 + ((mMinutes == null) ? 0 : mMinutes.length())
    389                 + ((mSeconds == null) ? 0 : mSeconds.length())
    390                 + ((mHundredths == null) ? 0 : mHundredths.length());
    391     }
    392 
    393     private void calcTotalTextWidth() {
    394         mTotalTextWidth = mBigHours.calcTotalWidth(mHours) + mBigMinutes.calcTotalWidth(mMinutes)
    395                 + mBigThinSeconds.calcTotalWidth(mSeconds)
    396                 + mMedHundredths.calcTotalWidth(mHundredths);
    397     }
    398 
    399     private void setTotalTextWidth() {
    400         calcTotalTextWidth();
    401         // To determine the maximum width, we find the minimum of the height and width (since the
    402         // circle we are trying to fit the text into has its radius sized to the smaller of the
    403         // two.
    404         int width = Math.min(getWidth(), getHeight());
    405         if (width != 0) {
    406             float wantWidth = (int)(TEXT_SIZE_TO_WIDTH_RATIO * width);
    407             // If the text is too wide, reduce all the paint text sizes
    408             while (mTotalTextWidth > wantWidth) {
    409                 // Get fixed and variant parts of the total size
    410                 float fixedWidths = mBigHours.getLabelWidth() + mBigMinutes.getLabelWidth()
    411                         + mBigThinSeconds.getLabelWidth() + mMedHundredths.getLabelWidth();
    412                 float varWidths = mBigHours.getWidth() + mBigMinutes.getWidth()
    413                         + mBigThinSeconds.getWidth() + mMedHundredths.getWidth();
    414                 // Avoid divide by zero || sizeRatio == 1 || sizeRatio <= 0
    415                 if (varWidths == 0 || fixedWidths == 0 || fixedWidths >= wantWidth) {
    416                     break;
    417                 }
    418                 // Variant-section reduction
    419                 float sizeRatio = (wantWidth - fixedWidths) / varWidths;
    420                 mPaintBig.setTextSize(mPaintBig.getTextSize() * sizeRatio);
    421                 mPaintBigThin.setTextSize(mPaintBigThin.getTextSize() * sizeRatio);
    422                 mPaintMed.setTextSize(mPaintMed.getTextSize() * sizeRatio);
    423                 //recalculate the new total text width and half text height
    424                 mTextHeight = mPaintBig.getTextSize();
    425                 calcTotalTextWidth();
    426             }
    427         }
    428     }
    429 
    430     public void blinkTimeStr(boolean blink) {
    431         if (blink) {
    432             removeCallbacks(mBlinkThread);
    433             post(mBlinkThread);
    434         } else {
    435             removeCallbacks(mBlinkThread);
    436             showTime(true);
    437         }
    438     }
    439 
    440     public void showTime(boolean visible) {
    441         mShowTimeStr = visible;
    442         invalidate();
    443     }
    444 
    445     public void redTimeStr(boolean red, boolean forceUpdate) {
    446         mDefaultColor = red ? mRedColor : mWhiteColor;
    447         setTextColor(mDefaultColor);
    448         if (forceUpdate) {
    449             invalidate();
    450         }
    451     }
    452 
    453     public String getTimeString() {
    454         // Though only called from Stopwatch Share, so hundredth are never null,
    455         // protect the future and check for null mHundredths
    456         if (mHundredths == null) {
    457             if (mHours == null) {
    458                 return String.format("%s:%s", mMinutes, mSeconds);
    459             }
    460             return String.format("%s:%s:%s", mHours, mMinutes, mSeconds);
    461         } else if (mHours == null) {
    462             return String.format("%s:%s.%s", mMinutes, mSeconds, mHundredths);
    463         }
    464         return String.format("%s:%s:%s.%s", mHours, mMinutes, mSeconds, mHundredths);
    465     }
    466 
    467     private static String getTimeStringForAccessibility(int hours, int minutes, int seconds,
    468             boolean showNeg, Resources r) {
    469         StringBuilder s = new StringBuilder();
    470         if (showNeg) {
    471             // This must be followed by a non-zero number or it will be audible as "hyphen"
    472             // instead of "minus".
    473             s.append("-");
    474         }
    475         if (showNeg && hours == 0 && minutes == 0) {
    476             // Non-negative time will always have minutes, eg. "0 minutes 7 seconds", but negative
    477             // time must start with non-zero digit, eg. -0m7s will be audible as just "-7 seconds"
    478             s.append(String.format(
    479                     r.getQuantityText(R.plurals.Nseconds_description, seconds).toString(),
    480                     seconds));
    481         } else if (hours == 0) {
    482             s.append(String.format(
    483                     r.getQuantityText(R.plurals.Nminutes_description, minutes).toString(),
    484                     minutes));
    485             s.append(" ");
    486             s.append(String.format(
    487                     r.getQuantityText(R.plurals.Nseconds_description, seconds).toString(),
    488                     seconds));
    489         } else {
    490             s.append(String.format(
    491                     r.getQuantityText(R.plurals.Nhours_description, hours).toString(),
    492                     hours));
    493             s.append(" ");
    494             s.append(String.format(
    495                     r.getQuantityText(R.plurals.Nminutes_description, minutes).toString(),
    496                     minutes));
    497             s.append(" ");
    498             s.append(String.format(
    499                     r.getQuantityText(R.plurals.Nseconds_description, seconds).toString(),
    500                     seconds));
    501         }
    502         return s.toString();
    503     }
    504 
    505     public void setVirtualButtonEnabled(boolean enabled) {
    506         mVirtualButtonEnabled = enabled;
    507     }
    508 
    509     private void virtualButtonPressed(boolean pressedOn) {
    510         mVirtualButtonPressedOn = pressedOn;
    511         mStopStartTextView.setTextColor(pressedOn ? mPressedColor : mWhiteColor);
    512         invalidate();
    513     }
    514 
    515     private boolean withinVirtualButtonBounds(float x, float y) {
    516         int width = getWidth();
    517         int height = getHeight();
    518         float centerX = width / 2;
    519         float centerY = height / 2;
    520         float radius = Math.min(width, height) / 2;
    521 
    522         // Within the circle button if distance to the center is less than the radius.
    523         double distance = Math.sqrt(Math.pow(centerX - x, 2) + Math.pow(centerY - y, 2));
    524         return distance < radius;
    525     }
    526 
    527     public void registerVirtualButtonAction(final Runnable runnable) {
    528         if (!mAccessibilityManager.isEnabled()) {
    529             this.setOnTouchListener(new OnTouchListener() {
    530                 @Override
    531                 public boolean onTouch(View v, MotionEvent event) {
    532                     if (mVirtualButtonEnabled) {
    533                         switch (event.getAction()) {
    534                             case MotionEvent.ACTION_DOWN:
    535                                 if (withinVirtualButtonBounds(event.getX(), event.getY())) {
    536                                     virtualButtonPressed(true);
    537                                     return true;
    538                                 } else {
    539                                     virtualButtonPressed(false);
    540                                     return false;
    541                                 }
    542                             case MotionEvent.ACTION_CANCEL:
    543                                 virtualButtonPressed(false);
    544                                 return true;
    545                             case MotionEvent.ACTION_OUTSIDE:
    546                                 virtualButtonPressed(false);
    547                                 return false;
    548                             case MotionEvent.ACTION_UP:
    549                                 virtualButtonPressed(false);
    550                                 if (withinVirtualButtonBounds(event.getX(), event.getY())) {
    551                                     runnable.run();
    552                                 }
    553                                 return true;
    554                         }
    555                     }
    556                     return false;
    557                 }
    558             });
    559         } else {
    560             this.setOnClickListener(new OnClickListener() {
    561                 @Override
    562                 public void onClick(View v) {
    563                     runnable.run();
    564                 }
    565             });
    566         }
    567     }
    568 
    569     @Override
    570     public void onDraw(Canvas canvas) {
    571         // Blink functionality.
    572         if (!mShowTimeStr && !mVirtualButtonPressedOn) {
    573             return;
    574         }
    575 
    576         int width = getWidth();
    577         if (mRemeasureText && width != 0) {
    578             setTotalTextWidth();
    579             width = getWidth();
    580             mRemeasureText = false;
    581         }
    582 
    583         int xCenter = width / 2;
    584         int yCenter = getHeight() / 2;
    585 
    586         float textXstart = xCenter - mTotalTextWidth / 2;
    587         float textYstart = yCenter + mTextHeight/2 - (mTextHeight * FONT_VERTICAL_OFFSET);
    588         // align the labels vertically to the top of the rest of the text
    589         float labelYStart = textYstart - (mTextHeight * (1 - 2 * FONT_VERTICAL_OFFSET))
    590                 + (1 - 2 * FONT_VERTICAL_OFFSET) * mPaintLabel.getTextSize();
    591 
    592         // Text color differs based on pressed state.
    593         int textColor;
    594         if (mVirtualButtonPressedOn) {
    595             textColor = mPressedColor;
    596             mStopStartTextView.setTextColor(mPressedColor);
    597         } else {
    598             textColor = mDefaultColor;
    599         }
    600         mPaintBig.setColor(textColor);
    601         mPaintBigThin.setColor(textColor);
    602         mPaintLabel.setColor(textColor);
    603         mPaintMed.setColor(textColor);
    604 
    605         if (mHours != null) {
    606             textXstart = mBigHours.draw(canvas, mHours, textXstart, textYstart, labelYStart);
    607         }
    608         if (mMinutes != null) {
    609             textXstart = mBigMinutes.draw(canvas, mMinutes, textXstart, textYstart, labelYStart);
    610         }
    611         if (mSeconds != null) {
    612             textXstart = mBigThinSeconds.draw(canvas, mSeconds,
    613                     textXstart, textYstart, labelYStart);
    614         }
    615         if (mHundredths != null) {
    616             textXstart = mMedHundredths.draw(canvas, mHundredths,
    617                     textXstart, textYstart, textYstart);
    618         }
    619     }
    620 
    621     public void registerStopTextView(TextView stopStartTextView) {
    622         mStopStartTextView = stopStartTextView;
    623     }
    624 }
    625