Animated progress bar

This commit is contained in:
Phillip Hsu 2016-08-08 02:57:21 -07:00
parent 07de78e8e7
commit 74ec0fd883
6 changed files with 87 additions and 8 deletions

View File

@ -18,6 +18,16 @@ public abstract class Timer extends ObjectWithId implements Parcelable {
private long endTime; private long endTime;
private long pauseTime; private long pauseTime;
private long duration;
// Using this crashes the app when we create a Timer and start it...
// timeRemaining() is returning a negative value... but it doesn't even
// consider duration()....?
// My guess is the hour, minute, and second getters are returning 0
// at this point...?
// private final long normalDuration = TimeUnit.HOURS.toMillis(hour())
// + TimeUnit.MINUTES.toMillis(minute())
// + TimeUnit.SECONDS.toMillis(second());
public abstract int hour(); public abstract int hour();
public abstract int minute(); public abstract int minute();
@ -61,9 +71,12 @@ public abstract class Timer extends ObjectWithId implements Parcelable {
} }
public long duration() { public long duration() {
return TimeUnit.HOURS.toMillis(hour()) if (duration == 0) {
+ TimeUnit.MINUTES.toMillis(minute()) duration = TimeUnit.HOURS.toMillis(hour())
+ TimeUnit.SECONDS.toMillis(second()); + TimeUnit.MINUTES.toMillis(minute())
+ TimeUnit.SECONDS.toMillis(second());
}
return duration;
} }
public void start() { public void start() {
@ -89,6 +102,7 @@ public abstract class Timer extends ObjectWithId implements Parcelable {
public void stop() { public void stop() {
endTime = 0; endTime = 0;
pauseTime = 0; pauseTime = 0;
duration = 0;
} }
public void addOneMinute() { public void addOneMinute() {
@ -97,8 +111,17 @@ public abstract class Timer extends ObjectWithId implements Parcelable {
// throw new IllegalStateException("Cannot extend a timer that is not running"); // throw new IllegalStateException("Cannot extend a timer that is not running");
if (expired()) { if (expired()) {
endTime = SystemClock.elapsedRealtime() + MINUTE; endTime = SystemClock.elapsedRealtime() + MINUTE;
// If the timer's normal duration is >= MINUTE, then an extra run time of one minute
// will still be within the normal duration. Thus, the progress calculation does not
// need to change. For example, if the timer's normal duration is 2 minutes, an extra
// 1 minute run time is fully encapsulated within the 2 minute upper bound.
if (duration < MINUTE) {
// This scales the progress bar to a full minute.
duration = MINUTE;
}
} else { } else {
endTime += MINUTE; endTime += MINUTE;
duration += MINUTE;
} }
} }
@ -131,6 +154,13 @@ public abstract class Timer extends ObjectWithId implements Parcelable {
return pauseTime; return pauseTime;
} }
/**
* TO ONLY BE CALLED BY TIMERDATABASEHELPER.
*/
public void setDuration(long duration) {
this.duration = duration;
}
@Override @Override
public int describeContents() { public int describeContents() {
return 0; return 0;
@ -146,6 +176,7 @@ public abstract class Timer extends ObjectWithId implements Parcelable {
dest.writeLong(getId()); dest.writeLong(getId());
dest.writeLong(endTime); dest.writeLong(endTime);
dest.writeLong(pauseTime); dest.writeLong(pauseTime);
dest.writeLong(duration);
} }
public static final Creator<Timer> CREATOR = new Creator<Timer>() { public static final Creator<Timer> CREATOR = new Creator<Timer>() {
@ -166,6 +197,7 @@ public abstract class Timer extends ObjectWithId implements Parcelable {
t.setId(source.readLong()); t.setId(source.readLong());
t.endTime = source.readLong(); t.endTime = source.readLong();
t.pauseTime = source.readLong(); t.pauseTime = source.readLong();
t.duration = source.readLong();
return t; return t;
} }
} }

View File

@ -26,6 +26,7 @@ public class TimerCursor extends BaseItemCursor<Timer> {
t.setId(getLong(getColumnIndexOrThrow(TimersTable.COLUMN_ID))); t.setId(getLong(getColumnIndexOrThrow(TimersTable.COLUMN_ID)));
t.setEndTime(getLong(getColumnIndexOrThrow(TimersTable.COLUMN_END_TIME))); t.setEndTime(getLong(getColumnIndexOrThrow(TimersTable.COLUMN_END_TIME)));
t.setPauseTime(getLong(getColumnIndexOrThrow(TimersTable.COLUMN_PAUSE_TIME))); t.setPauseTime(getLong(getColumnIndexOrThrow(TimersTable.COLUMN_PAUSE_TIME)));
t.setDuration(getLong(getColumnIndexOrThrow(TimersTable.COLUMN_DURATION)));
return t; return t;
} }
} }

