Background loading of single Alarm in relevant components, added queryEnabledAlarms() in database helper
This commit is contained in:
parent
5f138f2756
commit
b1657c221e
10
app/src/main/java/com/philliphsu/clock2/BaseFragment.java
Normal file
10
app/src/main/java/com/philliphsu/clock2/BaseFragment.java
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package com.philliphsu.clock2;
|
||||||
|
|
||||||
|
import android.app.Fragment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Phillip Hsu on 6/30/2016.
|
||||||
|
*/
|
||||||
|
public abstract class BaseFragment extends Fragment {
|
||||||
|
public abstract void onFabClick();
|
||||||
|
}
|
||||||
@ -59,13 +59,15 @@ public class OnBootUpAlarmScheduler extends IntentService {
|
|||||||
@Override
|
@Override
|
||||||
protected void onHandleIntent(Intent intent) {
|
protected void onHandleIntent(Intent intent) {
|
||||||
if (intent != null) {
|
if (intent != null) {
|
||||||
// IntentService already works in a background thread, so we don't need to use a loader.
|
// IntentService works in a background thread, so this won't hold us up.
|
||||||
AlarmCursor cursor = DatabaseManager.getInstance(this).queryAlarms();
|
AlarmCursor cursor = DatabaseManager.getInstance(this).queryEnabledAlarms();
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
Alarm alarm = cursor.getAlarm();
|
Alarm alarm = cursor.getAlarm();
|
||||||
if (alarm.isEnabled()) {
|
if (!alarm.isEnabled()) {
|
||||||
AlarmUtils.scheduleAlarm(this, alarm, false);
|
throw new IllegalStateException(
|
||||||
|
"queryEnabledAlarms() returned alarm(s) that aren't enabled");
|
||||||
}
|
}
|
||||||
|
AlarmUtils.scheduleAlarm(this, alarm, false);
|
||||||
}
|
}
|
||||||
cursor.close();
|
cursor.close();
|
||||||
|
|
||||||
|
|||||||
@ -159,6 +159,7 @@ public class AlarmsFragment extends Fragment implements LoaderCallbacks<Cursor>,
|
|||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
DatabaseManager.getInstance(getActivity()).insertAlarm(item);
|
DatabaseManager.getInstance(getActivity()).insertAlarm(item);
|
||||||
|
getLoaderManager().restartLoader(0, null, AlarmsFragment.this);
|
||||||
if (item.isEnabled()) {
|
if (item.isEnabled()) {
|
||||||
AlarmUtils.scheduleAlarm(getActivity(), item, true);
|
AlarmUtils.scheduleAlarm(getActivity(), item, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,6 +32,8 @@ public class AlarmDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
// and defines all the columns.
|
// and defines all the columns.
|
||||||
// TODO: Consider defining index constants for each column,
|
// TODO: Consider defining index constants for each column,
|
||||||
// and then removing all cursor getColumnIndex() calls.
|
// and then removing all cursor getColumnIndex() calls.
|
||||||
|
// TODO: Consider making these public, so callers can customize their
|
||||||
|
// WHERE queries.
|
||||||
private static final String TABLE_ALARMS = "alarms";
|
private static final String TABLE_ALARMS = "alarms";
|
||||||
private static final String COLUMN_ID = "_id";
|
private static final String COLUMN_ID = "_id";
|
||||||
private static final String COLUMN_HOUR = "hour";
|
private static final String COLUMN_HOUR = "hour";
|
||||||
@ -149,8 +151,16 @@ public class AlarmDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
|
|
||||||
public AlarmCursor queryAlarms() {
|
public AlarmCursor queryAlarms() {
|
||||||
// Select all rows and columns
|
// Select all rows and columns
|
||||||
|
return queryAlarms(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AlarmCursor queryEnabledAlarms() {
|
||||||
|
return queryAlarms(COLUMN_ENABLED + " = " + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AlarmCursor queryAlarms(String where) {
|
||||||
Cursor c = getReadableDatabase().query(TABLE_ALARMS,
|
Cursor c = getReadableDatabase().query(TABLE_ALARMS,
|
||||||
null, null, null, null, null, SORT_ORDER);
|
null, where, null, null, null, SORT_ORDER);
|
||||||
return new AlarmCursor(c);
|
return new AlarmCursor(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -79,4 +79,8 @@ public class DatabaseManager {
|
|||||||
public AlarmCursor queryAlarms() {
|
public AlarmCursor queryAlarms() {
|
||||||
return mHelper.queryAlarms();
|
return mHelper.queryAlarms();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AlarmCursor queryEnabledAlarms() {
|
||||||
|
return mHelper.queryEnabledAlarms();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,28 +5,27 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.v4.content.Loader;
|
||||||
import android.support.v4.content.LocalBroadcastManager;
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
|
||||||
import com.philliphsu.clock2.Alarm;
|
import com.philliphsu.clock2.Alarm;
|
||||||
import com.philliphsu.clock2.R;
|
import com.philliphsu.clock2.R;
|
||||||
import com.philliphsu.clock2.model.DatabaseManager;
|
import com.philliphsu.clock2.model.AlarmLoader;
|
||||||
import com.philliphsu.clock2.util.AlarmUtils;
|
import com.philliphsu.clock2.util.AlarmUtils;
|
||||||
import com.philliphsu.clock2.util.LocalBroadcastHelper;
|
import com.philliphsu.clock2.util.LocalBroadcastHelper;
|
||||||
|
|
||||||
import static com.philliphsu.clock2.util.Preconditions.checkNotNull;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An example full-screen activity that shows and hides the system UI (i.e.
|
* An example full-screen activity that shows and hides the system UI (i.e.
|
||||||
* status bar and navigation/system bar) with user interaction.
|
* status bar and navigation/system bar) with user interaction.
|
||||||
*
|
*
|
||||||
* TODO: Make this abstract and make appropriate subclasses for Alarms and Timers.
|
* TODO: Make this abstract and make appropriate subclasses for Alarms and Timers.
|
||||||
*/
|
*/
|
||||||
public class RingtoneActivity extends AppCompatActivity {
|
public class RingtoneActivity extends AppCompatActivity implements
|
||||||
|
android.support.v4.app.LoaderManager.LoaderCallbacks<Alarm> {
|
||||||
private static final String TAG = "RingtoneActivity";
|
private static final String TAG = "RingtoneActivity";
|
||||||
|
|
||||||
// Shared with RingtoneService
|
// Shared with RingtoneService
|
||||||
@ -35,6 +34,7 @@ public class RingtoneActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
private static boolean sIsAlive = false;
|
private static boolean sIsAlive = false;
|
||||||
|
|
||||||
|
private long mAlarmId;
|
||||||
private Alarm mAlarm;
|
private Alarm mAlarm;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -48,19 +48,17 @@ public class RingtoneActivity extends AppCompatActivity {
|
|||||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
|
||||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
|
||||||
|
|
||||||
long id = getIntent().getLongExtra(EXTRA_ITEM_ID, -1);
|
mAlarmId = getIntent().getLongExtra(EXTRA_ITEM_ID, -1);
|
||||||
if (id < 0) {
|
if (mAlarmId < 0) {
|
||||||
throw new IllegalStateException("Cannot start RingtoneActivity without item's id");
|
throw new IllegalStateException("Cannot start RingtoneActivity without item's id");
|
||||||
}
|
}
|
||||||
mAlarm = checkNotNull(DatabaseManager.getInstance(this).getAlarm(id));
|
// The reason we don't use a thread to load the alarm is because this is an
|
||||||
Log.d(TAG, "Ringing alarm " + mAlarm);
|
// Activity, which has complex lifecycle. LoaderManager is designed to help
|
||||||
|
// us through the vagaries of the lifecycle that could affect loading data.
|
||||||
// TODO: If the upcoming alarm notification isn't present, verify other notifications aren't affected.
|
getSupportLoaderManager().initLoader(0, null, this);
|
||||||
// This could be the case if we're starting a new instance of this activity after leaving the first launch.
|
|
||||||
AlarmUtils.removeUpcomingAlarmNotification(this, mAlarm);
|
|
||||||
|
|
||||||
Intent intent = new Intent(this, RingtoneService.class)
|
Intent intent = new Intent(this, RingtoneService.class)
|
||||||
.putExtra(EXTRA_ITEM_ID, id);
|
.putExtra(EXTRA_ITEM_ID, mAlarmId);
|
||||||
startService(intent);
|
startService(intent);
|
||||||
|
|
||||||
// TODO: Butterknife binding
|
// TODO: Butterknife binding
|
||||||
@ -139,19 +137,44 @@ public class RingtoneActivity extends AppCompatActivity {
|
|||||||
sIsAlive = false;
|
sIsAlive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Loader<Alarm> onCreateLoader(int id, Bundle args) {
|
||||||
|
return new AlarmLoader(this, mAlarmId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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.
|
||||||
|
AlarmUtils.removeUpcomingAlarmNotification(this, mAlarm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoaderReset(Loader<Alarm> loader) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isAlive() {
|
public static boolean isAlive() {
|
||||||
return sIsAlive;
|
return sIsAlive;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void snooze() {
|
private void snooze() {
|
||||||
|
if (mAlarm != null) {
|
||||||
AlarmUtils.snoozeAlarm(this, mAlarm);
|
AlarmUtils.snoozeAlarm(this, mAlarm);
|
||||||
|
}
|
||||||
// Can't call dismiss() because we don't want to also call cancelAlarm()! Why? For example,
|
// 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.
|
// we don't want the alarm, if it has no recurrence, to be turned off right now.
|
||||||
stopAndFinish();
|
stopAndFinish();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dismiss() {
|
private void dismiss() {
|
||||||
AlarmUtils.cancelAlarm(this, mAlarm, false); // TODO do we really need to cancel the intent and alarm?
|
if (mAlarm != null) {
|
||||||
|
// TODO do we really need to cancel the intent and alarm?
|
||||||
|
AlarmUtils.cancelAlarm(this, mAlarm, false);
|
||||||
|
}
|
||||||
stopAndFinish();
|
stopAndFinish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -64,7 +64,8 @@ public class RingtoneService extends Service { // TODO: abstract this, make subc
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
mAutoSilenced = true;
|
mAutoSilenced = true;
|
||||||
AlarmUtils.cancelAlarm(RingtoneService.this, mAlarm, false); // TODO do we really need to cancel the alarm and intent?
|
// TODO do we really need to cancel the alarm and intent?
|
||||||
|
AlarmUtils.cancelAlarm(RingtoneService.this, mAlarm, false);
|
||||||
finishActivity();
|
finishActivity();
|
||||||
stopSelf();
|
stopSelf();
|
||||||
}
|
}
|
||||||
@ -81,18 +82,29 @@ public class RingtoneService extends Service { // TODO: abstract this, make subc
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
long id = intent.getLongExtra(EXTRA_ITEM_ID, -1);
|
final long id = intent.getLongExtra(EXTRA_ITEM_ID, -1);
|
||||||
if (id < 0)
|
if (id < 0)
|
||||||
throw new IllegalStateException("No item id set");
|
throw new IllegalStateException("No item id set");
|
||||||
Alarm alarm = checkNotNull(DatabaseManager.getInstance(this).getAlarm(id));
|
|
||||||
|
|
||||||
if (intent.getAction() == null) {
|
if (intent.getAction() == null) {
|
||||||
playRingtone(alarm);
|
// http://stackoverflow.com/q/8696146/5055032
|
||||||
|
// Start our own thread to load the alarm instead of using a loader,
|
||||||
|
// because Services do not have a built-in LoaderManager (because they have no need for one since
|
||||||
|
// their lifecycle is not complex like in Activities/Fragments) and our
|
||||||
|
// work is simple enough that getting loaders to work here is not
|
||||||
|
// worth the effort.
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
mAlarm = checkNotNull(DatabaseManager
|
||||||
|
.getInstance(RingtoneService.this).getAlarm(id));
|
||||||
|
playRingtone();
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
} else {
|
} else {
|
||||||
if (ACTION_SNOOZE.equals(intent.getAction())) {
|
if (ACTION_SNOOZE.equals(intent.getAction())) {
|
||||||
AlarmUtils.snoozeAlarm(this, alarm);
|
AlarmUtils.snoozeAlarm(this, mAlarm);
|
||||||
} else if (ACTION_DISMISS.equals(intent.getAction())) {
|
} else if (ACTION_DISMISS.equals(intent.getAction())) {
|
||||||
AlarmUtils.cancelAlarm(this, alarm, false); // TODO do we really need to cancel the intent and alarm?
|
AlarmUtils.cancelAlarm(this, mAlarm, false); // TODO do we really need to cancel the intent and alarm?
|
||||||
} else {
|
} else {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
@ -143,9 +155,8 @@ public class RingtoneService extends Service { // TODO: abstract this, make subc
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void playRingtone(@NonNull Alarm alarm) {
|
private void playRingtone() {
|
||||||
if (mAudioManager == null && mRingtone == null) {
|
if (mAudioManager == null && mRingtone == null) {
|
||||||
mAlarm = checkNotNull(alarm);
|
|
||||||
// TODO: The below call requires a notification, and there is no way to provide one suitable
|
// TODO: The below call requires a notification, and there is no way to provide one suitable
|
||||||
// for both Alarms and Timers. Consider making this class abstract, and have subclasses
|
// for both Alarms and Timers. Consider making this class abstract, and have subclasses
|
||||||
// implement an abstract method that calls startForeground(). You would then call that
|
// implement an abstract method that calls startForeground(). You would then call that
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user