diff options
author | Jake Hamby <jhamby@google.com> | 2013-03-04 22:53:51 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2013-03-04 22:53:51 +0000 |
commit | 66e6f6f52f19c8eac696dd8fbb0cee2e6044da61 (patch) | |
tree | 3c7d471ec840861eb35e051d6648c7c56b876b88 | |
parent | 8413a412cec00f9c1838fb01c24fbfdc9f396a5e (diff) | |
parent | 4589e8a8dde484424a96d4644772d793043ad029 (diff) | |
download | android_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.xml | 3 | ||||
-rw-r--r-- | res/values/strings.xml | 21 | ||||
-rw-r--r-- | res/xml/preferences.xml | 7 | ||||
-rw-r--r-- | src/com/android/cellbroadcastreceiver/CellBroadcastAlertAudio.java | 54 | ||||
-rw-r--r-- | src/com/android/cellbroadcastreceiver/CellBroadcastAlertFullScreen.java | 6 | ||||
-rw-r--r-- | src/com/android/cellbroadcastreceiver/CellBroadcastAlertReminder.java | 173 | ||||
-rw-r--r-- | src/com/android/cellbroadcastreceiver/CellBroadcastSettings.java | 17 |
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 |