View File

@ -27,9 +27,9 @@ public final class TimersTable {
public static final String COLUMN_END_TIME = "end_time"; public static final String COLUMN_END_TIME = "end_time";
public static final String COLUMN_PAUSE_TIME = "pause_time"; public static final String COLUMN_PAUSE_TIME = "pause_time";
public static final String COLUMN_DURATION = "duration";
public static final String SORT_ORDER = public static final String SORT_ORDER = COLUMN_HOUR + " ASC, "
COLUMN_HOUR + " ASC, "
+ COLUMN_MINUTE + " ASC, " + COLUMN_MINUTE + " ASC, "
+ COLUMN_SECOND + " ASC, " + COLUMN_SECOND + " ASC, "
// All else equal, newer timers first // All else equal, newer timers first
@ -44,7 +44,8 @@ public final class TimersTable {
+ COLUMN_LABEL + " TEXT NOT NULL, " + COLUMN_LABEL + " TEXT NOT NULL, "
// + COLUMN_GROUP + " TEXT NOT NULL, " // + COLUMN_GROUP + " TEXT NOT NULL, "
+ COLUMN_END_TIME + " INTEGER NOT NULL, " + COLUMN_END_TIME + " INTEGER NOT NULL, "
+ COLUMN_PAUSE_TIME + " INTEGER NOT NULL);"); + COLUMN_PAUSE_TIME + " INTEGER NOT NULL, "
+ COLUMN_DURATION + " INTEGER NOT NULL);");
} }
public static void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { public static void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

View File

@ -53,6 +53,7 @@ public class TimersTableManager extends DatabaseTableManager<Timer> {
Log.d(TAG, "endTime = " + timer.endTime() + ", pauseTime = " + timer.pauseTime()); Log.d(TAG, "endTime = " + timer.endTime() + ", pauseTime = " + timer.pauseTime());
cv.put(TimersTable.COLUMN_END_TIME, timer.endTime()); cv.put(TimersTable.COLUMN_END_TIME, timer.endTime());
cv.put(TimersTable.COLUMN_PAUSE_TIME, timer.pauseTime()); cv.put(TimersTable.COLUMN_PAUSE_TIME, timer.pauseTime());
cv.put(TimersTable.COLUMN_DURATION, timer.duration());
return cv; return cv;
} }

View File

