Loading res/values/strings.xml +13 −0 Original line number Diff line number Diff line Loading @@ -921,6 +921,12 @@ --> <string name="alarm_is_dismissed"><xliff:g id="alarm_time" example="14:20">%s</xliff:g> alarm dismissed</string> <!-- String that represents that the user has deleted an alarm through a voice action. %s represents alarm time, e.g. 14:20 [CHAR LIMIT=NONE] --> <string name="alarm_is_deleted"><xliff:g id="alarm_time" example="14:20">%s</xliff:g> alarm deleted</string> <!-- String that represents that the user has dismissed an alarm through a voice action. %s represents alarm time, e.g. 14:20 [CHAR LIMIT=NONE] Loading Loading @@ -1026,6 +1032,13 @@ --> <string name="pick_alarm_to_dismiss">Pick which alarm to dismiss</string> <!-- String that represents that further action is needed from the user in case their voice command was ambiguous or there are more than 1 alarms that match their request. The user needs to choose an alarm they want to delete through the UI [CHAR LIMIT=NONE] --> <string name="pick_alarm_to_delete">Pick which alarm to delete</string> <!-- String that represents that the user doesn't have any alarms firing at the moment. [CHAR LIMIT=NONE] --> Loading src/com/android/deskclock/AlarmSelectionActivity.java +27 −3 Original line number Diff line number Diff line Loading @@ -34,10 +34,22 @@ import java.util.List; public class AlarmSelectionActivity extends ListActivity { /** Used by default when neither ACTION_DISMISS nor ACTION_DELETE are provided */ private static final int ACTION_INVALID = -1; /** Action used to signify alarm should be dismissed on selection */ public static final int ACTION_DISMISS = 0; /** Action used to signify alarm should be deleted on selection */ public static final int ACTION_DELETE = 1; public static final String EXTRA_ACTION = "com.android.deskclock.EXTRA_ACTION"; public static final String EXTRA_ALARMS = "com.android.deskclock.EXTRA_ALARMS"; private final List<AlarmSelection> mSelections = new ArrayList<>(); private int mAction; @Override protected void onCreate(Bundle savedInstanceState) { // this activity is shown if: Loading @@ -61,6 +73,7 @@ public class AlarmSelectionActivity extends ListActivity { final Intent intent = getIntent(); final Parcelable[] alarmsFromIntent = intent.getParcelableArrayExtra(EXTRA_ALARMS); mAction = intent.getIntExtra(EXTRA_ACTION, ACTION_INVALID); // reading alarms from intent // PickSelection is started only if there are more than 1 relevant alarm Loading @@ -83,7 +96,7 @@ public class AlarmSelectionActivity extends ListActivity { final AlarmSelection selection = mSelections.get((int) id); final Alarm alarm = selection.getAlarm(); if (alarm != null) { new ProcessAlarmActionAsync(alarm, this).execute(); new ProcessAlarmActionAsync(alarm, this, mAction).execute(); } finish(); } Loading @@ -92,15 +105,26 @@ public class AlarmSelectionActivity extends ListActivity { private final Alarm mAlarm; private final Activity mActivity; private final int mAction; public ProcessAlarmActionAsync(Alarm alarm, Activity activity) { public ProcessAlarmActionAsync(Alarm alarm, Activity activity, int action) { mAlarm = alarm; mActivity = activity; mAction = action; } @Override protected Void doInBackground(Void... parameters) { switch (mAction) { case ACTION_DISMISS: HandleApiCalls.dismissAlarm(mAlarm, mActivity); break; case ACTION_DELETE: HandleApiCalls.deleteAlarm(mAlarm, mActivity); break; case ACTION_INVALID: LogUtils.i("Invalid action"); } return null; } } Loading src/com/android/deskclock/HandleApiCalls.java +148 −3 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.os.Bundle; import android.os.Parcelable; import android.provider.AlarmClock; import android.support.annotation.StringRes; import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import android.text.format.DateFormat; Loading @@ -47,6 +48,10 @@ import java.util.Iterator; import java.util.List; import static android.text.format.DateUtils.SECOND_IN_MILLIS; import static com.android.deskclock.AlarmSelectionActivity.ACTION_DELETE; import static com.android.deskclock.AlarmSelectionActivity.ACTION_DISMISS; import static com.android.deskclock.AlarmSelectionActivity.EXTRA_ACTION; import static com.android.deskclock.AlarmSelectionActivity.EXTRA_ALARMS; import static com.android.deskclock.provider.AlarmInstance.DISMISSED_STATE; import static com.android.deskclock.provider.AlarmInstance.FIRED_STATE; import static com.android.deskclock.provider.AlarmInstance.HIDE_NOTIFICATION_STATE; Loading @@ -66,6 +71,9 @@ import static com.android.deskclock.uidata.UiDataModel.Tab.TIMERS; */ public class HandleApiCalls extends Activity { @VisibleForTesting static final String ACTION_DELETE_ALARM = "android.intent.action.DELETE_ALARM"; private Context mAppContext; @Override Loading Loading @@ -93,12 +101,16 @@ public class HandleApiCalls extends Activity { break; case AlarmClock.ACTION_SNOOZE_ALARM: handleSnoozeAlarm(intent); break; case ACTION_DELETE_ALARM: handleDeleteAlarm(intent); } } finally { finish(); } } private void handleDismissAlarm(Intent intent) { // Change to the alarms tab. UiDataModel.getUiDataModel().setSelectedTab(ALARMS); Loading Loading @@ -151,6 +163,7 @@ public class HandleApiCalls extends Activity { Events.sendAlarmEvent(R.string.action_dismiss, R.string.label_intent); } private static class DismissAlarmAsync extends AsyncTask<Void, Void, Void> { private final Context mContext; Loading Loading @@ -219,8 +232,8 @@ public class HandleApiCalls extends Activity { final Intent pickSelectionIntent = new Intent(mContext, AlarmSelectionActivity.class) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra(AlarmSelectionActivity.EXTRA_ALARMS, alarms.toArray(new Parcelable[alarms.size()])); .putExtra(EXTRA_ACTION, ACTION_DISMISS) .putExtra(EXTRA_ALARMS, alarms.toArray(new Parcelable[alarms.size()])); mContext.startActivity(pickSelectionIntent); Voice.notifySuccess(mActivity, mContext.getString(R.string.pick_alarm_to_dismiss)); return null; Loading @@ -237,7 +250,8 @@ public class HandleApiCalls extends Activity { if (!AlarmClock.ALARM_SEARCH_MODE_ALL.equals(searchMode) && matchingAlarms.size() > 1) { final Intent pickSelectionIntent = new Intent(mContext, AlarmSelectionActivity.class) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra(AlarmSelectionActivity.EXTRA_ALARMS, .putExtra(EXTRA_ACTION, ACTION_DISMISS) .putExtra(EXTRA_ALARMS, matchingAlarms.toArray(new Parcelable[matchingAlarms.size()])); mContext.startActivity(pickSelectionIntent); Voice.notifySuccess(mActivity, mContext.getString(R.string.pick_alarm_to_dismiss)); Loading Loading @@ -351,6 +365,137 @@ public class HandleApiCalls extends Activity { Events.sendAlarmEvent(R.string.action_snooze, R.string.label_intent); } private void handleDeleteAlarm(Intent intent) { new DeleteAlarmAsync(mAppContext, intent, this).execute(); } public static void deleteAlarm(Alarm alarm, Activity activity) { Utils.enforceNotMainLooper(); final Context context = activity.getApplicationContext(); final AlarmInstance instance = AlarmInstance.getNextUpcomingInstanceByAlarmId( context.getContentResolver(), alarm.id); if (instance == null) { final String reason = context.getString(R.string.no_alarm_scheduled_for_this_time); Voice.notifyFailure(activity, reason); LogUtils.i(reason); return; } deleteAlarmInstance(instance, activity); } public static void deleteAlarmInstance(AlarmInstance instance, Activity activity) { Utils.enforceNotMainLooper(); final Context context = activity.getApplicationContext(); final Date alarmTime = instance.getAlarmTime().getTime(); final String time = DateFormat.getTimeFormat(context).format(alarmTime); final String reason = context.getString(R.string.alarm_is_deleted, time); LogUtils.i(reason); Voice.notifySuccess(activity, reason); AlarmStateManager.deleteInstanceAndUpdateParent(context, instance); Events.sendAlarmEvent(R.string.action_delete, R.string.label_intent); } private static class DeleteAlarmAsync extends AsyncTask<Void, Void, Void> { private final Context mContext; private final Intent mIntent; private final Activity mActivity; public DeleteAlarmAsync(Context context, Intent intent, Activity activity) { mContext = context; mIntent = intent; mActivity = activity; } @Override protected Void doInBackground(Void... params) { final ContentResolver cr = mContext.getContentResolver(); final Uri deepLink = mIntent.getData(); if (deepLink != null) { final AlarmInstance instance = AlarmInstance.getInstance(cr, deepLink); if (instance == null) { final String reason = mContext.getString(R.string.cannot_locate_alarm); LogUtils.i(reason); Voice.notifyFailure(mActivity, reason); return null; } deleteAlarmInstance(instance, mActivity); return null; } final List<Alarm> alarms = getAllAlarms(mContext); if (alarms.isEmpty()) { final String reason = mContext.getString(R.string.no_alarms); LogUtils.i(reason); Voice.notifyFailure(mActivity, reason); return null; } final String searchMode = mIntent.getStringExtra(AlarmClock.EXTRA_ALARM_SEARCH_MODE); if (searchMode == null && alarms.size() > 1) { // shows the UI where user picks which alarm they want to ACTION_DELETE final Intent pickSelectionIntent = new Intent(mContext, AlarmSelectionActivity.class) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra(EXTRA_ACTION, ACTION_DELETE) .putExtra(EXTRA_ALARMS, alarms.toArray(new Parcelable[alarms.size()])); mContext.startActivity(pickSelectionIntent); Voice.notifySuccess(mActivity, mContext.getString(R.string.pick_alarm_to_delete)); return null; } // fetch the alarms that are specified by the intent final FetchMatchingAlarmsAction fmaa = new FetchMatchingAlarmsAction(mContext, alarms, mIntent, mActivity); fmaa.run(); final List<Alarm> matchingAlarms = fmaa.getMatchingAlarms(); // If there are multiple matching alarms and it wasn't expected // disambiguate what the user meant if (!AlarmClock.ALARM_SEARCH_MODE_ALL.equals(searchMode) && matchingAlarms.size() > 1) { final Intent pickSelectionIntent = new Intent(mContext, AlarmSelectionActivity.class) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra(EXTRA_ACTION, ACTION_DELETE) .putExtra(EXTRA_ALARMS, matchingAlarms.toArray(new Parcelable[matchingAlarms.size()])); mContext.startActivity(pickSelectionIntent); Voice.notifySuccess(mActivity, mContext.getString(R.string.pick_alarm_to_delete)); return null; } // Apply the action to the matching alarms boolean matches = false; for (Alarm alarm : matchingAlarms) { final AlarmInstance instance = AlarmInstance.getNextUpcomingInstanceByAlarmId(cr, alarm.id); if (AlarmClock.ALARM_SEARCH_MODE_ALL.equals(searchMode) && instance == null) { continue; } matches = true; deleteAlarmInstance(instance, mActivity); LogUtils.i("Alarm %s is deleted", alarm); } if (!matches) { final String reason = mContext.getString(R.string.no_alarm_scheduled_for_this_time); Voice.notifyFailure(mActivity, reason); LogUtils.i(reason); } return null; } private static List<Alarm> getAllAlarms(Context context) { return Alarm.getAlarms(context.getContentResolver(), null, null); } } /*** * Processes the SET_ALARM intent * @param intent Intent passed to the app Loading Loading
res/values/strings.xml +13 −0 Original line number Diff line number Diff line Loading @@ -921,6 +921,12 @@ --> <string name="alarm_is_dismissed"><xliff:g id="alarm_time" example="14:20">%s</xliff:g> alarm dismissed</string> <!-- String that represents that the user has deleted an alarm through a voice action. %s represents alarm time, e.g. 14:20 [CHAR LIMIT=NONE] --> <string name="alarm_is_deleted"><xliff:g id="alarm_time" example="14:20">%s</xliff:g> alarm deleted</string> <!-- String that represents that the user has dismissed an alarm through a voice action. %s represents alarm time, e.g. 14:20 [CHAR LIMIT=NONE] Loading Loading @@ -1026,6 +1032,13 @@ --> <string name="pick_alarm_to_dismiss">Pick which alarm to dismiss</string> <!-- String that represents that further action is needed from the user in case their voice command was ambiguous or there are more than 1 alarms that match their request. The user needs to choose an alarm they want to delete through the UI [CHAR LIMIT=NONE] --> <string name="pick_alarm_to_delete">Pick which alarm to delete</string> <!-- String that represents that the user doesn't have any alarms firing at the moment. [CHAR LIMIT=NONE] --> Loading
src/com/android/deskclock/AlarmSelectionActivity.java +27 −3 Original line number Diff line number Diff line Loading @@ -34,10 +34,22 @@ import java.util.List; public class AlarmSelectionActivity extends ListActivity { /** Used by default when neither ACTION_DISMISS nor ACTION_DELETE are provided */ private static final int ACTION_INVALID = -1; /** Action used to signify alarm should be dismissed on selection */ public static final int ACTION_DISMISS = 0; /** Action used to signify alarm should be deleted on selection */ public static final int ACTION_DELETE = 1; public static final String EXTRA_ACTION = "com.android.deskclock.EXTRA_ACTION"; public static final String EXTRA_ALARMS = "com.android.deskclock.EXTRA_ALARMS"; private final List<AlarmSelection> mSelections = new ArrayList<>(); private int mAction; @Override protected void onCreate(Bundle savedInstanceState) { // this activity is shown if: Loading @@ -61,6 +73,7 @@ public class AlarmSelectionActivity extends ListActivity { final Intent intent = getIntent(); final Parcelable[] alarmsFromIntent = intent.getParcelableArrayExtra(EXTRA_ALARMS); mAction = intent.getIntExtra(EXTRA_ACTION, ACTION_INVALID); // reading alarms from intent // PickSelection is started only if there are more than 1 relevant alarm Loading @@ -83,7 +96,7 @@ public class AlarmSelectionActivity extends ListActivity { final AlarmSelection selection = mSelections.get((int) id); final Alarm alarm = selection.getAlarm(); if (alarm != null) { new ProcessAlarmActionAsync(alarm, this).execute(); new ProcessAlarmActionAsync(alarm, this, mAction).execute(); } finish(); } Loading @@ -92,15 +105,26 @@ public class AlarmSelectionActivity extends ListActivity { private final Alarm mAlarm; private final Activity mActivity; private final int mAction; public ProcessAlarmActionAsync(Alarm alarm, Activity activity) { public ProcessAlarmActionAsync(Alarm alarm, Activity activity, int action) { mAlarm = alarm; mActivity = activity; mAction = action; } @Override protected Void doInBackground(Void... parameters) { switch (mAction) { case ACTION_DISMISS: HandleApiCalls.dismissAlarm(mAlarm, mActivity); break; case ACTION_DELETE: HandleApiCalls.deleteAlarm(mAlarm, mActivity); break; case ACTION_INVALID: LogUtils.i("Invalid action"); } return null; } } Loading
src/com/android/deskclock/HandleApiCalls.java +148 −3 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.os.Bundle; import android.os.Parcelable; import android.provider.AlarmClock; import android.support.annotation.StringRes; import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import android.text.format.DateFormat; Loading @@ -47,6 +48,10 @@ import java.util.Iterator; import java.util.List; import static android.text.format.DateUtils.SECOND_IN_MILLIS; import static com.android.deskclock.AlarmSelectionActivity.ACTION_DELETE; import static com.android.deskclock.AlarmSelectionActivity.ACTION_DISMISS; import static com.android.deskclock.AlarmSelectionActivity.EXTRA_ACTION; import static com.android.deskclock.AlarmSelectionActivity.EXTRA_ALARMS; import static com.android.deskclock.provider.AlarmInstance.DISMISSED_STATE; import static com.android.deskclock.provider.AlarmInstance.FIRED_STATE; import static com.android.deskclock.provider.AlarmInstance.HIDE_NOTIFICATION_STATE; Loading @@ -66,6 +71,9 @@ import static com.android.deskclock.uidata.UiDataModel.Tab.TIMERS; */ public class HandleApiCalls extends Activity { @VisibleForTesting static final String ACTION_DELETE_ALARM = "android.intent.action.DELETE_ALARM"; private Context mAppContext; @Override Loading Loading @@ -93,12 +101,16 @@ public class HandleApiCalls extends Activity { break; case AlarmClock.ACTION_SNOOZE_ALARM: handleSnoozeAlarm(intent); break; case ACTION_DELETE_ALARM: handleDeleteAlarm(intent); } } finally { finish(); } } private void handleDismissAlarm(Intent intent) { // Change to the alarms tab. UiDataModel.getUiDataModel().setSelectedTab(ALARMS); Loading Loading @@ -151,6 +163,7 @@ public class HandleApiCalls extends Activity { Events.sendAlarmEvent(R.string.action_dismiss, R.string.label_intent); } private static class DismissAlarmAsync extends AsyncTask<Void, Void, Void> { private final Context mContext; Loading Loading @@ -219,8 +232,8 @@ public class HandleApiCalls extends Activity { final Intent pickSelectionIntent = new Intent(mContext, AlarmSelectionActivity.class) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra(AlarmSelectionActivity.EXTRA_ALARMS, alarms.toArray(new Parcelable[alarms.size()])); .putExtra(EXTRA_ACTION, ACTION_DISMISS) .putExtra(EXTRA_ALARMS, alarms.toArray(new Parcelable[alarms.size()])); mContext.startActivity(pickSelectionIntent); Voice.notifySuccess(mActivity, mContext.getString(R.string.pick_alarm_to_dismiss)); return null; Loading @@ -237,7 +250,8 @@ public class HandleApiCalls extends Activity { if (!AlarmClock.ALARM_SEARCH_MODE_ALL.equals(searchMode) && matchingAlarms.size() > 1) { final Intent pickSelectionIntent = new Intent(mContext, AlarmSelectionActivity.class) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra(AlarmSelectionActivity.EXTRA_ALARMS, .putExtra(EXTRA_ACTION, ACTION_DISMISS) .putExtra(EXTRA_ALARMS, matchingAlarms.toArray(new Parcelable[matchingAlarms.size()])); mContext.startActivity(pickSelectionIntent); Voice.notifySuccess(mActivity, mContext.getString(R.string.pick_alarm_to_dismiss)); Loading Loading @@ -351,6 +365,137 @@ public class HandleApiCalls extends Activity { Events.sendAlarmEvent(R.string.action_snooze, R.string.label_intent); } private void handleDeleteAlarm(Intent intent) { new DeleteAlarmAsync(mAppContext, intent, this).execute(); } public static void deleteAlarm(Alarm alarm, Activity activity) { Utils.enforceNotMainLooper(); final Context context = activity.getApplicationContext(); final AlarmInstance instance = AlarmInstance.getNextUpcomingInstanceByAlarmId( context.getContentResolver(), alarm.id); if (instance == null) { final String reason = context.getString(R.string.no_alarm_scheduled_for_this_time); Voice.notifyFailure(activity, reason); LogUtils.i(reason); return; } deleteAlarmInstance(instance, activity); } public static void deleteAlarmInstance(AlarmInstance instance, Activity activity) { Utils.enforceNotMainLooper(); final Context context = activity.getApplicationContext(); final Date alarmTime = instance.getAlarmTime().getTime(); final String time = DateFormat.getTimeFormat(context).format(alarmTime); final String reason = context.getString(R.string.alarm_is_deleted, time); LogUtils.i(reason); Voice.notifySuccess(activity, reason); AlarmStateManager.deleteInstanceAndUpdateParent(context, instance); Events.sendAlarmEvent(R.string.action_delete, R.string.label_intent); } private static class DeleteAlarmAsync extends AsyncTask<Void, Void, Void> { private final Context mContext; private final Intent mIntent; private final Activity mActivity; public DeleteAlarmAsync(Context context, Intent intent, Activity activity) { mContext = context; mIntent = intent; mActivity = activity; } @Override protected Void doInBackground(Void... params) { final ContentResolver cr = mContext.getContentResolver(); final Uri deepLink = mIntent.getData(); if (deepLink != null) { final AlarmInstance instance = AlarmInstance.getInstance(cr, deepLink); if (instance == null) { final String reason = mContext.getString(R.string.cannot_locate_alarm); LogUtils.i(reason); Voice.notifyFailure(mActivity, reason); return null; } deleteAlarmInstance(instance, mActivity); return null; } final List<Alarm> alarms = getAllAlarms(mContext); if (alarms.isEmpty()) { final String reason = mContext.getString(R.string.no_alarms); LogUtils.i(reason); Voice.notifyFailure(mActivity, reason); return null; } final String searchMode = mIntent.getStringExtra(AlarmClock.EXTRA_ALARM_SEARCH_MODE); if (searchMode == null && alarms.size() > 1) { // shows the UI where user picks which alarm they want to ACTION_DELETE final Intent pickSelectionIntent = new Intent(mContext, AlarmSelectionActivity.class) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra(EXTRA_ACTION, ACTION_DELETE) .putExtra(EXTRA_ALARMS, alarms.toArray(new Parcelable[alarms.size()])); mContext.startActivity(pickSelectionIntent); Voice.notifySuccess(mActivity, mContext.getString(R.string.pick_alarm_to_delete)); return null; } // fetch the alarms that are specified by the intent final FetchMatchingAlarmsAction fmaa = new FetchMatchingAlarmsAction(mContext, alarms, mIntent, mActivity); fmaa.run(); final List<Alarm> matchingAlarms = fmaa.getMatchingAlarms(); // If there are multiple matching alarms and it wasn't expected // disambiguate what the user meant if (!AlarmClock.ALARM_SEARCH_MODE_ALL.equals(searchMode) && matchingAlarms.size() > 1) { final Intent pickSelectionIntent = new Intent(mContext, AlarmSelectionActivity.class) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra(EXTRA_ACTION, ACTION_DELETE) .putExtra(EXTRA_ALARMS, matchingAlarms.toArray(new Parcelable[matchingAlarms.size()])); mContext.startActivity(pickSelectionIntent); Voice.notifySuccess(mActivity, mContext.getString(R.string.pick_alarm_to_delete)); return null; } // Apply the action to the matching alarms boolean matches = false; for (Alarm alarm : matchingAlarms) { final AlarmInstance instance = AlarmInstance.getNextUpcomingInstanceByAlarmId(cr, alarm.id); if (AlarmClock.ALARM_SEARCH_MODE_ALL.equals(searchMode) && instance == null) { continue; } matches = true; deleteAlarmInstance(instance, mActivity); LogUtils.i("Alarm %s is deleted", alarm); } if (!matches) { final String reason = mContext.getString(R.string.no_alarm_scheduled_for_this_time); Voice.notifyFailure(mActivity, reason); LogUtils.i(reason); } return null; } private static List<Alarm> getAllAlarms(Context context) { return Alarm.getAlarms(context.getContentResolver(), null, null); } } /*** * Processes the SET_ALARM intent * @param intent Intent passed to the app Loading