14243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson/*
2761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas * Copyright (C) 2012 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;
24761d99173a2a9d4221845b58b0b7412740807972Robyn Coultasimport android.text.TextUtils;
254243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelsonimport android.util.AttributeSet;
26e40b31200dee36341e5697b2774799555ca79c9bSara Tingimport android.view.MotionEvent;
274243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelsonimport android.view.View;
28932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Tingimport android.view.accessibility.AccessibilityManager;
29386d8b05069c95899149c4b18542fc18c3761314Sam Blitzsteinimport android.widget.TextView;
304243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
31761d99173a2a9d4221845b58b0b7412740807972Robyn Coultasimport com.android.deskclock.Log;
324243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelsonimport com.android.deskclock.R;
330f6e3350fed144e3909ba4e45f3006f042c0187cSam Blitzsteinimport com.android.deskclock.Utils;
344243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
354243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
36bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas/**
37bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas * Class to measure and draw the time in the {@link com.android.deskclock.CircleTimerView}.
38bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas * This class manages and sums the work of the four members mBigHours, mBigMinutes,
39bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas * mBigSeconds and mMedHundredths. Those members are each tasked with measuring, sizing and
40bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas * drawing digits (and optional label) of the time set in {@link #setTime(long, boolean, boolean)}
41bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas */
424243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelsonpublic class CountingTimerView extends View {
434243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private static final String TWO_DIGITS = "%02d";
444243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private static final String ONE_DIGIT = "%01d";
454243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private static final String NEG_TWO_DIGITS = "-%02d";
464243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private static final String NEG_ONE_DIGIT = "-%01d";
4763421edeba18751e88532c2891f3a8e51a471496Robyn Coultas    private static final float TEXT_SIZE_TO_WIDTH_RATIO = 0.85f;
48bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas    // This is the ratio of the font height needed to vertically offset the font for alignment
49bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas    // from the center.
50c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee    private static final float FONT_VERTICAL_OFFSET = 0.14f;
51e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas    // Ratio of the space trailing the Hours and Minutes
5263421edeba18751e88532c2891f3a8e51a471496Robyn Coultas    private static final float HOURS_MINUTES_SPACING = 0.4f;
53e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas    // Ratio of the space leading the Hundredths
54e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas    private static final float HUNDREDTHS_SPACING = 0.5f;
5563421edeba18751e88532c2891f3a8e51a471496Robyn Coultas    // Radial offset of the enclosing circle
5663421edeba18751e88532c2891f3a8e51a471496Robyn Coultas    private final float mRadiusOffset;
574243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
58761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas    private String mHours, mMinutes, mSeconds, mHundredths;
594243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
604243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private boolean mShowTimeStr = true;
614243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private final Paint mPaintBigThin = new Paint();
624243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private final Paint mPaintMed = new Paint();
63761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas    private final float mBigFontSize, mSmallFontSize;
64bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas    // Hours and minutes are signed for when a timer goes past the set time and thus negative
6569ba4ed9235ad2a77177629bac241ab0e6915087Isaac Katzenelson    private final SignedTime mBigHours, mBigMinutes;
66bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas    // Seconds are always shown with minutes, so are never signed
67bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas    private final UnsignedTime mBigSeconds;
6869ba4ed9235ad2a77177629bac241ab0e6915087Isaac Katzenelson    private final Hundredths mMedHundredths;
69c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee    private float mTextHeight = 0;
704243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private float mTotalTextWidth;
714243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    private boolean mRemeasureText = true;
7222ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas
7322ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas    private int mDefaultColor;
74e40b31200dee36341e5697b2774799555ca79c9bSara Ting    private final int mPressedColor;
7522ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas    private final int mWhiteColor;
7622ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas    private final int mRedColor;
77386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein    private TextView mStopStartTextView;
78932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting    private final AccessibilityManager mAccessibilityManager;
79e40b31200dee36341e5697b2774799555ca79c9bSara Ting
80e40b31200dee36341e5697b2774799555ca79c9bSara Ting    // Fields for the text serving as a virtual button.
81e40b31200dee36341e5697b2774799555ca79c9bSara Ting    private boolean mVirtualButtonEnabled = false;
82e40b31200dee36341e5697b2774799555ca79c9bSara Ting    private boolean mVirtualButtonPressedOn = false;
834243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
844243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    Runnable mBlinkThread = new Runnable() {
85386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein        private boolean mVisible = true;
864243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        @Override
874243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        public void run() {
88386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein            mVisible = !mVisible;
89386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein            CountingTimerView.this.showTime(mVisible);
904243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            postDelayed(mBlinkThread, 500);
914243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
924243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
934243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    };
944243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
95bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas    /**
96bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas     * Class to measure and draw the digit pairs of hours, minutes, seconds or hundredths. Digits
97bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas     * may have an optional label. for hours, minutes and seconds, this label trails the digits
98bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas     * and for seconds, precedes the digits.
99bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas     */
100e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas    static class UnsignedTime {
101761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        protected Paint mPaint;
102761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        protected float mEm;
103761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        protected float mWidth = 0;
10469ba4ed9235ad2a77177629bac241ab0e6915087Isaac Katzenelson        private final String mWidest;
105e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas        protected final float mSpacingRatio;
106761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        private float mLabelWidth = 0;
107761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas
108e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas        public UnsignedTime(Paint paint, float spacingRatio, String allDigits) {
109761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            mPaint = paint;
110e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas            mSpacingRatio = spacingRatio;
111761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas
112761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            if (TextUtils.isEmpty(allDigits)) {
113761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                Log.wtf("Locale digits missing - using English");
114761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                allDigits = "0123456789";
115761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            }
116761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas
117761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            float widths[] = new float[allDigits.length()];
118761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            int ll = mPaint.getTextWidths(allDigits, widths);
119761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            int largest = 0;
120761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            for (int ii = 1; ii < ll; ii++) {
121761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                if (widths[ii] > widths[largest]) {
122761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                    largest = ii;
123761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                }
124761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            }
125761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas
126761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            mEm = widths[largest];
127761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            mWidest = allDigits.substring(largest, largest + 1);
128761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        }
129761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas
130e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas        public UnsignedTime(UnsignedTime unsignedTime, float spacingRatio) {
131761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            this.mPaint = unsignedTime.mPaint;
132761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            this.mEm = unsignedTime.mEm;
133761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            this.mWidth = unsignedTime.mWidth;
134761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            this.mWidest = unsignedTime.mWidest;
135e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas            this.mSpacingRatio = spacingRatio;
136761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        }
137761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas
138761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        protected void updateWidth(final String time) {
139761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            mEm = mPaint.measureText(mWidest);
140e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas            mLabelWidth = mSpacingRatio * mEm;
141761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            mWidth = time.length() * mEm;
142761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        }
143761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas
144761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        protected void resetWidth() {
145761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            mWidth = mLabelWidth = 0;
146761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        }
147761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas
148761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        public float calcTotalWidth(final String time) {
149761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            if (time != null) {
150761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                updateWidth(time);
151761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                return mWidth + mLabelWidth;
152761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            } else {
153761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                resetWidth();
154761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                return 0;
155761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            }
156761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        }
157761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas
158761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        public float getLabelWidth() {
159761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            return mLabelWidth;
160761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        }
161761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas
162e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas        /**
163e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas         * Draws each character with a fixed spacing from time starting at ii.
164e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas         * @param canvas the canvas on which the time segment will be drawn
165e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas         * @param time time segment
166e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas         * @param ii what character to start the draw
167e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas         * @param x offset
168e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas         * @param y offset
169e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas         * @return X location for the next segment
170e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas         */
171761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        protected float drawTime(Canvas canvas, final String time, int ii, float x, float y) {
172761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            float textEm  = mEm / 2f;
173761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            while (ii < time.length()) {
174761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                x += textEm;
175761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                canvas.drawText(time.substring(ii, ii + 1), x, y, mPaint);
176761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                x += textEm;
177761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                ii++;
178761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            }
179761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            return x;
180761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        }
181761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas
182e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas        /**
183e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas         * Draw this time segment and append the intra-segment spacing to the x
184e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas         * @param canvas the canvas on which the time segment will be drawn
185e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas         * @param time time segment
186e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas         * @param x offset
187e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas         * @param y offset
188e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas         * @return X location for the next segment
189e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas         */
190e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas        public float draw(Canvas canvas, final String time, float x, float y) {
191e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas            return drawTime(canvas, time, 0, x, y) + getLabelWidth();
192761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        }
193761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas    }
194761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas
195bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas    /**
196bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas     * Special derivation to handle the hundredths painting with the label in front.
197bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas     */
198e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas    static class Hundredths extends UnsignedTime {
199e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas        public Hundredths(Paint paint, float spacingRatio, final String allDigits) {
200e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas            super(paint, spacingRatio, allDigits);
201761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        }
202761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas
203e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas        /**
204e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas         * Draw this time segment after prepending the intra-segment spacing to the x location.
205e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas         * {@link UnsignedTime#draw(android.graphics.Canvas, String, float, float)}
206e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas         */
207761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        @Override
208e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas        public float draw(Canvas canvas, final String time, float x, float y) {
209761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            return drawTime(canvas, time, 0, x + getLabelWidth(), y);
210761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        }
211761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas    }
212761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas
213bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas    /**
214bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas     * Special derivation to handle a negative number
215bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas     */
216e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas    static class SignedTime extends UnsignedTime {
217761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        private float mMinusWidth = 0;
218761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas
219e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas        public SignedTime (UnsignedTime unsignedTime, float spacingRatio) {
220e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas            super(unsignedTime, spacingRatio);
221761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        }
222761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas
223761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        @Override
224761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        protected void updateWidth(final String time) {
225761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            super.updateWidth(time);
226761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            if (time.contains("-")) {
227761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                mMinusWidth = mPaint.measureText("-");
228761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                mWidth += (mMinusWidth - mEm);
229761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            } else {
230761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                mMinusWidth = 0;
231761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            }
232761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        }
233761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas
234761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        @Override
235761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        protected void resetWidth() {
236761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            super.resetWidth();
237761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            mMinusWidth = 0;
238761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        }
239761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas
240e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas        /**
241e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas         * Draws each character with a fixed spacing from time, handling the special negative
242e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas         * number case.
243e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas         * {@link UnsignedTime#draw(android.graphics.Canvas, String, float, float)}
244e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas         */
245761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        @Override
246e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas        public float draw(Canvas canvas, final String time, float x, float y) {
247761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            int ii = 0;
248761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            if (mMinusWidth != 0f) {
249761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                float minusWidth = mMinusWidth / 2;
250761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                x += minusWidth;
251e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas                //TODO:hyphen is too thick when painted
252bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas                canvas.drawText(time.substring(0, 1), x, y, mPaint);
253761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                x += minusWidth;
254761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                ii++;
255761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            }
256e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas            return drawTime(canvas, time, ii, x, y) + getLabelWidth();
257761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        }
258761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas    }
2594243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
260bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas    @SuppressWarnings("unused")
2614243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    public CountingTimerView(Context context) {
2624243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        this(context, null);
2634243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    }
2644243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
2654243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    public CountingTimerView(Context context, AttributeSet attrs) {
2664243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        super(context, attrs);
267932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting        mAccessibilityManager =
268932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
2694243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        Resources r = context.getResources();
27022ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas        mWhiteColor = r.getColor(R.color.clock_white);
27122ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas        mDefaultColor = mWhiteColor;
2720f6e3350fed144e3909ba4e45f3006f042c0187cSam Blitzstein        mPressedColor = r.getColor(Utils.getPressedColorId());
27322ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas        mRedColor = r.getColor(R.color.clock_red);
274761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        mBigFontSize = r.getDimension(R.dimen.big_font_size);
275761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        mSmallFontSize = r.getDimension(R.dimen.small_font_size);
27622ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas
277bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas        Typeface androidClockMonoThin = Typeface.
278bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas                createFromAsset(context.getAssets(), "fonts/AndroidClockMono-Thin.ttf");
2794243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mPaintBigThin.setAntiAlias(true);
2804243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mPaintBigThin.setStyle(Paint.Style.STROKE);
281761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        mPaintBigThin.setTextAlign(Paint.Align.CENTER);
282bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas        mPaintBigThin.setTypeface(androidClockMonoThin);
2834243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
284bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas        Typeface androidClockMonoLight = Typeface.
285bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas                createFromAsset(context.getAssets(), "fonts/AndroidClockMono-Light.ttf");
2864243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mPaintMed.setAntiAlias(true);
2874243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mPaintMed.setStyle(Paint.Style.STROKE);
288761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        mPaintMed.setTextAlign(Paint.Align.CENTER);
289bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas        mPaintMed.setTypeface(androidClockMonoLight);
2904243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
291761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        resetTextSize();
29222ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas        setTextColor(mDefaultColor);
293761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas
294761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        // allDigits will contain ten digits: "0123456789" in the default locale
295761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        final String allDigits = String.format("%010d", 123456789);
296e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas        mBigSeconds = new UnsignedTime(mPaintBigThin, 0.f, allDigits);
297e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas        mBigHours = new SignedTime(mBigSeconds, HOURS_MINUTES_SPACING);
298e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas        mBigMinutes = new SignedTime(mBigSeconds, HOURS_MINUTES_SPACING);
299e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas        mMedHundredths = new Hundredths(mPaintMed, HUNDREDTHS_SPACING, allDigits);
30063421edeba18751e88532c2891f3a8e51a471496Robyn Coultas
30163421edeba18751e88532c2891f3a8e51a471496Robyn Coultas        mRadiusOffset = Utils.calculateRadiusOffset(r);
302761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas    }
303761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas
304761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas    protected void resetTextSize() {
305761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        mTextHeight = mBigFontSize;
306761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        mPaintBigThin.setTextSize(mBigFontSize);
307761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        mPaintMed.setTextSize(mSmallFontSize);
30822ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas    }
30922ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas
31022ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas    protected void setTextColor(int textColor) {
31122ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas        mPaintBigThin.setColor(textColor);
31222ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas        mPaintMed.setColor(textColor);
3134243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    }
3144243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
315bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas    /**
316bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas     * Update the time to display. Separates that time into the hours, minutes, seconds and
317bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas     * hundredths. If update is true, the view is invalidated so that it will draw again.
318bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas     *
319bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas     * @param time new time to display - in milliseconds
320bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas     * @param showHundredths flag to show hundredths resolution
321bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas     * @param update to invalidate the view - otherwise the time is examined to see if it is within
322bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas     *               100 milliseconds of zero seconds and when so, invalidate the view.
323bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas     */
324e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas    // TODO:showHundredths S/B attribute or setter - i.e. unchanging over object life
325644a5d887d7033e669ac362a8ed6e79197d13c72Itzhak Katzenelson    public void setTime(long time, boolean showHundredths, boolean update) {
326bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas        int oldLength = getDigitsLength();
327644a5d887d7033e669ac362a8ed6e79197d13c72Itzhak Katzenelson        boolean neg = false, showNeg = false;
328bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas        String format;
3294243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (time < 0) {
33031943258b338ee3a36d07ff57a6fb0f94bb3650fIsaac Katzenelson            time = -time;
331644a5d887d7033e669ac362a8ed6e79197d13c72Itzhak Katzenelson            neg = showNeg = true;
3324243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
3334243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        long hundreds, seconds, minutes, hours;
334113e1daddd8cb0e890084aa4b6ea3194d8ad4826Sam Blitzstein        seconds = time / 1000;
335113e1daddd8cb0e890084aa4b6ea3194d8ad4826Sam Blitzstein        hundreds = (time - seconds * 1000) / 10;
3364243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        minutes = seconds / 60;
3374243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        seconds = seconds - minutes * 60;
3384243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        hours = minutes / 60;
3394243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        minutes = minutes - hours * 60;
34045a2e4b95dafe286ab0fac957d50431124c3427eRobyn Coultas        if (hours > 999) {
3414243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            hours = 0;
3424243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
343bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas        // The time  can be between 0 and -1 seconds, but the "truncated" equivalent time of hours
344bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas        // and minutes and seconds could be zero, so since we do not show fractions of seconds
34531943258b338ee3a36d07ff57a6fb0f94bb3650fIsaac Katzenelson        // when counting down, do not show the minus sign.
346e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas        // TODO:does it matter that we do not look at showHundredths?
347bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas        if (hours == 0 && minutes == 0 && seconds == 0) {
348644a5d887d7033e669ac362a8ed6e79197d13c72Itzhak Katzenelson            showNeg = false;
34931943258b338ee3a36d07ff57a6fb0f94bb3650fIsaac Katzenelson        }
350761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas
351bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas        // Normalize and check if it is 'time' to invalidate
35220ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein        if (!showHundredths) {
35320ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein            if (!neg && hundreds != 0) {
35420ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein                seconds++;
35520ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein                if (seconds == 60) {
35620ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein                    seconds = 0;
35720ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein                    minutes++;
35820ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein                    if (minutes == 60) {
35920ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein                        minutes = 0;
36020ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein                        hours++;
36120ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein                    }
36220ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein                }
36320ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein            }
36420ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein            if (hundreds < 10 || hundreds > 90) {
36520ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein                update = true;
36620ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein            }
36720ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein        }
36820ea36e23e699484aefc8706d4357266ec136da8Sam Blitzstein
369bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas        // Hours may be empty
3704243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (hours >= 10) {
371644a5d887d7033e669ac362a8ed6e79197d13c72Itzhak Katzenelson            format = showNeg ? NEG_TWO_DIGITS : TWO_DIGITS;
3724243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mHours = String.format(format, hours);
3734243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        } else if (hours > 0) {
374644a5d887d7033e669ac362a8ed6e79197d13c72Itzhak Katzenelson            format = showNeg ? NEG_ONE_DIGIT : ONE_DIGIT;
3754243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mHours = String.format(format, hours);
3764243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        } else {
3774243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mHours = null;
3784243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
3794243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
380bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas        // Minutes are never empty and when hours are non-empty, must be two digits
3814243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (minutes >= 10 || hours > 0) {
382644a5d887d7033e669ac362a8ed6e79197d13c72Itzhak Katzenelson            format = (showNeg && hours == 0) ? NEG_TWO_DIGITS : TWO_DIGITS;
3834243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mMinutes = String.format(format, minutes);
3844243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        } else {
385644a5d887d7033e669ac362a8ed6e79197d13c72Itzhak Katzenelson            format = (showNeg && hours == 0) ? NEG_ONE_DIGIT : ONE_DIGIT;
3864243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mMinutes = String.format(format, minutes);
3874243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
3884243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
389bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas        // Seconds are always two digits
3904243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        mSeconds = String.format(TWO_DIGITS, seconds);
391bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas
392bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas        // Hundredths are optional and then two digits
3934243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (showHundredths) {
394761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            mHundredths = String.format(TWO_DIGITS, hundreds);
3954243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        } else {
396761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            mHundredths = null;
397761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        }
398761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas
399761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        int newLength = getDigitsLength();
400761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        if (oldLength != newLength) {
401761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            if (oldLength > newLength) {
402761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                resetTextSize();
403761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            }
404761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            mRemeasureText = true;
4054243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
406644a5d887d7033e669ac362a8ed6e79197d13c72Itzhak Katzenelson
407644a5d887d7033e669ac362a8ed6e79197d13c72Itzhak Katzenelson        if (update) {
408932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            setContentDescription(getTimeStringForAccessibility((int) hours, (int) minutes,
409932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    (int) seconds, showNeg, getResources()));
410644a5d887d7033e669ac362a8ed6e79197d13c72Itzhak Katzenelson            invalidate();
411644a5d887d7033e669ac362a8ed6e79197d13c72Itzhak Katzenelson        }
4124243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    }
4134243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
414761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas    private int getDigitsLength() {
415761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        return ((mHours == null) ? 0 : mHours.length())
416761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                + ((mMinutes == null) ? 0 : mMinutes.length())
417761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                + ((mSeconds == null) ? 0 : mSeconds.length())
418761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                + ((mHundredths == null) ? 0 : mHundredths.length());
419761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas    }
420761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas
421761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas    private void calcTotalTextWidth() {
422761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        mTotalTextWidth = mBigHours.calcTotalWidth(mHours) + mBigMinutes.calcTotalWidth(mMinutes)
423bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas                + mBigSeconds.calcTotalWidth(mSeconds)
424761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                + mMedHundredths.calcTotalWidth(mHundredths);
425761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas    }
426761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas
42763421edeba18751e88532c2891f3a8e51a471496Robyn Coultas    /**
42863421edeba18751e88532c2891f3a8e51a471496Robyn Coultas     * Adjust the size of the fonts to fit within the the circle and painted object in
42963421edeba18751e88532c2891f3a8e51a471496Robyn Coultas     * {@link com.android.deskclock.CircleTimerView#onDraw(android.graphics.Canvas)}
43063421edeba18751e88532c2891f3a8e51a471496Robyn Coultas     */
431761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas    private void setTotalTextWidth() {
432761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        calcTotalTextWidth();
433c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee        // To determine the maximum width, we find the minimum of the height and width (since the
434c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee        // circle we are trying to fit the text into has its radius sized to the smaller of the
435c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee        // two.
436c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee        int width = Math.min(getWidth(), getHeight());
4374243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (width != 0) {
43863421edeba18751e88532c2891f3a8e51a471496Robyn Coultas            // Shrink 'width' to account for circle stroke and other painted objects.
43963421edeba18751e88532c2891f3a8e51a471496Robyn Coultas            // Note on the "4 *": (1) To reduce divisions, using the diameter instead of the radius.
44063421edeba18751e88532c2891f3a8e51a471496Robyn Coultas            // (2) The radius of the enclosing circle is reduced by mRadiusOffset and the
44163421edeba18751e88532c2891f3a8e51a471496Robyn Coultas            // text needs to fit within a circle further reduced by mRadiusOffset.
44263421edeba18751e88532c2891f3a8e51a471496Robyn Coultas            width -= (int) (4 * mRadiusOffset + 0.5f);
44363421edeba18751e88532c2891f3a8e51a471496Robyn Coultas
44463421edeba18751e88532c2891f3a8e51a471496Robyn Coultas            final float wantDiameter2 = TEXT_SIZE_TO_WIDTH_RATIO * width * width;
44563421edeba18751e88532c2891f3a8e51a471496Robyn Coultas            float totalDiameter2 = getHypotenuseSquared();
44663421edeba18751e88532c2891f3a8e51a471496Robyn Coultas
44763421edeba18751e88532c2891f3a8e51a471496Robyn Coultas            // If the hypotenuse of the bounding box is too large, reduce all the paint text sizes
44863421edeba18751e88532c2891f3a8e51a471496Robyn Coultas            while (totalDiameter2 > wantDiameter2) {
44963421edeba18751e88532c2891f3a8e51a471496Robyn Coultas                // Convergence is slightly difficult due to quantization in the mTotalTextWidth
45063421edeba18751e88532c2891f3a8e51a471496Robyn Coultas                // calculation. Reducing the ratio by 1% converges more quickly without excessive
45163421edeba18751e88532c2891f3a8e51a471496Robyn Coultas                // loss of quality.
45263421edeba18751e88532c2891f3a8e51a471496Robyn Coultas                float sizeRatio = 0.99f * (float) Math.sqrt(wantDiameter2/totalDiameter2);
453761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                mPaintBigThin.setTextSize(mPaintBigThin.getTextSize() * sizeRatio);
454761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                mPaintMed.setTextSize(mPaintMed.getTextSize() * sizeRatio);
455bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas                // Recalculate the new total text height and half-width
456bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas                mTextHeight = mPaintBigThin.getTextSize();
457761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                calcTotalTextWidth();
45863421edeba18751e88532c2891f3a8e51a471496Robyn Coultas                totalDiameter2 = getHypotenuseSquared();
4594243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            }
4604243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
4614243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    }
4624243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
46363421edeba18751e88532c2891f3a8e51a471496Robyn Coultas    /**
46463421edeba18751e88532c2891f3a8e51a471496Robyn Coultas     * Calculate the square of the diameter to use in {@link CountingTimerView#setTotalTextWidth()}
46563421edeba18751e88532c2891f3a8e51a471496Robyn Coultas     */
46663421edeba18751e88532c2891f3a8e51a471496Robyn Coultas    private float getHypotenuseSquared() {
46763421edeba18751e88532c2891f3a8e51a471496Robyn Coultas        return mTotalTextWidth * mTotalTextWidth + mTextHeight * mTextHeight;
46863421edeba18751e88532c2891f3a8e51a471496Robyn Coultas    }
46963421edeba18751e88532c2891f3a8e51a471496Robyn Coultas
4704243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    public void blinkTimeStr(boolean blink) {
4714243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (blink) {
4724243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            removeCallbacks(mBlinkThread);
473ae0cdcb45f8bb679c5c7a9a832f40d3eaa8f07e1Isaac Katzenelson            post(mBlinkThread);
4744243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        } else {
4754243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            removeCallbacks(mBlinkThread);
476386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein            showTime(true);
4774243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
4784243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    }
4794243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
480386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein    public void showTime(boolean visible) {
481386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein        mShowTimeStr = visible;
482386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein        invalidate();
483386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein    }
484386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein
48522ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas    public void redTimeStr(boolean red, boolean forceUpdate) {
48622ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas        mDefaultColor = red ? mRedColor : mWhiteColor;
48722ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas        setTextColor(mDefaultColor);
48822ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas        if (forceUpdate) {
48922ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas            invalidate();
49022ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas        }
49122ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas    }
49222ad7c9ea127b06ee0dc0cb809b59bab33afbf42Robyn Coultas
4934243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    public String getTimeString() {
494761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        // Though only called from Stopwatch Share, so hundredth are never null,
495761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        // protect the future and check for null mHundredths
496761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        if (mHundredths == null) {
497761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            if (mHours == null) {
498761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas                return String.format("%s:%s", mMinutes, mSeconds);
499761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            }
500761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            return String.format("%s:%s:%s", mHours, mMinutes, mSeconds);
501761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        } else if (mHours == null) {
502761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas            return String.format("%s:%s.%s", mMinutes, mSeconds, mHundredths);
5034243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
504761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        return String.format("%s:%s:%s.%s", mHours, mMinutes, mSeconds, mHundredths);
5054243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    }
5064243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
507932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting    private static String getTimeStringForAccessibility(int hours, int minutes, int seconds,
508932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            boolean showNeg, Resources r) {
509932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting        StringBuilder s = new StringBuilder();
510932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting        if (showNeg) {
511932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            // This must be followed by a non-zero number or it will be audible as "hyphen"
512932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            // instead of "minus".
513932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            s.append("-");
514932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting        }
515932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting        if (showNeg && hours == 0 && minutes == 0) {
516932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            // Non-negative time will always have minutes, eg. "0 minutes 7 seconds", but negative
517932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            // time must start with non-zero digit, eg. -0m7s will be audible as just "-7 seconds"
518932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            s.append(String.format(
519932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    r.getQuantityText(R.plurals.Nseconds_description, seconds).toString(),
520932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    seconds));
521932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting        } else if (hours == 0) {
522932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            s.append(String.format(
523932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    r.getQuantityText(R.plurals.Nminutes_description, minutes).toString(),
524932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    minutes));
525932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            s.append(" ");
526932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            s.append(String.format(
527932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    r.getQuantityText(R.plurals.Nseconds_description, seconds).toString(),
528932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    seconds));
529932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting        } else {
530932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            s.append(String.format(
531932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    r.getQuantityText(R.plurals.Nhours_description, hours).toString(),
532932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    hours));
533932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            s.append(" ");
534932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            s.append(String.format(
535932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    r.getQuantityText(R.plurals.Nminutes_description, minutes).toString(),
536932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    minutes));
537932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            s.append(" ");
538932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            s.append(String.format(
539932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    r.getQuantityText(R.plurals.Nseconds_description, seconds).toString(),
540932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    seconds));
541932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting        }
542932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting        return s.toString();
543932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting    }
544932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting
545e40b31200dee36341e5697b2774799555ca79c9bSara Ting    public void setVirtualButtonEnabled(boolean enabled) {
546e40b31200dee36341e5697b2774799555ca79c9bSara Ting        mVirtualButtonEnabled = enabled;
547e40b31200dee36341e5697b2774799555ca79c9bSara Ting    }
548e40b31200dee36341e5697b2774799555ca79c9bSara Ting
549e40b31200dee36341e5697b2774799555ca79c9bSara Ting    private void virtualButtonPressed(boolean pressedOn) {
550e40b31200dee36341e5697b2774799555ca79c9bSara Ting        mVirtualButtonPressedOn = pressedOn;
551386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein        mStopStartTextView.setTextColor(pressedOn ? mPressedColor : mWhiteColor);
552e40b31200dee36341e5697b2774799555ca79c9bSara Ting        invalidate();
553e40b31200dee36341e5697b2774799555ca79c9bSara Ting    }
554e40b31200dee36341e5697b2774799555ca79c9bSara Ting
555e40b31200dee36341e5697b2774799555ca79c9bSara Ting    private boolean withinVirtualButtonBounds(float x, float y) {
556e40b31200dee36341e5697b2774799555ca79c9bSara Ting        int width = getWidth();
557e40b31200dee36341e5697b2774799555ca79c9bSara Ting        int height = getHeight();
558e40b31200dee36341e5697b2774799555ca79c9bSara Ting        float centerX = width / 2;
559e40b31200dee36341e5697b2774799555ca79c9bSara Ting        float centerY = height / 2;
560e40b31200dee36341e5697b2774799555ca79c9bSara Ting        float radius = Math.min(width, height) / 2;
561e40b31200dee36341e5697b2774799555ca79c9bSara Ting
562e40b31200dee36341e5697b2774799555ca79c9bSara Ting        // Within the circle button if distance to the center is less than the radius.
563e40b31200dee36341e5697b2774799555ca79c9bSara Ting        double distance = Math.sqrt(Math.pow(centerX - x, 2) + Math.pow(centerY - y, 2));
564e40b31200dee36341e5697b2774799555ca79c9bSara Ting        return distance < radius;
565e40b31200dee36341e5697b2774799555ca79c9bSara Ting    }
566e40b31200dee36341e5697b2774799555ca79c9bSara Ting
567e40b31200dee36341e5697b2774799555ca79c9bSara Ting    public void registerVirtualButtonAction(final Runnable runnable) {
568932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting        if (!mAccessibilityManager.isEnabled()) {
569932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            this.setOnTouchListener(new OnTouchListener() {
570932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                @Override
571932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                public boolean onTouch(View v, MotionEvent event) {
572932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    if (mVirtualButtonEnabled) {
573932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                        switch (event.getAction()) {
574932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                            case MotionEvent.ACTION_DOWN:
575932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                                if (withinVirtualButtonBounds(event.getX(), event.getY())) {
576932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                                    virtualButtonPressed(true);
577932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                                    return true;
578932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                                } else {
579932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                                    virtualButtonPressed(false);
580932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                                    return false;
581357497c341662c56945e22e458852f52a977efdfSam Blitzstein                                }
582932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                            case MotionEvent.ACTION_CANCEL:
583932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                                virtualButtonPressed(false);
584e40b31200dee36341e5697b2774799555ca79c9bSara Ting                                return true;
585932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                            case MotionEvent.ACTION_OUTSIDE:
586e40b31200dee36341e5697b2774799555ca79c9bSara Ting                                virtualButtonPressed(false);
587e40b31200dee36341e5697b2774799555ca79c9bSara Ting                                return false;
588932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                            case MotionEvent.ACTION_UP:
589932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                                virtualButtonPressed(false);
590932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                                if (withinVirtualButtonBounds(event.getX(), event.getY())) {
591932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                                    runnable.run();
592357497c341662c56945e22e458852f52a977efdfSam Blitzstein                                }
593932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                                return true;
594932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                        }
595e40b31200dee36341e5697b2774799555ca79c9bSara Ting                    }
596932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    return false;
597e40b31200dee36341e5697b2774799555ca79c9bSara Ting                }
598932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            });
599932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting        } else {
600932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            this.setOnClickListener(new OnClickListener() {
601932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                @Override
602932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                public void onClick(View v) {
603932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                    runnable.run();
604932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting                }
605932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting            });
606932e0129e244e2874dfa3c6be58b2fe9a20cc89dSara Ting        }
607e40b31200dee36341e5697b2774799555ca79c9bSara Ting    }
608e40b31200dee36341e5697b2774799555ca79c9bSara Ting
6094243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    @Override
6104243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    public void onDraw(Canvas canvas) {
611e40b31200dee36341e5697b2774799555ca79c9bSara Ting        // Blink functionality.
612e40b31200dee36341e5697b2774799555ca79c9bSara Ting        if (!mShowTimeStr && !mVirtualButtonPressedOn) {
613e40b31200dee36341e5697b2774799555ca79c9bSara Ting            return;
614e40b31200dee36341e5697b2774799555ca79c9bSara Ting        }
615e40b31200dee36341e5697b2774799555ca79c9bSara Ting
6164243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        int width = getWidth();
6174243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (mRemeasureText && width != 0) {
6184243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            setTotalTextWidth();
619c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee            width = getWidth();
6204243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson            mRemeasureText = false;
6214243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
6224243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
6234243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        int xCenter = width / 2;
6244243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        int yCenter = getHeight() / 2;
6254243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson
626bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas        float xTextStart = xCenter - mTotalTextWidth / 2;
627bad6946a9d5b6474fda3d659f460d87e5c7439baRobyn Coultas        float yTextStart = yCenter + mTextHeight/2 - (mTextHeight * FONT_VERTICAL_OFFSET);
628e40b31200dee36341e5697b2774799555ca79c9bSara Ting
629e40b31200dee36341e5697b2774799555ca79c9bSara Ting        // Text color differs based on pressed state.
630e40b31200dee36341e5697b2774799555ca79c9bSara Ting        int textColor;
631e40b31200dee36341e5697b2774799555ca79c9bSara Ting        if (mVirtualButtonPressedOn) {
632e40b31200dee36341e5697b2774799555ca79c9bSara Ting            textColor = mPressedColor;
633386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein            mStopStartTextView.setTextColor(mPressedColor);
634e40b31200dee36341e5697b2774799555ca79c9bSara Ting        } else {
635e40b31200dee36341e5697b2774799555ca79c9bSara Ting            textColor = mDefaultColor;
636e40b31200dee36341e5697b2774799555ca79c9bSara Ting        }
637e40b31200dee36341e5697b2774799555ca79c9bSara Ting        mPaintBigThin.setColor(textColor);
638e40b31200dee36341e5697b2774799555ca79c9bSara Ting        mPaintMed.setColor(textColor);
639e40b31200dee36341e5697b2774799555ca79c9bSara Ting
6404243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (mHours != null) {
641e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas            xTextStart = mBigHours.draw(canvas, mHours, xTextStart, yTextStart);
6424243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
6434243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (mMinutes != null) {
644e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas            xTextStart = mBigMinutes.draw(canvas, mMinutes, xTextStart, yTextStart);
6454243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
6464243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        if (mSeconds != null) {
647e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas            xTextStart = mBigSeconds.draw(canvas, mSeconds, xTextStart, yTextStart);
648761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        }
649761d99173a2a9d4221845b58b0b7412740807972Robyn Coultas        if (mHundredths != null) {
650e43161db8942cab73adb74d66a07bedd1fafa433Robyn Coultas            mMedHundredths.draw(canvas, mHundredths, xTextStart, yTextStart);
6514243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson        }
652386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein    }
653c78b68bfacfb9542e80ae2d37ed66ff27917578fYorke Lee
654de58829c88927b4eb9c7b483da7f0394c538a071Robyn Coultas    @Override
655de58829c88927b4eb9c7b483da7f0394c538a071Robyn Coultas    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
656de58829c88927b4eb9c7b483da7f0394c538a071Robyn Coultas        super.onSizeChanged(w, h, oldw, oldh);
657de58829c88927b4eb9c7b483da7f0394c538a071Robyn Coultas        mRemeasureText = true;
658144d2a0809347eb9a7f8c6e5ac5b7b72114ada32Robyn Coultas        resetTextSize();
659de58829c88927b4eb9c7b483da7f0394c538a071Robyn Coultas    }
660de58829c88927b4eb9c7b483da7f0394c538a071Robyn Coultas
661386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein    public void registerStopTextView(TextView stopStartTextView) {
662386d8b05069c95899149c4b18542fc18c3761314Sam Blitzstein        mStopStartTextView = stopStartTextView;
6634243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson    }
6644243fb005ecf4810eb2a1adc28df73abf431f178Isaac Katzenelson}
665