1fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson/*
293e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas * Copyright (C) 2012 The Android Open Source Project
3fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson *
4fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson * Licensed under the Apache License, Version 2.0 (the "License");
5fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson * you may not use this file except in compliance with the License.
6fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson * You may obtain a copy of the License at
7fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson *
8fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson *      http://www.apache.org/licenses/LICENSE-2.0
9fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson *
10fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson * Unless required by applicable law or agreed to in writing, software
11fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson * distributed under the License is distributed on an "AS IS" BASIS,
12fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson * See the License for the specific language governing permissions and
14fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson * limitations under the License.
15fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson */
16fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson
17fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelsonpackage com.android.deskclock.timer;
18fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson
19fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelsonimport android.content.Context;
20fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelsonimport android.content.res.Resources;
21fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelsonimport android.graphics.Canvas;
227b5c5b16646932fbe991c86ffd9404cdbdc0f84fJustin Klaassenimport android.graphics.Color;
23fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelsonimport android.graphics.Paint;
24fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelsonimport android.graphics.Typeface;
2593e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultasimport android.text.TextUtils;
26fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelsonimport android.util.AttributeSet;
274261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Tingimport android.view.MotionEvent;
28fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelsonimport android.view.View;
291c4ab87072b0fb21e05ed221acd28560d6110b12Sara Tingimport android.view.accessibility.AccessibilityManager;
30fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson
31cdbb71b08c13c84af850f4036febc0b90dcfcc7dJustin Klaassenimport com.android.deskclock.LogUtils;
32fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelsonimport com.android.deskclock.R;
3372332c2290d17c344ece074726f72b35ea469f31Sam Blitzsteinimport com.android.deskclock.Utils;
34fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson
356a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas/**
366a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas * Class to measure and draw the time in the {@link com.android.deskclock.CircleTimerView}.
376a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas * This class manages and sums the work of the four members mBigHours, mBigMinutes,
386a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas * mBigSeconds and mMedHundredths. Those members are each tasked with measuring, sizing and
396a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas * drawing digits (and optional label) of the time set in {@link #setTime(long, boolean, boolean)}
406a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas */
41fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelsonpublic class CountingTimerView extends View {
42fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson    private static final String TWO_DIGITS = "%02d";
43fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson    private static final String ONE_DIGIT = "%01d";
44fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson    private static final String NEG_TWO_DIGITS = "-%02d";
45fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson    private static final String NEG_ONE_DIGIT = "-%01d";
46efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas    private static final float TEXT_SIZE_TO_WIDTH_RATIO = 0.85f;
476a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas    // This is the ratio of the font height needed to vertically offset the font for alignment
486a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas    // from the center.
49d049a230efed6c04cf7d2ab8cc176d9f4f56e51aYorke Lee    private static final float FONT_VERTICAL_OFFSET = 0.14f;
50d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas    // Ratio of the space trailing the Hours and Minutes
51efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas    private static final float HOURS_MINUTES_SPACING = 0.4f;
52d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas    // Ratio of the space leading the Hundredths
53d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas    private static final float HUNDREDTHS_SPACING = 0.5f;
54efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas    // Radial offset of the enclosing circle
55efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas    private final float mRadiusOffset;
56fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson
5793e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas    private String mHours, mMinutes, mSeconds, mHundredths;
58fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson
59fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson    private boolean mShowTimeStr = true;
60fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson    private final Paint mPaintBigThin = new Paint();
61fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson    private final Paint mPaintMed = new Paint();
6293e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas    private final float mBigFontSize, mSmallFontSize;
636a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas    // Hours and minutes are signed for when a timer goes past the set time and thus negative
649cddb91746c17851627e874f5824b6f3cb1bfeffIsaac Katzenelson    private final SignedTime mBigHours, mBigMinutes;
656a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas    // Seconds are always shown with minutes, so are never signed
666a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas    private final UnsignedTime mBigSeconds;
679cddb91746c17851627e874f5824b6f3cb1bfeffIsaac Katzenelson    private final Hundredths mMedHundredths;
68d049a230efed6c04cf7d2ab8cc176d9f4f56e51aYorke Lee    private float mTextHeight = 0;
69fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson    private float mTotalTextWidth;
70fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson    private boolean mRemeasureText = true;
711a1b54d6fe0c9e4f478a9e96f3cb6b49f72720edRobyn Coultas
721a1b54d6fe0c9e4f478a9e96f3cb6b49f72720edRobyn Coultas    private int mDefaultColor;
734261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting    private final int mPressedColor;
741a1b54d6fe0c9e4f478a9e96f3cb6b49f72720edRobyn Coultas    private final int mWhiteColor;
7569a4b4f2810100f19e997b4e539f1b0e56b0afb5rachelzhang    private final int mAccentColor;
761c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting    private final AccessibilityManager mAccessibilityManager;
774261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting
784261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting    // Fields for the text serving as a virtual button.
794261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting    private boolean mVirtualButtonEnabled = false;
804261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting    private boolean mVirtualButtonPressedOn = false;
81fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson
82fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson    Runnable mBlinkThread = new Runnable() {
8300cb5981c984bc13aa2f6af510a9dd9a784f2af0Sam Blitzstein        private boolean mVisible = true;
84fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        @Override
85fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        public void run() {
8600cb5981c984bc13aa2f6af510a9dd9a784f2af0Sam Blitzstein            mVisible = !mVisible;
8700cb5981c984bc13aa2f6af510a9dd9a784f2af0Sam Blitzstein            CountingTimerView.this.showTime(mVisible);
88fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson            postDelayed(mBlinkThread, 500);
89fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        }
90fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson    };
91fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson
926a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas    /**
936a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas     * Class to measure and draw the digit pairs of hours, minutes, seconds or hundredths. Digits
946a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas     * may have an optional label. for hours, minutes and seconds, this label trails the digits
956a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas     * and for seconds, precedes the digits.
966a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas     */
97d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas    static class UnsignedTime {
9893e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        protected Paint mPaint;
9993e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        protected float mEm;
10093e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        protected float mWidth = 0;
1019cddb91746c17851627e874f5824b6f3cb1bfeffIsaac Katzenelson        private final String mWidest;
102d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas        protected final float mSpacingRatio;
10393e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        private float mLabelWidth = 0;
10493e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas
105d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas        public UnsignedTime(Paint paint, float spacingRatio, String allDigits) {
10693e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            mPaint = paint;
107d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas            mSpacingRatio = spacingRatio;
10893e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas
10993e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            if (TextUtils.isEmpty(allDigits)) {
110cdbb71b08c13c84af850f4036febc0b90dcfcc7dJustin Klaassen                LogUtils.wtf("Locale digits missing - using English");
11193e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                allDigits = "0123456789";
11293e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            }
11393e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas
11493e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            float widths[] = new float[allDigits.length()];
11593e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            int ll = mPaint.getTextWidths(allDigits, widths);
11693e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            int largest = 0;
11793e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            for (int ii = 1; ii < ll; ii++) {
11893e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                if (widths[ii] > widths[largest]) {
11993e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                    largest = ii;
12093e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                }
12193e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            }
12293e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas
12393e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            mEm = widths[largest];
12493e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            mWidest = allDigits.substring(largest, largest + 1);
12593e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        }
12693e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas
127d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas        public UnsignedTime(UnsignedTime unsignedTime, float spacingRatio) {
12893e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            this.mPaint = unsignedTime.mPaint;
12993e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            this.mEm = unsignedTime.mEm;
13093e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            this.mWidth = unsignedTime.mWidth;
13193e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            this.mWidest = unsignedTime.mWidest;
132d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas            this.mSpacingRatio = spacingRatio;
13393e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        }
13493e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas
13593e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        protected void updateWidth(final String time) {
13693e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            mEm = mPaint.measureText(mWidest);
137d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas            mLabelWidth = mSpacingRatio * mEm;
13893e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            mWidth = time.length() * mEm;
13993e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        }
14093e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas
14193e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        protected void resetWidth() {
14293e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            mWidth = mLabelWidth = 0;
14393e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        }
14493e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas
14593e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        public float calcTotalWidth(final String time) {
14693e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            if (time != null) {
14793e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                updateWidth(time);
14893e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                return mWidth + mLabelWidth;
14993e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            } else {
15093e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                resetWidth();
15193e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                return 0;
15293e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            }
15393e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        }
15493e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas
15593e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        public float getLabelWidth() {
15693e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            return mLabelWidth;
15793e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        }
15893e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas
159d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas        /**
160d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas         * Draws each character with a fixed spacing from time starting at ii.
161d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas         * @param canvas the canvas on which the time segment will be drawn
162d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas         * @param time time segment
163d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas         * @param ii what character to start the draw
164d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas         * @param x offset
165d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas         * @param y offset
166d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas         * @return X location for the next segment
167d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas         */
16893e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        protected float drawTime(Canvas canvas, final String time, int ii, float x, float y) {
16993e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            float textEm  = mEm / 2f;
17093e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            while (ii < time.length()) {
17193e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                x += textEm;
17293e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                canvas.drawText(time.substring(ii, ii + 1), x, y, mPaint);
17393e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                x += textEm;
17493e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                ii++;
17593e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            }
17693e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            return x;
17793e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        }
17893e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas
179d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas        /**
180d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas         * Draw this time segment and append the intra-segment spacing to the x
181d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas         * @param canvas the canvas on which the time segment will be drawn
182d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas         * @param time time segment
183d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas         * @param x offset
184d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas         * @param y offset
185d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas         * @return X location for the next segment
186d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas         */
187d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas        public float draw(Canvas canvas, final String time, float x, float y) {
188d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas            return drawTime(canvas, time, 0, x, y) + getLabelWidth();
18993e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        }
19093e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas    }
19193e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas
1926a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas    /**
1936a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas     * Special derivation to handle the hundredths painting with the label in front.
1946a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas     */
195d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas    static class Hundredths extends UnsignedTime {
196d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas        public Hundredths(Paint paint, float spacingRatio, final String allDigits) {
197d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas            super(paint, spacingRatio, allDigits);
19893e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        }
19993e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas
200d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas        /**
201d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas         * Draw this time segment after prepending the intra-segment spacing to the x location.
202d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas         * {@link UnsignedTime#draw(android.graphics.Canvas, String, float, float)}
203d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas         */
20493e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        @Override
205d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas        public float draw(Canvas canvas, final String time, float x, float y) {
20693e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            return drawTime(canvas, time, 0, x + getLabelWidth(), y);
20793e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        }
20893e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas    }
20993e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas
2106a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas    /**
2116a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas     * Special derivation to handle a negative number
2126a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas     */
213d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas    static class SignedTime extends UnsignedTime {
21493e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        private float mMinusWidth = 0;
21593e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas
216d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas        public SignedTime (UnsignedTime unsignedTime, float spacingRatio) {
217d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas            super(unsignedTime, spacingRatio);
21893e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        }
21993e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas
22093e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        @Override
22193e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        protected void updateWidth(final String time) {
22293e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            super.updateWidth(time);
22393e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            if (time.contains("-")) {
22493e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                mMinusWidth = mPaint.measureText("-");
22593e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                mWidth += (mMinusWidth - mEm);
22693e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            } else {
22793e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                mMinusWidth = 0;
22893e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            }
22993e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        }
23093e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas
23193e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        @Override
23293e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        protected void resetWidth() {
23393e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            super.resetWidth();
23493e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            mMinusWidth = 0;
23593e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        }
23693e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas
237d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas        /**
238d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas         * Draws each character with a fixed spacing from time, handling the special negative
239d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas         * number case.
240d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas         * {@link UnsignedTime#draw(android.graphics.Canvas, String, float, float)}
241d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas         */
24293e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        @Override
243d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas        public float draw(Canvas canvas, final String time, float x, float y) {
24493e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            int ii = 0;
24593e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            if (mMinusWidth != 0f) {
24693e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                float minusWidth = mMinusWidth / 2;
24793e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                x += minusWidth;
248d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas                //TODO:hyphen is too thick when painted
2496a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas                canvas.drawText(time.substring(0, 1), x, y, mPaint);
25093e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                x += minusWidth;
25193e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                ii++;
25293e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            }
253d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas            return drawTime(canvas, time, ii, x, y) + getLabelWidth();
25493e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        }
25593e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas    }
256fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson
2576a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas    @SuppressWarnings("unused")
258fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson    public CountingTimerView(Context context) {
259fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        this(context, null);
260fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson    }
261fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson
262fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson    public CountingTimerView(Context context, AttributeSet attrs) {
263fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        super(context, attrs);
2641c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting        mAccessibilityManager =
2651c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
266fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        Resources r = context.getResources();
2677b5c5b16646932fbe991c86ffd9404cdbdc0f84fJustin Klaassen        mDefaultColor = mWhiteColor = r.getColor(R.color.clock_white);
2687b5c5b16646932fbe991c86ffd9404cdbdc0f84fJustin Klaassen        mPressedColor = mAccentColor = Utils.obtainStyledColor(
2697b5c5b16646932fbe991c86ffd9404cdbdc0f84fJustin Klaassen                context, R.attr.colorAccent, Color.RED);
27093e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        mBigFontSize = r.getDimension(R.dimen.big_font_size);
27193e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        mSmallFontSize = r.getDimension(R.dimen.small_font_size);
2721a1b54d6fe0c9e4f478a9e96f3cb6b49f72720edRobyn Coultas
2736a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas        Typeface androidClockMonoThin = Typeface.
2746a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas                createFromAsset(context.getAssets(), "fonts/AndroidClockMono-Thin.ttf");
275fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        mPaintBigThin.setAntiAlias(true);
276fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        mPaintBigThin.setStyle(Paint.Style.STROKE);
27793e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        mPaintBigThin.setTextAlign(Paint.Align.CENTER);
2786a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas        mPaintBigThin.setTypeface(androidClockMonoThin);
279fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson
2806a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas        Typeface androidClockMonoLight = Typeface.
2816a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas                createFromAsset(context.getAssets(), "fonts/AndroidClockMono-Light.ttf");
282fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        mPaintMed.setAntiAlias(true);
283fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        mPaintMed.setStyle(Paint.Style.STROKE);
28493e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        mPaintMed.setTextAlign(Paint.Align.CENTER);
2856a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas        mPaintMed.setTypeface(androidClockMonoLight);
286fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson
28793e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        resetTextSize();
2881a1b54d6fe0c9e4f478a9e96f3cb6b49f72720edRobyn Coultas        setTextColor(mDefaultColor);
28993e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas
29093e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        // allDigits will contain ten digits: "0123456789" in the default locale
29193e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        final String allDigits = String.format("%010d", 123456789);
292d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas        mBigSeconds = new UnsignedTime(mPaintBigThin, 0.f, allDigits);
293d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas        mBigHours = new SignedTime(mBigSeconds, HOURS_MINUTES_SPACING);
294d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas        mBigMinutes = new SignedTime(mBigSeconds, HOURS_MINUTES_SPACING);
295d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas        mMedHundredths = new Hundredths(mPaintMed, HUNDREDTHS_SPACING, allDigits);
296efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas
297efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas        mRadiusOffset = Utils.calculateRadiusOffset(r);
29893e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas    }
29993e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas
30093e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas    protected void resetTextSize() {
30193e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        mTextHeight = mBigFontSize;
30293e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        mPaintBigThin.setTextSize(mBigFontSize);
30393e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        mPaintMed.setTextSize(mSmallFontSize);
3041a1b54d6fe0c9e4f478a9e96f3cb6b49f72720edRobyn Coultas    }
3051a1b54d6fe0c9e4f478a9e96f3cb6b49f72720edRobyn Coultas
3061a1b54d6fe0c9e4f478a9e96f3cb6b49f72720edRobyn Coultas    protected void setTextColor(int textColor) {
3071a1b54d6fe0c9e4f478a9e96f3cb6b49f72720edRobyn Coultas        mPaintBigThin.setColor(textColor);
3081a1b54d6fe0c9e4f478a9e96f3cb6b49f72720edRobyn Coultas        mPaintMed.setColor(textColor);
309fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson    }
310fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson
3116a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas    /**
3126a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas     * Update the time to display. Separates that time into the hours, minutes, seconds and
3136a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas     * hundredths. If update is true, the view is invalidated so that it will draw again.
3146a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas     *
3156a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas     * @param time new time to display - in milliseconds
3166a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas     * @param showHundredths flag to show hundredths resolution
3176a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas     * @param update to invalidate the view - otherwise the time is examined to see if it is within
3186a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas     *               100 milliseconds of zero seconds and when so, invalidate the view.
3196a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas     */
320d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas    // TODO:showHundredths S/B attribute or setter - i.e. unchanging over object life
32126fd3d012330e5abf4f6452e811938374004d0d8Sam Blitzstein    public void setTime(long time, boolean showHundredths, boolean update) {
3226a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas        int oldLength = getDigitsLength();
32326fd3d012330e5abf4f6452e811938374004d0d8Sam Blitzstein        boolean neg = false, showNeg = false;
3246a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas        String format;
325fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        if (time < 0) {
3260cb4a8571f6473c422cb69e469aa12f17458ff0dIsaac Katzenelson            time = -time;
32726fd3d012330e5abf4f6452e811938374004d0d8Sam Blitzstein            neg = showNeg = true;
328fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        }
329fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        long hundreds, seconds, minutes, hours;
330287f2d82aaf91d881f5de50a133d21684a8d4821Sam Blitzstein        seconds = time / 1000;
331287f2d82aaf91d881f5de50a133d21684a8d4821Sam Blitzstein        hundreds = (time - seconds * 1000) / 10;
332fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        minutes = seconds / 60;
333fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        seconds = seconds - minutes * 60;
334fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        hours = minutes / 60;
335fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        minutes = minutes - hours * 60;
33687bf27c22cf71bfb89e97eba92d4513148d3d8c8Robyn Coultas        if (hours > 999) {
337fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson            hours = 0;
338fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        }
3396a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas        // The time  can be between 0 and -1 seconds, but the "truncated" equivalent time of hours
3406a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas        // and minutes and seconds could be zero, so since we do not show fractions of seconds
3410cb4a8571f6473c422cb69e469aa12f17458ff0dIsaac Katzenelson        // when counting down, do not show the minus sign.
342d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas        // TODO:does it matter that we do not look at showHundredths?
3436a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas        if (hours == 0 && minutes == 0 && seconds == 0) {
34426fd3d012330e5abf4f6452e811938374004d0d8Sam Blitzstein            showNeg = false;
3450cb4a8571f6473c422cb69e469aa12f17458ff0dIsaac Katzenelson        }
34693e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas
3476a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas        // Normalize and check if it is 'time' to invalidate
3488daab6be9a079de0bf2c0f26601a753ca9a71617Sam Blitzstein        if (!showHundredths) {
3498daab6be9a079de0bf2c0f26601a753ca9a71617Sam Blitzstein            if (!neg && hundreds != 0) {
3508daab6be9a079de0bf2c0f26601a753ca9a71617Sam Blitzstein                seconds++;
3518daab6be9a079de0bf2c0f26601a753ca9a71617Sam Blitzstein                if (seconds == 60) {
3528daab6be9a079de0bf2c0f26601a753ca9a71617Sam Blitzstein                    seconds = 0;
3538daab6be9a079de0bf2c0f26601a753ca9a71617Sam Blitzstein                    minutes++;
3548daab6be9a079de0bf2c0f26601a753ca9a71617Sam Blitzstein                    if (minutes == 60) {
3558daab6be9a079de0bf2c0f26601a753ca9a71617Sam Blitzstein                        minutes = 0;
3568daab6be9a079de0bf2c0f26601a753ca9a71617Sam Blitzstein                        hours++;
3578daab6be9a079de0bf2c0f26601a753ca9a71617Sam Blitzstein                    }
3588daab6be9a079de0bf2c0f26601a753ca9a71617Sam Blitzstein                }
3598daab6be9a079de0bf2c0f26601a753ca9a71617Sam Blitzstein            }
3608daab6be9a079de0bf2c0f26601a753ca9a71617Sam Blitzstein            if (hundreds < 10 || hundreds > 90) {
3618daab6be9a079de0bf2c0f26601a753ca9a71617Sam Blitzstein                update = true;
3628daab6be9a079de0bf2c0f26601a753ca9a71617Sam Blitzstein            }
3638daab6be9a079de0bf2c0f26601a753ca9a71617Sam Blitzstein        }
3648daab6be9a079de0bf2c0f26601a753ca9a71617Sam Blitzstein
3656a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas        // Hours may be empty
366fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        if (hours >= 10) {
36726fd3d012330e5abf4f6452e811938374004d0d8Sam Blitzstein            format = showNeg ? NEG_TWO_DIGITS : TWO_DIGITS;
368fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson            mHours = String.format(format, hours);
369fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        } else if (hours > 0) {
37026fd3d012330e5abf4f6452e811938374004d0d8Sam Blitzstein            format = showNeg ? NEG_ONE_DIGIT : ONE_DIGIT;
371fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson            mHours = String.format(format, hours);
372fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        } else {
373fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson            mHours = null;
374fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        }
375fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson
3766a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas        // Minutes are never empty and when hours are non-empty, must be two digits
377fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        if (minutes >= 10 || hours > 0) {
37826fd3d012330e5abf4f6452e811938374004d0d8Sam Blitzstein            format = (showNeg && hours == 0) ? NEG_TWO_DIGITS : TWO_DIGITS;
379fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson            mMinutes = String.format(format, minutes);
380fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        } else {
38126fd3d012330e5abf4f6452e811938374004d0d8Sam Blitzstein            format = (showNeg && hours == 0) ? NEG_ONE_DIGIT : ONE_DIGIT;
382fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson            mMinutes = String.format(format, minutes);
383fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        }
384fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson
3856a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas        // Seconds are always two digits
386fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        mSeconds = String.format(TWO_DIGITS, seconds);
3876a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas
3886a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas        // Hundredths are optional and then two digits
389fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        if (showHundredths) {
39093e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            mHundredths = String.format(TWO_DIGITS, hundreds);
391fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        } else {
39293e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            mHundredths = null;
39393e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        }
39493e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas
39593e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        int newLength = getDigitsLength();
39693e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        if (oldLength != newLength) {
39793e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            if (oldLength > newLength) {
39893e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                resetTextSize();
39993e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            }
40093e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas            mRemeasureText = true;
401fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        }
40226fd3d012330e5abf4f6452e811938374004d0d8Sam Blitzstein
40326fd3d012330e5abf4f6452e811938374004d0d8Sam Blitzstein        if (update) {
4041c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting            setContentDescription(getTimeStringForAccessibility((int) hours, (int) minutes,
4051c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                    (int) seconds, showNeg, getResources()));
4065f594531901d5f562c0c6a8e1d4ffd2e57709764Daria Evdokimova            postInvalidateOnAnimation();
40726fd3d012330e5abf4f6452e811938374004d0d8Sam Blitzstein        }
408fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson    }
409fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson
41093e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas    private int getDigitsLength() {
41193e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        return ((mHours == null) ? 0 : mHours.length())
41293e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                + ((mMinutes == null) ? 0 : mMinutes.length())
41393e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                + ((mSeconds == null) ? 0 : mSeconds.length())
41493e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                + ((mHundredths == null) ? 0 : mHundredths.length());
41593e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas    }
41693e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas
41793e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas    private void calcTotalTextWidth() {
41893e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        mTotalTextWidth = mBigHours.calcTotalWidth(mHours) + mBigMinutes.calcTotalWidth(mMinutes)
4196a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas                + mBigSeconds.calcTotalWidth(mSeconds)
42093e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                + mMedHundredths.calcTotalWidth(mHundredths);
42193e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas    }
42293e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas
423efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas    /**
424efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas     * Adjust the size of the fonts to fit within the the circle and painted object in
425efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas     * {@link com.android.deskclock.CircleTimerView#onDraw(android.graphics.Canvas)}
426efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas     */
42793e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas    private void setTotalTextWidth() {
42893e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        calcTotalTextWidth();
429d049a230efed6c04cf7d2ab8cc176d9f4f56e51aYorke Lee        // To determine the maximum width, we find the minimum of the height and width (since the
430d049a230efed6c04cf7d2ab8cc176d9f4f56e51aYorke Lee        // circle we are trying to fit the text into has its radius sized to the smaller of the
431d049a230efed6c04cf7d2ab8cc176d9f4f56e51aYorke Lee        // two.
432d049a230efed6c04cf7d2ab8cc176d9f4f56e51aYorke Lee        int width = Math.min(getWidth(), getHeight());
433fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        if (width != 0) {
434efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas            // Shrink 'width' to account for circle stroke and other painted objects.
435efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas            // Note on the "4 *": (1) To reduce divisions, using the diameter instead of the radius.
436efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas            // (2) The radius of the enclosing circle is reduced by mRadiusOffset and the
437efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas            // text needs to fit within a circle further reduced by mRadiusOffset.
438efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas            width -= (int) (4 * mRadiusOffset + 0.5f);
439efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas
440efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas            final float wantDiameter2 = TEXT_SIZE_TO_WIDTH_RATIO * width * width;
441efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas            float totalDiameter2 = getHypotenuseSquared();
442efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas
443efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas            // If the hypotenuse of the bounding box is too large, reduce all the paint text sizes
444efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas            while (totalDiameter2 > wantDiameter2) {
445efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas                // Convergence is slightly difficult due to quantization in the mTotalTextWidth
446efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas                // calculation. Reducing the ratio by 1% converges more quickly without excessive
447efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas                // loss of quality.
448efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas                float sizeRatio = 0.99f * (float) Math.sqrt(wantDiameter2/totalDiameter2);
44993e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                mPaintBigThin.setTextSize(mPaintBigThin.getTextSize() * sizeRatio);
45093e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                mPaintMed.setTextSize(mPaintMed.getTextSize() * sizeRatio);
4516a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas                // Recalculate the new total text height and half-width
4526a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas                mTextHeight = mPaintBigThin.getTextSize();
45393e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas                calcTotalTextWidth();
454efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas                totalDiameter2 = getHypotenuseSquared();
455fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson            }
456fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        }
457fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson    }
458fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson
459efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas    /**
460efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas     * Calculate the square of the diameter to use in {@link CountingTimerView#setTotalTextWidth()}
461efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas     */
462efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas    private float getHypotenuseSquared() {
463efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas        return mTotalTextWidth * mTotalTextWidth + mTextHeight * mTextHeight;
464efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas    }
465efc4ad3bce5f68e6a64c4aeac4f2359ab9bae5a8Robyn Coultas
466fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson    public void blinkTimeStr(boolean blink) {
467fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        if (blink) {
468fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson            removeCallbacks(mBlinkThread);
4699cddb91746c17851627e874f5824b6f3cb1bfeffIsaac Katzenelson            post(mBlinkThread);
470fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        } else {
471fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson            removeCallbacks(mBlinkThread);
47200cb5981c984bc13aa2f6af510a9dd9a784f2af0Sam Blitzstein            showTime(true);
473fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        }
474fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson    }
475fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson
47600cb5981c984bc13aa2f6af510a9dd9a784f2af0Sam Blitzstein    public void showTime(boolean visible) {
47700cb5981c984bc13aa2f6af510a9dd9a784f2af0Sam Blitzstein        mShowTimeStr = visible;
47800cb5981c984bc13aa2f6af510a9dd9a784f2af0Sam Blitzstein        invalidate();
47900cb5981c984bc13aa2f6af510a9dd9a784f2af0Sam Blitzstein    }
48000cb5981c984bc13aa2f6af510a9dd9a784f2af0Sam Blitzstein
48180eafdf26fc496ffa112b9eb1b44d7b80333c80arachelzhang    public void setTimeStrTextColor(boolean active, boolean forceUpdate) {
48269a4b4f2810100f19e997b4e539f1b0e56b0afb5rachelzhang        mDefaultColor = active ? mAccentColor : mWhiteColor;
4831a1b54d6fe0c9e4f478a9e96f3cb6b49f72720edRobyn Coultas        setTextColor(mDefaultColor);
4841a1b54d6fe0c9e4f478a9e96f3cb6b49f72720edRobyn Coultas        if (forceUpdate) {
4851a1b54d6fe0c9e4f478a9e96f3cb6b49f72720edRobyn Coultas            invalidate();
4861a1b54d6fe0c9e4f478a9e96f3cb6b49f72720edRobyn Coultas        }
4871a1b54d6fe0c9e4f478a9e96f3cb6b49f72720edRobyn Coultas    }
4881a1b54d6fe0c9e4f478a9e96f3cb6b49f72720edRobyn Coultas
4891c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting    private static String getTimeStringForAccessibility(int hours, int minutes, int seconds,
4901c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting            boolean showNeg, Resources r) {
4911c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting        StringBuilder s = new StringBuilder();
4921c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting        if (showNeg) {
4931c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting            // This must be followed by a non-zero number or it will be audible as "hyphen"
4941c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting            // instead of "minus".
4951c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting            s.append("-");
4961c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting        }
4971c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting        if (showNeg && hours == 0 && minutes == 0) {
4981c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting            // Non-negative time will always have minutes, eg. "0 minutes 7 seconds", but negative
4991c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting            // time must start with non-zero digit, eg. -0m7s will be audible as just "-7 seconds"
5001c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting            s.append(String.format(
5011c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                    r.getQuantityText(R.plurals.Nseconds_description, seconds).toString(),
5021c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                    seconds));
5031c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting        } else if (hours == 0) {
5041c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting            s.append(String.format(
5051c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                    r.getQuantityText(R.plurals.Nminutes_description, minutes).toString(),
5061c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                    minutes));
5071c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting            s.append(" ");
5081c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting            s.append(String.format(
5091c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                    r.getQuantityText(R.plurals.Nseconds_description, seconds).toString(),
5101c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                    seconds));
5111c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting        } else {
5121c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting            s.append(String.format(
5131c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                    r.getQuantityText(R.plurals.Nhours_description, hours).toString(),
5141c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                    hours));
5151c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting            s.append(" ");
5161c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting            s.append(String.format(
5171c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                    r.getQuantityText(R.plurals.Nminutes_description, minutes).toString(),
5181c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                    minutes));
5191c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting            s.append(" ");
5201c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting            s.append(String.format(
5211c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                    r.getQuantityText(R.plurals.Nseconds_description, seconds).toString(),
5221c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                    seconds));
5231c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting        }
5241c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting        return s.toString();
5251c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting    }
5261c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting
5274261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting    public void setVirtualButtonEnabled(boolean enabled) {
5284261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting        mVirtualButtonEnabled = enabled;
5294261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting    }
5304261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting
5314261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting    private void virtualButtonPressed(boolean pressedOn) {
5324261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting        mVirtualButtonPressedOn = pressedOn;
5334261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting        invalidate();
5344261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting    }
5354261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting
5364261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting    private boolean withinVirtualButtonBounds(float x, float y) {
5374261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting        int width = getWidth();
5384261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting        int height = getHeight();
5394261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting        float centerX = width / 2;
5404261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting        float centerY = height / 2;
5414261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting        float radius = Math.min(width, height) / 2;
5424261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting
5434261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting        // Within the circle button if distance to the center is less than the radius.
5444261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting        double distance = Math.sqrt(Math.pow(centerX - x, 2) + Math.pow(centerY - y, 2));
5454261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting        return distance < radius;
5464261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting    }
5474261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting
5484261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting    public void registerVirtualButtonAction(final Runnable runnable) {
5491c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting        if (!mAccessibilityManager.isEnabled()) {
5501c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting            this.setOnTouchListener(new OnTouchListener() {
5511c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                @Override
5521c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                public boolean onTouch(View v, MotionEvent event) {
5531c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                    if (mVirtualButtonEnabled) {
5541c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                        switch (event.getAction()) {
5551c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                            case MotionEvent.ACTION_DOWN:
5561c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                                if (withinVirtualButtonBounds(event.getX(), event.getY())) {
5571c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                                    virtualButtonPressed(true);
5581c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                                    return true;
5591c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                                } else {
5601c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                                    virtualButtonPressed(false);
5611c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                                    return false;
562832a20480e1e76d1df846ac20c0eb1fe702a41f8Sam Blitzstein                                }
5631c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                            case MotionEvent.ACTION_CANCEL:
5641c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                                virtualButtonPressed(false);
5654261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting                                return true;
5661c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                            case MotionEvent.ACTION_OUTSIDE:
5674261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting                                virtualButtonPressed(false);
5684261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting                                return false;
5691c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                            case MotionEvent.ACTION_UP:
5701c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                                virtualButtonPressed(false);
5711c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                                if (withinVirtualButtonBounds(event.getX(), event.getY())) {
5721c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                                    runnable.run();
573832a20480e1e76d1df846ac20c0eb1fe702a41f8Sam Blitzstein                                }
5741c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                                return true;
5751c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                        }
5764261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting                    }
5771c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                    return false;
5784261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting                }
5791c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting            });
5801c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting        } else {
5811c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting            this.setOnClickListener(new OnClickListener() {
5821c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                @Override
5831c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                public void onClick(View v) {
5841c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                    runnable.run();
5851c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting                }
5861c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting            });
5871c4ab87072b0fb21e05ed221acd28560d6110b12Sara Ting        }
5884261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting    }
5894261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting
590fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson    @Override
591fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson    public void onDraw(Canvas canvas) {
5924261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting        // Blink functionality.
5934261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting        if (!mShowTimeStr && !mVirtualButtonPressedOn) {
5944261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting            return;
5954261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting        }
5964261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting
597fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        int width = getWidth();
598fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        if (mRemeasureText && width != 0) {
599fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson            setTotalTextWidth();
600d049a230efed6c04cf7d2ab8cc176d9f4f56e51aYorke Lee            width = getWidth();
601fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson            mRemeasureText = false;
602fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        }
603fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson
604fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        int xCenter = width / 2;
605fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        int yCenter = getHeight() / 2;
606fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson
6076a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas        float xTextStart = xCenter - mTotalTextWidth / 2;
6086a2ab617a8815be26c7af9ec6bcb9fadb17d5f7bRobyn Coultas        float yTextStart = yCenter + mTextHeight/2 - (mTextHeight * FONT_VERTICAL_OFFSET);
6094261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting
6104261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting        // Text color differs based on pressed state.
611478b04fa5ebfa1eda1062afa38f057102f32f9afBudi Kusmiantoro        final int textColor = mVirtualButtonPressedOn ? mPressedColor : mDefaultColor;
6124261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting        mPaintBigThin.setColor(textColor);
6134261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting        mPaintMed.setColor(textColor);
6144261870a93db4e92bf5c08a9a253c9c5ff80d41cSara Ting
615fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        if (mHours != null) {
616d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas            xTextStart = mBigHours.draw(canvas, mHours, xTextStart, yTextStart);
617fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        }
618fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        if (mMinutes != null) {
619d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas            xTextStart = mBigMinutes.draw(canvas, mMinutes, xTextStart, yTextStart);
620fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        }
621fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        if (mSeconds != null) {
622d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas            xTextStart = mBigSeconds.draw(canvas, mSeconds, xTextStart, yTextStart);
62393e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        }
62493e1bb511545434e57d2c46e3191cd9f1b6d2a68Robyn Coultas        if (mHundredths != null) {
625d27c983901df08a8ef3b839fe6d262073e50fc32Robyn Coultas            mMedHundredths.draw(canvas, mHundredths, xTextStart, yTextStart);
626fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson        }
62700cb5981c984bc13aa2f6af510a9dd9a784f2af0Sam Blitzstein    }
628d049a230efed6c04cf7d2ab8cc176d9f4f56e51aYorke Lee
629c9524f8b8216c7854de4c69c44c9bb63e4097a18Robyn Coultas    @Override
630c9524f8b8216c7854de4c69c44c9bb63e4097a18Robyn Coultas    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
631c9524f8b8216c7854de4c69c44c9bb63e4097a18Robyn Coultas        super.onSizeChanged(w, h, oldw, oldh);
632c9524f8b8216c7854de4c69c44c9bb63e4097a18Robyn Coultas        mRemeasureText = true;
633b9accf1c0da8bfb4b7daffb237c581f27f7a0823Robyn Coultas        resetTextSize();
634c9524f8b8216c7854de4c69c44c9bb63e4097a18Robyn Coultas    }
635fd544b56e462c0e871441bcd48797e022f61d8efIsaac Katzenelson}
636