Created TimerNotificationService, TimesUpActivity. Schedule alarms with AlarmManager for timers.
This commit is contained in:
parent
6f8d22f15b
commit
9e4369282d
@ -21,13 +21,13 @@
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
<!--<activity
|
||||
android:name=".ringtone.RingtoneActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:label="@string/title_activity_ringtone"
|
||||
android:launchMode="singleTask"
|
||||
android:taskAffinity="com.philliphsu.clock2.RingtoneActivity">
|
||||
</activity>
|
||||
</activity>-->
|
||||
|
||||
<service
|
||||
android:name=".ringtone.RingtoneService"
|
||||
@ -79,14 +79,33 @@
|
||||
android:exported="false">
|
||||
</service>
|
||||
|
||||
<activity android:name=".edittimer.EditTimerActivity"
|
||||
android:label="@string/title_activity_create_timer"
|
||||
android:parentActivityName=".MainActivity"
|
||||
android:windowSoftInputMode="adjustNothing">
|
||||
<activity
|
||||
android:name=".edittimer.EditTimerActivity"
|
||||
android:label="@string/title_activity_create_timer"
|
||||
android:parentActivityName=".MainActivity"
|
||||
android:windowSoftInputMode="adjustNothing">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="com.philliphsu.clock2.MainActivity"/>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".timers.TimerNotificationService"
|
||||
android:exported="false">
|
||||
</service>
|
||||
|
||||
<activity android:name=".timers.TimesUpActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:label="@string/title_activity_ringtone"
|
||||
android:launchMode="singleTask"
|
||||
android:taskAffinity="com.philliphsu.clock2.RingtoneActivity">
|
||||
</activity>
|
||||
<activity android:name=".alarms.AlarmActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:label="@string/title_activity_ringtone"
|
||||
android:launchMode="singleTask"
|
||||
android:taskAffinity="com.philliphsu.clock2.RingtoneActivity">
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@ -203,8 +203,9 @@ public abstract class Alarm extends ObjectWithId implements JsonSerializable, Pa
|
||||
return !ignoreUpcomingRingTime && ringsIn() <= TimeUnit.HOURS.toMillis(hours);
|
||||
}
|
||||
|
||||
// TODO: Rename to getIntId() so usages refer to ObjectWithId#getIntId(), then delete this method.
|
||||
public int intId() {
|
||||
return (int) getId();
|
||||
return getIntId();
|
||||
}
|
||||
|
||||
// TODO: Remove method signature from JsonSerializable interface.
|
||||
|
||||
@ -1,14 +1,22 @@
|
||||
package com.philliphsu.clock2;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
|
||||
import com.philliphsu.clock2.alarms.ScrollHandler;
|
||||
import com.philliphsu.clock2.model.TimersTableManager;
|
||||
import com.philliphsu.clock2.timers.TimerNotificationService;
|
||||
import com.philliphsu.clock2.timers.TimesUpActivity;
|
||||
|
||||
/**
|
||||
* Created by Phillip Hsu on 8/2/2016.
|
||||
*/
|
||||
public final class AsyncTimersTableUpdateHandler extends AsyncDatabaseTableUpdateHandler<Timer, TimersTableManager> {
|
||||
private static final String TAG = "TimersTableUpdater"; // TAG max 23 chars
|
||||
|
||||
public AsyncTimersTableUpdateHandler(Context context, ScrollHandler scrollHandler) {
|
||||
super(context, scrollHandler);
|
||||
@ -21,16 +29,55 @@ public final class AsyncTimersTableUpdateHandler extends AsyncDatabaseTableUpdat
|
||||
|
||||
@Override
|
||||
protected void onPostAsyncDelete(Integer result, Timer timer) {
|
||||
// TODO: Cancel the alarm scheduled for this timer
|
||||
cancelAlarm(timer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostAsyncInsert(Long result, Timer timer) {
|
||||
// TODO: if running, schedule alarm
|
||||
Log.d(TAG, "onPostAsyncInsert()");
|
||||
if (timer.isRunning()) {
|
||||
Log.d(TAG, "Scheduling alarm for timer launch");
|
||||
scheduleAlarm(timer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostAsyncUpdate(Long result, Timer timer) {
|
||||
// TODO: cancel and reschedule
|
||||
if (timer.isRunning()) {
|
||||
// We don't need to cancel the previous alarm, because this one
|
||||
// will remove and replace it.
|
||||
scheduleAlarm(timer);
|
||||
} else {
|
||||
cancelAlarm(timer);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Consider changing to just a long id param
|
||||
private PendingIntent createTimesUpIntent(Timer timer) {
|
||||
Intent intent = new Intent(getContext(), TimesUpActivity.class);
|
||||
// intent.putExtra(TimesUpActivity.EXTRA_ITEM_ID, timer.getId());
|
||||
// There's no point to determining whether to retrieve a previous instance, because
|
||||
// we chose to ignore it since we had issues with NPEs. TODO: Perhaps these issues
|
||||
// were caused by you using the same reference variable for every Intent/PI that
|
||||
// needed to be recreated, and you reassigning the reference each time you were done with
|
||||
// one of them, which leaves the one before unreferenced and hence eligible for GC.
|
||||
return PendingIntent.getActivity(getContext(), timer.getIntId(), intent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
}
|
||||
|
||||
private void scheduleAlarm(Timer timer) {
|
||||
Log.d(TAG, String.format("now = %d, endTime = %d", SystemClock.elapsedRealtime(), timer.endTime()));
|
||||
AlarmManager am = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
|
||||
am.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, timer.endTime(), createTimesUpIntent(timer));
|
||||
TimerNotificationService.showNotification(getContext(), timer.getId());
|
||||
}
|
||||
|
||||
private void cancelAlarm(Timer timer) {
|
||||
// Cancel the alarm scheduled. If one was never scheduled, does nothing.
|
||||
AlarmManager am = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
|
||||
PendingIntent pi = createTimesUpIntent(timer);
|
||||
// Now can't be null
|
||||
am.cancel(pi);
|
||||
pi.cancel();
|
||||
TimerNotificationService.cancelNotification(getContext(), timer.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,78 @@
|
||||
package com.philliphsu.clock2.alarms;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
import com.philliphsu.clock2.Alarm;
|
||||
import com.philliphsu.clock2.R;
|
||||
import com.philliphsu.clock2.model.AlarmLoader;
|
||||
import com.philliphsu.clock2.ringtone.RingtoneActivity;
|
||||
import com.philliphsu.clock2.util.AlarmController;
|
||||
|
||||
public class AlarmActivity extends RingtoneActivity<Alarm> {
|
||||
|
||||
private AlarmController mAlarmController;
|
||||
// TODO: Write a getter method instead in the base class?
|
||||
private Alarm mAlarm;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mAlarmController = new AlarmController(this, null);
|
||||
// TODO: Butterknife binding
|
||||
Button snooze = (Button) findViewById(R.id.btn_snooze);
|
||||
snooze.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
snooze();
|
||||
}
|
||||
});
|
||||
Button dismiss = (Button) findViewById(R.id.btn_dismiss);
|
||||
dismiss.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<Alarm> onCreateLoader(long id) {
|
||||
return new AlarmLoader(this, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Alarm> loader, Alarm data) {
|
||||
super.onLoadFinished(loader, data);
|
||||
mAlarm = data;
|
||||
if (data != null) {
|
||||
// TODO: If the upcoming alarm notification isn't present, verify other notifications aren't affected.
|
||||
// This could be the case if we're starting a new instance of this activity after leaving the first launch.
|
||||
mAlarmController.removeUpcomingAlarmNotification(data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int layoutResource() {
|
||||
return R.layout.activity_ringtone;
|
||||
}
|
||||
|
||||
private void snooze() {
|
||||
if (mAlarm != null) {
|
||||
mAlarmController.snoozeAlarm(mAlarm);
|
||||
}
|
||||
// Can't call dismiss() because we don't want to also call cancelAlarm()! Why? For example,
|
||||
// we don't want the alarm, if it has no recurrence, to be turned off right now.
|
||||
stopAndFinish();
|
||||
}
|
||||
|
||||
private void dismiss() {
|
||||
if (mAlarm != null) {
|
||||
// TODO do we really need to cancel the intent and alarm?
|
||||
mAlarmController.cancelAlarm(mAlarm, false);
|
||||
}
|
||||
stopAndFinish();
|
||||
}
|
||||
}
|
||||
@ -28,8 +28,8 @@ import com.philliphsu.clock2.BaseActivity;
|
||||
import com.philliphsu.clock2.DaysOfWeek;
|
||||
import com.philliphsu.clock2.R;
|
||||
import com.philliphsu.clock2.SharedPreferencesHelper;
|
||||
import com.philliphsu.clock2.alarms.AlarmActivity;
|
||||
import com.philliphsu.clock2.model.AlarmLoader;
|
||||
import com.philliphsu.clock2.ringtone.RingtoneActivity;
|
||||
import com.philliphsu.clock2.util.AlarmController;
|
||||
import com.philliphsu.clock2.util.AlarmUtils;
|
||||
import com.philliphsu.clock2.util.DateFormatUtils;
|
||||
@ -543,8 +543,8 @@ public class EditAlarmActivity extends BaseActivity implements
|
||||
@Override
|
||||
public void cancelAlarm(Alarm alarm, boolean showToast) {
|
||||
new AlarmController(this, mMainContent).cancelAlarm(alarm, true);
|
||||
if (RingtoneActivity.isAlive()) {
|
||||
LocalBroadcastHelper.sendBroadcast(this, RingtoneActivity.ACTION_FINISH);
|
||||
if (AlarmActivity.isAlive()) {
|
||||
LocalBroadcastHelper.sendBroadcast(this, AlarmActivity.ACTION_FINISH);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -11,6 +11,8 @@ public class AlarmLoader extends DataLoader<Alarm> {
|
||||
|
||||
private long mAlarmId;
|
||||
|
||||
// TODO: Consider writing a super ctor that has the id param, so
|
||||
// subclasses don't need to write their own.
|
||||
public AlarmLoader(Context context, long alarmId) {
|
||||
super(context);
|
||||
mAlarmId = alarmId;
|
||||
|
||||
@ -6,6 +6,9 @@ import android.support.v4.content.AsyncTaskLoader;
|
||||
/**
|
||||
* Created by Phillip Hsu on 6/30/2016.
|
||||
*/
|
||||
// TODO: Consider adding a DatabaseTableManager type param, so we can then
|
||||
// implement loadInBackground for subclasses. You would, however, need to write
|
||||
// an abstract method getTableManager() that subclasses implement for us.
|
||||
public abstract class DataLoader<D> extends AsyncTaskLoader<D> {
|
||||
|
||||
private D mData;
|
||||
|
||||
@ -15,4 +15,8 @@ public abstract class ObjectWithId {
|
||||
public final void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public final int getIntId() {
|
||||
return (int) id;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
package com.philliphsu.clock2.model;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.philliphsu.clock2.Timer;
|
||||
|
||||
/**
|
||||
* Created by Phillip Hsu on 8/3/2016.
|
||||
*/
|
||||
public class TimerLoader extends DataLoader<Timer> {
|
||||
|
||||
private long mTimerId;
|
||||
|
||||
// TODO: Consider writing a super ctor that has the id param, so
|
||||
// subclasses don't need to write their own.
|
||||
public TimerLoader(Context context, long timerId) {
|
||||
super(context);
|
||||
mTimerId = timerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Timer loadInBackground() {
|
||||
return new TimersTableManager(getContext()).queryItem(mTimerId).getItem();
|
||||
}
|
||||
}
|
||||
@ -4,26 +4,20 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.LayoutRes;
|
||||
import android.support.v4.app.LoaderManager.LoaderCallbacks;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Button;
|
||||
|
||||
import com.philliphsu.clock2.Alarm;
|
||||
import com.philliphsu.clock2.R;
|
||||
import com.philliphsu.clock2.model.AlarmLoader;
|
||||
import com.philliphsu.clock2.util.AlarmController;
|
||||
import com.philliphsu.clock2.util.LocalBroadcastHelper;
|
||||
|
||||
/**
|
||||
* An example full-screen activity that shows and hides the system UI (i.e.
|
||||
* status bar and navigation/system bar) with user interaction.
|
||||
*
|
||||
* TODO: Make this abstract and make appropriate subclasses for Alarms and Timers.
|
||||
*/
|
||||
public class RingtoneActivity extends AppCompatActivity implements
|
||||
android.support.v4.app.LoaderManager.LoaderCallbacks<Alarm> {
|
||||
public abstract class RingtoneActivity<T> extends AppCompatActivity implements LoaderCallbacks<T> {
|
||||
private static final String TAG = "RingtoneActivity";
|
||||
|
||||
// Shared with RingtoneService
|
||||
@ -32,14 +26,19 @@ public class RingtoneActivity extends AppCompatActivity implements
|
||||
|
||||
private static boolean sIsAlive = false;
|
||||
|
||||
private long mAlarmId;
|
||||
private Alarm mAlarm;
|
||||
private AlarmController mAlarmController;
|
||||
private long mItemId;
|
||||
private T mItem;
|
||||
|
||||
public abstract Loader<T> onCreateLoader(long itemId);
|
||||
|
||||
// TODO: Should we extend from BaseActivity instead?
|
||||
@LayoutRes
|
||||
public abstract int layoutResource();
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_ringtone);
|
||||
setContentView(layoutResource());
|
||||
sIsAlive = true;
|
||||
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
|
||||
@ -47,8 +46,8 @@ public class RingtoneActivity extends AppCompatActivity implements
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
|
||||
|
||||
mAlarmId = getIntent().getLongExtra(EXTRA_ITEM_ID, -1);
|
||||
if (mAlarmId < 0) {
|
||||
mItemId = getIntent().getLongExtra(EXTRA_ITEM_ID, -1);
|
||||
if (mItemId < 0) {
|
||||
throw new IllegalStateException("Cannot start RingtoneActivity without item's id");
|
||||
}
|
||||
// The reason we don't use a thread to load the alarm is because this is an
|
||||
@ -57,43 +56,31 @@ public class RingtoneActivity extends AppCompatActivity implements
|
||||
getSupportLoaderManager().initLoader(0, null, this);
|
||||
|
||||
Intent intent = new Intent(this, RingtoneService.class)
|
||||
.putExtra(EXTRA_ITEM_ID, mAlarmId);
|
||||
.putExtra(EXTRA_ITEM_ID, mItemId);
|
||||
startService(intent);
|
||||
|
||||
mAlarmController = new AlarmController(this, null);
|
||||
|
||||
// TODO: Butterknife binding
|
||||
Button snooze = (Button) findViewById(R.id.btn_snooze);
|
||||
snooze.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
snooze();
|
||||
}
|
||||
});
|
||||
Button dismiss = (Button) findViewById(R.id.btn_dismiss);
|
||||
dismiss.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
// TODO: Do we need this anymore? I think this broadcast was only sent from
|
||||
// EditAlarmActivity?
|
||||
LocalBroadcastHelper.registerReceiver(this, mFinishReceiver, ACTION_FINISH);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
// TODO: Do we need this anymore? I think this broadcast was only sent from
|
||||
// EditAlarmActivity?
|
||||
LocalBroadcastHelper.unregisterReceiver(this, mFinishReceiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
//super.onNewIntent(intent); // Not needed since no fragments hosted?
|
||||
// TODO: Do we need this anymore? I think the broadcast that calls through to
|
||||
// this was only sent from EditAlarmActivity?
|
||||
|
||||
// Notifies alarm missed and stops the service
|
||||
LocalBroadcastHelper.sendBroadcast(this, RingtoneService.ACTION_NOTIFY_MISSED);
|
||||
@ -138,22 +125,17 @@ public class RingtoneActivity extends AppCompatActivity implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<Alarm> onCreateLoader(int id, Bundle args) {
|
||||
return new AlarmLoader(this, mAlarmId);
|
||||
public Loader<T> onCreateLoader(int id, Bundle args) {
|
||||
return onCreateLoader(mItemId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Alarm> loader, Alarm data) {
|
||||
mAlarm = data;
|
||||
if (mAlarm != null) {
|
||||
// TODO: If the upcoming alarm notification isn't present, verify other notifications aren't affected.
|
||||
// This could be the case if we're starting a new instance of this activity after leaving the first launch.
|
||||
mAlarmController.removeUpcomingAlarmNotification(mAlarm);
|
||||
}
|
||||
public void onLoadFinished(Loader<T> loader, T data) {
|
||||
mItem = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Alarm> loader) {
|
||||
public void onLoaderReset(Loader<T> loader) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@ -161,28 +143,17 @@ public class RingtoneActivity extends AppCompatActivity implements
|
||||
return sIsAlive;
|
||||
}
|
||||
|
||||
private void snooze() {
|
||||
if (mAlarm != null) {
|
||||
mAlarmController.snoozeAlarm(mAlarm);
|
||||
}
|
||||
// Can't call dismiss() because we don't want to also call cancelAlarm()! Why? For example,
|
||||
// we don't want the alarm, if it has no recurrence, to be turned off right now.
|
||||
stopAndFinish();
|
||||
}
|
||||
|
||||
private void dismiss() {
|
||||
if (mAlarm != null) {
|
||||
// TODO do we really need to cancel the intent and alarm?
|
||||
mAlarmController.cancelAlarm(mAlarm, false);
|
||||
}
|
||||
stopAndFinish();
|
||||
}
|
||||
|
||||
private void stopAndFinish() {
|
||||
/**
|
||||
* Exposed to subclasses so they can force us to stop the
|
||||
* ringtone and finish us.
|
||||
*/
|
||||
protected final void stopAndFinish() {
|
||||
stopService(new Intent(this, RingtoneService.class));
|
||||
finish();
|
||||
}
|
||||
|
||||
// TODO: Do we need this anymore? I think this broadcast was only sent from
|
||||
// EditAlarmActivity?
|
||||
private final BroadcastReceiver mFinishReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
|
||||
@ -220,6 +220,10 @@ public class RingtoneService extends Service { // TODO: abstract this, make subc
|
||||
}
|
||||
|
||||
private void finishActivity() {
|
||||
// I think this will be received by all instances of RingtoneActivity
|
||||
// subclasses in memory.. but since we realistically expect only one
|
||||
// instance alive at any given time, we don't need to worry about having
|
||||
// to restrict the broadcast to only the subclass that's alive.
|
||||
LocalBroadcastHelper.sendBroadcast(this, RingtoneActivity.ACTION_FINISH);
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
package com.philliphsu.clock2.timers;
|
||||
|
||||
import android.widget.ImageButton;
|
||||
|
||||
import com.philliphsu.clock2.Timer;
|
||||
|
||||
/**
|
||||
@ -9,27 +7,26 @@ import com.philliphsu.clock2.Timer;
|
||||
*/
|
||||
public class TimerController {
|
||||
private final Timer mTimer;
|
||||
private final CountdownChronometer mChronometer;
|
||||
private final ImageButton mAddOneMinute;
|
||||
private final ImageButton mStartPause;
|
||||
private final ImageButton mStop;
|
||||
|
||||
public TimerController(Timer timer, CountdownChronometer chronometer, ImageButton addOneMinute,
|
||||
ImageButton startPause, ImageButton stop) {
|
||||
mTimer = timer;
|
||||
mChronometer = chronometer;
|
||||
mAddOneMinute = addOneMinute;
|
||||
mStartPause = startPause;
|
||||
mStop = stop;
|
||||
|
||||
// init();
|
||||
/**
|
||||
* Calls the appropriate state on the given Timer, based on
|
||||
* its current state.
|
||||
*/
|
||||
public static void startPause(Timer timer) {
|
||||
if (timer.hasStarted()) {
|
||||
if (timer.isRunning()) {
|
||||
timer.pause();
|
||||
} else {
|
||||
timer.resume();
|
||||
}
|
||||
} else {
|
||||
timer.start();
|
||||
}
|
||||
}
|
||||
|
||||
public TimerController(Timer timer) {
|
||||
mTimer = timer;
|
||||
}
|
||||
|
||||
// private void init() {
|
||||
// mChronometer.setBase(SystemClock.elapsedRealtime() + mTimer.duration());
|
||||
// updateStartPauseIcon();
|
||||
// setSecondaryButtonsVisible(false);
|
||||
// }
|
||||
|
||||
public void start() {
|
||||
mTimer.start();
|
||||
|
||||
@ -0,0 +1,171 @@
|
||||
package com.philliphsu.clock2.timers;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
|
||||
import com.philliphsu.clock2.MainActivity;
|
||||
import com.philliphsu.clock2.R;
|
||||
import com.philliphsu.clock2.Timer;
|
||||
import com.philliphsu.clock2.model.TimersTableManager;
|
||||
|
||||
/**
|
||||
* An {@link IntentService} subclass for handling asynchronous task requests in
|
||||
* a service on a separate handler thread.
|
||||
* <p/>
|
||||
* TODO: Customize class - update intent actions, extra parameters and static
|
||||
* helper methods.
|
||||
*/
|
||||
public class TimerNotificationService extends IntentService {
|
||||
private static final String TAG = "TimerNotificationService";
|
||||
|
||||
public static final String ACTION_ADD_ONE_MINUTE = "com.philliphsu.clock2.timers.action.ADD_ONE_MINUTE";
|
||||
public static final String ACTION_START_PAUSE = "com.philliphsu.clock2.timers.action.START_PAUSE";
|
||||
public static final String ACTION_STOP = "com.philliphsu.clock2.timers.action.STOP";
|
||||
|
||||
public static final String EXTRA_TIMER_ID = "com.philliphsu.clock2.timers.extra.TIMER_ID";
|
||||
|
||||
private TimersTableManager mTableManager;
|
||||
|
||||
public TimerNotificationService() {
|
||||
super("TimerNotificationService");
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to start this Service for its default action: to show
|
||||
* the notification for the Timer with the given id.
|
||||
*/
|
||||
public static void showNotification(Context context, long timerId) {
|
||||
Intent intent = new Intent(context, TimerNotificationService.class);
|
||||
intent.putExtra(EXTRA_TIMER_ID, timerId);
|
||||
context.startService(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to cancel the notification previously shown from calling
|
||||
* {@link #showNotification(Context, long)}. This does NOT start the Service
|
||||
* and call through to {@link #onHandleIntent(Intent)}.
|
||||
* @param timerId the id of the Timer associated with the notification
|
||||
* you want to cancel
|
||||
*/
|
||||
public static void cancelNotification(Context context, long timerId) {
|
||||
NotificationManager nm = (NotificationManager)
|
||||
context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
nm.cancel(TAG, (int) timerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
mTableManager = new TimersTableManager(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
if (intent != null) {
|
||||
final long timerId = intent.getLongExtra(EXTRA_TIMER_ID, -1);
|
||||
if (timerId == -1) {
|
||||
throw new IllegalStateException("Did not pass in timer id");
|
||||
}
|
||||
final String action = intent.getAction();
|
||||
if (action == null) {
|
||||
showNotification(timerId);
|
||||
} else if (ACTION_ADD_ONE_MINUTE.equals(action)) {
|
||||
handleAddOneMinute(timerId);
|
||||
} else if (ACTION_START_PAUSE.equals(action)) {
|
||||
handleStartPause(timerId);
|
||||
} else if (ACTION_STOP.equals(action)) {
|
||||
handleStop(timerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showNotification(long timerId) {
|
||||
Timer timer = getTimer(timerId);
|
||||
|
||||
// Base note
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
|
||||
// TODO: correct icon
|
||||
.setSmallIcon(R.drawable.ic_half_day_1_black_24dp)
|
||||
.setShowWhen(false)
|
||||
.setOngoing(true);
|
||||
// TODO: Set content intent so that when clicked, we launch
|
||||
// TimersFragment and scroll to the given timer id. The following
|
||||
// is merely pseudocode.
|
||||
Intent contentIntent = new Intent(this, MainActivity.class);
|
||||
contentIntent.putExtra(null/*TODO:MainActivity.EXTRA_SHOW_PAGE*/,
|
||||
1/*TODO:The tab index of the timers page*/);
|
||||
contentIntent.putExtra(null/*TODO:MainActivity.EXTRA_SCROLL_TO_ID*/,
|
||||
timerId);
|
||||
builder.setContentIntent(PendingIntent.getActivity(
|
||||
this,
|
||||
0, // TODO: Request code not needed? Since any multiple notifications
|
||||
// should be able to use the same PendingIntent for this action....
|
||||
// unless the underlying *Intent* and its id extra are overwritten
|
||||
// per notification when retrieving the PendingIntent..
|
||||
contentIntent,
|
||||
0/*Shouldn't need a flag..*/));
|
||||
// TODO: Use a handler to continually update the countdown text
|
||||
|
||||
String title = timer.label();
|
||||
if (title.isEmpty()) {
|
||||
title = getString(R.string.timer);
|
||||
}
|
||||
builder.setContentTitle(title);
|
||||
|
||||
addAction(builder, ACTION_ADD_ONE_MINUTE,
|
||||
timer.getId(), R.drawable.ic_add_circle_24dp/*TODO: correct icon*/);
|
||||
addAction(builder, ACTION_START_PAUSE,
|
||||
timer.getId(), R.drawable.ic_add_circle_24dp/*TODO: correct icon*/);
|
||||
addAction(builder, ACTION_STOP,
|
||||
timer.getId(), R.drawable.ic_add_circle_24dp/*TODO: correct icon*/);
|
||||
|
||||
NotificationManager nm = (NotificationManager)
|
||||
getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
nm.notify(TAG, timer.getIntId(), builder.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and adds the specified action to the notification's builder.
|
||||
*/
|
||||
private void addAction(NotificationCompat.Builder noteBuilder, String action,
|
||||
long timerId, @DrawableRes int icon) {
|
||||
Intent intent = new Intent(this, TimerNotificationService.class)
|
||||
.setAction(action)
|
||||
.putExtra(EXTRA_TIMER_ID, timerId);
|
||||
PendingIntent pi = PendingIntent.getService(this,
|
||||
(int) timerId, intent, 0/*no flags*/);
|
||||
noteBuilder.addAction(icon, ""/*no action title*/, pi);
|
||||
}
|
||||
|
||||
private void handleAddOneMinute(long timerId) {
|
||||
Timer timer = getTimer(timerId);
|
||||
timer.addOneMinute();
|
||||
updateTimer(timer);
|
||||
// TODO: Verify the notification countdown is extended by one minute.
|
||||
}
|
||||
|
||||
private void handleStartPause(long timerId) {
|
||||
Timer t = getTimer(timerId);
|
||||
TimerController.startPause(t);
|
||||
updateTimer(t);
|
||||
}
|
||||
|
||||
private void handleStop(long timerId) {
|
||||
Timer t = getTimer(timerId);
|
||||
t.stop();
|
||||
updateTimer(t);
|
||||
}
|
||||
|
||||
private void updateTimer(Timer timer) {
|
||||
mTableManager.updateItem(timer.getId(), timer);
|
||||
}
|
||||
|
||||
private Timer getTimer(long timerId) {
|
||||
return mTableManager.queryItem(timerId).getItem();
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,5 @@
|
||||
package com.philliphsu.clock2.timers;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageButton;
|
||||
@ -52,17 +51,7 @@ public class TimerViewHolder extends BaseViewHolder<Timer> {
|
||||
|
||||
@OnClick(R.id.start_pause)
|
||||
void startPause() {
|
||||
Timer t = getItem();
|
||||
if (t.isRunning()) {
|
||||
// mController.pause();
|
||||
t.pause();
|
||||
} else {
|
||||
if (t.hasStarted()) {
|
||||
t.resume();
|
||||
} else {
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
TimerController.startPause(getItem());
|
||||
// Persist value changes
|
||||
update();
|
||||
}
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
package com.philliphsu.clock2.timers;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
|
||||
public class TimesUpActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public Loader<Timer> onCreateLoader(long itemId) {
|
||||
// return new TimerLoader(this, itemId);
|
||||
// }
|
||||
|
||||
// @Override
|
||||
// public int layoutResource() {
|
||||
// return R.layout.activity_ringtone;
|
||||
// }
|
||||
}
|
||||
@ -12,8 +12,8 @@ import com.philliphsu.clock2.Alarm;
|
||||
import com.philliphsu.clock2.PendingAlarmScheduler;
|
||||
import com.philliphsu.clock2.R;
|
||||
import com.philliphsu.clock2.UpcomingAlarmReceiver;
|
||||
import com.philliphsu.clock2.alarms.AlarmActivity;
|
||||
import com.philliphsu.clock2.model.AlarmsTableManager;
|
||||
import com.philliphsu.clock2.ringtone.RingtoneActivity;
|
||||
import com.philliphsu.clock2.ringtone.RingtoneService;
|
||||
|
||||
import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
|
||||
@ -183,8 +183,8 @@ public final class AlarmController {
|
||||
|
||||
private PendingIntent alarmIntent(Alarm alarm, boolean retrievePrevious) {
|
||||
// TODO: Use appropriate subclass instead
|
||||
Intent intent = new Intent(mAppContext, RingtoneActivity.class)
|
||||
.putExtra(RingtoneActivity.EXTRA_ITEM_ID, alarm.id());
|
||||
Intent intent = new Intent(mAppContext, AlarmActivity.class)
|
||||
.putExtra(AlarmActivity.EXTRA_ITEM_ID, alarm.id());
|
||||
int flag = retrievePrevious ? FLAG_NO_CREATE : FLAG_CANCEL_CURRENT;
|
||||
PendingIntent pi = getActivity(mAppContext, alarm.intId(), intent, flag);
|
||||
// Even when we try to retrieve a previous instance that actually did exist,
|
||||
|
||||
@ -13,8 +13,8 @@ import com.philliphsu.clock2.Alarm;
|
||||
import com.philliphsu.clock2.PendingAlarmScheduler;
|
||||
import com.philliphsu.clock2.R;
|
||||
import com.philliphsu.clock2.UpcomingAlarmReceiver;
|
||||
import com.philliphsu.clock2.alarms.AlarmActivity;
|
||||
import com.philliphsu.clock2.model.AlarmsTableManager;
|
||||
import com.philliphsu.clock2.ringtone.RingtoneActivity;
|
||||
import com.philliphsu.clock2.ringtone.RingtoneService;
|
||||
|
||||
import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
|
||||
@ -180,8 +180,8 @@ public final class AlarmUtils {
|
||||
|
||||
private static PendingIntent alarmIntent(Context context, Alarm alarm, boolean retrievePrevious) {
|
||||
// TODO: Use appropriate subclass instead
|
||||
Intent intent = new Intent(context, RingtoneActivity.class)
|
||||
.putExtra(RingtoneActivity.EXTRA_ITEM_ID, alarm.id());
|
||||
Intent intent = new Intent(context, AlarmActivity.class)
|
||||
.putExtra(AlarmActivity.EXTRA_ITEM_ID, alarm.id());
|
||||
int flag = retrievePrevious ? FLAG_NO_CREATE : FLAG_CANCEL_CURRENT;
|
||||
PendingIntent pi = getActivity(context, alarm.intId(), intent, flag);
|
||||
// Even when we try to retrieve a previous instance that actually did exist,
|
||||
|
||||
@ -191,4 +191,6 @@
|
||||
|
||||
<!-- TODO: Remove or change this placeholder text -->
|
||||
<string name="title_activity_create_timer">CreateTimerActivity</string>
|
||||
|
||||
<string name="timer">Timer</string>
|
||||
</resources>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user