Sync chronometers and start/pause icon state in stopwatch notification and StopwatchFragment when resuming and pausing the stopwatch
This commit is contained in:
parent
0f16cebd7c
commit
8f01874cbd
@ -4,6 +4,7 @@ import android.animation.Animator;
|
|||||||
import android.animation.ObjectAnimator;
|
import android.animation.ObjectAnimator;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
@ -107,6 +108,7 @@ public class StopwatchFragment extends RecyclerViewFragment<
|
|||||||
// visibility to invisible in XML. We haven't initialized the WeakReference to
|
// visibility to invisible in XML. We haven't initialized the WeakReference to
|
||||||
// our Activity's FAB yet, so this call does nothing with the FAB.
|
// our Activity's FAB yet, so this call does nothing with the FAB.
|
||||||
setMiniFabsVisible(isStopwatchRunning());
|
setMiniFabsVisible(isStopwatchRunning());
|
||||||
|
mPrefs.registerOnSharedPreferenceChangeListener(mPrefChangeListener);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,6 +164,7 @@ public class StopwatchFragment extends RecyclerViewFragment<
|
|||||||
Log.d(TAG, "onDestroyView()");
|
Log.d(TAG, "onDestroyView()");
|
||||||
Log.d(TAG, "mStartTime = " + mStartTime
|
Log.d(TAG, "mStartTime = " + mStartTime
|
||||||
+ ", mPauseTime = " + mPauseTime);
|
+ ", mPauseTime = " + mPauseTime);
|
||||||
|
mPrefs.unregisterOnSharedPreferenceChangeListener(mPrefChangeListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -220,50 +223,18 @@ public class StopwatchFragment extends RecyclerViewFragment<
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFabClick() {
|
public void onFabClick() {
|
||||||
final Intent serviceIntent = new Intent(getActivity(), StopwatchNotificationService.class);
|
|
||||||
|
|
||||||
final boolean running = mChronometer.isRunning();
|
final boolean running = mChronometer.isRunning();
|
||||||
syncFabIconWithStopwatchState(!running/*invert the current state*/);
|
syncFabIconWithStopwatchState(!running/*invert the current state*/);
|
||||||
|
|
||||||
if (running) {
|
final Intent serviceIntent = new Intent(getActivity(), StopwatchNotificationService.class);
|
||||||
mPauseTime = SystemClock.elapsedRealtime();
|
if (mStartTime == 0) {
|
||||||
mChronometer.stop();
|
// addNewLap() won't call through unless chronometer is running, which
|
||||||
mCurrentLap.pause();
|
// we can't start until we compute mStartTime
|
||||||
mUpdateHandler.asyncUpdate(mCurrentLap.getId(), mCurrentLap);
|
mCurrentLap = new Lap();
|
||||||
// No issues controlling the animator here, because onLoadFinished() can't
|
mUpdateHandler.asyncInsert(mCurrentLap);
|
||||||
// call through to startNewProgressBarAnimator(), because by that point
|
setMiniFabsVisible(true);
|
||||||
// the chronometer won't be running.
|
// Handle the default action, i.e. post the notification for the first time.
|
||||||
if (mProgressAnimator != null) {
|
getActivity().startService(serviceIntent);
|
||||||
// We may as well call cancel(), since our resume() call would be
|
|
||||||
// rendered meaningless.
|
|
||||||
// mProgressAnimator.pause();
|
|
||||||
mProgressAnimator.cancel();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (mStartTime == 0) {
|
|
||||||
// addNewLap() won't call through unless chronometer is running, which
|
|
||||||
// we can't start until we compute mStartTime
|
|
||||||
mCurrentLap = new Lap();
|
|
||||||
mUpdateHandler.asyncInsert(mCurrentLap);
|
|
||||||
setMiniFabsVisible(true);
|
|
||||||
// Handle the default action, i.e. post the notification for the first time.
|
|
||||||
getActivity().startService(serviceIntent);
|
|
||||||
}
|
|
||||||
mStartTime += SystemClock.elapsedRealtime() - mPauseTime;
|
|
||||||
mPauseTime = 0;
|
|
||||||
mChronometer.setBase(mStartTime);
|
|
||||||
mChronometer.start();
|
|
||||||
if (!mCurrentLap.isRunning()) {
|
|
||||||
mCurrentLap.resume();
|
|
||||||
mUpdateHandler.asyncUpdate(mCurrentLap.getId(), mCurrentLap);
|
|
||||||
}
|
|
||||||
// This animator instance will end up having end() called on it. When
|
|
||||||
// the table update prompts us to requery, onLoadFinished will be called as a result.
|
|
||||||
// There, it calls startNewProgressAnimator() to end this animation and starts an
|
|
||||||
// entirely new animator instance.
|
|
||||||
// if (mProgressAnimator != null) {
|
|
||||||
// mProgressAnimator.resume();
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
serviceIntent.setAction(StopwatchNotificationService.ACTION_START_PAUSE);
|
serviceIntent.setAction(StopwatchNotificationService.ACTION_START_PAUSE);
|
||||||
getActivity().startService(serviceIntent);
|
getActivity().startService(serviceIntent);
|
||||||
@ -333,11 +304,56 @@ public class StopwatchFragment extends RecyclerViewFragment<
|
|||||||
syncFabIconWithStopwatchState(false);
|
syncFabIconWithStopwatchState(false);
|
||||||
|
|
||||||
// Remove the notification. This will also write to prefs and clear the laps table.
|
// Remove the notification. This will also write to prefs and clear the laps table.
|
||||||
|
//
|
||||||
|
// The service will make changes to shared prefs, which will fire our
|
||||||
|
// OnSharedPrefChangeListener, which will call this stop() method. As such, this
|
||||||
|
// stop() method will be called twice: first from the click, and the second from
|
||||||
|
// the OnSharedPrefChange callback.
|
||||||
|
// TODO: Make similar changes as you did with onFabClick() so that the above method calls
|
||||||
|
// are made in the OnSharedPrefChange callback. You may find it helpful to extract
|
||||||
|
// the above method calls into private helper methods, just as you did for
|
||||||
|
// onFabClick().
|
||||||
Intent stop = new Intent(getActivity(), StopwatchNotificationService.class)
|
Intent stop = new Intent(getActivity(), StopwatchNotificationService.class)
|
||||||
.setAction(StopwatchNotificationService.ACTION_STOP);
|
.setAction(StopwatchNotificationService.ACTION_STOP);
|
||||||
getActivity().startService(stop);
|
getActivity().startService(stop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void pauseStopwatch() {
|
||||||
|
mPauseTime = SystemClock.elapsedRealtime();
|
||||||
|
mChronometer.stop();
|
||||||
|
mCurrentLap.pause();
|
||||||
|
mUpdateHandler.asyncUpdate(mCurrentLap.getId(), mCurrentLap);
|
||||||
|
// No issues controlling the animator here, because onLoadFinished() can't
|
||||||
|
// call through to startNewProgressBarAnimator(), because by that point
|
||||||
|
// the chronometer won't be running.
|
||||||
|
if (mProgressAnimator != null) {
|
||||||
|
// We may as well call cancel(), since our resume() call would be
|
||||||
|
// rendered meaningless.
|
||||||
|
// mProgressAnimator.pause();
|
||||||
|
mProgressAnimator.cancel();
|
||||||
|
}
|
||||||
|
syncFabIconWithStopwatchState(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runStopwatch() {
|
||||||
|
mStartTime += SystemClock.elapsedRealtime() - mPauseTime;
|
||||||
|
mPauseTime = 0;
|
||||||
|
mChronometer.setBase(mStartTime);
|
||||||
|
mChronometer.start();
|
||||||
|
if (!mCurrentLap.isRunning()) {
|
||||||
|
mCurrentLap.resume();
|
||||||
|
mUpdateHandler.asyncUpdate(mCurrentLap.getId(), mCurrentLap);
|
||||||
|
}
|
||||||
|
// This animator instance will end up having end() called on it. When
|
||||||
|
// the table update prompts us to requery, onLoadFinished will be called as a result.
|
||||||
|
// There, it calls startNewProgressAnimator() to end this animation and starts an
|
||||||
|
// entirely new animator instance.
|
||||||
|
// if (mProgressAnimator != null) {
|
||||||
|
// mProgressAnimator.resume();
|
||||||
|
// }
|
||||||
|
syncFabIconWithStopwatchState(true);
|
||||||
|
}
|
||||||
|
|
||||||
private void setMiniFabsVisible(boolean visible) {
|
private void setMiniFabsVisible(boolean visible) {
|
||||||
int vis = visible ? View.VISIBLE : View.INVISIBLE;
|
int vis = visible ? View.VISIBLE : View.INVISIBLE;
|
||||||
mNewLapButton.setVisibility(vis);
|
mNewLapButton.setVisibility(vis);
|
||||||
@ -410,6 +426,7 @@ public class StopwatchFragment extends RecyclerViewFragment<
|
|||||||
return mPreviousLap.elapsed() - mCurrentLap.elapsed();
|
return mPreviousLap.elapsed() - mCurrentLap.elapsed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Delete.
|
||||||
private void savePrefs() {
|
private void savePrefs() {
|
||||||
mPrefs.edit().putLong(KEY_START_TIME, mStartTime)
|
mPrefs.edit().putLong(KEY_START_TIME, mStartTime)
|
||||||
.putLong(KEY_PAUSE_TIME, mPauseTime)
|
.putLong(KEY_PAUSE_TIME, mPauseTime)
|
||||||
@ -425,6 +442,43 @@ public class StopwatchFragment extends RecyclerViewFragment<
|
|||||||
return mChronometer.isRunning() || mPrefs.getBoolean(KEY_CHRONOMETER_RUNNING, false);
|
return mChronometer.isRunning() || mPrefs.getBoolean(KEY_CHRONOMETER_RUNNING, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final OnSharedPreferenceChangeListener mPrefChangeListener = new OnSharedPreferenceChangeListener() {
|
||||||
|
// TOneverDO: initial value >= 0
|
||||||
|
private long mNewStartTime = -1;
|
||||||
|
private long mNewPauseTime = -1;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||||
|
// int instead of boolean because -1 indicates "uninitialized" better than false
|
||||||
|
int setRunningFlag = -1;
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case KEY_CHRONOMETER_RUNNING:
|
||||||
|
setRunningFlag = sharedPreferences.getBoolean(key, false) ? 1 : 0;
|
||||||
|
break;
|
||||||
|
case KEY_START_TIME:
|
||||||
|
mNewStartTime = sharedPreferences.getLong(key, 0);
|
||||||
|
break;
|
||||||
|
case KEY_PAUSE_TIME:
|
||||||
|
mNewPauseTime = sharedPreferences.getLong(key, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setRunningFlag == 0) {
|
||||||
|
if (mNewStartTime == 0 && mNewPauseTime == 0) {
|
||||||
|
stop();
|
||||||
|
} else {
|
||||||
|
pauseStopwatch();
|
||||||
|
}
|
||||||
|
} else if (setRunningFlag == 1) {
|
||||||
|
// We don't need to check the values of mNewStartTime and mNewPauseTime, because
|
||||||
|
// we don't need to deduce between different methods to call; this is the only method
|
||||||
|
// that runs the stopwatch.
|
||||||
|
runStopwatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// ======================= DO NOT IMPLEMENT ============================
|
// ======================= DO NOT IMPLEMENT ============================
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user