summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Lemieux <jplemieux@google.com>2016-08-01 22:50:06 -0700
committerJames Lemieux <jplemieux@google.com>2016-08-02 16:28:00 -0700
commitbd250dfa5bdcd618c5dfd7f48b662cf4b51c31a2 (patch)
tree95b20361d75ed64c8737511135db6b7ebbb13ecc
parent3c3744a315faf0e2fe7d018028826ec557956018 (diff)
Replace Voice class with VoiceController
Bug: 29538369 The static Voice class is now implemented as a delegate controller behind the Controller singleton. This is a more natural location for the code and prepares the code base for alternate implementations of the VoiceController interface for testing purposes. Change-Id: I0c9b669e4f47da7fb30b4b55177c039a7e8970e4
-rw-r--r--src/com/android/deskclock/FetchMatchingAlarmsAction.java3
-rw-r--r--src/com/android/deskclock/HandleApiCalls.java68
-rw-r--r--src/com/android/deskclock/HandleDeskClockApiCalls.java17
-rw-r--r--src/com/android/deskclock/Voice.java79
-rw-r--r--src/com/android/deskclock/controller/Controller.java52
-rw-r--r--src/com/android/deskclock/controller/DefaultVoiceController.java46
-rw-r--r--src/com/android/deskclock/controller/VoiceController.java42
7 files changed, 187 insertions, 120 deletions
diff --git a/src/com/android/deskclock/FetchMatchingAlarmsAction.java b/src/com/android/deskclock/FetchMatchingAlarmsAction.java
index 9b797b479..667b64da4 100644
--- a/src/com/android/deskclock/FetchMatchingAlarmsAction.java
+++ b/src/com/android/deskclock/FetchMatchingAlarmsAction.java
@@ -24,6 +24,7 @@ import android.os.Looper;
import android.provider.AlarmClock;
import com.android.deskclock.alarms.AlarmStateManager;
+import com.android.deskclock.controller.Controller;
import com.android.deskclock.provider.Alarm;
import com.android.deskclock.provider.AlarmInstance;
@@ -175,6 +176,6 @@ class FetchMatchingAlarmsAction implements Runnable {
private void notifyFailureAndLog(String reason, Activity activity) {
LogUtils.e(reason);
- Voice.notifyFailure(activity, reason);
+ Controller.getController().notifyVoiceFailure(activity, reason);
}
}
diff --git a/src/com/android/deskclock/HandleApiCalls.java b/src/com/android/deskclock/HandleApiCalls.java
index b4bef3a6c..8bf0aef39 100644
--- a/src/com/android/deskclock/HandleApiCalls.java
+++ b/src/com/android/deskclock/HandleApiCalls.java
@@ -32,6 +32,7 @@ import android.text.TextUtils;
import android.text.format.DateFormat;
import com.android.deskclock.alarms.AlarmStateManager;
+import com.android.deskclock.controller.Controller;
import com.android.deskclock.data.DataModel;
import com.android.deskclock.data.Timer;
import com.android.deskclock.data.Weekdays;
@@ -127,7 +128,7 @@ public class HandleApiCalls extends Activity {
context.getContentResolver(), alarm.id);
if (instance == null) {
final String reason = context.getString(R.string.no_alarm_scheduled_for_this_time);
- Voice.notifyFailure(activity, reason);
+ Controller.getController().notifyVoiceFailure(activity, reason);
LogUtils.i(reason);
return;
}
@@ -152,18 +153,17 @@ public class HandleApiCalls extends Activity {
// Otherwise the alarm cannot be dismissed at this time.
final String reason = context.getString(
R.string.alarm_cant_be_dismissed_still_more_than_24_hours_away, time);
- Voice.notifyFailure(activity, reason);
+ Controller.getController().notifyVoiceFailure(activity, reason);
LogUtils.i(reason);
}
// Log the successful dismissal.
final String reason = context.getString(R.string.alarm_is_dismissed, time);
LogUtils.i(reason);
- Voice.notifySuccess(activity, reason);
+ Controller.getController().notifyVoiceSuccess(activity, reason);
Events.sendAlarmEvent(R.string.action_dismiss, R.string.label_intent);
}
-
private static class DismissAlarmAsync extends AsyncTask<Void, Void, Void> {
private final Context mContext;
@@ -186,7 +186,7 @@ public class HandleApiCalls extends Activity {
if (instance == null) {
final String reason = mContext.getString(R.string.cannot_locate_alarm);
LogUtils.i(reason);
- Voice.notifyFailure(mActivity, reason);
+ Controller.getController().notifyVoiceFailure(mActivity, reason);
return null;
}
@@ -194,14 +194,14 @@ public class HandleApiCalls extends Activity {
if (state == MISSED_STATE) {
final String reason = mContext.getString(R.string.alarm_is_already_missed);
LogUtils.i(reason);
- Voice.notifyFailure(mActivity, reason);
+ Controller.getController().notifyVoiceFailure(mActivity, reason);
return null;
}
if (state == PREDISMISSED_STATE || state == DISMISSED_STATE) {
final String reason = mContext.getString(R.string.alarm_is_already_dismissed);
LogUtils.i(reason);
- Voice.notifyFailure(mActivity, reason);
+ Controller.getController().notifyVoiceFailure(mActivity, reason);
return null;
}
@@ -213,7 +213,7 @@ public class HandleApiCalls extends Activity {
if (alarms.isEmpty()) {
final String reason = mContext.getString(R.string.no_scheduled_alarms);
LogUtils.i(reason);
- Voice.notifyFailure(mActivity, reason);
+ Controller.getController().notifyVoiceFailure(mActivity, reason);
return null;
}
@@ -235,7 +235,8 @@ public class HandleApiCalls extends Activity {
.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));
+ final String voiceMessage = mContext.getString(R.string.pick_alarm_to_dismiss);
+ Controller.getController().notifyVoiceSuccess(mActivity, voiceMessage);
return null;
}
@@ -254,7 +255,8 @@ public class HandleApiCalls extends Activity {
.putExtra(EXTRA_ALARMS,
matchingAlarms.toArray(new Parcelable[matchingAlarms.size()]));
mContext.startActivity(pickSelectionIntent);
- Voice.notifySuccess(mActivity, mContext.getString(R.string.pick_alarm_to_dismiss));
+ final String voiceMessage = mContext.getString(R.string.pick_alarm_to_dismiss);
+ Controller.getController().notifyVoiceSuccess(mActivity, voiceMessage);
return null;
}
@@ -299,7 +301,7 @@ public class HandleApiCalls extends Activity {
if (instance == null) {
final String reason = mContext.getString(R.string.cannot_locate_alarm);
LogUtils.i(reason);
- Voice.notifyFailure(mActivity, reason);
+ Controller.getController().notifyVoiceFailure(mActivity, reason);
return null;
}
@@ -332,7 +334,7 @@ public class HandleApiCalls extends Activity {
final String reason = mContext.getString(failureMessageId);
LogUtils.i(reason);
- Voice.notifyFailure(mActivity, reason);
+ Controller.getController().notifyVoiceFailure(mActivity, reason);
return null;
}
@@ -341,7 +343,7 @@ public class HandleApiCalls extends Activity {
if (alarmInstances.isEmpty()) {
final String reason = mContext.getString(R.string.no_firing_alarms);
LogUtils.i(reason);
- Voice.notifyFailure(mActivity, reason);
+ Controller.getController().notifyVoiceFailure(mActivity, reason);
return null;
}
@@ -359,7 +361,7 @@ public class HandleApiCalls extends Activity {
alarmInstance.getAlarmTime().getTime());
final String reason = context.getString(R.string.alarm_is_snoozed, time);
LogUtils.i(reason);
- Voice.notifySuccess(activity, reason);
+ Controller.getController().notifyVoiceSuccess(activity, reason);
AlarmStateManager.setSnoozeState(context, alarmInstance, true);
LogUtils.i("Snooze %d:%d", alarmInstance.mHour, alarmInstance.mMinute);
Events.sendAlarmEvent(R.string.action_snooze, R.string.label_intent);
@@ -376,7 +378,7 @@ public class HandleApiCalls extends Activity {
context.getContentResolver(), alarm.id);
if (instance == null) {
final String reason = context.getString(R.string.no_alarm_scheduled_for_this_time);
- Voice.notifyFailure(activity, reason);
+ Controller.getController().notifyVoiceFailure(activity, reason);
LogUtils.i(reason);
return;
}
@@ -393,7 +395,7 @@ public class HandleApiCalls extends Activity {
final String reason = context.getString(R.string.alarm_is_deleted, time);
LogUtils.i(reason);
- Voice.notifySuccess(activity, reason);
+ Controller.getController().notifyVoiceSuccess(activity, reason);
AlarmStateManager.deleteInstanceAndUpdateParent(context, instance);
Events.sendAlarmEvent(R.string.action_delete, R.string.label_intent);
}
@@ -420,7 +422,7 @@ public class HandleApiCalls extends Activity {
if (instance == null) {
final String reason = mContext.getString(R.string.cannot_locate_alarm);
LogUtils.i(reason);
- Voice.notifyFailure(mActivity, reason);
+ Controller.getController().notifyVoiceFailure(mActivity, reason);
return null;
}
@@ -432,7 +434,7 @@ public class HandleApiCalls extends Activity {
if (alarms.isEmpty()) {
final String reason = mContext.getString(R.string.no_alarms);
LogUtils.i(reason);
- Voice.notifyFailure(mActivity, reason);
+ Controller.getController().notifyVoiceFailure(mActivity, reason);
return null;
}
@@ -445,7 +447,8 @@ public class HandleApiCalls extends Activity {
.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));
+ final String voiceMessage = mContext.getString(R.string.pick_alarm_to_delete);
+ Controller.getController().notifyVoiceSuccess(mActivity, voiceMessage);
return null;
}
@@ -465,7 +468,8 @@ public class HandleApiCalls extends Activity {
.putExtra(EXTRA_ALARMS,
matchingAlarms.toArray(new Parcelable[matchingAlarms.size()]));
mContext.startActivity(pickSelectionIntent);
- Voice.notifySuccess(mActivity, mContext.getString(R.string.pick_alarm_to_delete));
+ final String voiceMessage = mContext.getString(R.string.pick_alarm_to_delete);
+ Controller.getController().notifyVoiceSuccess(mActivity, voiceMessage);
return null;
}
@@ -484,7 +488,7 @@ public class HandleApiCalls extends Activity {
if (!matches) {
final String reason = mContext.getString(R.string.no_alarm_scheduled_for_this_time);
- Voice.notifyFailure(mActivity, reason);
+ Controller.getController().notifyVoiceFailure(mActivity, reason);
LogUtils.i(reason);
}
@@ -507,7 +511,8 @@ public class HandleApiCalls extends Activity {
hour = intent.getIntExtra(AlarmClock.EXTRA_HOUR, hour);
if (hour < 0 || hour > 23) {
final int mins = intent.getIntExtra(AlarmClock.EXTRA_MINUTES, 0);
- Voice.notifyFailure(this, getString(R.string.invalid_time, hour, mins, " "));
+ final String voiceMessage = getString(R.string.invalid_time, hour, mins, " ");
+ Controller.getController().notifyVoiceFailure(this, voiceMessage);
LogUtils.i("HandleApiCalls given illegal hour: " + hour);
return;
}
@@ -516,7 +521,8 @@ public class HandleApiCalls extends Activity {
// Validate the minute, if one was given.
final int minutes = intent.getIntExtra(AlarmClock.EXTRA_MINUTES, 0);
if (minutes < 0 || minutes > 59) {
- Voice.notifyFailure(this, getString(R.string.invalid_time, hour, minutes, " "));
+ final String voiceMessage = getString(R.string.invalid_time, hour, minutes, " ");
+ Controller.getController().notifyVoiceFailure(this, voiceMessage);
LogUtils.i("HandleApiCalls given illegal minute: " + minutes);
return;
}
@@ -529,7 +535,8 @@ public class HandleApiCalls extends Activity {
// Attempt to locate the alarm via the deeplink.
final Alarm alarm = Alarm.getAlarm(cr, deeplink);
if (alarm == null) {
- Voice.notifyFailure(this, getString(R.string.cannot_locate_alarm));
+ final String voiceMessage = getString(R.string.cannot_locate_alarm);
+ Controller.getController().notifyVoiceFailure(this, voiceMessage);
LogUtils.i(String.format("HandleApiCalls cannot locate alarm using: %s", deeplink));
return;
}
@@ -549,7 +556,8 @@ public class HandleApiCalls extends Activity {
final String time = DateFormat.getTimeFormat(this).format(
alarmInstance.getAlarmTime().getTime());
- Voice.notifySuccess(this, getString(R.string.alarm_is_set, time));
+ final String voiceMessage = getString(R.string.alarm_is_set, time);
+ Controller.getController().notifyVoiceSuccess(this, voiceMessage);
Events.sendAlarmEvent(R.string.action_update, R.string.label_intent);
LogUtils.i("HandleApiCalls updated existing alarm: %s", alarm);
return;
@@ -568,7 +576,8 @@ public class HandleApiCalls extends Activity {
// Open DeskClock which is now positioned on the alarms tab.
startActivity(createAlarm);
- Voice.notifyFailure(this, getString(R.string.invalid_time, hour, minutes, " "));
+ final String voiceMessage = getString(R.string.invalid_time, hour, minutes, " ");
+ Controller.getController().notifyVoiceFailure(this, voiceMessage);
LogUtils.i("HandleApiCalls not given time information; opening UI");
return;
}
@@ -609,7 +618,7 @@ public class HandleApiCalls extends Activity {
Events.sendAlarmEvent(R.string.action_create, R.string.label_intent);
final String time = DateFormat.getTimeFormat(this).format(
alarmInstance.getAlarmTime().getTime());
- Voice.notifySuccess(this, getString(R.string.alarm_is_set, time));
+ Controller.getController().notifyVoiceSuccess(this, getString(R.string.alarm_is_set, time));
LogUtils.i("HandleApiCalls created alarm: %s", alarm);
}
@@ -648,7 +657,8 @@ public class HandleApiCalls extends Activity {
// Verify that the timer length is between one second and one day.
final long lengthMillis = SECOND_IN_MILLIS * intent.getIntExtra(AlarmClock.EXTRA_LENGTH, 0);
if (lengthMillis < Timer.MIN_LENGTH || lengthMillis > Timer.MAX_LENGTH) {
- Voice.notifyFailure(this, getString(R.string.invalid_timer_length));
+ final String voiceMessage = getString(R.string.invalid_timer_length);
+ Controller.getController().notifyVoiceFailure(this, voiceMessage);
LogUtils.i("Invalid timer length requested: " + lengthMillis);
return;
}
@@ -676,7 +686,7 @@ public class HandleApiCalls extends Activity {
// Start the selected timer.
DataModel.getDataModel().startTimer(timer);
Events.sendTimerEvent(R.string.action_start, R.string.label_intent);
- Voice.notifySuccess(this, getString(R.string.timer_created));
+ Controller.getController().notifyVoiceSuccess(this, getString(R.string.timer_created));
// If not instructed to skip the UI, display the running timer.
if (!skipUi) {
diff --git a/src/com/android/deskclock/HandleDeskClockApiCalls.java b/src/com/android/deskclock/HandleDeskClockApiCalls.java
index 23be9b81e..0ac6670df 100644
--- a/src/com/android/deskclock/HandleDeskClockApiCalls.java
+++ b/src/com/android/deskclock/HandleDeskClockApiCalls.java
@@ -21,6 +21,7 @@ import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
+import com.android.deskclock.controller.Controller;
import com.android.deskclock.data.City;
import com.android.deskclock.data.DataModel;
import com.android.deskclock.data.Timer;
@@ -164,9 +165,9 @@ public class HandleDeskClockApiCalls extends Activity {
}
if (fail) {
- Voice.notifyFailure(this, reason);
+ Controller.getController().notifyVoiceFailure(this, reason);
} else {
- Voice.notifySuccess(this, reason);
+ Controller.getController().notifyVoiceSuccess(this, reason);
}
LogUtils.i(reason);
}
@@ -211,7 +212,7 @@ public class HandleDeskClockApiCalls extends Activity {
}
if (timer == null) {
- Voice.notifyFailure(this, reason);
+ Controller.getController().notifyVoiceFailure(this, reason);
} else {
timerId = timer.getId();
@@ -243,7 +244,7 @@ public class HandleDeskClockApiCalls extends Activity {
throw new IllegalArgumentException("unknown timer action: " + action);
}
- Voice.notifySuccess(this, reason);
+ Controller.getController().notifyVoiceSuccess(this, reason);
}
LogUtils.i(reason);
@@ -285,7 +286,7 @@ public class HandleDeskClockApiCalls extends Activity {
if (cityName == null) {
reason = getString(R.string.no_city_selected);
LogUtils.i(reason);
- Voice.notifySuccess(this, reason);
+ Controller.getController().notifyVoiceSuccess(this, reason);
startActivity(new Intent(this, CitySelectionActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
return;
@@ -296,7 +297,7 @@ public class HandleDeskClockApiCalls extends Activity {
if (city == null) {
reason = getString(R.string.the_city_you_specified_is_not_available);
LogUtils.i(reason);
- Voice.notifyFailure(this, reason);
+ Controller.getController().notifyVoiceFailure(this, reason);
startActivity(new Intent(this, CitySelectionActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
return;
@@ -337,9 +338,9 @@ public class HandleDeskClockApiCalls extends Activity {
}
if (fail) {
- Voice.notifyFailure(this, reason);
+ Controller.getController().notifyVoiceFailure(this, reason);
} else {
- Voice.notifySuccess(this, reason);
+ Controller.getController().notifyVoiceSuccess(this, reason);
}
LogUtils.i(reason);
}
diff --git a/src/com/android/deskclock/Voice.java b/src/com/android/deskclock/Voice.java
deleted file mode 100644
index f33bbe7ab..000000000
--- a/src/com/android/deskclock/Voice.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2015 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.deskclock;
-
-import android.annotation.TargetApi;
-import android.app.Activity;
-import android.app.VoiceInteractor;
-import android.os.Build;
-
-/**
- * Notifies Voice Interactor about whether the action
- * was successful. Voice Interactor is called only if
- * the build version is post-Lollipop.
- */
-public final class Voice {
-
- private static Delegate sDelegate = new VoiceInteractorDelegate();
-
- private Voice() { }
-
- public static void setDelegate(Delegate delegate) {
- sDelegate = delegate;
- }
-
- public static Delegate getDelegate() {
- return sDelegate;
- }
-
- public static void notifySuccess(Activity activity, String message) {
- if (Utils.isMOrLater()) {
- sDelegate.notifySuccess(activity.getVoiceInteractor(), message);
- }
- }
-
- public static void notifyFailure(Activity activity, String message) {
- if (Utils.isMOrLater()) {
- sDelegate.notifyFailure(activity.getVoiceInteractor(), message);
- }
- }
-
- public interface Delegate {
- void notifySuccess(VoiceInteractor vi, String message);
-
- void notifyFailure(VoiceInteractor vi, String message);
- }
-
- @TargetApi(Build.VERSION_CODES.M)
- private static class VoiceInteractorDelegate implements Delegate {
- @Override
- public void notifySuccess(VoiceInteractor vi, String message) {
- if (vi != null) {
- final VoiceInteractor.Prompt prompt = new VoiceInteractor.Prompt(message);
- vi.submitRequest(new VoiceInteractor.CompleteVoiceRequest(prompt, null));
- }
- }
-
- @Override
- public void notifyFailure(VoiceInteractor vi, String message) {
- if (vi != null) {
- final VoiceInteractor.Prompt prompt = new VoiceInteractor.Prompt(message);
- vi.submitRequest(new VoiceInteractor.AbortVoiceRequest(prompt, null));
- }
- }
- }
-} \ No newline at end of file
diff --git a/src/com/android/deskclock/controller/Controller.java b/src/com/android/deskclock/controller/Controller.java
index ad15db6e4..82487e0f7 100644
--- a/src/com/android/deskclock/controller/Controller.java
+++ b/src/com/android/deskclock/controller/Controller.java
@@ -16,16 +16,27 @@
package com.android.deskclock.controller;
+import android.app.Activity;
import android.content.Context;
import com.android.deskclock.Utils;
-public final class Controller {
+import static com.android.deskclock.Utils.enforceMainLooper;
+
+/**
+ * Interactions with Android framework components responsible for part of the user experience are
+ * handled via this singleton.
+ */
+public final class Controller implements VoiceController {
private static final Controller sController = new Controller();
private Context mContext;
+ /** The controller that interacts with voice interaction sessions on M+. */
+ private VoiceController mVoiceController;
+
+ /** The controller that creates and updates launcher shortcuts on N MR1+ */
private ShortcutController mShortcutController;
private Controller() {}
@@ -39,15 +50,50 @@ public final class Controller {
throw new IllegalStateException("context has already been set");
}
mContext = context;
+ if (Utils.isMOrLater()) {
+ mVoiceController = new DefaultVoiceController();
+ }
if (Utils.isNMR1OrLater()) {
mShortcutController = new ShortcutController(mContext);
}
}
+ //
+ // Voice Interaction
+ //
+
+ /**
+ * @param voiceController the new delegate to control future voice interaction sessions
+ * @return the old delegate that controlled prior voice interaction sessions
+ */
+ public VoiceController setVoiceController(VoiceController voiceController) {
+ final VoiceController oldVoiceController = mVoiceController;
+ mVoiceController = voiceController;
+ return oldVoiceController;
+ }
+
+ @Override
+ public void notifyVoiceSuccess(Activity activity, String message) {
+ if (mVoiceController != null) {
+ mVoiceController.notifyVoiceSuccess(activity, message);
+ }
+ }
+
+ @Override
+ public void notifyVoiceFailure(Activity activity, String message) {
+ if (mVoiceController != null) {
+ mVoiceController.notifyVoiceFailure(activity, message);
+ }
+ }
+
+ //
+ // Shortcuts
+ //
+
public void updateShortcuts() {
- Utils.enforceMainLooper();
+ enforceMainLooper();
if (mShortcutController != null) {
mShortcutController.updateShortcuts();
}
}
-}
+} \ No newline at end of file
diff --git a/src/com/android/deskclock/controller/DefaultVoiceController.java b/src/com/android/deskclock/controller/DefaultVoiceController.java
new file mode 100644
index 000000000..4329120b4
--- /dev/null
+++ b/src/com/android/deskclock/controller/DefaultVoiceController.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 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.deskclock.controller;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.app.VoiceInteractor;
+import android.app.VoiceInteractor.AbortVoiceRequest;
+import android.app.VoiceInteractor.CompleteVoiceRequest;
+import android.app.VoiceInteractor.Prompt;
+import android.os.Build;
+
+@TargetApi(Build.VERSION_CODES.M)
+class DefaultVoiceController implements VoiceController {
+ @Override
+ public void notifyVoiceSuccess(Activity activity, String message) {
+ final VoiceInteractor voiceInteractor = activity.getVoiceInteractor();
+ if (voiceInteractor != null) {
+ final Prompt prompt = new Prompt(message);
+ voiceInteractor.submitRequest(new CompleteVoiceRequest(prompt, null));
+ }
+ }
+
+ @Override
+ public void notifyVoiceFailure(Activity activity, String message) {
+ final VoiceInteractor voiceInteractor = activity.getVoiceInteractor();
+ if (voiceInteractor != null) {
+ final Prompt prompt = new Prompt(message);
+ voiceInteractor.submitRequest(new AbortVoiceRequest(prompt, null));
+ }
+ }
+} \ No newline at end of file
diff --git a/src/com/android/deskclock/controller/VoiceController.java b/src/com/android/deskclock/controller/VoiceController.java
new file mode 100644
index 000000000..c4327e309
--- /dev/null
+++ b/src/com/android/deskclock/controller/VoiceController.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 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.deskclock.controller;
+
+import android.app.Activity;
+
+/**
+ * Implementations of this interface are consulted to terminate a voice interaction session.
+ */
+public interface VoiceController {
+ /**
+ * If the {@code activity} is currently hosting a voice interaction session, indicate the voice
+ * command was processed successfully.
+ *
+ * @param activity an Activity that may be hosting a voice interaction session
+ * @param message to be spoken to the user to indicate success
+ */
+ void notifyVoiceSuccess(Activity activity, String message);
+
+ /**
+ * If the {@code activity} is currently hosting a voice interaction session, indicate the voice
+ * command failed and must be aborted.
+ *
+ * @param activity an Activity that may be hosting a voice interaction session
+ * @param message to be spoken to the user to indicate failure
+ */
+ void notifyVoiceFailure(Activity activity, String message);
+} \ No newline at end of file