summaryrefslogtreecommitdiffstats
path: root/src/com
diff options
context:
space:
mode:
authorJames Lemieux <jplemieux@google.com>2016-12-05 14:41:03 -0800
committerJames Lemieux <jplemieux@google.com>2016-12-05 23:06:13 +0000
commit0777a082e248c33c2e5a32690bfcdbd5de7d3f9e (patch)
tree9dbbd50143df748b79cf27aab7f51423f3a73d4e /src/com
parentfadb6b35070b69632f7da14457dc49f31c5a2602 (diff)
downloadandroid_packages_apps_DeskClock-0777a082e248c33c2e5a32690bfcdbd5de7d3f9e.tar.gz
android_packages_apps_DeskClock-0777a082e248c33c2e5a32690bfcdbd5de7d3f9e.tar.bz2
android_packages_apps_DeskClock-0777a082e248c33c2e5a32690bfcdbd5de7d3f9e.zip
Remove DataModel.getSharedPreferences()
Bug: 33251787 All access to SharedPreferences now occurs privately within DataModel and UiDataModel. This ensures SharedPreferences are created precisely once and cached in member variables (in the delegate models behind DataModel). Tests can now be isolated from each other by rebuilding those delegate models. Change-Id: Ie7bf69bed13be1604e73dca6058877bc05e8afea
Diffstat (limited to 'src/com')
-rw-r--r--src/com/android/deskclock/AlarmInitReceiver.java2
-rw-r--r--src/com/android/deskclock/AsyncRingtonePlayer.java91
-rw-r--r--src/com/android/deskclock/DeskClockBackupAgent.java15
-rw-r--r--src/com/android/deskclock/RingtonePreviewKlaxon.java6
-rw-r--r--src/com/android/deskclock/alarms/AlarmActivity.java14
-rw-r--r--src/com/android/deskclock/alarms/AlarmKlaxon.java19
-rw-r--r--src/com/android/deskclock/alarms/AlarmStateManager.java34
-rw-r--r--src/com/android/deskclock/data/AlarmModel.java17
-rw-r--r--src/com/android/deskclock/data/DataModel.java80
-rw-r--r--src/com/android/deskclock/data/SettingsDAO.java95
-rw-r--r--src/com/android/deskclock/data/SettingsModel.java37
-rw-r--r--src/com/android/deskclock/data/TimerModel.java8
-rw-r--r--src/com/android/deskclock/provider/AlarmInstance.java14
-rw-r--r--src/com/android/deskclock/timer/TimerKlaxon.java11
14 files changed, 302 insertions, 141 deletions
diff --git a/src/com/android/deskclock/AlarmInitReceiver.java b/src/com/android/deskclock/AlarmInitReceiver.java
index 3848ad109..8bd7cdec7 100644
--- a/src/com/android/deskclock/AlarmInitReceiver.java
+++ b/src/com/android/deskclock/AlarmInitReceiver.java
@@ -58,7 +58,7 @@ public class AlarmInitReceiver extends BroadcastReceiver {
wl.acquire();
// We need to increment the global id out of the async task to prevent race conditions
- AlarmStateManager.updateGlobalIntentId(context);
+ DataModel.getDataModel().updateGlobalIntentId();
// Updates stopwatch and timer data after a device reboot so they are as accurate as
// possible.
diff --git a/src/com/android/deskclock/AsyncRingtonePlayer.java b/src/com/android/deskclock/AsyncRingtonePlayer.java
index e5aaca047..afb46d664 100644
--- a/src/com/android/deskclock/AsyncRingtonePlayer.java
+++ b/src/com/android/deskclock/AsyncRingtonePlayer.java
@@ -14,9 +14,6 @@ import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.telephony.TelephonyManager;
-import android.text.format.DateUtils;
-
-import com.android.deskclock.data.DataModel;
import java.io.IOException;
import java.lang.reflect.Method;
@@ -54,8 +51,6 @@ public final class AsyncRingtonePlayer {
private static final LogUtils.Logger LOGGER = new LogUtils.Logger("AsyncRingtonePlayer");
- private static final String DEFAULT_CRESCENDO_LENGTH = "0";
-
// Volume suggested by media team for in-call alarms.
private static final float IN_CALL_VOLUME = 0.125f;
@@ -64,6 +59,7 @@ public final class AsyncRingtonePlayer {
private static final int EVENT_STOP = 2;
private static final int EVENT_VOLUME = 3;
private static final String RINGTONE_URI_KEY = "RINGTONE_URI_KEY";
+ private static final String CRESCENDO_DURATION_KEY = "CRESCENDO_DURATION_KEY";
/** Handler running on the ringtone thread. */
private Handler mHandler;
@@ -74,28 +70,20 @@ public final class AsyncRingtonePlayer {
/** The context. */
private final Context mContext;
- /** The key of the preference that controls the crescendo behavior when playing a ringtone. */
- private final String mCrescendoPrefKey;
-
- /**
- * @param crescendoPrefKey the key to the user preference that defines the crescendo behavior
- * associated with this ringtone player, or null to ignore crescendo
- */
- public AsyncRingtonePlayer(Context context, String crescendoPrefKey) {
+ public AsyncRingtonePlayer(Context context) {
mContext = context;
- mCrescendoPrefKey = crescendoPrefKey;
}
/** Plays the ringtone. */
- public void play(Uri ringtoneUri) {
+ public void play(Uri ringtoneUri, long crescendoDuration) {
LOGGER.d("Posting play.");
- postMessage(EVENT_PLAY, ringtoneUri, 0);
+ postMessage(EVENT_PLAY, ringtoneUri, crescendoDuration, 0);
}
/** Stops playing the ringtone. */
public void stop() {
LOGGER.d("Posting stop.");
- postMessage(EVENT_STOP, null, 0);
+ postMessage(EVENT_STOP, null, 0, 0);
}
/** Schedules an adjustment of the playback volume 50ms in the future. */
@@ -106,17 +94,19 @@ public final class AsyncRingtonePlayer {
mHandler.removeMessages(EVENT_VOLUME);
// Queue the next volume adjustment.
- postMessage(EVENT_VOLUME, null, 50);
+ postMessage(EVENT_VOLUME, null, 0, 50);
}
/**
* Posts a message to the ringtone-thread handler.
*
- * @param messageCode The message to post.
- * @param ringtoneUri The ringtone in question, if any.
- * @param delayMillis The amount of time to delay sending the message, if any.
+ * @param messageCode the message to post
+ * @param ringtoneUri the ringtone in question, if any
+ * @param crescendoDuration the length of time, in ms, over which to crescendo the ringtone
+ * @param delayMillis the amount of time to delay sending the message, if any
*/
- private void postMessage(int messageCode, Uri ringtoneUri, long delayMillis) {
+ private void postMessage(int messageCode, Uri ringtoneUri, long crescendoDuration,
+ long delayMillis) {
synchronized (this) {
if (mHandler == null) {
mHandler = getNewHandler();
@@ -126,6 +116,7 @@ public final class AsyncRingtonePlayer {
if (ringtoneUri != null) {
final Bundle bundle = new Bundle();
bundle.putParcelable(RINGTONE_URI_KEY, ringtoneUri);
+ bundle.putLong(CRESCENDO_DURATION_KEY, crescendoDuration);
message.setData(bundle);
}
@@ -146,8 +137,10 @@ public final class AsyncRingtonePlayer {
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_PLAY:
- final Uri ringtoneUri = msg.getData().getParcelable(RINGTONE_URI_KEY);
- if (getPlaybackDelegate().play(mContext, ringtoneUri)) {
+ final Bundle data = msg.getData();
+ final Uri ringtoneUri = data.getParcelable(RINGTONE_URI_KEY);
+ final long crescendoDuration = data.getLong(CRESCENDO_DURATION_KEY);
+ if (getPlaybackDelegate().play(mContext, ringtoneUri, crescendoDuration)) {
scheduleVolumeAdjustment();
}
break;
@@ -221,22 +214,6 @@ public final class AsyncRingtonePlayer {
}
/**
- * @return {@code true} if the crescendo is enabled and its duration is more than 0 seconds
- */
- private boolean isCrescendoEnabled() {
- return mCrescendoPrefKey != null && getCrescendoDurationMillis() > 0;
- }
-
- /**
- * @return the duration of the crescendo in milliseconds
- */
- private long getCrescendoDurationMillis() {
- final String crescendoSecondsStr = DataModel.getSharedPreferences()
- .getString(mCrescendoPrefKey, DEFAULT_CRESCENDO_LENGTH);
- return Integer.parseInt(crescendoSecondsStr) * DateUtils.SECOND_IN_MILLIS;
- }
-
- /**
* @return the platform-specific playback delegate to use to play the ringtone
*/
private PlaybackDelegate getPlaybackDelegate() {
@@ -265,7 +242,11 @@ public final class AsyncRingtonePlayer {
/**
* @return {@code true} iff a {@link #adjustVolume volume adjustment} should be scheduled
*/
- boolean play(Context context, Uri ringtoneUri);
+ boolean play(Context context, Uri ringtoneUri, long crescendoDuration);
+
+ /**
+ * Stop any ongoing ringtone playback.
+ */
void stop(Context context);
/**
@@ -295,8 +276,9 @@ public final class AsyncRingtonePlayer {
* Starts the actual playback of the ringtone. Executes on ringtone-thread.
*/
@Override
- public boolean play(final Context context, Uri ringtoneUri) {
+ public boolean play(final Context context, Uri ringtoneUri, long crescendoDuration) {
checkAsyncRingtonePlayerThread();
+ mCrescendoDuration = crescendoDuration;
LOGGER.i("Play ringtone via android.media.MediaPlayer.");
@@ -322,14 +304,13 @@ public final class AsyncRingtonePlayer {
}
});
- final boolean crescendo = isCrescendoEnabled();
try {
// If alarmNoise is a custom ringtone on the sd card the app must be granted
// android.permission.READ_EXTERNAL_STORAGE. Pre-M this is ensured at app
// installation time. M+, this permission can be revoked by the user any time.
mMediaPlayer.setDataSource(context, alarmNoise);
- return startPlayback(inTelephoneCall, crescendo);
+ return startPlayback(inTelephoneCall);
} catch (Throwable t) {
LOGGER.e("Using the fallback ringtone, could not play " + alarmNoise, t);
// The alarmNoise may be on the sd card which could be busy right now.
@@ -338,7 +319,7 @@ public final class AsyncRingtonePlayer {
// Must reset the media player to clear the error state.
mMediaPlayer.reset();
mMediaPlayer.setDataSource(context, getFallbackRingtoneUri(context));
- return startPlayback(inTelephoneCall, crescendo);
+ return startPlayback(inTelephoneCall);
} catch (Throwable t2) {
// At this point we just don't play anything.
LOGGER.e("Failed to play fallback ringtone", t2);
@@ -353,11 +334,10 @@ public final class AsyncRingtonePlayer {
* playback.
*
* @param inTelephoneCall {@code true} if there is currently an active telephone call
- * @param crescendo {@code true} if the playback volume should start soft and crescendo
* @return {@code true} if a crescendo has started and future volume adjustments are
* required to advance the crescendo effect
*/
- private boolean startPlayback(boolean inTelephoneCall, boolean crescendo)
+ private boolean startPlayback(boolean inTelephoneCall)
throws IOException {
// Do not play alarms if stream volume is 0 (typically because ringer mode is silent).
if (mAudioManager.getStreamVolume(STREAM_ALARM) == 0) {
@@ -378,11 +358,10 @@ public final class AsyncRingtonePlayer {
if (inTelephoneCall) {
LOGGER.v("Using the in-call alarm");
mMediaPlayer.setVolume(IN_CALL_VOLUME, IN_CALL_VOLUME);
- } else if (crescendo) {
+ } else if (mCrescendoDuration > 0) {
mMediaPlayer.setVolume(0, 0);
// Compute the time at which the crescendo will stop.
- mCrescendoDuration = getCrescendoDurationMillis();
mCrescendoStopTime = Utils.now() + mCrescendoDuration;
scheduleVolumeAdjustment = true;
}
@@ -494,8 +473,9 @@ public final class AsyncRingtonePlayer {
* Starts the actual playback of the ringtone. Executes on ringtone-thread.
*/
@Override
- public boolean play(Context context, Uri ringtoneUri) {
+ public boolean play(Context context, Uri ringtoneUri, long crescendoDuration) {
checkAsyncRingtonePlayerThread();
+ mCrescendoDuration = crescendoDuration;
LOGGER.i("Play ringtone via android.media.Ringtone.");
@@ -535,15 +515,14 @@ public final class AsyncRingtonePlayer {
mRingtone = RingtoneManager.getRingtone(context, ringtoneUri);
}
- final boolean crescendo = isCrescendoEnabled();
try {
- return startPlayback(inTelephoneCall, crescendo);
+ return startPlayback(inTelephoneCall);
} catch (Throwable t) {
LOGGER.e("Using the fallback ringtone, could not play " + ringtoneUri, t);
// Recover from any/all playback errors by attempting to play the fallback tone.
mRingtone = RingtoneManager.getRingtone(context, getFallbackRingtoneUri(context));
try {
- return startPlayback(inTelephoneCall, crescendo);
+ return startPlayback(inTelephoneCall);
} catch (Throwable t2) {
// At this point we just don't play anything.
LOGGER.e("Failed to play fallback ringtone", t2);
@@ -557,11 +536,10 @@ public final class AsyncRingtonePlayer {
* Prepare the Ringtone for playback, then start the playback.
*
* @param inTelephoneCall {@code true} if there is currently an active telephone call
- * @param crescendo {@code true} if the playback volume should start soft and crescendo
* @return {@code true} if a crescendo has started and future volume adjustments are
* required to advance the crescendo effect
*/
- private boolean startPlayback(boolean inTelephoneCall, boolean crescendo) {
+ private boolean startPlayback(boolean inTelephoneCall) {
// Indicate the ringtone should be played via the alarm stream.
if (Utils.isLOrLater()) {
mRingtone.setAudioAttributes(new AudioAttributes.Builder()
@@ -575,11 +553,10 @@ public final class AsyncRingtonePlayer {
if (inTelephoneCall) {
LOGGER.v("Using the in-call alarm");
setRingtoneVolume(IN_CALL_VOLUME);
- } else if (crescendo) {
+ } else if (mCrescendoDuration > 0) {
setRingtoneVolume(0);
// Compute the time at which the crescendo will stop.
- mCrescendoDuration = getCrescendoDurationMillis();
mCrescendoStopTime = Utils.now() + mCrescendoDuration;
scheduleVolumeAdjustment = true;
}
diff --git a/src/com/android/deskclock/DeskClockBackupAgent.java b/src/com/android/deskclock/DeskClockBackupAgent.java
index 6948f663e..791cb05a9 100644
--- a/src/com/android/deskclock/DeskClockBackupAgent.java
+++ b/src/com/android/deskclock/DeskClockBackupAgent.java
@@ -24,7 +24,6 @@ import android.app.backup.BackupDataOutput;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.os.ParcelFileDescriptor;
import android.os.SystemClock;
import android.support.annotation.NonNull;
@@ -43,8 +42,6 @@ public class DeskClockBackupAgent extends BackupAgent {
private static final LogUtils.Logger LOGGER = new LogUtils.Logger("DeskClockBackupAgent");
- private static final String KEY_RESTORE_FINISHED = "restore_finished";
-
public static final String ACTION_COMPLETE_RESTORE =
"com.android.deskclock.action.COMPLETE_RESTORE";
@@ -87,9 +84,8 @@ public class DeskClockBackupAgent extends BackupAgent {
// the device-encrypted storage area
}
- // Write a preference to indicate a data restore has been completed.
- final SharedPreferences prefs = DataModel.getSharedPreferences();
- prefs.edit().putBoolean(KEY_RESTORE_FINISHED, true).apply();
+ // Indicate a data restore has been completed.
+ DataModel.getDataModel().setRestoreBackupFinished(true);
// Create an Intent to send into DeskClock indicating restore is complete.
final PendingIntent restoreIntent = PendingIntent.getBroadcast(this, 0,
@@ -111,9 +107,8 @@ public class DeskClockBackupAgent extends BackupAgent {
* @return {@code true} if restore data was processed; {@code false} otherwise.
*/
public static boolean processRestoredData(Context context) {
- // If the preference indicates data was not recently restored, there is nothing to do.
- final SharedPreferences prefs = DataModel.getSharedPreferences();
- if (!prefs.getBoolean(KEY_RESTORE_FINISHED, false)) {
+ // If data was not recently restored, there is nothing to do.
+ if (!DataModel.getDataModel().isRestoreBackupFinished()) {
return false;
}
@@ -143,7 +138,7 @@ public class DeskClockBackupAgent extends BackupAgent {
}
// Remove the preference to avoid executing this logic multiple times.
- prefs.edit().remove(KEY_RESTORE_FINISHED).apply();
+ DataModel.getDataModel().setRestoreBackupFinished(false);
LOGGER.i("processRestoredData() completed");
return true;
diff --git a/src/com/android/deskclock/RingtonePreviewKlaxon.java b/src/com/android/deskclock/RingtonePreviewKlaxon.java
index d11ed7e5d..eea5fe387 100644
--- a/src/com/android/deskclock/RingtonePreviewKlaxon.java
+++ b/src/com/android/deskclock/RingtonePreviewKlaxon.java
@@ -34,14 +34,14 @@ public final class RingtonePreviewKlaxon {
public static void start(Context context, Uri uri) {
stop(context);
LogUtils.i("RingtonePreviewKlaxon.start()");
- getAsyncRingtonePlayer(context).play(uri);
+ getAsyncRingtonePlayer(context).play(uri, 0);
}
private static synchronized AsyncRingtonePlayer getAsyncRingtonePlayer(Context context) {
if (sAsyncRingtonePlayer == null) {
- sAsyncRingtonePlayer = new AsyncRingtonePlayer(context.getApplicationContext(), null);
+ sAsyncRingtonePlayer = new AsyncRingtonePlayer(context.getApplicationContext());
}
return sAsyncRingtonePlayer;
}
-}
+} \ No newline at end of file
diff --git a/src/com/android/deskclock/alarms/AlarmActivity.java b/src/com/android/deskclock/alarms/AlarmActivity.java
index 7c370249b..b25a892ac 100644
--- a/src/com/android/deskclock/alarms/AlarmActivity.java
+++ b/src/com/android/deskclock/alarms/AlarmActivity.java
@@ -56,9 +56,9 @@ import com.android.deskclock.LogUtils;
import com.android.deskclock.R;
import com.android.deskclock.Utils;
import com.android.deskclock.data.DataModel;
+import com.android.deskclock.data.DataModel.AlarmVolumeButtonBehavior;
import com.android.deskclock.events.Events;
import com.android.deskclock.provider.AlarmInstance;
-import com.android.deskclock.settings.SettingsActivity;
import com.android.deskclock.uidata.UiDataModel;
import com.android.deskclock.widget.CircleView;
@@ -127,7 +127,7 @@ public class AlarmActivity extends AppCompatActivity
private AlarmInstance mAlarmInstance;
private boolean mAlarmHandled;
- private String mVolumeBehavior;
+ private AlarmVolumeButtonBehavior mVolumeBehavior;
private int mCurrentHourColor;
private boolean mReceiverRegistered;
/** Whether the AlarmService is currently bound */
@@ -173,9 +173,7 @@ public class AlarmActivity extends AppCompatActivity
LOGGER.i("Displaying alarm for instance: %s", mAlarmInstance);
// Get the volume/camera button behavior setting
- mVolumeBehavior = DataModel.getSharedPreferences()
- .getString(SettingsActivity.KEY_VOLUME_BUTTONS,
- SettingsActivity.DEFAULT_VOLUME_BEHAVIOR);
+ mVolumeBehavior = DataModel.getDataModel().getAlarmVolumeButtonBehavior();
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
@@ -299,12 +297,12 @@ public class AlarmActivity extends AppCompatActivity
case KeyEvent.KEYCODE_FOCUS:
if (!mAlarmHandled) {
switch (mVolumeBehavior) {
- case SettingsActivity.VOLUME_BEHAVIOR_SNOOZE:
+ case SNOOZE:
if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
snooze();
}
return true;
- case SettingsActivity.VOLUME_BEHAVIOR_DISMISS:
+ case DISMISS:
if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
dismiss();
}
@@ -491,7 +489,7 @@ public class AlarmActivity extends AppCompatActivity
final int accentColor = Utils.obtainStyledColor(this, R.attr.colorAccent, Color.RED);
setAnimatedFractions(1.0f /* snoozeFraction */, 0.0f /* dismissFraction */);
- final int snoozeMinutes = AlarmStateManager.getSnoozedMinutes(this);
+ final int snoozeMinutes = DataModel.getDataModel().getSnoozeLength();
final String infoText = getResources().getQuantityString(
R.plurals.alarm_alert_snooze_duration, snoozeMinutes, snoozeMinutes);
final String accessibilityText = getResources().getQuantityString(
diff --git a/src/com/android/deskclock/alarms/AlarmKlaxon.java b/src/com/android/deskclock/alarms/AlarmKlaxon.java
index cf2bd88e7..a1624239f 100644
--- a/src/com/android/deskclock/alarms/AlarmKlaxon.java
+++ b/src/com/android/deskclock/alarms/AlarmKlaxon.java
@@ -25,14 +25,15 @@ import android.os.Vibrator;
import com.android.deskclock.AsyncRingtonePlayer;
import com.android.deskclock.LogUtils;
import com.android.deskclock.Utils;
+import com.android.deskclock.data.DataModel;
import com.android.deskclock.provider.AlarmInstance;
-import com.android.deskclock.settings.SettingsActivity;
/**
- * Manages playing ringtone and vibrating the device.
+ * Manages playing alarm ringtones and vibrating the device.
*/
-public final class AlarmKlaxon {
- private static final long[] sVibratePattern = {500, 500};
+final class AlarmKlaxon {
+
+ private static final long[] VIBRATE_PATTERN = {500, 500};
private static boolean sStarted = false;
private static AsyncRingtonePlayer sAsyncRingtonePlayer;
@@ -54,7 +55,8 @@ public final class AlarmKlaxon {
LogUtils.v("AlarmKlaxon.start()");
if (!AlarmInstance.NO_RINGTONE_URI.equals(instance.mRingtone)) {
- getAsyncRingtonePlayer(context).play(instance.mRingtone);
+ final long crescendoDuration = DataModel.getDataModel().getAlarmCrescendoDuration();
+ getAsyncRingtonePlayer(context).play(instance.mRingtone, crescendoDuration);
}
if (instance.mVibrate) {
@@ -62,7 +64,7 @@ public final class AlarmKlaxon {
if (Utils.isLOrLater()) {
vibrateLOrLater(vibrator);
} else {
- vibrator.vibrate(sVibratePattern, 0);
+ vibrator.vibrate(VIBRATE_PATTERN, 0);
}
}
@@ -71,7 +73,7 @@ public final class AlarmKlaxon {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static void vibrateLOrLater(Vibrator vibrator) {
- vibrator.vibrate(sVibratePattern, 0, new AudioAttributes.Builder()
+ vibrator.vibrate(VIBRATE_PATTERN, 0, new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_ALARM)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build());
@@ -83,8 +85,7 @@ public final class AlarmKlaxon {
private static synchronized AsyncRingtonePlayer getAsyncRingtonePlayer(Context context) {
if (sAsyncRingtonePlayer == null) {
- sAsyncRingtonePlayer = new AsyncRingtonePlayer(context.getApplicationContext(),
- SettingsActivity.KEY_ALARM_CRESCENDO);
+ sAsyncRingtonePlayer = new AsyncRingtonePlayer(context.getApplicationContext());
}
return sAsyncRingtonePlayer;
diff --git a/src/com/android/deskclock/alarms/AlarmStateManager.java b/src/com/android/deskclock/alarms/AlarmStateManager.java
index a5e2516aa..a73d79390 100644
--- a/src/com/android/deskclock/alarms/AlarmStateManager.java
+++ b/src/com/android/deskclock/alarms/AlarmStateManager.java
@@ -22,7 +22,6 @@ import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
@@ -44,7 +43,6 @@ import com.android.deskclock.data.DataModel;
import com.android.deskclock.events.Events;
import com.android.deskclock.provider.Alarm;
import com.android.deskclock.provider.AlarmInstance;
-import com.android.deskclock.settings.SettingsActivity;
import java.util.Calendar;
import java.util.Collections;
@@ -104,9 +102,6 @@ import static android.provider.Settings.System.NEXT_ALARM_FORMATTED;
* parent to see if it should disable or schedule a new alarm instance.
*/
public final class AlarmStateManager extends BroadcastReceiver {
- // These defaults must match the values in res/xml/settings.xml
- private static final String DEFAULT_SNOOZE_MINUTES = "10";
-
// Intent action to trigger an instance state change.
public static final String CHANGE_STATE_ACTION = "change_state";
@@ -162,17 +157,6 @@ public final class AlarmStateManager extends BroadcastReceiver {
sStateChangeScheduler = stateChangeScheduler;
}
- public static int getGlobalIntentId(Context context) {
- SharedPreferences prefs = DataModel.getSharedPreferences();
- return prefs.getInt(ALARM_GLOBAL_ID_EXTRA, -1);
- }
-
- public static void updateGlobalIntentId(Context context) {
- SharedPreferences prefs = DataModel.getSharedPreferences();
- int globalId = prefs.getInt(ALARM_GLOBAL_ID_EXTRA, -1) + 1;
- prefs.edit().putInt(ALARM_GLOBAL_ID_EXTRA, globalId).commit();
- }
-
/**
* Update the next alarm stored in framework. This value is also displayed in digital widgets
* and the clock tab in this app.
@@ -328,7 +312,7 @@ public final class AlarmStateManager extends BroadcastReceiver {
Intent intent = AlarmInstance.createIntent(context, AlarmService.class, instance.mId);
intent.setAction(CHANGE_STATE_ACTION);
intent.addCategory(tag);
- intent.putExtra(ALARM_GLOBAL_ID_EXTRA, getGlobalIntentId(context));
+ intent.putExtra(ALARM_GLOBAL_ID_EXTRA, DataModel.getDataModel().getGlobalIntentId());
if (state != null) {
intent.putExtra(ALARM_STATE_EXTRA, state.intValue());
}
@@ -472,7 +456,7 @@ public final class AlarmStateManager extends BroadcastReceiver {
Events.sendAlarmEvent(R.string.action_fire, 0);
- Calendar timeout = instance.getTimeout(context);
+ Calendar timeout = instance.getTimeout();
if (timeout != null) {
scheduleInstanceStateChange(context, timeout, instance, AlarmInstance.MISSED_STATE);
}
@@ -495,9 +479,7 @@ public final class AlarmStateManager extends BroadcastReceiver {
AlarmService.stopAlarm(context, instance);
// Calculate the new snooze alarm time
- String snoozeMinutesStr = DataModel.getSharedPreferences()
- .getString(SettingsActivity.KEY_ALARM_SNOOZE, DEFAULT_SNOOZE_MINUTES);
- final int snoozeMinutes = Integer.parseInt(snoozeMinutesStr);
+ final int snoozeMinutes = DataModel.getDataModel().getSnoozeLength();
Calendar newAlarmTime = Calendar.getInstance();
newAlarmTime.add(Calendar.MINUTE, snoozeMinutes);
@@ -532,12 +514,6 @@ public final class AlarmStateManager extends BroadcastReceiver {
updateNextAlarm(context);
}
- public static int getSnoozedMinutes(Context context) {
- final String snoozeMinutesStr = DataModel.getSharedPreferences()
- .getString(SettingsActivity.KEY_ALARM_SNOOZE, DEFAULT_SNOOZE_MINUTES);
- return Integer.parseInt(snoozeMinutesStr);
- }
-
/**
* This will set the alarm instance to the MISSED_STATE and update
* the application notifications and schedule any state changes that need
@@ -680,7 +656,7 @@ public final class AlarmStateManager extends BroadcastReceiver {
final Alarm alarm = Alarm.getAlarm(cr, instance.mAlarmId);
final Calendar currentTime = getCurrentTime();
final Calendar alarmTime = instance.getAlarmTime();
- final Calendar timeoutTime = instance.getTimeout(context);
+ final Calendar timeoutTime = instance.getTimeout();
final Calendar lowNotificationTime = instance.getLowNotificationTime();
final Calendar highNotificationTime = instance.getHighNotificationTime();
final Calendar missedTTL = instance.getMissedTimeToLive();
@@ -932,7 +908,7 @@ public final class AlarmStateManager extends BroadcastReceiver {
return;
}
- int globalId = getGlobalIntentId(context);
+ int globalId = DataModel.getDataModel().getGlobalIntentId();
int intentId = intent.getIntExtra(ALARM_GLOBAL_ID_EXTRA, -1);
int alarmState = intent.getIntExtra(ALARM_STATE_EXTRA, -1);
if (intentId != globalId) {
diff --git a/src/com/android/deskclock/data/AlarmModel.java b/src/com/android/deskclock/data/AlarmModel.java
index bf27b8b9c..da50fe550 100644
--- a/src/com/android/deskclock/data/AlarmModel.java
+++ b/src/com/android/deskclock/data/AlarmModel.java
@@ -23,6 +23,7 @@ import android.net.Uri;
import android.os.Handler;
import android.provider.Settings;
+import com.android.deskclock.data.DataModel.AlarmVolumeButtonBehavior;
import com.android.deskclock.provider.Alarm;
/**
@@ -61,6 +62,22 @@ final class AlarmModel {
}
}
+ long getAlarmCrescendoDuration() {
+ return mSettingsModel.getAlarmCrescendoDuration();
+ }
+
+ AlarmVolumeButtonBehavior getAlarmVolumeButtonBehavior() {
+ return mSettingsModel.getAlarmVolumeButtonBehavior();
+ }
+
+ int getAlarmTimeout() {
+ return mSettingsModel.getAlarmTimeout();
+ }
+
+ int getSnoozeLength() {
+ return mSettingsModel.getSnoozeLength();
+ }
+
/**
* This receiver is notified when system settings change. Cached information built on
* those system settings must be cleared.
diff --git a/src/com/android/deskclock/data/DataModel.java b/src/com/android/deskclock/data/DataModel.java
index 537dd7774..f5d9328ac 100644
--- a/src/com/android/deskclock/data/DataModel.java
+++ b/src/com/android/deskclock/data/DataModel.java
@@ -56,6 +56,9 @@ public final class DataModel {
/** Indicates the preferred sort order of cities. */
public enum CitySort {NAME, UTC_OFFSET}
+ /** Indicates the preferred behavior of hardware volume buttons when firing alarms. */
+ public enum AlarmVolumeButtonBehavior {NOTHING, SNOOZE, DISMISS}
+
/** Indicates the reason alarms may not fire or may fire silently. */
public enum SilentSetting {
DO_NOT_DISTURB(R.string.alarms_blocked_by_dnd, 0, null),
@@ -169,17 +172,10 @@ public final class DataModel {
/** The model from which ringtone data are fetched. */
private RingtoneModel mRingtoneModel;
- /** Single instance of SharedPreferences for the current session. */
- private static SharedPreferences sSharedPreferences;
-
public static DataModel getDataModel() {
return sDataModel;
}
- public static SharedPreferences getSharedPreferences() {
- return sSharedPreferences;
- }
-
private DataModel() {}
/**
@@ -189,7 +185,6 @@ public final class DataModel {
if (mContext != context) {
mContext = context.getApplicationContext();
- sSharedPreferences = prefs;
mTimeModel = new TimeModel(mContext);
mWidgetModel = new WidgetModel(prefs);
mSettingsModel = new SettingsModel(mContext, prefs);
@@ -643,6 +638,15 @@ public final class DataModel {
}
/**
+ * @return the duration, in milliseconds, of the crescendo to apply to timer ringtone playback;
+ * {@code 0} implies no crescendo should be applied
+ */
+ public long getTimerCrescendoDuration() {
+ enforceMainLooper();
+ return mTimerModel.getTimerCrescendoDuration();
+ }
+
+ /**
* @return whether vibrate is enabled for all timers.
*/
public boolean getTimerVibrate() {
@@ -678,6 +682,37 @@ public final class DataModel {
mAlarmModel.setDefaultAlarmRingtoneUri(uri);
}
+ /**
+ * @return the duration, in milliseconds, of the crescendo to apply to alarm ringtone playback;
+ * {@code 0} implies no crescendo should be applied
+ */
+ public long getAlarmCrescendoDuration() {
+ enforceMainLooper();
+ return mAlarmModel.getAlarmCrescendoDuration();
+ }
+
+ /**
+ * @return the behavior to execute when volume buttons are pressed while firing an alarm
+ */
+ public AlarmVolumeButtonBehavior getAlarmVolumeButtonBehavior() {
+ enforceMainLooper();
+ return mAlarmModel.getAlarmVolumeButtonBehavior();
+ }
+
+ /**
+ * @return the number of minutes an alarm may ring before it has timed out and becomes missed
+ */
+ public int getAlarmTimeout() {
+ return mAlarmModel.getAlarmTimeout();
+ }
+
+ /**
+ * @return the number of minutes an alarm will remain snoozed before it rings again
+ */
+ public int getSnoozeLength() {
+ return mAlarmModel.getSnoozeLength();
+ }
+
//
// Stopwatch
//
@@ -888,6 +923,21 @@ public final class DataModel {
}
/**
+ * @return the id used to discriminate relevant AlarmManager callbacks from defunct ones
+ */
+ public int getGlobalIntentId() {
+ return mSettingsModel.getGlobalIntentId();
+ }
+
+ /**
+ * Update the id used to discriminate relevant AlarmManager callbacks from defunct ones
+ */
+ public void updateGlobalIntentId() {
+ enforceMainLooper();
+ mSettingsModel.updateGlobalIntentId();
+ }
+
+ /**
* @return the style of clock to display in the clock application
*/
public ClockStyle getClockStyle() {
@@ -946,6 +996,20 @@ public final class DataModel {
}
/**
+ * @return {@code true} if the restore process (of backup and restore) has completed
+ */
+ public boolean isRestoreBackupFinished() {
+ return mSettingsModel.isRestoreBackupFinished();
+ }
+
+ /**
+ * @param finished {@code true} means the restore process (of backup and restore) has completed
+ */
+ public void setRestoreBackupFinished(boolean finished) {
+ mSettingsModel.setRestoreBackupFinished(finished);
+ }
+
+ /**
* Used to execute a delegate runnable and track its completion.
*/
private static class ExecutedRunnable implements Runnable {
diff --git a/src/com/android/deskclock/data/SettingsDAO.java b/src/com/android/deskclock/data/SettingsDAO.java
index e98a706c0..7a6861bb9 100644
--- a/src/com/android/deskclock/data/SettingsDAO.java
+++ b/src/com/android/deskclock/data/SettingsDAO.java
@@ -20,8 +20,10 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.net.Uri;
import android.provider.Settings;
+import android.text.format.DateUtils;
import com.android.deskclock.R;
+import com.android.deskclock.data.DataModel.AlarmVolumeButtonBehavior;
import com.android.deskclock.data.DataModel.CitySort;
import com.android.deskclock.data.DataModel.ClockStyle;
import com.android.deskclock.settings.ScreensaverSettingsActivity;
@@ -31,6 +33,9 @@ import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
+import static com.android.deskclock.data.DataModel.AlarmVolumeButtonBehavior.DISMISS;
+import static com.android.deskclock.data.DataModel.AlarmVolumeButtonBehavior.NOTHING;
+import static com.android.deskclock.data.DataModel.AlarmVolumeButtonBehavior.SNOOZE;
import static com.android.deskclock.data.Weekdays.Order.MON_TO_SUN;
import static com.android.deskclock.data.Weekdays.Order.SAT_TO_FRI;
import static com.android.deskclock.data.Weekdays.Order.SUN_TO_SAT;
@@ -49,9 +54,30 @@ final class SettingsDAO {
/** Key to a preference that stores the default ringtone for new alarms. */
private static final String KEY_DEFAULT_ALARM_RINGTONE_URI = "default_alarm_ringtone_uri";
+ /** Key to a preference that stores the global broadcast id. */
+ private static final String KEY_ALARM_GLOBAL_ID = "intent.extra.alarm.global.id";
+
+ /** Key to a preference that indicates whether restore (of backup and restore) has completed. */
+ private static final String KEY_RESTORE_BACKUP_FINISHED = "restore_finished";
+
private SettingsDAO() {}
/**
+ * @return the id used to discriminate relevant AlarmManager callbacks from defunct ones
+ */
+ static int getGlobalIntentId(SharedPreferences prefs) {
+ return prefs.getInt(KEY_ALARM_GLOBAL_ID, -1);
+ }
+
+ /**
+ * Update the id used to discriminate relevant AlarmManager callbacks from defunct ones
+ */
+ static void updateGlobalIntentId(SharedPreferences prefs) {
+ final int globalId = prefs.getInt(KEY_ALARM_GLOBAL_ID, -1) + 1;
+ prefs.edit().putInt(KEY_ALARM_GLOBAL_ID, globalId).apply();
+ }
+
+ /**
* @return an enumerated value indicating the order in which cities are ordered
*/
static CitySort getCitySort(SharedPreferences prefs) {
@@ -192,6 +218,24 @@ final class SettingsDAO {
}
/**
+ * @return the duration, in milliseconds, of the crescendo to apply to alarm ringtone playback;
+ * {@code 0} implies no crescendo should be applied
+ */
+ static long getAlarmCrescendoDuration(SharedPreferences prefs) {
+ final String crescendoSeconds = prefs.getString(SettingsActivity.KEY_ALARM_CRESCENDO, "0");
+ return Integer.parseInt(crescendoSeconds) * DateUtils.SECOND_IN_MILLIS;
+ }
+
+ /**
+ * @return the duration, in milliseconds, of the crescendo to apply to timer ringtone playback;
+ * {@code 0} implies no crescendo should be applied
+ */
+ static long getTimerCrescendoDuration(SharedPreferences prefs) {
+ final String crescendoSeconds = prefs.getString(SettingsActivity.KEY_TIMER_CRESCENDO, "0");
+ return Integer.parseInt(crescendoSeconds) * DateUtils.SECOND_IN_MILLIS;
+ }
+
+ /**
* @return the display order of the weekdays, which can start with {@link Calendar#SATURDAY},
* {@link Calendar#SUNDAY} or {@link Calendar#MONDAY}
*/
@@ -208,6 +252,57 @@ final class SettingsDAO {
}
}
+ /**
+ * @return {@code true} if the restore process (of backup and restore) has completed
+ */
+ static boolean isRestoreBackupFinished(SharedPreferences prefs) {
+ return prefs.getBoolean(KEY_RESTORE_BACKUP_FINISHED, false);
+ }
+
+ /**
+ * @param finished {@code true} means the restore process (of backup and restore) has completed
+ */
+ static void setRestoreBackupFinished(SharedPreferences prefs, boolean finished) {
+ if (finished) {
+ prefs.edit().putBoolean(KEY_RESTORE_BACKUP_FINISHED, true).apply();
+ } else {
+ prefs.edit().remove(KEY_RESTORE_BACKUP_FINISHED).apply();
+ }
+ }
+
+ /**
+ * @return the behavior to execute when volume buttons are pressed while firing an alarm
+ */
+ static AlarmVolumeButtonBehavior getAlarmVolumeButtonBehavior(SharedPreferences prefs) {
+ final String defaultValue = SettingsActivity.DEFAULT_VOLUME_BEHAVIOR;
+ final String value = prefs.getString(SettingsActivity.KEY_VOLUME_BUTTONS, defaultValue);
+ switch (value) {
+ case SettingsActivity.DEFAULT_VOLUME_BEHAVIOR: return NOTHING;
+ case SettingsActivity.VOLUME_BEHAVIOR_SNOOZE: return SNOOZE;
+ case SettingsActivity.VOLUME_BEHAVIOR_DISMISS: return DISMISS;
+ default:
+ throw new IllegalArgumentException("Unknown volume button behavior: " + value);
+ }
+ }
+
+ /**
+ * @return the number of minutes an alarm may ring before it has timed out and becomes missed
+ */
+ static int getAlarmTimeout(SharedPreferences prefs) {
+ // Default value must match the one in res/xml/settings.xml
+ final String string = prefs.getString(SettingsActivity.KEY_AUTO_SILENCE, "10");
+ return Integer.parseInt(string);
+ }
+
+ /**
+ * @return the number of minutes an alarm will remain snoozed before it rings again
+ */
+ static int getSnoozeLength(SharedPreferences prefs) {
+ // Default value must match the one in res/xml/settings.xml
+ final String string = prefs.getString(SettingsActivity.KEY_ALARM_SNOOZE, "10");
+ return Integer.parseInt(string);
+ }
+
private static ClockStyle getClockStyle(Context context, SharedPreferences prefs, String key) {
final String defaultStyle = context.getString(R.string.default_clock_style);
final String clockStyle = prefs.getString(key, defaultStyle);
diff --git a/src/com/android/deskclock/data/SettingsModel.java b/src/com/android/deskclock/data/SettingsModel.java
index 21e928f0f..c02befa1f 100644
--- a/src/com/android/deskclock/data/SettingsModel.java
+++ b/src/com/android/deskclock/data/SettingsModel.java
@@ -22,6 +22,7 @@ import android.net.Uri;
import com.android.deskclock.R;
import com.android.deskclock.Utils;
+import com.android.deskclock.data.DataModel.AlarmVolumeButtonBehavior;
import com.android.deskclock.data.DataModel.CitySort;
import com.android.deskclock.data.DataModel.ClockStyle;
@@ -49,6 +50,14 @@ final class SettingsModel {
SettingsDAO.setDefaultDisplayClockSeconds(mContext, prefs);
}
+ int getGlobalIntentId() {
+ return SettingsDAO.getGlobalIntentId(mPrefs);
+ }
+
+ void updateGlobalIntentId() {
+ SettingsDAO.updateGlobalIntentId(mPrefs);
+ }
+
CitySort getCitySort() {
return SettingsDAO.getCitySort(mPrefs);
}
@@ -109,6 +118,18 @@ final class SettingsModel {
return SettingsDAO.getTimerRingtoneUri(mPrefs, getDefaultTimerRingtoneUri());
}
+ AlarmVolumeButtonBehavior getAlarmVolumeButtonBehavior() {
+ return SettingsDAO.getAlarmVolumeButtonBehavior(mPrefs);
+ }
+
+ int getAlarmTimeout() {
+ return SettingsDAO.getAlarmTimeout(mPrefs);
+ }
+
+ int getSnoozeLength() {
+ return SettingsDAO.getSnoozeLength(mPrefs);
+ }
+
Uri getDefaultAlarmRingtoneUri() {
return SettingsDAO.getDefaultAlarmRingtoneUri(mPrefs);
}
@@ -117,10 +138,26 @@ final class SettingsModel {
SettingsDAO.setDefaultAlarmRingtoneUri(mPrefs, uri);
}
+ long getAlarmCrescendoDuration() {
+ return SettingsDAO.getAlarmCrescendoDuration(mPrefs);
+ }
+
+ long getTimerCrescendoDuration() {
+ return SettingsDAO.getTimerCrescendoDuration(mPrefs);
+ }
+
Weekdays.Order getWeekdayOrder() {
return SettingsDAO.getWeekdayOrder(mPrefs);
}
+ boolean isRestoreBackupFinished() {
+ return SettingsDAO.isRestoreBackupFinished(mPrefs);
+ }
+
+ void setRestoreBackupFinished(boolean finished) {
+ SettingsDAO.setRestoreBackupFinished(mPrefs, finished);
+ }
+
boolean getTimerVibrate() {
return SettingsDAO.getTimerVibrate(mPrefs);
}
diff --git a/src/com/android/deskclock/data/TimerModel.java b/src/com/android/deskclock/data/TimerModel.java
index fb2c06a3c..0b8b2aef6 100644
--- a/src/com/android/deskclock/data/TimerModel.java
+++ b/src/com/android/deskclock/data/TimerModel.java
@@ -448,6 +448,14 @@ final class TimerModel {
}
/**
+ * @return the duration, in milliseconds, of the crescendo to apply to timer ringtone playback;
+ * {@code 0} implies no crescendo should be applied
+ */
+ long getTimerCrescendoDuration() {
+ return mSettingsModel.getTimerCrescendoDuration();
+ }
+
+ /**
* @return {@code true} if the device vibrates when timers expire
*/
boolean getTimerVibrate() {
diff --git a/src/com/android/deskclock/provider/AlarmInstance.java b/src/com/android/deskclock/provider/AlarmInstance.java
index 22f00f971..9fb7a7b07 100644
--- a/src/com/android/deskclock/provider/AlarmInstance.java
+++ b/src/com/android/deskclock/provider/AlarmInstance.java
@@ -27,10 +27,8 @@ import android.net.Uri;
import com.android.deskclock.LogUtils;
import com.android.deskclock.R;
-import com.android.deskclock.Utils;
import com.android.deskclock.alarms.AlarmStateManager;
import com.android.deskclock.data.DataModel;
-import com.android.deskclock.settings.SettingsActivity;
import java.util.Calendar;
import java.util.LinkedList;
@@ -53,11 +51,6 @@ public final class AlarmInstance implements ClockContract.InstancesColumns {
private static final int MISSED_TIME_TO_LIVE_HOUR_OFFSET = 12;
/**
- * Default timeout for alarms in minutes.
- */
- private static final String DEFAULT_ALARM_TIMEOUT_SETTING = "10";
-
- /**
* AlarmInstances start with an invalid id when it hasn't been saved to the database.
*/
public static final long INVALID_ID = -1;
@@ -437,13 +430,10 @@ public final class AlarmInstance implements ClockContract.InstancesColumns {
/**
* Return the time when the alarm should stop firing and be marked as missed.
*
- * @param context to figure out the timeout setting
* @return the time when alarm should be silence, or null if never
*/
- public Calendar getTimeout(Context context) {
- String timeoutSetting = DataModel.getSharedPreferences()
- .getString(SettingsActivity.KEY_AUTO_SILENCE, DEFAULT_ALARM_TIMEOUT_SETTING);
- int timeoutMinutes = Integer.parseInt(timeoutSetting);
+ public Calendar getTimeout() {
+ final int timeoutMinutes = DataModel.getDataModel().getAlarmTimeout();
// Alarm silence has been set to "None"
if (timeoutMinutes < 0) {
diff --git a/src/com/android/deskclock/timer/TimerKlaxon.java b/src/com/android/deskclock/timer/TimerKlaxon.java
index c2f6dc14e..1c40b4a4d 100644
--- a/src/com/android/deskclock/timer/TimerKlaxon.java
+++ b/src/com/android/deskclock/timer/TimerKlaxon.java
@@ -27,9 +27,12 @@ import com.android.deskclock.AsyncRingtonePlayer;
import com.android.deskclock.LogUtils;
import com.android.deskclock.Utils;
import com.android.deskclock.data.DataModel;
-import com.android.deskclock.settings.SettingsActivity;
+/**
+ * Manages playing the timer ringtone and vibrating the device.
+ */
public abstract class TimerKlaxon {
+
private static final long[] VIBRATE_PATTERN = {500, 500};
private static boolean sStarted = false;
@@ -58,7 +61,8 @@ public abstract class TimerKlaxon {
LogUtils.i("Playing silent ringtone for timer");
} else {
final Uri uri = DataModel.getDataModel().getTimerRingtoneUri();
- getAsyncRingtonePlayer(context).play(uri);
+ final long crescendoDuration = DataModel.getDataModel().getTimerCrescendoDuration();
+ getAsyncRingtonePlayer(context).play(uri, crescendoDuration);
}
if (DataModel.getDataModel().getTimerVibrate()) {
@@ -86,8 +90,7 @@ public abstract class TimerKlaxon {
private static synchronized AsyncRingtonePlayer getAsyncRingtonePlayer(Context context) {
if (sAsyncRingtonePlayer == null) {
- sAsyncRingtonePlayer = new AsyncRingtonePlayer(context.getApplicationContext(),
- SettingsActivity.KEY_TIMER_CRESCENDO);
+ sAsyncRingtonePlayer = new AsyncRingtonePlayer(context.getApplicationContext());
}
return sAsyncRingtonePlayer;