19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/* 29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project 39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License. 69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at 79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and 149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License. 159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.widget; 189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context; 20a79a747ca006d78b969f85227670f36572fde042Dan Sandlerimport android.content.res.Resources; 219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.TypedArray; 229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.SystemClock; 239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.format.DateUtils; 249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.AttributeSet; 259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log; 269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.widget.RemoteViews.RemoteView; 279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 28ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinekimport com.android.internal.R; 29ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek 309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Formatter; 319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.IllegalFormatException; 329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Locale; 339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/** 359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Class that implements a simple timer. 369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <p> 379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You can give it a start time in the {@link SystemClock#elapsedRealtime} timebase, 389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * and it counts up from that, or if you don't give it a base time, it will use the 39ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek * time at which you call {@link #start}. 40ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek * 41ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek * <p>The timer can also count downward towards the base time by 42ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek * setting {@link #setCountDown(boolean)} to true. 43ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek * 44ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek * <p>By default it will display the current 459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * timer value in the form "MM:SS" or "H:MM:SS", or you can use {@link #setFormat} 469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * to format the timer value into an arbitrary string. 479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @attr ref android.R.styleable#Chronometer_format 49ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek * @attr ref android.R.styleable#Chronometer_countDown 509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project@RemoteView 529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class Chronometer extends TextView { 539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String TAG = "Chronometer"; 549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * A callback that notifies when the chronometer has incremented on its own. 579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public interface OnChronometerTickListener { 599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Notification that the chronometer has changed. 629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project void onChronometerTick(Chronometer chronometer); 649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private long mBase; 68a79a747ca006d78b969f85227670f36572fde042Dan Sandler private long mNow; // the currently displayed time 699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private boolean mVisible; 709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private boolean mStarted; 719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private boolean mRunning; 729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private boolean mLogged; 739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private String mFormat; 749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Formatter mFormatter; 759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Locale mFormatterLocale; 769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Object[] mFormatterArgs = new Object[1]; 779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private StringBuilder mFormatBuilder; 789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private OnChronometerTickListener mOnChronometerTickListener; 799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private StringBuilder mRecycle = new StringBuilder(8); 80ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek private boolean mCountDown; 819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Initialize this Chronometer object. 849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Sets the base to the current time. 859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public Chronometer(Context context) { 879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project this(context, null, 0); 889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Initialize with standard view layout information. 929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Sets the base to the current time. 939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public Chronometer(Context context, AttributeSet attrs) { 959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project this(context, attrs, 0); 969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Initialize with standard view layout information and style. 1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Sets the base to the current time. 1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 102617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette public Chronometer(Context context, AttributeSet attrs, int defStyleAttr) { 103617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette this(context, attrs, defStyleAttr, 0); 104617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette } 105617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette 106617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette public Chronometer(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 107617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette super(context, attrs, defStyleAttr, defStyleRes); 1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 109617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette final TypedArray a = context.obtainStyledAttributes( 110617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette attrs, com.android.internal.R.styleable.Chronometer, defStyleAttr, defStyleRes); 111ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek setFormat(a.getString(R.styleable.Chronometer_format)); 112ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek setCountDown(a.getBoolean(R.styleable.Chronometer_countDown, false)); 1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project a.recycle(); 1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project init(); 1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void init() { 1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mBase = SystemClock.elapsedRealtime(); 1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project updateText(mBase); 1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 124ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek * Set this view to count down to the base instead of counting up from it. 125ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek * 126ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek * @param countDown whether this view should count down 127ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek * 128ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek * @see #setBase(long) 129ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek */ 130ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek @android.view.RemotableViewMethod 131ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek public void setCountDown(boolean countDown) { 132ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek mCountDown = countDown; 133a2a0171e7cfe305309ea72270204141c588d469dSelim Cinek updateText(SystemClock.elapsedRealtime()); 134ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek } 135ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek 136ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek /** 137ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek * @return whether this view counts down 138ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek * 139ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek * @see #setCountDown(boolean) 140ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek */ 141ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek public boolean isCountDown() { 142ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek return mCountDown; 143ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek } 144ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek 145ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek /** 1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Set the time that the count-up timer is in reference to. 1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param base Use the {@link SystemClock#elapsedRealtime} time base. 1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @android.view.RemotableViewMethod 1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void setBase(long base) { 1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mBase = base; 1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dispatchChronometerTick(); 1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project updateText(SystemClock.elapsedRealtime()); 1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return the base time as set through {@link #setBase}. 1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public long getBase() { 1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mBase; 1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Sets the format string used for display. The Chronometer will display 1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * this string, with the first "%s" replaced by the current timer value in 1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * "MM:SS" or "H:MM:SS" form. 1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * If the format string is null, or if you never call setFormat(), the 1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Chronometer will simply display the timer value in "MM:SS" or "H:MM:SS" 1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * form. 1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param format the format string. 1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @android.view.RemotableViewMethod 1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void setFormat(String format) { 1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mFormat = format; 1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (format != null && mFormatBuilder == null) { 1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mFormatBuilder = new StringBuilder(format.length() * 2); 1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Returns the current format string as set through {@link #setFormat}. 1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public String getFormat() { 1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mFormat; 1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Sets the listener to be called when the chronometer changes. 1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param listener The listener. 1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void setOnChronometerTickListener(OnChronometerTickListener listener) { 1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mOnChronometerTickListener = listener; 1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @return The listener (may be null) that is listening for chronometer change 2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * events. 2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public OnChronometerTickListener getOnChronometerTickListener() { 2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mOnChronometerTickListener; 2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Start counting up. This does not affect the base as set from {@link #setBase}, just 2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the view display. 2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Chronometer works by regularly scheduling messages to the handler, even when the 2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Widget is not visible. To make sure resource leaks do not occur, the user should 2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * make sure that each start() call has a reciprocal call to {@link #stop}. 2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void start() { 2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mStarted = true; 2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project updateRunning(); 2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Stop counting up. This does not affect the base as set from {@link #setBase}, just 2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the view display. 2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This stops the messages to the handler, effectively releasing resources that would 2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * be held as the chronometer is running, via {@link #start}. 2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void stop() { 2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mStarted = false; 2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project updateRunning(); 2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * The same as calling {@link #start} or {@link #stop}. 2343ff7eb92ade41dd1098b9f549662af4e2e467840Jeffrey Sharkey * @hide pending API council approval 2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @android.view.RemotableViewMethod 2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void setStarted(boolean started) { 2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mStarted = started; 2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project updateRunning(); 2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project protected void onDetachedFromWindow() { 2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project super.onDetachedFromWindow(); 2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mVisible = false; 2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project updateRunning(); 2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project protected void onWindowVisibilityChanged(int visibility) { 2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project super.onWindowVisibilityChanged(visibility); 2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mVisible = visibility == VISIBLE; 2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project updateRunning(); 2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private synchronized void updateText(long now) { 257a79a747ca006d78b969f85227670f36572fde042Dan Sandler mNow = now; 258ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek long seconds = mCountDown ? mBase - now : now - mBase; 2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project seconds /= 1000; 260ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek boolean negative = false; 261ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek if (seconds < 0) { 262ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek seconds = -seconds; 263ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek negative = true; 264ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek } 2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String text = DateUtils.formatElapsedTime(mRecycle, seconds); 266ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek if (negative) { 267ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek text = getResources().getString(R.string.negative_duration, text); 268ed1a33cc86a80017be195f1c6485ef8089b992acSelim Cinek } 2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mFormat != null) { 2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Locale loc = Locale.getDefault(); 2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mFormatter == null || !loc.equals(mFormatterLocale)) { 2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mFormatterLocale = loc; 2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mFormatter = new Formatter(mFormatBuilder, loc); 2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mFormatBuilder.setLength(0); 2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mFormatterArgs[0] = text; 2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mFormatter.format(mFormat, mFormatterArgs); 2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project text = mFormatBuilder.toString(); 2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (IllegalFormatException ex) { 2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!mLogged) { 2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.w(TAG, "Illegal format string: " + mFormat); 2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mLogged = true; 2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project setText(text); 2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void updateRunning() { 2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean running = mVisible && mStarted; 2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (running != mRunning) { 2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (running) { 2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project updateText(SystemClock.elapsedRealtime()); 2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dispatchChronometerTick(); 297d0374c6b25c3ad8e638827bd8190553f80d9bf22John Reck postDelayed(mTickRunnable, 1000); 2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 299d0374c6b25c3ad8e638827bd8190553f80d9bf22John Reck removeCallbacks(mTickRunnable); 3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mRunning = running; 3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 304d0374c6b25c3ad8e638827bd8190553f80d9bf22John Reck 305d0374c6b25c3ad8e638827bd8190553f80d9bf22John Reck private final Runnable mTickRunnable = new Runnable() { 306d0374c6b25c3ad8e638827bd8190553f80d9bf22John Reck @Override 307d0374c6b25c3ad8e638827bd8190553f80d9bf22John Reck public void run() { 3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mRunning) { 3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project updateText(SystemClock.elapsedRealtime()); 3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dispatchChronometerTick(); 311d0374c6b25c3ad8e638827bd8190553f80d9bf22John Reck postDelayed(mTickRunnable, 1000); 3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project }; 3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project void dispatchChronometerTick() { 3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mOnChronometerTickListener != null) { 3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mOnChronometerTickListener.onChronometerTick(this); 3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3218a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov 322a79a747ca006d78b969f85227670f36572fde042Dan Sandler private static final int MIN_IN_SEC = 60; 323a79a747ca006d78b969f85227670f36572fde042Dan Sandler private static final int HOUR_IN_SEC = MIN_IN_SEC*60; 324a79a747ca006d78b969f85227670f36572fde042Dan Sandler private static String formatDuration(long ms) { 325a79a747ca006d78b969f85227670f36572fde042Dan Sandler final Resources res = Resources.getSystem(); 326a79a747ca006d78b969f85227670f36572fde042Dan Sandler final StringBuilder text = new StringBuilder(); 327a79a747ca006d78b969f85227670f36572fde042Dan Sandler 328a79a747ca006d78b969f85227670f36572fde042Dan Sandler int duration = (int) (ms / DateUtils.SECOND_IN_MILLIS); 329a79a747ca006d78b969f85227670f36572fde042Dan Sandler if (duration < 0) { 330a79a747ca006d78b969f85227670f36572fde042Dan Sandler duration = -duration; 331a79a747ca006d78b969f85227670f36572fde042Dan Sandler } 332a79a747ca006d78b969f85227670f36572fde042Dan Sandler 333a79a747ca006d78b969f85227670f36572fde042Dan Sandler int h = 0; 334a79a747ca006d78b969f85227670f36572fde042Dan Sandler int m = 0; 335a79a747ca006d78b969f85227670f36572fde042Dan Sandler 336a79a747ca006d78b969f85227670f36572fde042Dan Sandler if (duration >= HOUR_IN_SEC) { 337a79a747ca006d78b969f85227670f36572fde042Dan Sandler h = duration / HOUR_IN_SEC; 338a79a747ca006d78b969f85227670f36572fde042Dan Sandler duration -= h * HOUR_IN_SEC; 339a79a747ca006d78b969f85227670f36572fde042Dan Sandler } 340a79a747ca006d78b969f85227670f36572fde042Dan Sandler if (duration >= MIN_IN_SEC) { 341a79a747ca006d78b969f85227670f36572fde042Dan Sandler m = duration / MIN_IN_SEC; 342a79a747ca006d78b969f85227670f36572fde042Dan Sandler duration -= m * MIN_IN_SEC; 343a79a747ca006d78b969f85227670f36572fde042Dan Sandler } 344a79a747ca006d78b969f85227670f36572fde042Dan Sandler int s = duration; 345a79a747ca006d78b969f85227670f36572fde042Dan Sandler 346a79a747ca006d78b969f85227670f36572fde042Dan Sandler try { 347a79a747ca006d78b969f85227670f36572fde042Dan Sandler if (h > 0) { 348a79a747ca006d78b969f85227670f36572fde042Dan Sandler text.append(res.getQuantityString( 349a79a747ca006d78b969f85227670f36572fde042Dan Sandler com.android.internal.R.plurals.duration_hours, h, h)); 350a79a747ca006d78b969f85227670f36572fde042Dan Sandler } 351a79a747ca006d78b969f85227670f36572fde042Dan Sandler if (m > 0) { 352a79a747ca006d78b969f85227670f36572fde042Dan Sandler if (text.length() > 0) { 353a79a747ca006d78b969f85227670f36572fde042Dan Sandler text.append(' '); 354a79a747ca006d78b969f85227670f36572fde042Dan Sandler } 355a79a747ca006d78b969f85227670f36572fde042Dan Sandler text.append(res.getQuantityString( 356a79a747ca006d78b969f85227670f36572fde042Dan Sandler com.android.internal.R.plurals.duration_minutes, m, m)); 357a79a747ca006d78b969f85227670f36572fde042Dan Sandler } 358a79a747ca006d78b969f85227670f36572fde042Dan Sandler 359a79a747ca006d78b969f85227670f36572fde042Dan Sandler if (text.length() > 0) { 360a79a747ca006d78b969f85227670f36572fde042Dan Sandler text.append(' '); 361a79a747ca006d78b969f85227670f36572fde042Dan Sandler } 362a79a747ca006d78b969f85227670f36572fde042Dan Sandler text.append(res.getQuantityString( 363a79a747ca006d78b969f85227670f36572fde042Dan Sandler com.android.internal.R.plurals.duration_seconds, s, s)); 364a79a747ca006d78b969f85227670f36572fde042Dan Sandler } catch (Resources.NotFoundException e) { 365a79a747ca006d78b969f85227670f36572fde042Dan Sandler // Ignore; plurals throws an exception for an untranslated quantity for a given locale. 366a79a747ca006d78b969f85227670f36572fde042Dan Sandler return null; 367a79a747ca006d78b969f85227670f36572fde042Dan Sandler } 368a79a747ca006d78b969f85227670f36572fde042Dan Sandler return text.toString(); 369a79a747ca006d78b969f85227670f36572fde042Dan Sandler } 370a79a747ca006d78b969f85227670f36572fde042Dan Sandler 371a79a747ca006d78b969f85227670f36572fde042Dan Sandler @Override 372a79a747ca006d78b969f85227670f36572fde042Dan Sandler public CharSequence getContentDescription() { 373a79a747ca006d78b969f85227670f36572fde042Dan Sandler return formatDuration(mNow - mBase); 374a79a747ca006d78b969f85227670f36572fde042Dan Sandler } 375a79a747ca006d78b969f85227670f36572fde042Dan Sandler 3768a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov @Override 377a7bb6fbeab933326d58aa806d8194b7b13239d34Dianne Hackborn public CharSequence getAccessibilityClassName() { 378a7bb6fbeab933326d58aa806d8194b7b13239d34Dianne Hackborn return Chronometer.class.getName(); 3798a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov } 3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 381