diff options
author | Dianne Hackborn <hackbod@google.com> | 2014-04-25 17:06:18 -0700 |
---|---|---|
committer | Dianne Hackborn <hackbod@google.com> | 2014-04-28 10:54:15 -0700 |
commit | 18f0d357f9693fe787a3e3777d8fdf01357a6e3f (patch) | |
tree | 87ffa17a98fa81355a37e25b2c7fc649ffc4e9be | |
parent | 01c70711d5e4f1c3405bcd169be70605e92166f2 (diff) | |
download | frameworks_base-18f0d357f9693fe787a3e3777d8fdf01357a6e3f.zip frameworks_base-18f0d357f9693fe787a3e3777d8fdf01357a6e3f.tar.gz frameworks_base-18f0d357f9693fe787a3e3777d8fdf01357a6e3f.tar.bz2 |
Rework some of the voice interaction APIs.
On the app side, requests are now composed by subclassing
from various types of Request objects.
On the service side, starting a voice interaction session
involves starting another service that will then manage the
session. This leads the service design much more to what
we want, where the long-running main service is very tiny
and all the heavy-weight transient session work is elsewhere
in another process.
Change-Id: I46c074c6fe27b6c1cf2583c6d216aed1de2f1143
21 files changed, 699 insertions, 188 deletions
@@ -201,6 +201,7 @@ LOCAL_SRC_FILES += \ core/java/android/service/trust/ITrustAgentServiceCallback.aidl \ core/java/android/service/voice/IVoiceInteractionService.aidl \ core/java/android/service/voice/IVoiceInteractionSession.aidl \ + core/java/android/service/voice/IVoiceInteractionSessionService.aidl \ core/java/android/service/wallpaper/IWallpaperConnection.aidl \ core/java/android/service/wallpaper/IWallpaperEngine.aidl \ core/java/android/service/wallpaper/IWallpaperService.aidl \ diff --git a/api/current.txt b/api/current.txt index 4fef0d0..34b1ac8 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1009,6 +1009,7 @@ package android { field public static final int selectedDateVerticalBar = 16843591; // 0x1010347 field public static final int selectedWeekBackgroundColor = 16843586; // 0x1010342 field public static final int sequence = 16843815; // 0x1010427 + field public static final int sessionService = 16843850; // 0x101044a field public static final int settingsActivity = 16843301; // 0x1010225 field public static final int shadowColor = 16843105; // 0x1010161 field public static final int shadowDx = 16843106; // 0x1010162 @@ -1663,10 +1664,14 @@ package android { field public static final int decelerate_cubic = 17563651; // 0x10c0003 field public static final int decelerate_quad = 17563649; // 0x10c0001 field public static final int decelerate_quint = 17563653; // 0x10c0005 - field public static final int fast_out_linear_in = 17563663; // 0x10c000f - field public static final int fast_out_slow_in = 17563661; // 0x10c000d + field public static final int fast_out_linear_in = 17563667; // 0x10c0013 + field public static final int fast_out_slow_in = 17563665; // 0x10c0011 + field public static final int l_resource_pad1 = 17563664; // 0x10c0010 + field public static final int l_resource_pad2 = 17563663; // 0x10c000f + field public static final int l_resource_pad3 = 17563662; // 0x10c000e + field public static final int l_resource_pad4 = 17563661; // 0x10c000d field public static final int linear = 17563659; // 0x10c000b - field public static final int linear_out_slow_in = 17563662; // 0x10c000e + field public static final int linear_out_slow_in = 17563666; // 0x10c0012 field public static final int overshoot = 17563656; // 0x10c0008 } @@ -4842,20 +4847,26 @@ package android.app { } public class VoiceInteractor { - method public android.app.VoiceInteractor.Request startCommand(android.app.VoiceInteractor.Callback, java.lang.String, android.os.Bundle); - method public android.app.VoiceInteractor.Request startConfirmation(android.app.VoiceInteractor.Callback, java.lang.String, android.os.Bundle); + method public boolean submitRequest(android.app.VoiceInteractor.Request); method public boolean[] supportsCommands(java.lang.String[]); } - public static class VoiceInteractor.Callback { - ctor public VoiceInteractor.Callback(); - method public void onCancel(android.app.VoiceInteractor.Request); - method public void onCommandResult(android.app.VoiceInteractor.Request, android.os.Bundle); - method public void onConfirmationResult(android.app.VoiceInteractor.Request, boolean, android.os.Bundle); + public static class VoiceInteractor.CommandRequest extends android.app.VoiceInteractor.Request { + ctor public VoiceInteractor.CommandRequest(java.lang.String, android.os.Bundle); + method public void onCommandResult(android.os.Bundle); } - public static class VoiceInteractor.Request { + public static class VoiceInteractor.ConfirmationRequest extends android.app.VoiceInteractor.Request { + ctor public VoiceInteractor.ConfirmationRequest(java.lang.CharSequence, android.os.Bundle); + method public void onConfirmationResult(boolean, android.os.Bundle); + } + + public static abstract class VoiceInteractor.Request { + ctor public VoiceInteractor.Request(); method public void cancel(); + method public android.app.Activity getActivity(); + method public android.content.Context getContext(); + method public void onCancel(); } public final class WallpaperInfo implements android.os.Parcelable { @@ -24919,7 +24930,7 @@ package android.service.voice { public class VoiceInteractionService extends android.app.Service { ctor public VoiceInteractionService(); method public android.os.IBinder onBind(android.content.Intent); - method public void startVoiceActivity(android.content.Intent, android.service.voice.VoiceInteractionSession); + method public void startVoiceActivity(android.content.Intent, android.os.Bundle); field public static final java.lang.String SERVICE_INTERFACE = "android.service.voice.VoiceInteractionService"; field public static final java.lang.String SERVICE_META_DATA = "android.voice_interaction"; } @@ -24938,10 +24949,16 @@ package android.service.voice { public static class VoiceInteractionSession.Request { method public void sendCancelResult(); - method public void sendCommandResult(android.os.Bundle); + method public void sendCommandResult(boolean, android.os.Bundle); method public void sendConfirmResult(boolean, android.os.Bundle); } + public abstract class VoiceInteractionSessionService extends android.app.Service { + ctor public VoiceInteractionSessionService(); + method public android.os.IBinder onBind(android.content.Intent); + method public abstract android.service.voice.VoiceInteractionSession onNewSession(android.os.Bundle); + } + } package android.service.wallpaper { diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 197eaf8..8981c88 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -5454,7 +5454,7 @@ public class Activity extends ContextThemeWrapper mEmbeddedID = id; mLastNonConfigurationInstances = lastNonConfigurationInstances; mVoiceInteractor = voiceInteractor != null - ? new VoiceInteractor(this, voiceInteractor, Looper.myLooper()) : null; + ? new VoiceInteractor(this, this, voiceInteractor, Looper.myLooper()) : null; mWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java index 6820dfd..6dc48b0 100644 --- a/core/java/android/app/VoiceInteractor.java +++ b/core/java/android/app/VoiceInteractor.java @@ -22,6 +22,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.util.ArrayMap; import android.util.Log; import com.android.internal.app.IVoiceInteractor; import com.android.internal.app.IVoiceInteractorCallback; @@ -39,50 +40,85 @@ public class VoiceInteractor { static final boolean DEBUG = true; final Context mContext; + final Activity mActivity; final IVoiceInteractor mInteractor; final HandlerCaller mHandlerCaller; final HandlerCaller.Callback mHandlerCallerCallback = new HandlerCaller.Callback() { @Override public void executeMessage(Message msg) { SomeArgs args = (SomeArgs)msg.obj; + Request request; switch (msg.what) { case MSG_CONFIRMATION_RESULT: + request = pullRequest((IVoiceInteractorRequest)args.arg1, true); if (DEBUG) Log.d(TAG, "onConfirmResult: req=" - + ((IVoiceInteractorRequest)args.arg2).asBinder() - + " confirmed=" + msg.arg1 + " result=" + args.arg3); - ((Callback)args.arg1).onConfirmationResult( - findRequest((IVoiceInteractorRequest)args.arg2), - msg.arg1 != 0, (Bundle)args.arg3); + + ((IVoiceInteractorRequest)args.arg1).asBinder() + "/" + request + + " confirmed=" + msg.arg1 + " result=" + args.arg2); + if (request != null) { + ((ConfirmationRequest)request).onConfirmationResult(msg.arg1 != 0, + (Bundle) args.arg2); + request.clear(); + } break; case MSG_COMMAND_RESULT: + request = pullRequest((IVoiceInteractorRequest)args.arg1, msg.arg1 != 0); if (DEBUG) Log.d(TAG, "onCommandResult: req=" - + ((IVoiceInteractorRequest)args.arg2).asBinder() + + ((IVoiceInteractorRequest)args.arg1).asBinder() + "/" + request + " result=" + args.arg2); - ((Callback)args.arg1).onCommandResult( - findRequest((IVoiceInteractorRequest) args.arg2), - (Bundle) args.arg3); + if (request != null) { + ((CommandRequest)request).onCommandResult((Bundle) args.arg2); + if (msg.arg1 != 0) { + request.clear(); + } + } break; case MSG_CANCEL_RESULT: + request = pullRequest((IVoiceInteractorRequest)args.arg1, true); if (DEBUG) Log.d(TAG, "onCancelResult: req=" - + ((IVoiceInteractorRequest)args.arg2).asBinder()); - ((Callback)args.arg1).onCancel( - findRequest((IVoiceInteractorRequest) args.arg2)); + + ((IVoiceInteractorRequest)args.arg1).asBinder() + "/" + request); + if (request != null) { + request.onCancel(); + request.clear(); + } break; } } }; - final WeakHashMap<IBinder, Request> mActiveRequests = new WeakHashMap<IBinder, Request>(); + final IVoiceInteractorCallback.Stub mCallback = new IVoiceInteractorCallback.Stub() { + @Override + public void deliverConfirmationResult(IVoiceInteractorRequest request, boolean confirmed, + Bundle result) { + mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOO( + MSG_CONFIRMATION_RESULT, confirmed ? 1 : 0, request, result)); + } + + @Override + public void deliverCommandResult(IVoiceInteractorRequest request, boolean complete, + Bundle result) { + mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOO( + MSG_COMMAND_RESULT, complete ? 1 : 0, request, result)); + } + + @Override + public void deliverCancel(IVoiceInteractorRequest request) throws RemoteException { + mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO( + MSG_CANCEL_RESULT, request)); + } + }; + + final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<IBinder, Request>(); static final int MSG_CONFIRMATION_RESULT = 1; static final int MSG_COMMAND_RESULT = 2; static final int MSG_CANCEL_RESULT = 3; - public static class Request { - final IVoiceInteractorRequest mRequestInterface; + public static abstract class Request { + IVoiceInteractorRequest mRequestInterface; + Context mContext; + Activity mActivity; - Request(IVoiceInteractorRequest requestInterface) { - mRequestInterface = requestInterface; + public Request() { } public void cancel() { @@ -92,126 +128,130 @@ public class VoiceInteractor { Log.w(TAG, "Voice interactor has died", e); } } + + public Context getContext() { + return mContext; + } + + public Activity getActivity() { + return mActivity; + } + + public void onCancel() { + } + + void clear() { + mRequestInterface = null; + mContext = null; + mActivity = null; + } + + abstract IVoiceInteractorRequest submit(IVoiceInteractor interactor, + String packageName, IVoiceInteractorCallback callback) throws RemoteException; } - public static class Callback { - VoiceInteractor mInteractor; + public static class ConfirmationRequest extends Request { + final CharSequence mPrompt; + final Bundle mExtras; - final IVoiceInteractorCallback.Stub mWrapper = new IVoiceInteractorCallback.Stub() { - @Override - public void deliverConfirmationResult(IVoiceInteractorRequest request, boolean confirmed, - Bundle result) { - mInteractor.mHandlerCaller.sendMessage(mInteractor.mHandlerCaller.obtainMessageIOOO( - MSG_CONFIRMATION_RESULT, confirmed ? 1 : 0, Callback.this, request, - result)); - } + /** + * Confirms an operation with the user via the trusted system + * VoiceInteractionService. This allows an Activity to complete an unsafe operation that + * would require the user to touch the screen when voice interaction mode is not enabled. + * The result of the confirmation will be returned through an asynchronous call to + * either {@link #onConfirmationResult(boolean, android.os.Bundle)} or + * {@link #onCancel()}. + * + * <p>In some cases this may be a simple yes / no confirmation or the confirmation could + * include context information about how the action will be completed + * (e.g. booking a cab might include details about how long until the cab arrives) + * so the user can give a confirmation. + * @param prompt Optional confirmation text to read to the user as the action being + * confirmed. + * @param extras Additional optional information. + */ + public ConfirmationRequest(CharSequence prompt, Bundle extras) { + mPrompt = prompt; + mExtras = extras; + } - @Override - public void deliverCommandResult(IVoiceInteractorRequest request, Bundle result) { - mInteractor.mHandlerCaller.sendMessage(mInteractor.mHandlerCaller.obtainMessageOOO( - MSG_COMMAND_RESULT, Callback.this, request, result)); - } + public void onConfirmationResult(boolean confirmed, Bundle result) { + } - @Override - public void deliverCancel(IVoiceInteractorRequest request) throws RemoteException { - mInteractor.mHandlerCaller.sendMessage(mInteractor.mHandlerCaller.obtainMessageOO( - MSG_CANCEL_RESULT, Callback.this, request)); - } - }; + IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName, + IVoiceInteractorCallback callback) throws RemoteException { + return interactor.startConfirmation(packageName, callback, mPrompt.toString(), mExtras); + } + } - public void onConfirmationResult(Request request, boolean confirmed, Bundle result) { + public static class CommandRequest extends Request { + final String mCommand; + final Bundle mArgs; + + /** + * Execute a command using the trusted system VoiceInteractionService. + * This allows an Activity to request additional information from the user needed to + * complete an action (e.g. booking a table might have several possible times that the + * user could select from or an app might need the user to agree to a terms of service). + * The result of the confirmation will be returned through an asynchronous call to + * either {@link #onCommandResult(android.os.Bundle)} or + * {@link #onCancel()}. + * + * <p>The command is a string that describes the generic operation to be performed. + * The command will determine how the properties in extras are interpreted and the set of + * available commands is expected to grow over time. An example might be + * "com.google.voice.commands.REQUEST_NUMBER_BAGS" to request the number of bags as part of + * airline check-in. (This is not an actual working example.) + * + * @param command The desired command to perform. + * @param args Additional arguments to control execution of the command. + */ + public CommandRequest(String command, Bundle args) { + mCommand = command; + mArgs = args; } - public void onCommandResult(Request request, Bundle result) { + public void onCommandResult(Bundle result) { } - public void onCancel(Request request) { + IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName, + IVoiceInteractorCallback callback) throws RemoteException { + return interactor.startConfirmation(packageName, callback, mCommand, mArgs); } - } + } - VoiceInteractor(Context context, IVoiceInteractor interactor, Looper looper) { + VoiceInteractor(Context context, Activity activity, IVoiceInteractor interactor, + Looper looper) { mContext = context; + mActivity = activity; mInteractor = interactor; mHandlerCaller = new HandlerCaller(context, looper, mHandlerCallerCallback, true); } - Request storeRequest(IVoiceInteractorRequest request) { - synchronized (mActiveRequests) { - Request req = new Request(request); - mActiveRequests.put(request.asBinder(), req); - return req; - } - } - - Request findRequest(IVoiceInteractorRequest request) { + Request pullRequest(IVoiceInteractorRequest request, boolean complete) { synchronized (mActiveRequests) { Request req = mActiveRequests.get(request.asBinder()); - if (req == null) { - throw new IllegalStateException("Received callback without active request: " - + request); + if (req != null && complete) { + mActiveRequests.remove(request.asBinder()); } return req; } } - /** - * Asynchronously confirms an operation with the user via the trusted system - * VoiceinteractionService. This allows an Activity to complete an unsafe operation that - * would require the user to touch the screen when voice interaction mode is not enabled. - * The result of the confirmation will be returned by calling the - * {@link Callback#onConfirmationResult Callback.onConfirmationResult} method. - * - * In some cases this may be a simple yes / no confirmation or the confirmation could - * include context information about how the action will be completed - * (e.g. booking a cab might include details about how long until the cab arrives) so the user - * can give informed consent. - * @param callback Required callback target for interaction results. - * @param prompt Optional confirmation text to read to the user as the action being confirmed. - * @param extras Additional optional information. - * @return Returns a new {@link Request} object representing this operation. - */ - public Request startConfirmation(Callback callback, String prompt, Bundle extras) { + public boolean submitRequest(Request request) { try { - callback.mInteractor = this; - Request req = storeRequest(mInteractor.startConfirmation( - mContext.getOpPackageName(), callback.mWrapper, prompt, extras)); - if (DEBUG) Log.d(TAG, "startConfirmation: req=" + req.mRequestInterface.asBinder() - + " prompt=" + prompt + " extras=" + extras); - return req; - } catch (RemoteException e) { - throw new RuntimeException("Voice interactor has died", e); - } - } - - /** - * Asynchronously executes a command using the trusted system VoiceinteractionService. - * This allows an Activity to request additional information from the user needed to - * complete an action (e.g. booking a table might have several possible times that the - * user could select from or an app might need the user to agree to a terms of service). - * - * The command is a string that describes the generic operation to be performed. - * The command will determine how the properties in extras are interpreted and the set of - * available commands is expected to grow over time. An example might be - * "com.google.voice.commands.REQUEST_NUMBER_BAGS" to request the number of bags as part of - * airline check-in. (This is not an actual working example.) - * The result of the command will be returned by calling the - * {@link Callback#onCommandResult Callback.onCommandResult} method. - * - * @param callback Required callback target for interaction results. - * @param command - * @param extras - * @return Returns a new {@link Request} object representing this operation. - */ - public Request startCommand(Callback callback, String command, Bundle extras) { - try { - callback.mInteractor = this; - Request req = storeRequest(mInteractor.startCommand( - mContext.getOpPackageName(), callback.mWrapper, command, extras)); - if (DEBUG) Log.d(TAG, "startCommand: req=" + req.mRequestInterface.asBinder() - + " command=" + command + " extras=" + extras); - return req; + IVoiceInteractorRequest ireq = request.submit(mInteractor, + mContext.getOpPackageName(), mCallback); + request.mRequestInterface = ireq; + request.mContext = mContext; + request.mActivity = mActivity; + synchronized (mActiveRequests) { + mActiveRequests.put(ireq.asBinder(), request); + } + return true; } catch (RemoteException e) { - throw new RuntimeException("Voice interactor has died", e); + Log.w(TAG, "Remove voice interactor service died", e); + return false; } } diff --git a/core/java/android/service/voice/IVoiceInteractionSessionService.aidl b/core/java/android/service/voice/IVoiceInteractionSessionService.aidl new file mode 100644 index 0000000..2519442 --- /dev/null +++ b/core/java/android/service/voice/IVoiceInteractionSessionService.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 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 android.service.voice; + +import android.os.Bundle; + +import android.service.voice.IVoiceInteractionSession; + +/** + * @hide + */ +oneway interface IVoiceInteractionSessionService { + void newSession(IBinder token, in Bundle args); +} diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java index ed93b74..d005890 100644 --- a/core/java/android/service/voice/VoiceInteractionService.java +++ b/core/java/android/service/voice/VoiceInteractionService.java @@ -21,6 +21,7 @@ import android.app.Instrumentation; import android.app.Service; import android.content.Context; import android.content.Intent; +import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; @@ -50,12 +51,11 @@ public class VoiceInteractionService extends Service { IVoiceInteractionManagerService mSystemService; - public void startVoiceActivity(Intent intent, VoiceInteractionSession session) { + public void startVoiceActivity(Intent intent, Bundle sessionArgs) { try { - int res = mSystemService.startVoiceActivity(intent, + mSystemService.startVoiceActivity(intent, intent.resolveType(getContentResolver()), - mInterface, session.mSession, session.mInteractor); - Instrumentation.checkStartActivityResult(res, intent); + mInterface, sessionArgs); } catch (RemoteException e) { } } diff --git a/core/java/android/service/voice/VoiceInteractionServiceInfo.java b/core/java/android/service/voice/VoiceInteractionServiceInfo.java new file mode 100644 index 0000000..a909ead --- /dev/null +++ b/core/java/android/service/voice/VoiceInteractionServiceInfo.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2014 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 android.service.voice; + +import android.Manifest; +import android.content.ComponentName; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.speech.RecognitionService; +import android.util.AttributeSet; +import android.util.Log; +import android.util.Xml; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +/** @hide */ +public class VoiceInteractionServiceInfo { + static final String TAG = "VoiceInteractionServiceInfo"; + + private String mParseError; + + private ServiceInfo mServiceInfo; + private String mSessionService; + private String mSettingsActivity; + + public VoiceInteractionServiceInfo(PackageManager pm, ComponentName comp) + throws PackageManager.NameNotFoundException { + this(pm, pm.getServiceInfo(comp, PackageManager.GET_META_DATA)); + } + + public VoiceInteractionServiceInfo(PackageManager pm, ServiceInfo si) { + if (!Manifest.permission.BIND_VOICE_INTERACTION.equals(si.permission)) { + mParseError = "Service does not require permission " + + Manifest.permission.BIND_VOICE_INTERACTION; + return; + } + + XmlResourceParser parser = null; + try { + parser = si.loadXmlMetaData(pm, VoiceInteractionService.SERVICE_META_DATA); + if (parser == null) { + mParseError = "No " + VoiceInteractionService.SERVICE_META_DATA + + " meta-data for " + si.packageName; + return; + } + + Resources res = pm.getResourcesForApplication(si.applicationInfo); + + AttributeSet attrs = Xml.asAttributeSet(parser); + + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && type != XmlPullParser.START_TAG) { + } + + String nodeName = parser.getName(); + if (!"voice-interaction-service".equals(nodeName)) { + mParseError = "Meta-data does not start with voice-interaction-service tag"; + return; + } + + TypedArray array = res.obtainAttributes(attrs, + com.android.internal.R.styleable.VoiceInteractionService); + mSessionService = array.getString( + com.android.internal.R.styleable.VoiceInteractionService_sessionService); + mSettingsActivity = array.getString( + com.android.internal.R.styleable.VoiceInteractionService_settingsActivity); + array.recycle(); + if (mSessionService == null) { + mParseError = "No sessionService specified"; + return; + } + } catch (XmlPullParserException e) { + mParseError = "Error parsing voice interation service meta-data: " + e; + Log.w(TAG, "error parsing voice interaction service meta-data", e); + return; + } catch (IOException e) { + mParseError = "Error parsing voice interation service meta-data: " + e; + Log.w(TAG, "error parsing voice interaction service meta-data", e); + return; + } catch (PackageManager.NameNotFoundException e) { + mParseError = "Error parsing voice interation service meta-data: " + e; + Log.w(TAG, "error parsing voice interaction service meta-data", e); + return; + } finally { + if (parser != null) parser.close(); + } + mServiceInfo = si; + } + + public String getParseError() { + return mParseError; + } + + public ServiceInfo getServiceInfo() { + return mServiceInfo; + } + + public String getSessionService() { + return mSessionService; + } + + public String getSettingsActivity() { + return mSettingsActivity; + } +} diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java index 59544be..963b6b4 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -96,11 +96,11 @@ public abstract class VoiceInteractionSession { } } - public void sendCommandResult(Bundle result) { + public void sendCommandResult(boolean complete, Bundle result) { try { if (DEBUG) Log.d(TAG, "sendCommandResult: req=" + mInterface + " result=" + result); - mCallback.deliverCommandResult(mInterface, result); + mCallback.deliverCommandResult(mInterface, complete, result); } catch (RemoteException e) { } } diff --git a/core/java/android/service/voice/VoiceInteractionSessionService.java b/core/java/android/service/voice/VoiceInteractionSessionService.java new file mode 100644 index 0000000..40e5bba --- /dev/null +++ b/core/java/android/service/voice/VoiceInteractionSessionService.java @@ -0,0 +1,82 @@ +/** + * Copyright (C) 2014 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 android.service.voice; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceManager; +import com.android.internal.app.IVoiceInteractionManagerService; +import com.android.internal.os.HandlerCaller; +import com.android.internal.os.SomeArgs; + +public abstract class VoiceInteractionSessionService extends Service { + + static final int MSG_NEW_SESSION = 1; + + IVoiceInteractionManagerService mSystemService; + + IVoiceInteractionSessionService mInterface = new IVoiceInteractionSessionService.Stub() { + public void newSession(IBinder token, Bundle args) { + mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOO(MSG_NEW_SESSION, + token, args)); + + } + }; + + HandlerCaller mHandlerCaller; + final HandlerCaller.Callback mHandlerCallerCallback = new HandlerCaller.Callback() { + @Override + public void executeMessage(Message msg) { + SomeArgs args = (SomeArgs)msg.obj; + switch (msg.what) { + case MSG_NEW_SESSION: + doNewSession((IBinder)args.arg1, (Bundle)args.arg2); + break; + } + } + }; + + @Override + public void onCreate() { + super.onCreate(); + mSystemService = IVoiceInteractionManagerService.Stub.asInterface( + ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE)); + mHandlerCaller = new HandlerCaller(this, Looper.myLooper(), + mHandlerCallerCallback, true); + } + + public abstract VoiceInteractionSession onNewSession(Bundle args); + + @Override + public IBinder onBind(Intent intent) { + return mInterface.asBinder(); + } + + void doNewSession(IBinder token, Bundle args) { + VoiceInteractionSession session = onNewSession(args); + try { + mSystemService.deliverNewSession(token, session.mSession, session.mInteractor); + } catch (RemoteException e) { + } + } +} diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl index e3c0728..3219ddd 100644 --- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl +++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl @@ -17,12 +17,15 @@ package com.android.internal.app; import android.content.Intent; +import android.os.Bundle; import com.android.internal.app.IVoiceInteractor; import android.service.voice.IVoiceInteractionService; import android.service.voice.IVoiceInteractionSession; interface IVoiceInteractionManagerService { - int startVoiceActivity(in Intent intent, String resolvedType, IVoiceInteractionService service, - IVoiceInteractionSession session, IVoiceInteractor interactor); + void startVoiceActivity(in Intent intent, String resolvedType, IVoiceInteractionService service, + in Bundle sessionArgs); + int deliverNewSession(IBinder token, IVoiceInteractionSession session, + IVoiceInteractor interactor); } diff --git a/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl b/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl index f5392e9..c6f93e1 100644 --- a/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl +++ b/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl @@ -26,6 +26,6 @@ import com.android.internal.app.IVoiceInteractorRequest; oneway interface IVoiceInteractorCallback { void deliverConfirmationResult(IVoiceInteractorRequest request, boolean confirmed, in Bundle result); - void deliverCommandResult(IVoiceInteractorRequest request, in Bundle result); + void deliverCommandResult(IVoiceInteractorRequest request, boolean complete, in Bundle result); void deliverCancel(IVoiceInteractorRequest request); } diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 326485d..69440be 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -6279,6 +6279,7 @@ its {@link android.service.voice.VoiceInteractionService#SERVICE_META_DATA} meta-data entry. Described here are the attributes that can be included in that tag. --> <declare-styleable name="VoiceInteractionService"> + <attr name="sessionService" format="string" /> <attr name="settingsActivity" /> </declare-styleable> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 6491b33..085b9c3 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2155,6 +2155,13 @@ <public type="attr" name="colorPrimaryDark" /> <public type="attr" name="colorAccent" /> <public type="attr" name="nestedScrollingEnabled" /> + <public type="attr" name="windowEnterTransition" /> + <public type="attr" name="windowExitTransition" /> + <public type="attr" name="windowSharedElementEnterTransition" /> + <public type="attr" name="windowSharedElementExitTransition" /> + <public type="attr" name="windowAllowExitTransitionOverlap" /> + <public type="attr" name="windowAllowEnterTransitionOverlap" /> + <public type="attr" name="sessionService" /> <public-padding type="dimen" name="l_resource_pad" end="0x01050010" /> @@ -2385,17 +2392,14 @@ <public type="style" name="Widget.Holo.Light.Button.Borderless" /> + <public-padding type="interpolator" name="l_resource_pad" end="0x010c0010" /> + <!-- An interpolator which accelerates fast but decelerates slowly. --> <public type="interpolator" name="fast_out_slow_in" /> <!-- An interpolator which starts with a peak non-zero velocity and decelerates slowly. --> <public type="interpolator" name="linear_out_slow_in" /> <!-- An interpolator which accelerates fast and keeps accelerating until the end. --> <public type="interpolator" name="fast_out_linear_in" /> - <public type="attr" name="windowEnterTransition" /> - <public type="attr" name="windowExitTransition" /> - <public type="attr" name="windowSharedElementEnterTransition" /> - <public type="attr" name="windowSharedElementExitTransition" /> - <public type="attr" name="windowAllowExitTransitionOverlap" /> - <public type="attr" name="windowAllowEnterTransitionOverlap" /> + <public type="transition" name="no_transition" id="0x010f0000"/> </resources> diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 9e2bcab..045c0f6 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -16,14 +16,18 @@ package com.android.server.voiceinteraction; +import android.Manifest; import android.app.ActivityManager; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.database.ContentObserver; import android.os.Binder; +import android.os.Bundle; import android.os.Handler; +import android.os.IBinder; import android.os.UserHandle; import android.provider.Settings; import android.service.voice.IVoiceInteractionService; @@ -134,9 +138,8 @@ public class VoiceInteractionManagerService extends SystemService { } @Override - public int startVoiceActivity(Intent intent, String resolvedType, - IVoiceInteractionService service, - IVoiceInteractionSession session, IVoiceInteractor interactor) { + public void startVoiceActivity(Intent intent, String resolvedType, + IVoiceInteractionService service, Bundle args) { synchronized (this) { if (mImpl == null || service.asBinder() != mImpl.mService.asBinder()) { throw new SecurityException( @@ -146,8 +149,8 @@ public class VoiceInteractionManagerService extends SystemService { final int callingUid = Binder.getCallingUid(); final long caller = Binder.clearCallingIdentity(); try { - return mImpl.startVoiceActivityLocked(callingPid, callingUid, - intent, resolvedType, session, interactor); + mImpl.startVoiceActivityLocked(callingPid, callingUid, + intent, resolvedType, args); } finally { Binder.restoreCallingIdentity(caller); } @@ -155,8 +158,43 @@ public class VoiceInteractionManagerService extends SystemService { } @Override + public int deliverNewSession(IBinder token, IVoiceInteractionSession session, + IVoiceInteractor interactor) { + synchronized (this) { + if (mImpl == null) { + Slog.w(TAG, "deliverNewSession without running voice interaction service"); + return ActivityManager.START_CANCELED; + } + final int callingPid = Binder.getCallingPid(); + final int callingUid = Binder.getCallingUid(); + final long caller = Binder.clearCallingIdentity(); + try { + return mImpl.deliverNewSessionLocked(callingPid, callingUid, token, session, + interactor); + } finally { + Binder.restoreCallingIdentity(caller); + } + } + + } + + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); + if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump PowerManager from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; + } + synchronized (this) { + pw.println("VOICE INTERACTION MANAGER (dumpsys voiceinteraction)\n"); + if (mImpl == null) { + pw.println(" (No active implementation)"); + return; + } + mImpl.dumpLocked(fd, pw, args); + } } class SettingsObserver extends ContentObserver { diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java index af8ae1e..6bbd1c1 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java @@ -16,6 +16,7 @@ package com.android.server.voiceinteraction; +import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.IActivityManager; import android.content.ComponentName; @@ -24,29 +25,40 @@ import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; +import android.os.Binder; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; import android.service.voice.IVoiceInteractionService; import android.service.voice.IVoiceInteractionSession; +import android.service.voice.IVoiceInteractionSessionService; import android.service.voice.VoiceInteractionService; +import android.service.voice.VoiceInteractionServiceInfo; import android.util.Slog; import com.android.internal.app.IVoiceInteractor; +import java.io.FileDescriptor; +import java.io.PrintWriter; + class VoiceInteractionManagerServiceImpl { final static String TAG = "VoiceInteractionServiceManager"; + final boolean mValid; + final Context mContext; final Handler mHandler; final Object mLock; final int mUser; final ComponentName mComponent; final IActivityManager mAm; + final VoiceInteractionServiceInfo mInfo; + final ComponentName mSessionComponentName; boolean mBound = false; IVoiceInteractionService mService; - IVoiceInteractionSession mActiveSession; - IVoiceInteractor mActiveInteractor; + + SessionConnection mActiveSession; final ServiceConnection mConnection = new ServiceConnection() { @Override @@ -62,6 +74,72 @@ class VoiceInteractionManagerServiceImpl { } }; + final class SessionConnection implements ServiceConnection { + final IBinder mToken = new Binder(); + final Intent mIntent; + final String mResolvedType; + final Bundle mArgs; + boolean mBound; + IVoiceInteractionSessionService mService; + IVoiceInteractionSession mSession; + IVoiceInteractor mInteractor; + + SessionConnection(Intent intent, String resolvedType, Bundle args) { + mIntent = intent; + mResolvedType = resolvedType; + mArgs = args; + Intent serviceIntent = new Intent(VoiceInteractionService.SERVICE_INTERFACE); + serviceIntent.setComponent(mSessionComponentName); + mBound = mContext.bindServiceAsUser(serviceIntent, this, + Context.BIND_AUTO_CREATE, new UserHandle(mUser)); + if (!mBound) { + Slog.w(TAG, "Failed binding to voice interaction session service " + mComponent); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + synchronized (mLock) { + mService = IVoiceInteractionSessionService.Stub.asInterface(service); + if (mActiveSession == this) { + try { + mService.newSession(mToken, mArgs); + } catch (RemoteException e) { + Slog.w(TAG, "Failed making new session", e); + } + } + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + mService = null; + } + + public void cancel() { + if (mBound) { + mContext.unbindService(this); + mBound = false; + mService = null; + mSession = null; + mInteractor = null; + } + } + + public void dump(String prefix, PrintWriter pw) { + pw.print(prefix); pw.print("mToken="); pw.println(mToken); + pw.print(prefix); pw.print("mIntent="); pw.println(mIntent); + pw.print(" mResolvedType="); pw.println(mResolvedType); + pw.print(prefix); pw.print("mArgs="); pw.println(mArgs); + pw.print(prefix); pw.print("mBound="); pw.println(mBound); + if (mBound) { + pw.print(prefix); pw.print("mService="); pw.println(mService); + pw.print(prefix); pw.print("mSession="); pw.println(mSession); + pw.print(prefix); pw.print("mInteractor="); pw.println(mInteractor); + } + } + }; + VoiceInteractionManagerServiceImpl(Context context, Handler handler, Object lock, int userHandle, ComponentName service) { mContext = context; @@ -70,50 +148,85 @@ class VoiceInteractionManagerServiceImpl { mUser = userHandle; mComponent = service; mAm = ActivityManagerNative.getDefault(); - } - - public int startVoiceActivityLocked(int callingPid, int callingUid, Intent intent, - String resolvedType, IVoiceInteractionSession session, IVoiceInteractor interactor) { - if (session == null) { - throw new NullPointerException("session is null"); + VoiceInteractionServiceInfo info; + try { + info = new VoiceInteractionServiceInfo(context.getPackageManager(), service); + } catch (PackageManager.NameNotFoundException e) { + Slog.w(TAG, "Voice interaction service not found: " + service); + mInfo = null; + mSessionComponentName = null; + mValid = false; + return; } - if (interactor == null) { - throw new NullPointerException("interactor is null"); + mInfo = info; + if (mInfo.getParseError() != null) { + Slog.w(TAG, "Bad voice interaction service: " + mInfo.getParseError()); + mSessionComponentName = null; + mValid = false; + return; } + mValid = true; + mSessionComponentName = new ComponentName(service.getPackageName(), + mInfo.getSessionService()); + } + + public void startVoiceActivityLocked(int callingPid, int callingUid, Intent intent, + String resolvedType, Bundle args) { if (mActiveSession != null) { - // XXX cancel current session. + mActiveSession.cancel(); + mActiveSession = null; } + mActiveSession = new SessionConnection(intent, resolvedType, args); intent.addCategory(Intent.CATEGORY_VOICE); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); - mActiveSession = session; - mActiveInteractor = interactor; + } + + public int deliverNewSessionLocked(int callingPid, int callingUid, IBinder token, + IVoiceInteractionSession session, IVoiceInteractor interactor) { try { + if (mActiveSession == null || token != mActiveSession.mToken) { + Slog.w(TAG, "deliverNewSession does not match active session"); + return ActivityManager.START_CANCELED; + } + mActiveSession.mSession = session; + mActiveSession.mInteractor = interactor; return mAm.startVoiceActivity(mComponent.getPackageName(), callingPid, callingUid, - intent, resolvedType, mActiveSession, mActiveInteractor, + mActiveSession.mIntent, mActiveSession.mResolvedType, + mActiveSession.mSession, mActiveSession.mInteractor, 0, null, null, null, mUser); } catch (RemoteException e) { throw new IllegalStateException("Unexpected remote error", e); } } - void startLocked() { - Intent intent = new Intent(VoiceInteractionService.SERVICE_INTERFACE); - intent.setComponent(mComponent); - try { - ServiceInfo si = mContext.getPackageManager().getServiceInfo(mComponent, 0); - if (!android.Manifest.permission.BIND_VOICE_INTERACTION.equals(si.permission)) { - Slog.w(TAG, "Not using voice interaction service " + mComponent - + ": does not require permission " - + android.Manifest.permission.BIND_VOICE_INTERACTION); - return; + public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) { + if (!mValid) { + pw.print(" NOT VALID: "); + if (mInfo == null) { + pw.println("no info"); + } else { + pw.println(mInfo.getParseError()); } - } catch (PackageManager.NameNotFoundException e) { - Slog.w(TAG, "Unable to find voice interaction service: " + mComponent, e); return; } - mContext.bindServiceAsUser(intent, mConnection, + pw.print(" mComponent="); pw.println(mComponent.flattenToShortString()); + pw.print(" Session service="); pw.println(mInfo.getSessionService()); + pw.print(" Settings activity="); pw.println(mInfo.getSettingsActivity()); + pw.print(" mBound="); pw.print(mBound); pw.print(" mService="); pw.println(mService); + if (mActiveSession != null) { + pw.println(" Active session:"); + mActiveSession.dump(" ", pw); + } + } + + void startLocked() { + Intent intent = new Intent(VoiceInteractionService.SERVICE_INTERFACE); + intent.setComponent(mComponent); + mBound = mContext.bindServiceAsUser(intent, mConnection, Context.BIND_AUTO_CREATE, new UserHandle(mUser)); - mBound = true; + if (!mBound) { + Slog.w(TAG, "Failed binding to voice interaction service " + mComponent); + } } void shutdownLocked() { diff --git a/tests/VoiceInteraction/AndroidManifest.xml b/tests/VoiceInteraction/AndroidManifest.xml index 9c5acf9..ac0f701 100644 --- a/tests/VoiceInteraction/AndroidManifest.xml +++ b/tests/VoiceInteraction/AndroidManifest.xml @@ -12,10 +12,16 @@ <service android:name="MainInteractionService" android:permission="android.permission.BIND_VOICE_INTERACTION" android:process=":interactor"> + <meta-data android:name="android.voice_interaction" + android:resource="@xml/interaction_service" /> <intent-filter> <action android:name="android.service.voice.VoiceInteractionService" /> </intent-filter> </service> + <service android:name="MainInteractionSessionService" + android:permission="android.permission.BIND_VOICE_INTERACTION" + android:process=":session"> + </service> <activity android:name="TestInteractionActivity" android:label="Voice Interaction Target"> <intent-filter> <action android:name="android.intent.action.MAIN" /> diff --git a/tests/VoiceInteraction/res/xml/interaction_service.xml b/tests/VoiceInteraction/res/xml/interaction_service.xml new file mode 100644 index 0000000..45bd994d --- /dev/null +++ b/tests/VoiceInteraction/res/xml/interaction_service.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2014, 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. + */ +--> + +<voice-interaction-service xmlns:android="http://schemas.android.com/apk/res/android" + android:sessionService="com.android.test.voiceinteraction.MainInteractionSessionService" /> diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java index 35702f1..008d97b 100644 --- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java +++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java @@ -31,8 +31,7 @@ public class MainInteractionService extends VoiceInteractionService { @Override public int onStartCommand(Intent intent, int flags, int startId) { - startVoiceActivity(new Intent(this, TestInteractionActivity.class), - new MainInteractionSession(this)); + startVoiceActivity(new Intent(this, TestInteractionActivity.class), null); stopSelf(startId); return START_NOT_STICKY; } diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java index adc0df4..0fc563b 100644 --- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java +++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java @@ -24,8 +24,11 @@ import android.util.Log; public class MainInteractionSession extends VoiceInteractionSession { static final String TAG = "MainInteractionSession"; - MainInteractionSession(Context context) { + final Bundle mArgs; + + MainInteractionSession(Context context, Bundle args) { super(context); + mArgs = args; } @Override @@ -42,7 +45,7 @@ public class MainInteractionSession extends VoiceInteractionSession { @Override public void onCommand(Caller caller, Request request, String command, Bundle extras) { Log.i(TAG, "onCommand: command=" + command + " extras=" + extras); - request.sendCommandResult(null); + request.sendCommandResult(true, null); } @Override diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSessionService.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSessionService.java new file mode 100644 index 0000000..8864d71 --- /dev/null +++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSessionService.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 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.test.voiceinteraction; + +import android.os.Bundle; +import android.service.voice.VoiceInteractionSession; +import android.service.voice.VoiceInteractionSessionService; + +public class MainInteractionSessionService extends VoiceInteractionSessionService { + @Override + public VoiceInteractionSession onNewSession(Bundle args) { + return new MainInteractionSession(this, args); + } +} diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java index 016a80e..9c772ff 100644 --- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java +++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java @@ -30,28 +30,30 @@ public class TestInteractionActivity extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.test_interaction); if (!isVoiceInteraction()) { Log.w(TAG, "Not running as a voice interaction!"); finish(); return; } + setContentView(R.layout.test_interaction); + mInteractor = getVoiceInteractor(); - mInteractor.startConfirmation(new VoiceInteractor.Callback() { + VoiceInteractor.ConfirmationRequest req = new VoiceInteractor.ConfirmationRequest( + "This is a confirmation", null) { @Override - public void onConfirmationResult(VoiceInteractor.Request request, boolean confirmed, - Bundle result) { - Log.i(TAG, "Confirmation result: confirmed=" + confirmed + " result=" + result); - finish(); + public void onCancel() { + Log.i(TAG, "Canceled!"); + getActivity().finish(); } @Override - public void onCancel(VoiceInteractor.Request request) { - Log.i(TAG, "Canceled!"); - finish(); + public void onConfirmationResult(boolean confirmed, Bundle result) { + Log.i(TAG, "Confirmation result: confirmed=" + confirmed + " result=" + result); + getActivity().finish(); } - }, "This is a confirmation", null); + }; + mInteractor.submitRequest(req); } @Override |