@ -1,5 +1,7 @@
package com.philliphsu.clock2.timers; package com.philliphsu.clock2.timers;
import android.animation.ObjectAnimator;
import android.util.Log;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageButton; import android.widget.ImageButton;
@ -20,9 +22,11 @@ import butterknife.OnClick;
*/ */
public class TimerViewHolder extends BaseViewHolder<Timer> { public class TimerViewHolder extends BaseViewHolder<Timer> {
private static final String TAG = "TimerViewHolder"; private static final String TAG = "TimerViewHolder";
private static final int MAX_PROGRESS = 10000;
private final AsyncTimersTableUpdateHandler mAsyncTimersTableUpdateHandler; private final AsyncTimersTableUpdateHandler mAsyncTimersTableUpdateHandler;
private TimerController mController; private TimerController mController;
private ObjectAnimator mProgressAnimator;
@Bind(R.id.label) TextView mLabel; @Bind(R.id.label) TextView mLabel;
@Bind(R.id.duration) CountdownChronometer mChronometer; @Bind(R.id.duration) CountdownChronometer mChronometer;
@ -34,17 +38,20 @@ public class TimerViewHolder extends BaseViewHolder<Timer> {
public TimerViewHolder(ViewGroup parent, OnListItemInteractionListener<Timer> listener, public TimerViewHolder(ViewGroup parent, OnListItemInteractionListener<Timer> listener,
AsyncTimersTableUpdateHandler asyncTimersTableUpdateHandler) { AsyncTimersTableUpdateHandler asyncTimersTableUpdateHandler) {
super(parent, R.layout.item_timer, listener); super(parent, R.layout.item_timer, listener);
Log.d(TAG, "New TimerViewHolder");
mAsyncTimersTableUpdateHandler = asyncTimersTableUpdateHandler; mAsyncTimersTableUpdateHandler = asyncTimersTableUpdateHandler;
} }
@Override @Override
public void onBind(Timer timer) { public void onBind(final Timer timer) {
super.onBind(timer); super.onBind(timer);
Log.d(TAG, "Binding TimerViewHolder");
// TOneverDO: create before super // TOneverDO: create before super
mController = new TimerController(timer, mAsyncTimersTableUpdateHandler); mController = new TimerController(timer, mAsyncTimersTableUpdateHandler);
bindLabel(timer.label()); bindLabel(timer.label());
bindChronometer(timer); bindChronometer(timer);
bindButtonControls(timer); bindButtonControls(timer);
bindProgressBar(timer);
} }
@OnClick(R.id.start_pause) @OnClick(R.id.start_pause)
@ -106,4 +113,42 @@ public class TimerViewHolder extends BaseViewHolder<Timer> {
mAddOneMinute.setVisibility(visibility); mAddOneMinute.setVisibility(visibility);
mStop.setVisibility(visibility); mStop.setVisibility(visibility);
} }
private void bindProgressBar(Timer timer) {
mProgressBar.setMax(MAX_PROGRESS);
final long timeRemaining = timer.timeRemaining();
final int progress = (int) (MAX_PROGRESS * (double) timeRemaining / timer.duration());
// In case we're reusing an animator instance that could be running
if (mProgressAnimator != null && mProgressAnimator.isRunning()) {
mProgressAnimator.end();
}
if (!timer.isRunning()) {
mProgressBar.setProgress(progress);
} else {
mProgressAnimator = ObjectAnimator.ofInt(
// The object that has the property we wish to animate
mProgressBar,
// The name of the property of the object that identifies which setter method
// the animation will call to update its values. Here, a property name of
// "progress" will result in a call to the function setProgress() in ProgressBar.
// The docs for ObjectAnimator#setPropertyName() says that for best performance,
// the setter method should take a float or int parameter, and its return type
// should be void (both of which setProgress() satisfies).
"progress",
// The set of values to animate between. A single value implies that that value
// is the one being animated to. Two values imply starting and ending values.
// More than two values imply a starting value, values to animate through along
// the way, and an ending value (these values will be distributed evenly across
// the duration of the animation).
progress, 0);
mProgressAnimator.setDuration(timeRemaining);
// The algorithm that calculates intermediate values between keyframes. We use linear
// interpolation so that the animation runs at constant speed.
mProgressAnimator.setInterpolator(null/*results in linear interpolation*/);
// This MUST be run on the UI thread.
mProgressAnimator.start();
}
}
} }

View File

@ -34,7 +34,6 @@
android:id="@+id/progress_bar" android:id="@+id/progress_bar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:progress="90"
style="@style/Widget.AppCompat.ProgressBar.Horizontal" style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_below="@id/duration"/> android:layout_below="@id/duration"/>