[go: nahoru, domu]

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