Modify StopwatchNotificationService to extend ChronometerNotificationService

This commit is contained in:
Phillip Hsu 2016-09-11 12:37:33 -07:00
parent 31e0a71d9f
commit e157498147
5 changed files with 99 additions and 90 deletions

View File

@ -236,6 +236,13 @@ public abstract class ChronometerNotificationService extends Service {
mNoteBuilder.addAction(icon, actionTitle, pi); mNoteBuilder.addAction(icon, actionTitle, pi);
} }
/**
* Cancels the notification with the pair ({@link #getNoteTag() tag}, id)
*/
protected final void cancelNotification(int id) {
mNotificationManager.cancel(getNoteTag(), id);
}
/** /**
* Causes the handler thread's looper to terminate without processing * Causes the handler thread's looper to terminate without processing
* any more messages in the message queue. * any more messages in the message queue.

View File

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

View File

@ -36,9 +36,11 @@ public class StopwatchFragment extends RecyclerViewFragment<
LapCursor, LapCursor,
LapsAdapter> { LapsAdapter> {
private static final String TAG = "StopwatchFragment"; private static final String TAG = "StopwatchFragment";
private static final String KEY_START_TIME = "start_time";
private static final String KEY_PAUSE_TIME = "pause_time"; // Exposed for StopwatchNotificationService
private static final String KEY_CHRONOMETER_RUNNING = "chronometer_running"; 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 mStartTime;
private long mPauseTime; private long mPauseTime;

View File

@ -1,117 +1,109 @@
package com.philliphsu.clock2.stopwatch; package com.philliphsu.clock2.stopwatch;
import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.IBinder; import android.content.SharedPreferences;
import android.support.annotation.DrawableRes; import android.os.SystemClock;
import android.support.v4.app.NotificationCompat; import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.philliphsu.clock2.ChronometerNotificationThread; import com.philliphsu.clock2.ChronometerNotificationService;
import com.philliphsu.clock2.MainActivity; import com.philliphsu.clock2.MainActivity;
import com.philliphsu.clock2.R; import com.philliphsu.clock2.R;
public class StopwatchNotificationService extends Service { public class StopwatchNotificationService extends ChronometerNotificationService {
private static final String ACTION_ADD_LAP = "com.philliphsu.clock2.stopwatch.action.ADD_LAP"; private static final String ACTION_ADD_LAP = "com.philliphsu.clock2.stopwatch.action.ADD_LAP";
private static final String ACTION_START_PAUSE = "com.philliphsu.clock2.stopwatch.action.START_PAUSE";
private static final String ACTION_STOP = "com.philliphsu.clock2.stopwatch.action.STOP";
private NotificationCompat.Builder mNoteBuilder;
private NotificationManager mNotificationManager;
private AsyncLapsTableUpdateHandler mLapsTableUpdateHandler; private AsyncLapsTableUpdateHandler mLapsTableUpdateHandler;
private ChronometerNotificationThread mThread; private SharedPreferences mPrefs;
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mLapsTableUpdateHandler = new AsyncLapsTableUpdateHandler(this, null); mLapsTableUpdateHandler = new AsyncLapsTableUpdateHandler(this, null);
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
// Create base note
// TODO: I think we can make this a foreground service so even // TODO: I think we can make this a foreground service so even
// if the process is killed, this service remains alive. // if the process is killed, this service remains alive.
mNoteBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_stopwatch_24dp)
.setOngoing(true)
// .setUsesChronometer(true) // No way to pause/resume this native chronometer.
.setContentTitle(getString(R.string.stopwatch));
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra(null/*TODO:MainActivity.EXTRA_SHOW_PAGE*/, 2/*TODO:MainActivity.INDEX_STOPWATCH*/);
mNoteBuilder.setContentIntent(PendingIntent.getActivity(this, 0, intent, 0));
// TODO: Move adding these actions to the default case
// TODO: Change fillColor to white, to accommodate API < 21.
// Apparently, notifications on 21+ are automatically
// tinted to gray to contrast against the native notification background color.
addAction(ACTION_ADD_LAP, R.drawable.ic_add_lap_24dp, getString(R.string.lap));
// TODO: Set icon and title according to state of stopwatch
addAction(ACTION_START_PAUSE, R.drawable.ic_pause_24dp, getString(R.string.pause));
addAction(ACTION_STOP, R.drawable.ic_stop_24dp, getString(R.string.stop));
} }
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
quitThread(); // After being cancelled due to time being up, sometimes the active timer notification posts again
// with a static 00:00 text, along with the Time's up notification. My theory is
// our thread has enough leeway to sneak in a final call to post the notification before it
// is actually quit().
// As such, try cancelling the notification with this (tag, id) pair again.
cancelNotification(0);
} }
@Override @Override
public int onStartCommand(Intent intent, int flags, int startId) { protected int getSmallIcon() {
if (intent != null) { return R.drawable.ic_stopwatch_24dp;
final String action = intent.getAction(); }
if (action == null) {
// TODO: Read the stopwatch's start time in shared prefs. @Nullable
mNoteBuilder.setWhen(System.currentTimeMillis()); @Override
// TODO: Lap # content text protected PendingIntent getContentIntent() {
mNoteBuilder.setContentText("Lap 1"); Intent intent = new Intent(this, MainActivity.class);
// Use class name as tag instead of defining our own tag constant, because intent.putExtra(null/*TODO:MainActivity.EXTRA_SHOW_PAGE*/, 2/*TODO:MainActivity.INDEX_STOPWATCH*/);
// the latter is limited to 23 (?) chars if you also want to use it as return PendingIntent.getActivity(this, 0, intent, 0);
// a log tag. }
mNotificationManager.notify(getClass().getName(), 0, mNoteBuilder.build());
} else { @Override
switch (action) { protected boolean isCountDown() {
case ACTION_ADD_LAP: return false;
// mLapsTableUpdateHandler.asyncInsert(null/*TODO*/); }
break;
case ACTION_START_PAUSE: @Override
break; protected void handleDefaultAction(Intent intent, int flags, long startId) {
case ACTION_STOP: // TODO: String resource [Stopwatch: Lap %1$s]. If no laps, just [Stopwatch]
// Cancels all of the notifications issued by *this instance* of the manager, setContentTitle(getString(R.string.stopwatch));
// not those of any other instances (in this app or otherwise). syncNotificationWithStopwatchState(true/*always true*/);
// TODO: We could cancel by (tag, id) if we cared. // We don't need to write anything to SharedPrefs because if we're here, StopwatchFragment
mNotificationManager.cancelAll(); // already wrote the necessary values to file.
break; }
}
} @Override
protected void handleStartPauseAction(Intent intent, int flags, long startId) {
// TODO: Tell StopwatchFragment to start/pause itself.. perhaps with an Intent?
boolean running = mPrefs.getBoolean(StopwatchFragment.KEY_CHRONOMETER_RUNNING, false);
syncNotificationWithStopwatchState(!running);
SharedPreferences.Editor editor = mPrefs.edit();
editor.putBoolean(StopwatchFragment.KEY_CHRONOMETER_RUNNING, !running);
}
@Override
protected void handleStopAction(Intent intent, int flags, long startId) {
// TODO: Tell StopwatchFragment to stop itself.. perhaps with an Intent?
stopSelf();
}
@Override
protected void handleAction(@NonNull String action, Intent intent, int flags, long startId) {
if (ACTION_ADD_LAP.equals(action)) {
mLapsTableUpdateHandler.asyncInsert(null/*TODO*/);
} else {
throw new IllegalArgumentException("StopwatchNotificationService cannot handle action " + action);
} }
return super.onStartCommand(intent, flags, startId);
} }
@Override private void syncNotificationWithStopwatchState(boolean running) {
public IBinder onBind(Intent intent) { clearActions();
return null; // TODO: Change fillColor to white, to accommodate API < 21.
} // Apparently, notifications on 21+ are automatically
// tinted to gray to contrast against the native notification background color.
//
// No request code needed, so use 0.
addAction(ACTION_ADD_LAP, R.drawable.ic_add_lap_24dp, getString(R.string.lap), 0);
addStartPauseAction(running, 0);
addStopAction(0);
/** quitCurrentThread();
* Builds and adds the specified action to the notification's mNoteBuilder. if (running) {
*/ // TODO: Read the stopwatch's start time in shared prefs.
private void addAction(String action, @DrawableRes int icon, String actionTitle) { startNewThread(0, SystemClock.elapsedRealtime());
Intent intent = new Intent(this, StopwatchNotificationService.class)
.setAction(action);
PendingIntent pi = PendingIntent.getService(this, 0/*no requestCode*/,
intent, 0/*no flags*/);
mNoteBuilder.addAction(icon, actionTitle, pi);
}
/**
* Causes the handler thread's looper to terminate without processing
* any more messages in the message queue.
*/
private void quitThread() {
if (mThread != null && mThread.isAlive()) {
mThread.quit();
} }
} }
} }

View File

@ -1,6 +1,5 @@
package com.philliphsu.clock2.timers; package com.philliphsu.clock2.timers;
import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@ -97,8 +96,7 @@ public class TimerNotificationService extends ChronometerNotificationService {
// our thread has enough leeway to sneak in a final call to post the notification before it // our thread has enough leeway to sneak in a final call to post the notification before it
// is actually quit(). // is actually quit().
// As such, try cancelling the notification with this (tag, id) pair again. // As such, try cancelling the notification with this (tag, id) pair again.
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); cancelNotification(mTimer.getIntId());
nm.cancel(getNoteTag(), mTimer.getIntId());
} }
@Override @Override