/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.glass.sample.stopwatch;
import android.content.Context;
import android.os.Handler;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.FrameLayout;
import android.widget.TextView;
import java.util.concurrent.TimeUnit;
/**
* View used to display draw a running Chronometer.
*
* This code is greatly inspired by the Android's Chronometer widget.
*/
public class ChronometerView extends FrameLayout {
/**
* Interface to listen for changes on the view layout.
*/
public interface Listener {
/** Notified of a change in the view. */
public void onChange();
}
/** About 24 FPS, visible for testing. */
static final long DELAY_MILLIS = 41;
private final TextView mMinutesView;
private final TextView mSecondsView;
private final TextView mCentiSecondsView;
private final Handler mHandler = new Handler();
private final Runnable mUpdateTextRunnable = new Runnable() {
@Override
public void run() {
if (mRunning) {
updateText();
postDelayed(mUpdateTextRunnable, DELAY_MILLIS);
}
}
};
private boolean mStarted;
private boolean mForceStart;
private boolean mVisible;
private boolean mRunning;
private long mBaseMillis;
private Listener mChangeListener;
public ChronometerView(Context context) {
this(context, null, 0);
}
public ChronometerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ChronometerView(Context context, AttributeSet attrs, int style) {
super(context, attrs, style);
LayoutInflater.from(context).inflate(R.layout.card_chronometer, this);
mMinutesView = (TextView) findViewById(R.id.minute);
mSecondsView = (TextView) findViewById(R.id.second);
mCentiSecondsView = (TextView) findViewById(R.id.centi_second);
setBaseMillis(getElapsedRealtime());
}
/**
* Sets the base value of the chronometer in milliseconds.
*/
public void setBaseMillis(long baseMillis) {
mBaseMillis = baseMillis;
updateText();
}
/**
* Gets the base value of the chronometer in milliseconds.
*/
public long getBaseMillis() {
return mBaseMillis;
}
/**
* Sets a {@link Listener}.
*/
public void setListener(Listener listener) {
mChangeListener = listener;
}
/**
* Returns the set {@link Listener}.
*/
public Listener getListener() {
return mChangeListener;
}
/**
* Starts the chronometer.
*/
public void start() {
if (!mRunning) {
postDelayed(mUpdateTextRunnable, DELAY_MILLIS);
}
mRunning = true;
}
/**
* Stops the chronometer.
*/
public void stop() {
if (mRunning) {
removeCallbacks(mUpdateTextRunnable);
}
mRunning = false;
}
@Override
public boolean postDelayed(Runnable action, long delayMillis) {
return mHandler.postDelayed(action, delayMillis);
}
@Override
public boolean removeCallbacks(Runnable action) {
mHandler.removeCallbacks(action);
return true;
}
/**
* Returns {@link SystemClock.elapsedRealtime}, overridable for testing.
*/
protected long getElapsedRealtime() {
return SystemClock.elapsedRealtime();
}
/**
* Updates the value of the chronometer, visible for testing.
*/
void updateText() {
long millis = getElapsedRealtime() - mBaseMillis;
// Cap chronometer to one hour.
millis %= TimeUnit.HOURS.toMillis(1);
mMinutesView.setText(String.format("%02d", TimeUnit.MILLISECONDS.toMinutes(millis)));
millis %= TimeUnit.MINUTES.toMillis(1);
mSecondsView.setText(String.format("%02d", TimeUnit.MILLISECONDS.toSeconds(millis)));
millis = (millis % TimeUnit.SECONDS.toMillis(1)) / 10;
mCentiSecondsView.setText(String.format("%02d", millis));
if (mChangeListener != null) {
mChangeListener.onChange();
}
}
}