Wrote potentially unnecessary stuff

This commit is contained in:
Phillip Hsu 2016-09-12 00:00:46 -07:00
parent e157498147
commit d6eeac1db1
6 changed files with 257 additions and 109 deletions

View File

@ -0,0 +1,92 @@
package com.philliphsu.clock2.stopwatch;
import android.content.SharedPreferences;
import android.support.annotation.NonNull;
/**
* Created by Phillip Hsu on 9/11/2016.
*/
public abstract class BaseStopwatchController { // TODO: Extend this for use in StopwatchFragment and StopwatchNotificationSErvice
// TODO: EIther expose these to subclasses or write an API for them to call
// to write to prefs.
private static final String KEY_START_TIME = "start_time";
private static final String KEY_PAUSE_TIME = "pause_time";
private static final String KEY_RUNNING = "running";
private final AsyncLapsTableUpdateHandler mUpdateHandler;
private final SharedPreferences mPrefs;
private Stopwatch mStopwatch;
private Lap mCurrentLap;
private Lap mPreviousLap;
public BaseStopwatchController(@NonNull AsyncLapsTableUpdateHandler updateHandler,
@NonNull SharedPreferences prefs) {
mUpdateHandler = updateHandler;
mPrefs = prefs;
long startTime = mPrefs.getLong(KEY_START_TIME, 0);
long pauseTime = mPrefs.getLong(KEY_PAUSE_TIME, 0);
mStopwatch = new Stopwatch(startTime, pauseTime);
}
public void run() {
if (!mStopwatch.hasStarted()) {
// addNewLap() won't call through unless chronometer is running, which
// we can't start until we compute mStartTime
mCurrentLap = new Lap();
mUpdateHandler.asyncInsert(mCurrentLap);
}
mStopwatch.run();
if (!mCurrentLap.isRunning()) {
mCurrentLap.resume();
mUpdateHandler.asyncUpdate(mCurrentLap.getId(), mCurrentLap);
}
savePrefs();
}
public void pause() {
mStopwatch.pause();
mCurrentLap.pause();
mUpdateHandler.asyncUpdate(mCurrentLap.getId(), mCurrentLap);
savePrefs();
}
public void stop() {
mStopwatch.stop();
mCurrentLap = null;
mPreviousLap = null;
mUpdateHandler.asyncClear(); // Clear laps
savePrefs();
}
public void addNewLap(String currentLapTotalText) {
if (mCurrentLap != null) {
mCurrentLap.end(currentLapTotalText);
}
mPreviousLap = mCurrentLap;
mCurrentLap = new Lap();
if (mPreviousLap != null) {
// if (getAdapter().getItemCount() == 0) {
// mUpdateHandler.asyncInsert(mPreviousLap);
// } else {
mUpdateHandler.asyncUpdate(mPreviousLap.getId(), mPreviousLap);
// }
}
mUpdateHandler.asyncInsert(mCurrentLap);
}
public final Stopwatch getStopwatch() {
return mStopwatch;
}
public boolean isStopwatchRunning() {
return mPrefs.getBoolean(KEY_RUNNING, false);
}
private void savePrefs() {
mPrefs.edit().putLong(KEY_START_TIME, mStopwatch.getStartTime())
.putLong(KEY_PAUSE_TIME, mStopwatch.getPauseTime())
.putBoolean(KEY_RUNNING, mStopwatch.isRunning())
.apply();
}
}

View File

@ -0,0 +1,55 @@
package com.philliphsu.clock2.stopwatch;
import android.os.SystemClock;
/**
* Created by Phillip Hsu on 9/11/2016.
*/
public final class Stopwatch {
private static final String TAG = "Stopwatch";
private long mStartTime;
private long mPauseTime;
public Stopwatch(long startTime, long pauseTime) {
mStartTime = startTime;
mPauseTime = pauseTime;
}
public synchronized void pause() {
if (!isRunning())
throw new IllegalStateException("This stopwatch cannot be paused because it is not running");
if (mPauseTime > 0)
throw new IllegalStateException("This stopwatch is already paused");
mPauseTime = SystemClock.elapsedRealtime();
}
public synchronized void run() {
if (isRunning())
throw new IllegalStateException("This stopwatch is already running");
mStartTime += SystemClock.elapsedRealtime() - mPauseTime;
mPauseTime = 0;
}
public synchronized void stop() {
mStartTime = 0;
mPauseTime = 0;
}
public long getStartTime() {
return mStartTime;
}
public long getPauseTime() {
return mPauseTime;
}
public boolean isRunning() {
return hasStarted() && mPauseTime == 0;
}
public boolean hasStarted() {
// Not required to be presently running to have been started
return mStartTime > 0;
}
}

