Skip to content

Instantly share code, notes, and snippets.

@draekko
Created November 7, 2017 21:43
Show Gist options
  • Save draekko/3ae830055c708ffe73a7c6a1aecf75f8 to your computer and use it in GitHub Desktop.
Save draekko/3ae830055c708ffe73a7c6a1aecf75f8 to your computer and use it in GitHub Desktop.
Chronometer class to display timer in HH:MM:SS format based on the Android Chronometer class.
/*
* Copyright (C) 2008 The Android Open Source Project
* Copyright (C) 2017 Benoit Touchette (Draekko RAND)
*
* 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.draekko.clock;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.widget.RemoteViews.RemoteView;
import java.util.Locale;
/**
* Class that implements a simple timer.
* <p>
* You can give it a start time in the {@link SystemClock#elapsedRealtime} timebase,
* and it counts up from that, or if you don't give it a base time, it will use the
* time at which you call {@link #start}. The default time format is HH:MM:SS.
*/
@RemoteView
public class DRChronometer extends android.support.v7.widget.AppCompatTextView {
private static final String TAG = "DRChronometer";
private long mBase;
private boolean mVisible;
private boolean mStarted;
private boolean mRunning;
private boolean mLogged;
private String mDefaultFormat = "%02d:%02d:%02d";
/**
* Initialize this Chronometer object.
* Sets the base to the current time.
*/
public DRChronometer(Context context) {
this(context, null, 0);
}
/**
* Initialize with standard view layout information.
* Sets the base to the current time.
*/
public DRChronometer(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
/**
* Initialize with standard view layout information and style.
* Sets the base to the current time.
*/
public DRChronometer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
mBase = SystemClock.elapsedRealtime();
updateText(mBase);
}
/**
* Set the time that the count-up timer is in reference to.
*
* @param base Use the {@link SystemClock#elapsedRealtime} time base.
*/
public void setBase(long base) {
mBase = base;
updateText(SystemClock.elapsedRealtime());
}
/**
* Return the base time as set through {@link #setBase}.
*/
public long getBase() {
return mBase;
}
/**
* Start counting up. This does not affect the base as set from {@link #setBase}, just
* the view display.
*
* Chronometer works by regularly scheduling messages to the handler, even when the
* Widget is not visible. To make sure resource leaks do not occur, the user should
* make sure that each start() call has a reciprocal call to {@link #stop}.
*/
public void start() {
mStarted = true;
updateRunning();
}
/**
* Stop counting up. This does not affect the base as set from {@link #setBase}, just
* the view display.
*
* This stops the messages to the handler, effectively releasing resources that would
* be held as the chronometer is running, via {@link #start}.
*/
public void stop() {
mStarted = false;
updateRunning();
long now = SystemClock.elapsedRealtime();
setBase(now);
updateText(now);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mVisible = false;
updateRunning();
}
@Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
mVisible = visibility == VISIBLE;
updateRunning();
}
private void updateText(long now) {
long seconds = (now - mBase) / 1000;
int hh = (int)(seconds / 3600);
int mm = (int)((seconds % 3600) / 60);
int ss = (int)(seconds % 60);
String text = String.format(Locale.US, mDefaultFormat, hh, mm, ss);
setText(text);
}
private void updateRunning() {
boolean running = mVisible && mStarted;
if (running != mRunning) {
if (running) {
updateText(SystemClock.elapsedRealtime());
mHandler.sendMessageDelayed(Message.obtain(), 1000);
}
mRunning = running;
}
}
private Handler mHandler = new Handler() {
public void handleMessage(Message m) {
if (mStarted) {
updateText(SystemClock.elapsedRealtime());
sendMessageDelayed(Message.obtain(), 1000);
}
}
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment