14243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson/*
24243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson * Copyright (C) 2008 The Android Open Source Project
34243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson *
44243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson * Licensed under the Apache License, Version 2.0 (the "License");
54243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson * you may not use this file except in compliance with the License.
64243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson * You may obtain a copy of the License at
74243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson *
84243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson *      http://www.apache.org/licenses/LICENSE-2.0
94243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson *
104243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson * Unless required by applicable law or agreed to in writing, software
114243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson * distributed under the License is distributed on an "AS IS" BASIS,
124243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson * See the License for the specific language governing permissions and
144243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson * limitations under the License.
154243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson */
164243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
174243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelsonpackage com.android.deskclock.timer;
184243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
194243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelsonimport android.content.Context;
204243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelsonimport android.content.res.Resources;
214243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelsonimport android.graphics.Canvas;
224243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelsonimport android.graphics.Paint;
234243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelsonimport android.graphics.Typeface;
244243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelsonimport android.util.AttributeSet;
25e40b31200dee36341e5697b2774799555ca79c9bSara Tingimport android.view.MotionEvent;
264243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelsonimport android.view.View;
27932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Tingimport android.view.accessibility.AccessibilityManager;
28386d8b05069c95899149c4b18542fc18c3761314Sam Blitzsteinimport android.widget.TextView;
294243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
30357497c341662c56945e22e458852f52a977efdfSam Blitzsteinimport com.android.deskclock.DeskClock;
314243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelsonimport com.android.deskclock.R;
320f6e3350fed144e3909ba4e45f3006f042c0187cSam Blitzsteinimport com.android.deskclock.Utils;
334243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
344243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
354243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelsonpublic class CountingTimerView extends View {
364243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private static final String TWO_DIGITS = "%02d";
374243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private static final String ONE_DIGIT = "%01d";
384243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private static final String NEG_TWO_DIGITS = "-%02d";
394243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private static final String NEG_ONE_DIGIT = "-%01d";
40c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee    private static final float TEXT_SIZE_TO_WIDTH_RATIO = 0.75f;
41c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee    // This is the ratio of the font typeface we need to offset the font by vertically to align it
42c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee    // vertically center.
43c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee    private static final float FONT_VERTICAL_OFFSET = 0.14f;
444243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
454243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private String mHours, mMinutes, mSeconds, mHunderdths;
464243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private final String mHoursLabel, mMinutesLabel, mSecondsLabel;
474243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private float mHoursWidth, mMinutesWidth, mSecondsWidth, mHundredthsWidth;
484243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private float mHoursLabelWidth, mMinutesLabelWidth, mSecondsLabelWidth, mHundredthsSepWidth;
494243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
504243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private boolean mShowTimeStr = true;
51014575aefd55845714d0a614213d432ee5d9474bItzhak Katzenelson    private final Typeface mAndroidClockMonoThin, mAndroidClockMonoBold, mRobotoLabel, mAndroidClockMonoLight;
524243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private final Paint mPaintBig = new Paint();
534243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private final Paint mPaintBigThin = new Paint();
544243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private final Paint mPaintMed = new Paint();
554243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private final Paint mPaintLabel = new Paint();
56c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee    private float mTextHeight = 0;
574243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private float mTotalTextWidth;
584243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private static final String HUNDREDTH_SEPERATOR = ".";
594243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private boolean mRemeasureText = true;
6022ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas
6122ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas    private int mDefaultColor;
62e40b31200dee36341e5697b2774799555ca79c9bSara Ting    private final int mPressedColor;
6322ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas    private final int mWhiteColor;
6422ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas    private final int mRedColor;
65386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein    private TextView mStopStartTextView;
66932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting    private final AccessibilityManager mAccessibilityManager;
67e40b31200dee36341e5697b2774799555ca79c9bSara Ting
68e40b31200dee36341e5697b2774799555ca79c9bSara Ting    // Fields for the text serving as a virtual button.
69e40b31200dee36341e5697b2774799555ca79c9bSara Ting    private boolean mVirtualButtonEnabled = false;
70e40b31200dee36341e5697b2774799555ca79c9bSara Ting    private boolean mVirtualButtonPressedOn = false;
714243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
724243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    Runnable mBlinkThread = new Runnable() {
73386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein        private boolean mVisible = true;
744243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        @Override
754243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        public void run() {
76386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein            mVisible = !mVisible;
77386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein            CountingTimerView.this.showTime(mVisible);
784243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            postDelayed(mBlinkThread, 500);
794243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
804243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
814243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    };
824243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
834243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
844243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    public CountingTimerView(Context context) {
854243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        this(context, null);
864243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    }
874243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
884243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    public CountingTimerView(Context context, AttributeSet attrs) {
894243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        super(context, attrs);
90014575aefd55845714d0a614213d432ee5d9474bItzhak Katzenelson        mAndroidClockMonoThin = Typeface.createFromAsset(context.getAssets(),"fonts/AndroidClockMono-Thin.ttf");
91014575aefd55845714d0a614213d432ee5d9474bItzhak Katzenelson        mAndroidClockMonoBold = Typeface.createFromAsset(context.getAssets(),"fonts/AndroidClockMono-Bold.ttf");
92014575aefd55845714d0a614213d432ee5d9474bItzhak Katzenelson        mAndroidClockMonoLight = Typeface.createFromAsset(context.getAssets(),"fonts/AndroidClockMono-Light.ttf");
93932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting        mAccessibilityManager =
94932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
954243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mRobotoLabel= Typeface.create("sans-serif-condensed", Typeface.BOLD);
964243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        Resources r = context.getResources();
974243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mHoursLabel = r.getString(R.string.hours_label).toUpperCase();
984243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mMinutesLabel = r.getString(R.string.minutes_label).toUpperCase();
994243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mSecondsLabel = r.getString(R.string.seconds_label).toUpperCase();
10022ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas        mWhiteColor = r.getColor(R.color.clock_white);
10122ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas        mDefaultColor = mWhiteColor;
1020f6e3350fed144e3909ba4e45f3006f042c0187cSam Blitzstein        mPressedColor = r.getColor(Utils.getPressedColorId());
10322ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas        mRedColor = r.getColor(R.color.clock_red);
1044243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
1054243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mPaintBig.setAntiAlias(true);
1064243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mPaintBig.setStyle(Paint.Style.STROKE);
1074243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mPaintBig.setTextAlign(Paint.Align.LEFT);
108014575aefd55845714d0a614213d432ee5d9474bItzhak Katzenelson        mPaintBig.setTypeface(mAndroidClockMonoBold);
109c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee        float bigFontSize = r.getDimension(R.dimen.big_font_size);
110c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee        mPaintBig.setTextSize(bigFontSize);
111c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee        mTextHeight = bigFontSize;
11222ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas
1134243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mPaintBigThin.setAntiAlias(true);
1144243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mPaintBigThin.setStyle(Paint.Style.STROKE);
1154243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mPaintBigThin.setTextAlign(Paint.Align.LEFT);
116014575aefd55845714d0a614213d432ee5d9474bItzhak Katzenelson        mPaintBigThin.setTypeface(mAndroidClockMonoThin);
1174243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mPaintBigThin.setTextSize(r.getDimension(R.dimen.big_font_size));
1184243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
1194243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mPaintMed.setAntiAlias(true);
1204243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mPaintMed.setStyle(Paint.Style.STROKE);
1214243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mPaintMed.setTextAlign(Paint.Align.LEFT);
122014575aefd55845714d0a614213d432ee5d9474bItzhak Katzenelson        mPaintMed.setTypeface(mAndroidClockMonoLight);
1234243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mPaintMed.setTextSize(r.getDimension(R.dimen.small_font_size));
1244243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
1254243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mPaintLabel.setAntiAlias(true);
1264243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mPaintLabel.setStyle(Paint.Style.STROKE);
1274243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mPaintLabel.setTextAlign(Paint.Align.LEFT);
1284243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mPaintLabel.setTypeface(mRobotoLabel);
1294243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mPaintLabel.setTextSize(r.getDimension(R.dimen.label_font_size));
1304243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
13122ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas        setTextColor(mDefaultColor);
13222ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas    }
13322ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas
13422ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas    protected void setTextColor(int textColor) {
13522ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas        mPaintBig.setColor(textColor);
13622ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas        mPaintBigThin.setColor(textColor);
13722ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas        mPaintMed.setColor(textColor);
13822ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas        mPaintLabel.setColor(textColor);
1394243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    }
1404243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
141644a5d887d7033e669ac362a8ed6e79197d13c72Itzhak Katzenelson    public void setTime(long time, boolean showHundredths, boolean update) {
142644a5d887d7033e669ac362a8ed6e79197d13c72Itzhak Katzenelson        boolean neg = false, showNeg = false;
1434243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        String format = null;
1444243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (time < 0) {
14531943258b338ee3a36d07ff57a6fb0f94bb3650fIsaac Katzenelson            time = -time;
146644a5d887d7033e669ac362a8ed6e79197d13c72Itzhak Katzenelson            neg = showNeg = true;
1474243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
1484243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        long hundreds, seconds, minutes, hours;
149113e1daddd8cb0e890084aa4b6ea3194d8ad4826Sam Blitzstein        seconds = time / 1000;
150113e1daddd8cb0e890084aa4b6ea3194d8ad4826Sam Blitzstein        hundreds = (time - seconds * 1000) / 10;
1514243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        minutes = seconds / 60;
1524243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        seconds = seconds - minutes * 60;
1534243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        hours = minutes / 60;
1544243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        minutes = minutes - hours * 60;
1554243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (hours > 99) {
1564243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            hours = 0;
1574243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
15831943258b338ee3a36d07ff57a6fb0f94bb3650fIsaac Katzenelson        // time may less than a second below zero, since we do not show fractions of seconds
15931943258b338ee3a36d07ff57a6fb0f94bb3650fIsaac Katzenelson        // when counting down, do not show the minus sign.
16031943258b338ee3a36d07ff57a6fb0f94bb3650fIsaac Katzenelson        if (hours ==0 && minutes == 0 && seconds == 0) {
161644a5d887d7033e669ac362a8ed6e79197d13c72Itzhak Katzenelson            showNeg = false;
16231943258b338ee3a36d07ff57a6fb0f94bb3650fIsaac Katzenelson        }
1634243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        // TODO: must build to account for localization
16420ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein        if (!showHundredths) {
16520ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein            if (!neg && hundreds != 0) {
16620ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein                seconds++;
16720ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein                if (seconds == 60) {
16820ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein                    seconds = 0;
16920ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein                    minutes++;
17020ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein                    if (minutes == 60) {
17120ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein                        minutes = 0;
17220ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein                        hours++;
17320ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein                    }
17420ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein                }
17520ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein            }
17620ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein            if (hundreds < 10 || hundreds > 90) {
17720ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein                update = true;
17820ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein            }
17920ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein        }
18020ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein
1814243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (hours >= 10) {
182644a5d887d7033e669ac362a8ed6e79197d13c72Itzhak Katzenelson            format = showNeg ? NEG_TWO_DIGITS : TWO_DIGITS;
1834243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mHours = String.format(format, hours);
1844243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        } else if (hours > 0) {
185644a5d887d7033e669ac362a8ed6e79197d13c72Itzhak Katzenelson            format = showNeg ? NEG_ONE_DIGIT : ONE_DIGIT;
1864243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mHours = String.format(format, hours);
1874243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        } else {
1884243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mHours = null;
1894243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
1904243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
1914243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (minutes >= 10 || hours > 0) {
192644a5d887d7033e669ac362a8ed6e79197d13c72Itzhak Katzenelson            format = (showNeg && hours == 0) ? NEG_TWO_DIGITS : TWO_DIGITS;
1934243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mMinutes = String.format(format, minutes);
1944243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        } else {
195644a5d887d7033e669ac362a8ed6e79197d13c72Itzhak Katzenelson            format = (showNeg && hours == 0) ? NEG_ONE_DIGIT : ONE_DIGIT;
1964243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mMinutes = String.format(format, minutes);
1974243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
1984243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
1994243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mSeconds = String.format(TWO_DIGITS, seconds);
2004243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (showHundredths) {
2014243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mHunderdths = String.format(TWO_DIGITS, hundreds);
2024243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        } else {
2034243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mHunderdths = null;
2044243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
2054243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mRemeasureText = true;
206644a5d887d7033e669ac362a8ed6e79197d13c72Itzhak Katzenelson
207644a5d887d7033e669ac362a8ed6e79197d13c72Itzhak Katzenelson        if (update) {
208932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            setContentDescription(getTimeStringForAccessibility((int) hours, (int) minutes,
209932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    (int) seconds, showNeg, getResources()));
210644a5d887d7033e669ac362a8ed6e79197d13c72Itzhak Katzenelson            invalidate();
211644a5d887d7033e669ac362a8ed6e79197d13c72Itzhak Katzenelson        }
2124243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    }
2134243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private void setTotalTextWidth() {
2144243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mTotalTextWidth = 0;
2154243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (mHours != null) {
2164243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mHoursWidth = mPaintBig.measureText(mHours);
2174243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mTotalTextWidth += mHoursWidth;
2184243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mHoursLabelWidth = mPaintLabel.measureText(mHoursLabel);
2194243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mTotalTextWidth += mHoursLabelWidth;
2204243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
2214243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (mMinutes != null) {
2224243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mMinutesWidth =  mPaintBig.measureText(mMinutes);
2234243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mTotalTextWidth += mMinutesWidth;
2244243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mMinutesLabelWidth = mPaintLabel.measureText(mMinutesLabel);
2254243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mTotalTextWidth += mMinutesLabelWidth;
2264243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
2274243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (mSeconds != null) {
2284243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mSecondsWidth = mPaintBigThin.measureText(mSeconds);
2294243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mTotalTextWidth += mSecondsWidth;
2304243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mSecondsLabelWidth = mPaintLabel.measureText(mSecondsLabel);
2314243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mTotalTextWidth += mSecondsLabelWidth;
2324243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
2334243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (mHunderdths != null) {
2344243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mHundredthsWidth = mPaintMed.measureText(mHunderdths);
2354243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mTotalTextWidth += mHundredthsWidth;
2364243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mHundredthsSepWidth = mPaintLabel.measureText(HUNDREDTH_SEPERATOR);
2374243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mTotalTextWidth += mHundredthsSepWidth;
2384243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
2394243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
2404243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        // This is a hack: if the text is too wide, reduce all the paint text sizes
241c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee        // To determine the maximum width, we find the minimum of the height and width (since the
242c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee        // circle we are trying to fit the text into has its radius sized to the smaller of the
243c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee        // two.
244c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee        int width = Math.min(getWidth(), getHeight());
2454243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (width != 0) {
2464243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            float ratio = mTotalTextWidth / width;
2474243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            if (ratio > TEXT_SIZE_TO_WIDTH_RATIO) {
2484243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson                float sizeRatio = (TEXT_SIZE_TO_WIDTH_RATIO / ratio);
2494243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson                mPaintBig.setTextSize( mPaintBig.getTextSize() * sizeRatio);
2504243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson                mPaintBigThin.setTextSize( mPaintBigThin.getTextSize() * sizeRatio);
2514243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson                mPaintMed.setTextSize( mPaintMed.getTextSize() * sizeRatio);
2524243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson                mTotalTextWidth *= sizeRatio;
2534243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson                mMinutesWidth *= sizeRatio;
2544243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson                mHoursWidth *= sizeRatio;
2554243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson                mSecondsWidth *= sizeRatio;
2564243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson                mHundredthsWidth *= sizeRatio;
2574243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson                mHundredthsSepWidth *= sizeRatio;
258c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee                //recalculate the new total text width and half text height
259c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee                mTotalTextWidth = mHoursWidth + mMinutesWidth + mSecondsWidth +
260c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee                        mHundredthsWidth + mHundredthsSepWidth + mHoursLabelWidth +
261c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee                        mMinutesLabelWidth + mSecondsLabelWidth;
262c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee                mTextHeight = mPaintBig.getTextSize();
2634243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            }
2644243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
2654243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    }
2664243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
2674243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    public void blinkTimeStr(boolean blink) {
2684243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (blink) {
2694243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            removeCallbacks(mBlinkThread);
2704243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            postDelayed(mBlinkThread, 1000);
2714243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        } else {
2724243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            removeCallbacks(mBlinkThread);
273386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein            showTime(true);
2744243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
2754243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    }
2764243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
277386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein    public void showTime(boolean visible) {
278386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein        mShowTimeStr = visible;
279386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein        invalidate();
280386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein        mRemeasureText = true;
281386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein    }
282386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein
28322ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas    public void redTimeStr(boolean red, boolean forceUpdate) {
28422ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas        mDefaultColor = red ? mRedColor : mWhiteColor;
28522ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas        setTextColor(mDefaultColor);
28622ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas        if (forceUpdate) {
28722ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas            invalidate();
28822ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas        }
28922ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas    }
29022ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas
2914243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    public String getTimeString() {
2924243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (mHours == null) {
2934243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            return String.format("%s:%s.%s",mMinutes, mSeconds,  mHunderdths);
2944243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
2954243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        return String.format("%s:%s:%s.%s",mHours, mMinutes, mSeconds,  mHunderdths);
2964243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    }
2974243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
298932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting    private static String getTimeStringForAccessibility(int hours, int minutes, int seconds,
299932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            boolean showNeg, Resources r) {
300932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting        StringBuilder s = new StringBuilder();
301932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting        if (showNeg) {
302932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            // This must be followed by a non-zero number or it will be audible as "hyphen"
303932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            // instead of "minus".
304932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            s.append("-");
305932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting        }
306932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting        if (showNeg && hours == 0 && minutes == 0) {
307932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            // Non-negative time will always have minutes, eg. "0 minutes 7 seconds", but negative
308932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            // time must start with non-zero digit, eg. -0m7s will be audible as just "-7 seconds"
309932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            s.append(String.format(
310932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    r.getQuantityText(R.plurals.Nseconds_description, seconds).toString(),
311932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    seconds));
312932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting        } else if (hours == 0) {
313932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            s.append(String.format(
314932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    r.getQuantityText(R.plurals.Nminutes_description, minutes).toString(),
315932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    minutes));
316932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            s.append(" ");
317932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            s.append(String.format(
318932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    r.getQuantityText(R.plurals.Nseconds_description, seconds).toString(),
319932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    seconds));
320932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting        } else {
321932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            s.append(String.format(
322932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    r.getQuantityText(R.plurals.Nhours_description, hours).toString(),
323932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    hours));
324932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            s.append(" ");
325932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            s.append(String.format(
326932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    r.getQuantityText(R.plurals.Nminutes_description, minutes).toString(),
327932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    minutes));
328932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            s.append(" ");
329932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            s.append(String.format(
330932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    r.getQuantityText(R.plurals.Nseconds_description, seconds).toString(),
331932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    seconds));
332932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting        }
333932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting        return s.toString();
334932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting    }
335932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting
336e40b31200dee36341e5697b2774799555ca79c9bSara Ting    public void setVirtualButtonEnabled(boolean enabled) {
337e40b31200dee36341e5697b2774799555ca79c9bSara Ting        mVirtualButtonEnabled = enabled;
338e40b31200dee36341e5697b2774799555ca79c9bSara Ting    }
339e40b31200dee36341e5697b2774799555ca79c9bSara Ting
340e40b31200dee36341e5697b2774799555ca79c9bSara Ting    private void virtualButtonPressed(boolean pressedOn) {
341e40b31200dee36341e5697b2774799555ca79c9bSara Ting        mVirtualButtonPressedOn = pressedOn;
342386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein        mStopStartTextView.setTextColor(pressedOn ? mPressedColor : mWhiteColor);
343e40b31200dee36341e5697b2774799555ca79c9bSara Ting        invalidate();
344e40b31200dee36341e5697b2774799555ca79c9bSara Ting    }
345e40b31200dee36341e5697b2774799555ca79c9bSara Ting
346e40b31200dee36341e5697b2774799555ca79c9bSara Ting    private boolean withinVirtualButtonBounds(float x, float y) {
347e40b31200dee36341e5697b2774799555ca79c9bSara Ting        int width = getWidth();
348e40b31200dee36341e5697b2774799555ca79c9bSara Ting        int height = getHeight();
349e40b31200dee36341e5697b2774799555ca79c9bSara Ting        float centerX = width / 2;
350e40b31200dee36341e5697b2774799555ca79c9bSara Ting        float centerY = height / 2;
351e40b31200dee36341e5697b2774799555ca79c9bSara Ting        float radius = Math.min(width, height) / 2;
352e40b31200dee36341e5697b2774799555ca79c9bSara Ting
353e40b31200dee36341e5697b2774799555ca79c9bSara Ting        // Within the circle button if distance to the center is less than the radius.
354e40b31200dee36341e5697b2774799555ca79c9bSara Ting        double distance = Math.sqrt(Math.pow(centerX - x, 2) + Math.pow(centerY - y, 2));
355e40b31200dee36341e5697b2774799555ca79c9bSara Ting        return distance < radius;
356e40b31200dee36341e5697b2774799555ca79c9bSara Ting    }
357e40b31200dee36341e5697b2774799555ca79c9bSara Ting
358e40b31200dee36341e5697b2774799555ca79c9bSara Ting    public void registerVirtualButtonAction(final Runnable runnable) {
359932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting        if (!mAccessibilityManager.isEnabled()) {
360932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            this.setOnTouchListener(new OnTouchListener() {
361932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                @Override
362932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                public boolean onTouch(View v, MotionEvent event) {
363932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    if (mVirtualButtonEnabled) {
364932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                        switch (event.getAction()) {
365932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                            case MotionEvent.ACTION_DOWN:
366932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                                if (withinVirtualButtonBounds(event.getX(), event.getY())) {
367932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                                    virtualButtonPressed(true);
368932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                                    return true;
369932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                                } else {
370932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                                    virtualButtonPressed(false);
371932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                                    return false;
372357497c341662c56945e22e458852f52a977efdfSam Blitzstein                                }
373932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                            case MotionEvent.ACTION_CANCEL:
374932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                                virtualButtonPressed(false);
375e40b31200dee36341e5697b2774799555ca79c9bSara Ting                                return true;
376932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                            case MotionEvent.ACTION_OUTSIDE:
377e40b31200dee36341e5697b2774799555ca79c9bSara Ting                                virtualButtonPressed(false);
378e40b31200dee36341e5697b2774799555ca79c9bSara Ting                                return false;
379932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                            case MotionEvent.ACTION_UP:
380932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                                virtualButtonPressed(false);
381932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                                if (withinVirtualButtonBounds(event.getX(), event.getY())) {
382932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                                    runnable.run();
383357497c341662c56945e22e458852f52a977efdfSam Blitzstein                                }
384932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                                return true;
385932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                        }
386e40b31200dee36341e5697b2774799555ca79c9bSara Ting                    }
387932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    return false;
388e40b31200dee36341e5697b2774799555ca79c9bSara Ting                }
389932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            });
390932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting        } else {
391932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            this.setOnClickListener(new OnClickListener() {
392932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                @Override
393932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                public void onClick(View v) {
394932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    runnable.run();
395932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                }
396932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            });
397932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting        }
398e40b31200dee36341e5697b2774799555ca79c9bSara Ting    }
399e40b31200dee36341e5697b2774799555ca79c9bSara Ting
4004243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    @Override
4014243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    public void onDraw(Canvas canvas) {
402e40b31200dee36341e5697b2774799555ca79c9bSara Ting        // Blink functionality.
403e40b31200dee36341e5697b2774799555ca79c9bSara Ting        if (!mShowTimeStr && !mVirtualButtonPressedOn) {
404e40b31200dee36341e5697b2774799555ca79c9bSara Ting            return;
405e40b31200dee36341e5697b2774799555ca79c9bSara Ting        }
406e40b31200dee36341e5697b2774799555ca79c9bSara Ting
4074243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        int width = getWidth();
4084243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (mRemeasureText && width != 0) {
4094243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            setTotalTextWidth();
410c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee            width = getWidth();
4114243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mRemeasureText = false;
4124243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
4134243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
4144243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        int xCenter = width / 2;
4154243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        int yCenter = getHeight() / 2;
4164243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
4174243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        float textXstart = xCenter - mTotalTextWidth / 2;
418c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee        float textYstart = yCenter + mTextHeight/2 - (mTextHeight * FONT_VERTICAL_OFFSET);
419c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee        // align the labels vertically to the top of the rest of the text
420c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee        float labelYStart = textYstart - (mTextHeight * (1 - 2 * FONT_VERTICAL_OFFSET))
421c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee                + (1 - 2 * FONT_VERTICAL_OFFSET) * mPaintLabel.getTextSize();
422e40b31200dee36341e5697b2774799555ca79c9bSara Ting
423e40b31200dee36341e5697b2774799555ca79c9bSara Ting        // Text color differs based on pressed state.
424e40b31200dee36341e5697b2774799555ca79c9bSara Ting        int textColor;
425e40b31200dee36341e5697b2774799555ca79c9bSara Ting        if (mVirtualButtonPressedOn) {
426e40b31200dee36341e5697b2774799555ca79c9bSara Ting            textColor = mPressedColor;
427386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein            mStopStartTextView.setTextColor(mPressedColor);
428e40b31200dee36341e5697b2774799555ca79c9bSara Ting        } else {
429e40b31200dee36341e5697b2774799555ca79c9bSara Ting            textColor = mDefaultColor;
430e40b31200dee36341e5697b2774799555ca79c9bSara Ting        }
431e40b31200dee36341e5697b2774799555ca79c9bSara Ting        mPaintBig.setColor(textColor);
432e40b31200dee36341e5697b2774799555ca79c9bSara Ting        mPaintBigThin.setColor(textColor);
433e40b31200dee36341e5697b2774799555ca79c9bSara Ting        mPaintLabel.setColor(textColor);
434e40b31200dee36341e5697b2774799555ca79c9bSara Ting        mPaintMed.setColor(textColor);
435e40b31200dee36341e5697b2774799555ca79c9bSara Ting
4364243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (mHours != null) {
4374243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            canvas.drawText(mHours, textXstart, textYstart, mPaintBig);
4384243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            textXstart += mHoursWidth;
439c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee            canvas.drawText(mHoursLabel, textXstart, labelYStart, mPaintLabel);
4404243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            textXstart += mHoursLabelWidth;
4414243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
4424243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (mMinutes != null) {
4434243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            canvas.drawText(mMinutes, textXstart, textYstart, mPaintBig);
4444243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            textXstart += mMinutesWidth;
445c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee            canvas.drawText(mMinutesLabel, textXstart, labelYStart, mPaintLabel);
4464243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            textXstart += mMinutesLabelWidth;
4474243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
4484243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (mSeconds != null) {
4494243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            canvas.drawText(mSeconds, textXstart, textYstart, mPaintBigThin);
4504243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            textXstart += mSecondsWidth;
451c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee            canvas.drawText(mSecondsLabel, textXstart, labelYStart, mPaintLabel);
4524243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            textXstart += mSecondsLabelWidth;
4534243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
4544243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (mHunderdths != null) {
4554243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            canvas.drawText(HUNDREDTH_SEPERATOR, textXstart, textYstart, mPaintLabel);
4564243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            textXstart += mHundredthsSepWidth;
4574243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            canvas.drawText(mHunderdths, textXstart, textYstart, mPaintMed);
4584243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
459386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein    }
460c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee
461386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein    public void registerStopTextView(TextView stopStartTextView) {
462386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein        mStopStartTextView = stopStartTextView;
4634243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    }
4644243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson}
465