View File

@ -1,10 +0,0 @@
package com.philliphsu.clock2.stopwatch;
/**
* Created by Phillip Hsu on 9/11/2016.
*/
public final class StopwatchController {
}

View File

@ -6,7 +6,6 @@ import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.os.SystemClock;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton; import android.support.design.widget.FloatingActionButton;
@ -37,22 +36,17 @@ public class StopwatchFragment extends RecyclerViewFragment<
LapsAdapter> { LapsAdapter> {
private static final String TAG = "StopwatchFragment"; private static final String TAG = "StopwatchFragment";
// Exposed for StopwatchNotificationService
static final String KEY_START_TIME = "start_time";
static final String KEY_PAUSE_TIME = "pause_time";
static final String KEY_CHRONOMETER_RUNNING = "chronometer_running";
private long mStartTime;
private long mPauseTime;
private Lap mCurrentLap;
private Lap mPreviousLap;
private AsyncLapsTableUpdateHandler mUpdateHandler;
private ObjectAnimator mProgressAnimator; private ObjectAnimator mProgressAnimator;
private SharedPreferences mPrefs;
private WeakReference<FloatingActionButton> mActivityFab; private WeakReference<FloatingActionButton> mActivityFab;
private Drawable mStartDrawable; private Drawable mStartDrawable;
private Drawable mPauseDrawable; private Drawable mPauseDrawable;
// TODO: Actual subclass
private BaseStopwatchController mController;
// For read-only purposes within this Fragment.
// Actual changes are persisted by the controller.
private Lap mCurrentLap;
private Lap mPreviousLap;
@Bind(R.id.chronometer) ChronometerWithMillis mChronometer; @Bind(R.id.chronometer) ChronometerWithMillis mChronometer;
@Bind(R.id.new_lap) FloatingActionButton mNewLapButton; @Bind(R.id.new_lap) FloatingActionButton mNewLapButton;
@ -67,12 +61,6 @@ public class StopwatchFragment extends RecyclerViewFragment<
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
mUpdateHandler = new AsyncLapsTableUpdateHandler(getActivity(), null/*we shouldn't need a scroll handler*/);
mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
mStartTime = mPrefs.getLong(KEY_START_TIME, 0);
mPauseTime = mPrefs.getLong(KEY_PAUSE_TIME, 0);
Log.d(TAG, "mStartTime = " + mStartTime
+ ", mPauseTime = " + mPauseTime);
// TODO: Will these be kept alive after onDestroyView()? If not, we should move these to // TODO: Will these be kept alive after onDestroyView()? If not, we should move these to
// onCreateView() or any other callback that is guaranteed to be called. // onCreateView() or any other callback that is guaranteed to be called.
mStartDrawable = ContextCompat.getDrawable(getActivity(), R.drawable.ic_start_24dp); mStartDrawable = ContextCompat.getDrawable(getActivity(), R.drawable.ic_start_24dp);
@ -85,21 +73,13 @@ public class StopwatchFragment extends RecyclerViewFragment<
Log.d(TAG, "onCreateView()"); Log.d(TAG, "onCreateView()");
View view = super.onCreateView(inflater, container, savedInstanceState); View view = super.onCreateView(inflater, container, savedInstanceState);
mChronometer.setShowCentiseconds(true, true); AsyncLapsTableUpdateHandler updateHandler = new AsyncLapsTableUpdateHandler(
if (mStartTime > 0) { getActivity(), null/*we shouldn't need a scroll handler*/);
long base = mStartTime; SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
if (mPauseTime > 0) { // This can't be initialized until the layout is inflated, because we need the
base += SystemClock.elapsedRealtime() - mPauseTime; // mChronometer reference for the controller.
// We're not done pausing yet, so don't reset mPauseTime. mController = new StopwatchViewController(updateHandler, prefs, mChronometer);
}
mChronometer.setBase(base);
}
if (isStopwatchRunning()) {
mChronometer.start();
// Note: mChronometer.isRunning() will return false at this point and
// in other upcoming lifecycle methods because it is not yet visible
// (i.e. mVisible == false).
}
// The primary reason we call this is to show the mini FABs after rotate, // The primary reason we call this is to show the mini FABs after rotate,
// if the stopwatch is running. If the stopwatch is stopped, then this // if the stopwatch is running. If the stopwatch is stopped, then this
// would have hidden the mini FABs, if not for us already setting its // would have hidden the mini FABs, if not for us already setting its
@ -158,9 +138,6 @@ public class StopwatchFragment extends RecyclerViewFragment<
if (mProgressAnimator != null) { if (mProgressAnimator != null) {
mProgressAnimator.removeAllListeners(); mProgressAnimator.removeAllListeners();
} }
Log.d(TAG, "onDestroyView()");
Log.d(TAG, "mStartTime = " + mStartTime
+ ", mPauseTime = " + mPauseTime);
} }
@Override @Override
@ -203,7 +180,8 @@ public class StopwatchFragment extends RecyclerViewFragment<
// //
// NOTE: If we just recreated ourselves due to rotation, mChronometer.isRunning() == false, // NOTE: If we just recreated ourselves due to rotation, mChronometer.isRunning() == false,
// because it is not yet visible (i.e. mVisible == false). // because it is not yet visible (i.e. mVisible == false).
if (isStopwatchRunning()) { if (mController.isStopwatchRunning()) {
// TODO: I think we should just pass in the two laps as local params
startNewProgressBarAnimator(); startNewProgressBarAnimator();
} else { } else {
// I verified the bar was visible already without this, so we probably don't need this, // I verified the bar was visible already without this, so we probably don't need this,
@ -220,10 +198,7 @@ public class StopwatchFragment extends RecyclerViewFragment<
@Override @Override
public void onFabClick() { public void onFabClick() {
if (mChronometer.isRunning()) { if (mChronometer.isRunning()) {
mPauseTime = SystemClock.elapsedRealtime(); mController.pause();
mChronometer.stop();
mCurrentLap.pause();
mUpdateHandler.asyncUpdate(mCurrentLap.getId(), mCurrentLap);
// No issues controlling the animator here, because onLoadFinished() can't // No issues controlling the animator here, because onLoadFinished() can't
// call through to startNewProgressBarAnimator(), because by that point // call through to startNewProgressBarAnimator(), because by that point
// the chronometer won't be running. // the chronometer won't be running.
@ -234,20 +209,7 @@ public class StopwatchFragment extends RecyclerViewFragment<
mProgressAnimator.cancel(); mProgressAnimator.cancel();
} }
} else { } else {
if (mStartTime == 0) { mController.run();
// addNewLap() won't call through unless chronometer is running, which
// we can't start until we compute mStartTime
mCurrentLap = new Lap();
mUpdateHandler.asyncInsert(mCurrentLap);
}
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 // 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. // 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 // There, it calls startNewProgressAnimator() to end this animation and starts an
@ -257,7 +219,6 @@ public class StopwatchFragment extends RecyclerViewFragment<
// } // }
getActivity().startService(new Intent(getActivity(), StopwatchNotificationService.class)); getActivity().startService(new Intent(getActivity(), StopwatchNotificationService.class));
} }
savePrefs();
// TOneverDO: Precede savePrefs(), or else we don't save false to KEY_CHRONOMETER_RUNNING // TOneverDO: Precede savePrefs(), or else we don't save false to KEY_CHRONOMETER_RUNNING
/// and updateFab will update the wrong icon. /// and updateFab will update the wrong icon.
updateAllFabs(); updateAllFabs();
@ -280,23 +241,7 @@ public class StopwatchFragment extends RecyclerViewFragment<
@OnClick(R.id.new_lap) @OnClick(R.id.new_lap)
void addNewLap() { void addNewLap() {
if (!mChronometer.isRunning()) { mController.addNewLap(mChronometer.getText().toString());
Log.d(TAG, "Cannot add new lap");
return;
}
if (mCurrentLap != null) {
mCurrentLap.end(mChronometer.getText().toString());
}
mPreviousLap = mCurrentLap;
mCurrentLap = new Lap();
if (mPreviousLap != null) {
// if (getAdapter().getItemCount() == 0) {
// mUpdateHandler.asyncInsert(mPreviousLap);
// } else {
mUpdateHandler.asyncUpdate(mPreviousLap.getId(), mPreviousLap);
// }
}
mUpdateHandler.asyncInsert(mCurrentLap);
// This would end up being called twice: here, and in onLoadFinished(), because the // This would end up being called twice: here, and in onLoadFinished(), because the
// table updates will prompt us to requery. // table updates will prompt us to requery.
// startNewProgressBarAnimator(); // startNewProgressBarAnimator();
@ -304,17 +249,13 @@ public class StopwatchFragment extends RecyclerViewFragment<
@OnClick(R.id.stop) @OnClick(R.id.stop)
void stop() { void stop() {
mChronometer.stop();
mChronometer.setBase(SystemClock.elapsedRealtime());
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// TOneverDO: Precede these with mProgressAnimator.end(), otherwise our // TOneverDO: Precede these with mProgressAnimator.end(), otherwise our
// Animator.onAnimationEnd() callback won't hide SeekBar in time. // Animator.onAnimationEnd() callback won't hide SeekBar in time.
mStartTime = 0; mController.stop();
mPauseTime = 0;
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
mCurrentLap = null; mCurrentLap = null;
mPreviousLap = null; mPreviousLap = null;
mUpdateHandler.asyncClear(); // Clear laps
// No issues controlling the animator here, because onLoadFinished() can't // No issues controlling the animator here, because onLoadFinished() can't
// call through to startNewProgressBarAnimator(), because by that point // call through to startNewProgressBarAnimator(), because by that point
// the chronometer won't be running. // the chronometer won't be running.
@ -322,7 +263,6 @@ public class StopwatchFragment extends RecyclerViewFragment<
mProgressAnimator.end(); mProgressAnimator.end();
} }
mProgressAnimator = null; mProgressAnimator = null;
savePrefs();
// TOneverDO: Precede savePrefs(), or else we don't save false to KEY_CHRONOMETER_RUNNING // TOneverDO: Precede savePrefs(), or else we don't save false to KEY_CHRONOMETER_RUNNING
/// and updateFab will update the wrong icon. /// and updateFab will update the wrong icon.
updateAllFabs(); updateAllFabs();
@ -342,14 +282,14 @@ public class StopwatchFragment extends RecyclerViewFragment<
} }
private void updateMiniFabs() { private void updateMiniFabs() {
boolean started = mStartTime > 0; boolean started = mController.getStopwatch().hasStarted();
int vis = started ? View.VISIBLE : View.INVISIBLE; int vis = started ? View.VISIBLE : View.INVISIBLE;
mNewLapButton.setVisibility(vis); mNewLapButton.setVisibility(vis);
mStopButton.setVisibility(vis); mStopButton.setVisibility(vis);
} }
private void updateFab() { private void updateFab() {
mActivityFab.get().setImageDrawable(isStopwatchRunning() ? mPauseDrawable : mStartDrawable); mActivityFab.get().setImageDrawable(mController.isStopwatchRunning() ? mPauseDrawable : mStartDrawable);
} }
private void startNewProgressBarAnimator() { private void startNewProgressBarAnimator() {
@ -414,21 +354,6 @@ public class StopwatchFragment extends RecyclerViewFragment<
return mPreviousLap.elapsed() - mCurrentLap.elapsed(); return mPreviousLap.elapsed() - mCurrentLap.elapsed();
} }
private void savePrefs() {
mPrefs.edit().putLong(KEY_START_TIME, mStartTime)
.putLong(KEY_PAUSE_TIME, mPauseTime)
.putBoolean(KEY_CHRONOMETER_RUNNING, mChronometer.isRunning())
.apply();
}
/**
* @return the state of the stopwatch when we're in a resumed and visible state,
* or when we're going through a rotation
*/
private boolean isStopwatchRunning() {
return mChronometer.isRunning() || mPrefs.getBoolean(KEY_CHRONOMETER_RUNNING, false);
}
// ======================= DO NOT IMPLEMENT ============================ // ======================= DO NOT IMPLEMENT ============================
@Override @Override

View File

@ -68,10 +68,18 @@ public class StopwatchNotificationService extends ChronometerNotificationService
@Override @Override
protected void handleStartPauseAction(Intent intent, int flags, long startId) { protected void handleStartPauseAction(Intent intent, int flags, long startId) {
// TODO: Tell StopwatchFragment to start/pause itself.. perhaps with an Intent? // TODO: Tell StopwatchFragment to start/pause itself.. perhaps with an Intent?
boolean running = mPrefs.getBoolean(StopwatchFragment.KEY_CHRONOMETER_RUNNING, false); boolean running = mPrefs.getBoolean("KEY_RUNNING", false);
syncNotificationWithStopwatchState(!running); syncNotificationWithStopwatchState(!running);
SharedPreferences.Editor editor = mPrefs.edit(); SharedPreferences.Editor editor = mPrefs.edit();
editor.putBoolean(StopwatchFragment.KEY_CHRONOMETER_RUNNING, !running); editor.putBoolean("KEY_RUNNING", !running);
if (running) {
editor.putLong("key_pause_time", SystemClock.elapsedRealtime());
} else {
long startTime = mPrefs.getLong("key_start_time", 0);
long pauseTime = mPrefs.getLong("key_pause_time", 0);
editor.putLong("key_start_time", startTime + SystemClock.elapsedRealtime() - pauseTime);
}
editor.apply();
} }
@Override @Override

View File

@ -0,0 +1,78 @@
package com.philliphsu.clock2.stopwatch;
import android.content.SharedPreferences;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.util.Log;
/**
* Created by Phillip Hsu on 9/11/2016.
*/
public final class StopwatchViewController extends BaseStopwatchController {
private static final String TAG = "StopwatchViewController";
private final ChronometerWithMillis mChronometer;
public StopwatchViewController(@NonNull AsyncLapsTableUpdateHandler updateHandler,
@NonNull SharedPreferences prefs,
@NonNull ChronometerWithMillis chronometer) {
super(updateHandler, prefs);
mChronometer = chronometer;
mChronometer.setShowCentiseconds(true, true);
final Stopwatch sw = getStopwatch();
if (sw.getStartTime() > 0) {
long base = sw.getStartTime();
if (sw.getPauseTime() > 0) {
base += SystemClock.elapsedRealtime() - sw.getPauseTime();
// We're not done pausing yet, so don't reset mPauseTime.
}
chronometer.setBase(base);
}
if (isStopwatchRunning()) {
chronometer.start();
// Note: mChronometer.isRunning() will return false at this point and
// in other upcoming lifecycle methods because it is not yet visible
// (i.e. mVisible == false).
}
}
@Override
public void run() {
super.run();
mChronometer.setBase(getStopwatch().getStartTime());
mChronometer.start();
}
@Override
public void pause() {
// Keep this call and the call to Stopwatch.pause() close together to minimize the time delta.
mChronometer.stop();
super.pause();
}
@Override
public void stop() {
mChronometer.stop();
mChronometer.setBase(SystemClock.elapsedRealtime());
super.stop();
}
@Override
public void addNewLap(String currentLapTotalText) {
if (!mChronometer.isRunning()) {
Log.d(TAG, "Cannot add new lap");
return;
}
super.addNewLap(currentLapTotalText);
}
/**
* @return the state of the stopwatch when we're in a resumed and visible state,
* or when we're going through a rotation
*/
@Override
public boolean isStopwatchRunning() {
return mChronometer.isRunning() || super.isStopwatchRunning();
}
}