summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJake Hamby <jhamby@google.com>2013-03-04 22:53:51 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2013-03-04 22:53:51 +0000
commit66e6f6f52f19c8eac696dd8fbb0cee2e6044da61 (patch)
tree3c7d471ec840861eb35e051d6648c7c56b876b88
parent8413a412cec00f9c1838fb01c24fbfdc9f396a5e (diff)
parent4589e8a8dde484424a96d4644772d793043ad029 (diff)
downloadandroid_packages_apps_CellBroadcastReceiver-66e6f6f52f19c8eac696dd8fbb0cee2e6044da61.tar.gz
android_packages_apps_CellBroadcastReceiver-66e6f6f52f19c8eac696dd8fbb0cee2e6044da61.tar.bz2
android_packages_apps_CellBroadcastReceiver-66e6f6f52f19c8eac696dd8fbb0cee2e6044da61.zip
Merge "Add CMAS reminder alert feature." into jb-mr2-dev
-rw-r--r--AndroidManifest.xml3
-rw-r--r--res/values/strings.xml21
-rw-r--r--res/xml/preferences.xml7
-rw-r--r--src/com/android/cellbroadcastreceiver/CellBroadcastAlertAudio.java54
-rw-r--r--src/com/android/cellbroadcastreceiver/CellBroadcastAlertFullScreen.java6
-rw-r--r--src/com/android/cellbroadcastreceiver/CellBroadcastAlertReminder.java173
-rw-r--r--src/com/android/cellbroadcastreceiver/CellBroadcastSettings.java17
7 files changed, 269 insertions, 12 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 92fda98b..921fe4ac 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -43,6 +43,9 @@
<service android:name="CellBroadcastConfigService"
android:exported="false" />
+ <service android:name="CellBroadcastAlertReminder"
+ android:exported="false" />
+
<provider android:name="CellBroadcastContentProvider"
android:authorities="cellbroadcasts"
android:readPermission="android.permission.READ_CELL_BROADCASTS" />
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0a9b3870..21ed1904 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -93,6 +93,10 @@
<string name="alert_sound_duration_title">Alert sound duration</string>
<!-- Do not translate. Empty summary for alert duration (set by CellBroadcastSettings). -->
<string name="alert_sound_duration_summary"></string>
+ <!-- Preference title for alert reminder interval list. [CHAR LIMIT=30] -->
+ <string name="alert_reminder_interval_title">Alert reminder</string>
+ <!-- Do not translate. Empty summary for alert reminder (set by CellBroadcastSettings). -->
+ <string name="alert_reminder_interval_summary"></string>
<!-- Preference title for enable text-to-speech checkbox. [CHAR LIMIT=30] -->
<string name="enable_alert_speech_title">Speak alert message</string>
<!-- Preference summary for enable text-to-speech checkbox. [CHAR LIMIT=100] -->
@@ -243,4 +247,21 @@
<item>8</item>
<item>10</item>
</string-array>
+
+ <!-- Entries in the ListPreference for alert reminder intervals. [CHAR LIMIT=30] -->
+ <string-array name="alert_reminder_interval_entries">
+ <item>Once</item>
+ <item>Every 2 minutes</item>
+ <item>Every 15 minutes</item>
+ <item>Off</item>
+ </string-array>
+
+ <!-- Do not translate. Values that are retrieved from the ListPreference.
+ These must match the alert_reminder_interval_entries list above. -->
+ <string-array name="alert_reminder_interval_values">
+ <item>1</item>
+ <item>2</item>
+ <item>15</item>
+ <item>0</item>
+ </string-array>
</resources>
diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml
index 340830a8..8d3b1fa2 100644
--- a/res/xml/preferences.xml
+++ b/res/xml/preferences.xml
@@ -52,6 +52,13 @@
android:defaultValue="4"
android:dialogTitle="@string/alert_sound_duration_title" />
+ <ListPreference android:key="alert_reminder_interval"
+ android:title="@string/alert_reminder_interval_title"
+ android:entries="@array/alert_reminder_interval_entries"
+ android:entryValues="@array/alert_reminder_interval_values"
+ android:defaultValue="0"
+ android:dialogTitle="@string/alert_reminder_interval_title" />
+
<CheckBoxPreference android:defaultValue="true"
android:key="enable_alert_vibrate"
android:summary="@string/enable_alert_vibrate_summary"
diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertAudio.java b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertAudio.java
index bc44ed1d..52ed4e11 100644
--- a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertAudio.java
+++ b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertAudio.java
@@ -16,6 +16,7 @@
package com.android.cellbroadcastreceiver;
+import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
@@ -24,6 +25,9 @@ import android.content.res.Resources;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnErrorListener;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -97,6 +101,8 @@ public class CellBroadcastAlertAudio extends Service implements TextToSpeech.OnI
private TelephonyManager mTelephonyManager;
private int mInitialCallState;
+ private PendingIntent mPlayReminderIntent;
+
// Internal messages
private static final int ALERT_SOUND_FINISHED = 1000;
private static final int ALERT_PAUSE_FINISHED = 1001;
@@ -125,14 +131,14 @@ public class CellBroadcastAlertAudio extends Service implements TextToSpeech.OnI
mTts.speak(mMessageBody, TextToSpeech.QUEUE_FLUSH, null);
mState = STATE_SPEAKING;
} else {
- Log.w(TAG, "TTS engine not ready or language not supported");
+ loge("TTS engine not ready or language not supported");
stopSelf();
mState = STATE_IDLE;
}
break;
default:
- Log.e(TAG, "Handler received unknown message, what=" + msg.what);
+ loge("Handler received unknown message, what=" + msg.what);
}
}
};
@@ -162,7 +168,7 @@ public class CellBroadcastAlertAudio extends Service implements TextToSpeech.OnI
} else {
mTtsEngineReady = false;
mTts = null;
- Log.e(TAG, "onInit() TTS engine error: " + status);
+ loge("onInit() TTS engine error: " + status);
}
}
@@ -216,7 +222,7 @@ public class CellBroadcastAlertAudio extends Service implements TextToSpeech.OnI
mTts.shutdown();
} catch (IllegalStateException e) {
// catch "Unable to retrieve AudioTrack pointer for stop()" exception
- Log.e(TAG, "exception trying to shutdown text-to-speech");
+ loge("exception trying to shutdown text-to-speech");
}
}
// release CPU wake lock acquired by CellBroadcastAlertService
@@ -245,12 +251,13 @@ public class CellBroadcastAlertAudio extends Service implements TextToSpeech.OnI
mMessageLanguage = intent.getStringExtra(ALERT_AUDIO_MESSAGE_LANGUAGE);
mEnableVibrate = intent.getBooleanExtra(ALERT_AUDIO_VIBRATE_EXTRA, true);
- boolean forceVibrate = intent.getBooleanExtra(ALERT_AUDIO_ETWS_VIBRATE_EXTRA, false);
+ if (intent.getBooleanExtra(ALERT_AUDIO_ETWS_VIBRATE_EXTRA, false)) {
+ mEnableVibrate = true; // force enable vibration for ETWS alerts
+ }
switch (mAudioManager.getRingerMode()) {
case AudioManager.RINGER_MODE_SILENT:
if (DBG) log("Ringer mode: silent");
- mEnableVibrate = forceVibrate;
mEnableAudio = false;
break;
@@ -311,7 +318,7 @@ public class CellBroadcastAlertAudio extends Service implements TextToSpeech.OnI
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setOnErrorListener(new OnErrorListener() {
public boolean onError(MediaPlayer mp, int what, int extra) {
- Log.e(TAG, "Error occurred while playing audio.");
+ loge("Error occurred while playing audio.");
mp.stop();
mp.release();
mMediaPlayer = null;
@@ -324,7 +331,7 @@ public class CellBroadcastAlertAudio extends Service implements TextToSpeech.OnI
// sound at a low volume to not disrupt the call.
if (mTelephonyManager.getCallState()
!= TelephonyManager.CALL_STATE_IDLE) {
- Log.v(TAG, "in call: reducing volume");
+ log("in call: reducing volume");
mMediaPlayer.setVolume(IN_CALL_VOLUME, IN_CALL_VOLUME);
}
@@ -335,7 +342,7 @@ public class CellBroadcastAlertAudio extends Service implements TextToSpeech.OnI
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
startAlarm(mMediaPlayer);
} catch (Exception ex) {
- Log.e(TAG, "Failed to play alert sound", ex);
+ loge("Failed to play alert sound: " + ex);
}
}
@@ -363,12 +370,33 @@ public class CellBroadcastAlertAudio extends Service implements TextToSpeech.OnI
}
}
+ private void playAlertReminderSound() {
+ Uri notificationUri = RingtoneManager.getDefaultUri(
+ RingtoneManager.TYPE_NOTIFICATION | RingtoneManager.TYPE_ALARM);
+ if (notificationUri == null) {
+ loge("Can't get URI for alert reminder sound");
+ return;
+ }
+ Ringtone r = RingtoneManager.getRingtone(this, notificationUri);
+ if (r != null) {
+ log("playing alert reminder sound");
+ r.play();
+ } else {
+ loge("can't get Ringtone for alert reminder sound");
+ }
+ }
+
/**
* Stops alert audio and speech.
*/
public void stop() {
if (DBG) log("stop()");
+ if (mPlayReminderIntent != null) {
+ mPlayReminderIntent.cancel();
+ mPlayReminderIntent = null;
+ }
+
mHandler.removeMessages(ALERT_SOUND_FINISHED);
mHandler.removeMessages(ALERT_PAUSE_FINISHED);
@@ -380,7 +408,7 @@ public class CellBroadcastAlertAudio extends Service implements TextToSpeech.OnI
mMediaPlayer.release();
} catch (IllegalStateException e) {
// catch "Unable to retrieve AudioTrack pointer for stop()" exception
- Log.e(TAG, "exception trying to stop media player");
+ loge("exception trying to stop media player");
}
mMediaPlayer = null;
}
@@ -392,7 +420,7 @@ public class CellBroadcastAlertAudio extends Service implements TextToSpeech.OnI
mTts.stop();
} catch (IllegalStateException e) {
// catch "Unable to retrieve AudioTrack pointer for stop()" exception
- Log.e(TAG, "exception trying to stop text-to-speech");
+ loge("exception trying to stop text-to-speech");
}
}
mAudioManager.abandonAudioFocus(null);
@@ -402,4 +430,8 @@ public class CellBroadcastAlertAudio extends Service implements TextToSpeech.OnI
private static void log(String msg) {
Log.d(TAG, msg);
}
+
+ private static void loge(String msg) {
+ Log.e(TAG, msg);
+ }
}
diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertFullScreen.java b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertFullScreen.java
index cf6d7e5a..fdecc104 100644
--- a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertFullScreen.java
+++ b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertFullScreen.java
@@ -353,6 +353,9 @@ public class CellBroadcastAlertFullScreen extends Activity {
setTitle(titleId);
((TextView) findViewById(R.id.alertTitle)).setText(titleId);
((TextView) findViewById(R.id.message)).setText(message.getMessageBody());
+
+ // Set alert reminder depending on user preference
+ CellBroadcastAlertReminder.queueAlertReminder(this, true);
}
/**
@@ -386,6 +389,9 @@ public class CellBroadcastAlertFullScreen extends Activity {
// Stop playing alert sound/vibration/speech (if started)
stopService(new Intent(this, CellBroadcastAlertAudio.class));
+ // Cancel any pending alert reminder
+ CellBroadcastAlertReminder.cancelAlertReminder();
+
// Remove the current alert message from the list.
CellBroadcastMessage lastMessage = removeLatestMessage();
if (lastMessage == null) {
diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertReminder.java b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertReminder.java
new file mode 100644
index 00000000..e4cae6c1
--- /dev/null
+++ b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertReminder.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cellbroadcastreceiver;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.SystemClock;
+import android.preference.PreferenceManager;
+import android.util.Log;
+
+import static com.android.cellbroadcastreceiver.CellBroadcastReceiver.DBG;
+
+/**
+ * Manages alert reminder notification.
+ */
+public class CellBroadcastAlertReminder extends Service {
+ private static final String TAG = "CellBroadcastAlertReminder";
+
+ /** Action to wake up and play alert reminder sound. */
+ static final String ACTION_PLAY_ALERT_REMINDER = "ACTION_PLAY_ALERT_REMINDER";
+
+ /**
+ * Pending intent for alert reminder. This is static so that we don't have to start the
+ * service in order to cancel any pending reminders when user dismisses the alert dialog.
+ */
+ private static PendingIntent sPlayReminderIntent;
+
+ /**
+ * Alert reminder for current ringtone being played.
+ */
+ private static Ringtone sPlayReminderRingtone;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ // No intent or unrecognized action; tell the system not to restart us.
+ if (intent == null || !ACTION_PLAY_ALERT_REMINDER.equals(intent.getAction())) {
+ stopSelf();
+ return START_NOT_STICKY;
+ }
+
+ log("playing alert reminder");
+ playAlertReminderSound();
+
+ if (queueAlertReminder(this, false)) {
+ return START_STICKY;
+ } else {
+ log("no reminders queued");
+ stopSelf();
+ return START_NOT_STICKY;
+ }
+ }
+
+ /**
+ * Use the RingtoneManager to play the alert reminder sound.
+ */
+ private void playAlertReminderSound() {
+ Uri notificationUri = RingtoneManager.getDefaultUri(
+ RingtoneManager.TYPE_NOTIFICATION | RingtoneManager.TYPE_ALARM);
+ if (notificationUri == null) {
+ loge("Can't get URI for alert reminder sound");
+ return;
+ }
+ Ringtone r = RingtoneManager.getRingtone(this, notificationUri);
+ if (r != null) {
+ log("playing alert reminder sound");
+ r.play();
+ } else {
+ loge("can't get Ringtone for alert reminder sound");
+ }
+ }
+
+ /**
+ * Helper method to start the alert reminder service to queue the alert reminder.
+ * @return true if a pending reminder was set; false if there are no more reminders
+ */
+ static boolean queueAlertReminder(Context context, boolean firstTime) {
+ // Stop any alert reminder sound and cancel any previously queued reminders.
+ cancelAlertReminder();
+
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ String prefStr = prefs.getString(CellBroadcastSettings.KEY_ALERT_REMINDER_INTERVAL, null);
+
+ if (prefStr == null) {
+ if (DBG) log("no preference value for alert reminder");
+ return false;
+ }
+
+ int interval;
+ try {
+ interval = Integer.valueOf(prefStr);
+ } catch (NumberFormatException ignored) {
+ loge("invalid alert reminder interval preference: " + prefStr);
+ return false;
+ }
+
+ if (interval == 0 || (interval == 1 && !firstTime)) {
+ return false;
+ }
+ if (interval == 1) {
+ interval = 2; // "1" = one reminder after 2 minutes
+ }
+
+ if (DBG) log("queueAlertReminder() in " + interval + " minutes");
+
+ Intent playIntent = new Intent(context, CellBroadcastAlertReminder.class);
+ playIntent.setAction(ACTION_PLAY_ALERT_REMINDER);
+ sPlayReminderIntent = PendingIntent.getService(context, 0, playIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ if (alarmManager == null) {
+ loge("can't get Alarm Service");
+ return false;
+ }
+
+ // remind user after 2 minutes or 15 minutes
+ long triggerTime = SystemClock.elapsedRealtime() + (interval * 60000);
+ alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerTime, sPlayReminderIntent);
+ return true;
+ }
+
+ /**
+ * Stops alert reminder and cancels any queued reminders.
+ */
+ static void cancelAlertReminder() {
+ if (DBG) log("cancelAlertReminder()");
+ if (sPlayReminderRingtone != null) {
+ if (DBG) log("stopping play reminder ringtone");
+ sPlayReminderRingtone.stop();
+ sPlayReminderRingtone = null;
+ }
+ if (sPlayReminderIntent != null) {
+ if (DBG) log("canceling pending play reminder intent");
+ sPlayReminderIntent.cancel();
+ sPlayReminderIntent = null;
+ }
+ }
+
+ private static void log(String msg) {
+ Log.d(TAG, msg);
+ }
+
+ private static void loge(String msg) {
+ Log.e(TAG, msg);
+ }
+}
diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastSettings.java b/src/com/android/cellbroadcastreceiver/CellBroadcastSettings.java
index 20c2625c..85133311 100644
--- a/src/com/android/cellbroadcastreceiver/CellBroadcastSettings.java
+++ b/src/com/android/cellbroadcastreceiver/CellBroadcastSettings.java
@@ -27,7 +27,6 @@ import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
import android.provider.Settings;
import android.telephony.TelephonyManager;
-import android.util.Log;
/**
* Settings activity for the cell broadcast receiver.
@@ -85,6 +84,9 @@ public class CellBroadcastSettings extends PreferenceActivity {
// Preference key for initial opt-in/opt-out dialog.
public static final String KEY_SHOW_CMAS_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog";
+ // Alert reminder interval ("once" = single 2 minute reminder).
+ public static final String KEY_ALERT_REMINDER_INTERVAL = "alert_reminder_interval";
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -129,6 +131,19 @@ public class CellBroadcastSettings extends PreferenceActivity {
PreferenceCategory alertCategory = (PreferenceCategory)
findPreference(KEY_CATEGORY_ALERT_SETTINGS);
+ // alert reminder interval
+ ListPreference interval = (ListPreference) findPreference(KEY_ALERT_REMINDER_INTERVAL);
+ interval.setSummary(interval.getEntry());
+ interval.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference pref, Object newValue) {
+ final ListPreference listPref = (ListPreference) pref;
+ final int idx = listPref.findIndexOfValue((String) newValue);
+ listPref.setSummary(listPref.getEntries()[idx]);
+ return true;
+ }
+ });
+
// Show alert settings and ETWS categories for ETWS builds and developer mode.
if (enableDevSettings || showEtwsSettings) {
// enable/disable all alerts