Loading api/current.txt +9 −2 Original line number Diff line number Diff line Loading @@ -5029,6 +5029,11 @@ package android.app { method public boolean[] supportsCommands(java.lang.String[]); } public static class VoiceInteractor.AbortVoiceRequest extends android.app.VoiceInteractor.Request { ctor public VoiceInteractor.AbortVoiceRequest(java.lang.CharSequence, android.os.Bundle); method public void onAbortResult(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); Loading Loading @@ -26195,16 +26200,17 @@ package android.service.voice { method public android.view.LayoutInflater getLayoutInflater(); method public android.app.Dialog getWindow(); method public void hideWindow(); method public void onAbortVoice(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.CharSequence, android.os.Bundle); method public void onBackPressed(); method public abstract void onCancel(android.service.voice.VoiceInteractionSession.Request); method public void onCloseSystemDialogs(); method public abstract void onCommand(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.String, android.os.Bundle); method public void onComputeInsets(android.service.voice.VoiceInteractionSession.Insets); method public abstract void onConfirm(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.String, android.os.Bundle); method public abstract void onConfirm(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.CharSequence, android.os.Bundle); method public void onCreate(android.os.Bundle); method public android.view.View onCreateContentView(); method public void onDestroy(); method public abstract boolean[] onGetSupportedCommands(android.service.voice.VoiceInteractionSession.Caller, java.lang.String[]); method public boolean[] onGetSupportedCommands(android.service.voice.VoiceInteractionSession.Caller, java.lang.String[]); method public boolean onKeyDown(int, android.view.KeyEvent); method public boolean onKeyLongPress(int, android.view.KeyEvent); method public boolean onKeyMultiple(int, int, android.view.KeyEvent); Loading @@ -26231,6 +26237,7 @@ package android.service.voice { } public static class VoiceInteractionSession.Request { method public void sendAbortVoiceResult(android.os.Bundle); method public void sendCancelResult(); method public void sendCommandResult(boolean, android.os.Bundle); method public void sendConfirmResult(boolean, android.os.Bundle); core/java/android/app/VoiceInteractor.java +74 −5 Original line number Diff line number Diff line Loading @@ -33,7 +33,26 @@ import com.android.internal.os.SomeArgs; import java.util.ArrayList; /** * Interface for an {@link Activity} to interact with the user through voice. * Interface for an {@link Activity} to interact with the user through voice. Use * {@link android.app.Activity#getVoiceInteractor() Activity.getVoiceInteractor} * to retrieve the interface, if the activity is currently involved in a voice interaction. * * <p>The voice interactor revolves around submitting voice interaction requests to the * back-end voice interaction service that is working with the user. These requests are * submitted with {@link #submitRequest}, providing a new instance of a * {@link Request} subclass describing the type of operation to perform -- currently the * possible requests are {@link ConfirmationRequest} and {@link CommandRequest}. * * <p>Once a request is submitted, the voice system will process it and evetually deliver * the result to the request object. The application can cancel a pending request at any * time. * * <p>The VoiceInteractor is integrated with Activity's state saving mechanism, so that * if an activity is being restarted with retained state, it will retain the current * VoiceInteractor and any outstanding requests. Because of this, you should always use * {@link Request#getActivity() Request.getActivity} to get back to the activity of a * request, rather than holding on to the actvitity instance yourself, either explicitly * or implicitly through a non-static inner class. */ public class VoiceInteractor { static final String TAG = "VoiceInteractor"; Loading Loading @@ -62,6 +81,16 @@ public class VoiceInteractor { request.clear(); } break; case MSG_ABORT_VOICE_RESULT: request = pullRequest((IVoiceInteractorRequest)args.arg1, true); if (DEBUG) Log.d(TAG, "onAbortVoice: req=" + ((IVoiceInteractorRequest)args.arg1).asBinder() + "/" + request + " result=" + args.arg1); if (request != null) { ((AbortVoiceRequest)request).onAbortResult((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=" Loading Loading @@ -95,6 +124,12 @@ public class VoiceInteractor { MSG_CONFIRMATION_RESULT, confirmed ? 1 : 0, request, result)); } @Override public void deliverAbortVoiceResult(IVoiceInteractorRequest request, Bundle result) { mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOO( MSG_ABORT_VOICE_RESULT, request, result)); } @Override public void deliverCommandResult(IVoiceInteractorRequest request, boolean complete, Bundle result) { Loading @@ -112,8 +147,9 @@ public class VoiceInteractor { 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; static final int MSG_ABORT_VOICE_RESULT = 2; static final int MSG_COMMAND_RESULT = 3; static final int MSG_CANCEL_RESULT = 4; public static abstract class Request { IVoiceInteractorRequest mRequestInterface; Loading Loading @@ -188,7 +224,40 @@ public class VoiceInteractor { IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName, IVoiceInteractorCallback callback) throws RemoteException { return interactor.startConfirmation(packageName, callback, mPrompt.toString(), mExtras); return interactor.startConfirmation(packageName, callback, mPrompt, mExtras); } } public static class AbortVoiceRequest extends Request { final CharSequence mMessage; final Bundle mExtras; /** * Reports that the current interaction can not be complete with voice, so the * application will need to switch to a traditional input UI. Applications should * only use this when they need to completely bail out of the voice interaction * and switch to a traditional UI. When the resonsponse comes back, the voice * system has handled the request and is ready to switch; at that point the application * can start a new non-voice activity. Be sure when starting the new activity * to use {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK * Intent.FLAG_ACTIVITY_NEW_TASK} to keep the new activity out of the current voice * interaction task. * * @param message Optional message to tell user about not being able to complete * the interaction with voice. * @param extras Additional optional information. */ public AbortVoiceRequest(CharSequence message, Bundle extras) { mMessage = message; mExtras = extras; } public void onAbortResult(Bundle result) { } IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName, IVoiceInteractorCallback callback) throws RemoteException { return interactor.startAbortVoice(packageName, callback, mMessage, mExtras); } } Loading core/java/android/service/voice/VoiceInteractionSession.java +220 −22 Original line number Diff line number Diff line Loading @@ -47,9 +47,22 @@ import com.android.internal.app.IVoiceInteractorRequest; import com.android.internal.os.HandlerCaller; import com.android.internal.os.SomeArgs; import java.lang.ref.WeakReference; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; /** * An active voice interaction session, providing a facility for the implementation * to interact with the user in the voice interaction layer. This interface is no shown * by default, but you can request that it be shown with {@link #showWindow()}, which * will result in a later call to {@link #onCreateContentView()} in which the UI can be * built * * <p>A voice interaction session can be self-contained, ultimately calling {@link #finish} * when done. It can also initiate voice interactions with applications by calling * {@link #startVoiceActivity}</p>. */ public abstract class VoiceInteractionSession implements KeyEvent.Callback { static final String TAG = "VoiceInteractionSession"; static final boolean DEBUG = true; Loading Loading @@ -80,21 +93,34 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { final Insets mTmpInsets = new Insets(); final int[] mTmpLocation = new int[2]; final WeakReference<VoiceInteractionSession> mWeakRef = new WeakReference<VoiceInteractionSession>(this); final IVoiceInteractor mInteractor = new IVoiceInteractor.Stub() { @Override public IVoiceInteractorRequest startConfirmation(String callingPackage, IVoiceInteractorCallback callback, String prompt, Bundle extras) { Request request = findRequest(callback, true); IVoiceInteractorCallback callback, CharSequence prompt, Bundle extras) { Request request = newRequest(callback); mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_CONFIRMATION, new Caller(callingPackage, Binder.getCallingUid()), request, prompt, extras)); return request.mInterface; } @Override public IVoiceInteractorRequest startAbortVoice(String callingPackage, IVoiceInteractorCallback callback, CharSequence message, Bundle extras) { Request request = newRequest(callback); mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_ABORT_VOICE, new Caller(callingPackage, Binder.getCallingUid()), request, message, extras)); return request.mInterface; } @Override public IVoiceInteractorRequest startCommand(String callingPackage, IVoiceInteractorCallback callback, String command, Bundle extras) { Request request = findRequest(callback, true); Request request = newRequest(callback); mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_COMMAND, new Caller(callingPackage, Binder.getCallingUid()), request, command, extras)); Loading Loading @@ -143,29 +169,60 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { final IVoiceInteractorRequest mInterface = new IVoiceInteractorRequest.Stub() { @Override public void cancel() throws RemoteException { mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_CANCEL, Request.this)); VoiceInteractionSession session = mSession.get(); if (session != null) { session.mHandlerCaller.sendMessage( session.mHandlerCaller.obtainMessageO(MSG_CANCEL, Request.this)); } } }; final IVoiceInteractorCallback mCallback; final HandlerCaller mHandlerCaller; Request(IVoiceInteractorCallback callback, HandlerCaller handlerCaller) { final WeakReference<VoiceInteractionSession> mSession; Request(IVoiceInteractorCallback callback, VoiceInteractionSession session) { mCallback = callback; mHandlerCaller = handlerCaller; mSession = session.mWeakRef; } void finishRequest() { VoiceInteractionSession session = mSession.get(); if (session == null) { throw new IllegalStateException("VoiceInteractionSession has been destroyed"); } Request req = session.removeRequest(mInterface.asBinder()); if (req == null) { throw new IllegalStateException("Request not active: " + this); } else if (req != this) { throw new IllegalStateException("Current active request " + req + " not same as calling request " + this); } } public void sendConfirmResult(boolean confirmed, Bundle result) { try { if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface + " confirmed=" + confirmed + " result=" + result); finishRequest(); mCallback.deliverConfirmationResult(mInterface, confirmed, result); } catch (RemoteException e) { } } public void sendAbortVoiceResult(Bundle result) { try { if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface + " result=" + result); finishRequest(); mCallback.deliverAbortVoiceResult(mInterface, result); } catch (RemoteException e) { } } public void sendCommandResult(boolean complete, Bundle result) { try { if (DEBUG) Log.d(TAG, "sendCommandResult: req=" + mInterface + " result=" + result); finishRequest(); mCallback.deliverCommandResult(mInterface, complete, result); } catch (RemoteException e) { } Loading @@ -174,6 +231,7 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { public void sendCancelResult() { try { if (DEBUG) Log.d(TAG, "sendCancelResult: req=" + mInterface); finishRequest(); mCallback.deliverCancel(mInterface); } catch (RemoteException e) { } Loading @@ -191,9 +249,10 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } static final int MSG_START_CONFIRMATION = 1; static final int MSG_START_COMMAND = 2; static final int MSG_SUPPORTS_COMMANDS = 3; static final int MSG_CANCEL = 4; static final int MSG_START_ABORT_VOICE = 2; static final int MSG_START_COMMAND = 3; static final int MSG_SUPPORTS_COMMANDS = 4; static final int MSG_CANCEL = 5; static final int MSG_TASK_STARTED = 100; static final int MSG_TASK_FINISHED = 101; Loading @@ -209,7 +268,14 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { args = (SomeArgs)msg.obj; if (DEBUG) Log.d(TAG, "onConfirm: req=" + ((Request) args.arg2).mInterface + " prompt=" + args.arg3 + " extras=" + args.arg4); onConfirm((Caller)args.arg1, (Request)args.arg2, (String)args.arg3, onConfirm((Caller)args.arg1, (Request)args.arg2, (CharSequence)args.arg3, (Bundle)args.arg4); break; case MSG_START_ABORT_VOICE: args = (SomeArgs)msg.obj; if (DEBUG) Log.d(TAG, "onAbortVoice: req=" + ((Request) args.arg2).mInterface + " message=" + args.arg3 + " extras=" + args.arg4); onAbortVoice((Caller) args.arg1, (Request) args.arg2, (CharSequence) args.arg3, (Bundle) args.arg4); break; case MSG_START_COMMAND: Loading Loading @@ -329,18 +395,20 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { mCallbacks, true); } Request findRequest(IVoiceInteractorCallback callback, boolean newRequest) { Request newRequest(IVoiceInteractorCallback callback) { synchronized (this) { Request req = mActiveRequests.get(callback.asBinder()); if (req != null) { if (newRequest) { throw new IllegalArgumentException("Given request callback " + callback + " is already active"); } Request req = new Request(callback, this); mActiveRequests.put(req.mInterface.asBinder(), req); return req; } req = new Request(callback, mHandlerCaller); mActiveRequests.put(callback.asBinder(), req); } Request removeRequest(IBinder reqInterface) { synchronized (this) { Request req = mActiveRequests.get(reqInterface); if (req != null) { mActiveRequests.remove(req); } return req; } } Loading Loading @@ -425,6 +493,27 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { mTheme = theme; } /** * Ask that a new activity be started for voice interaction. This will create a * new dedicated task in the activity manager for this voice interaction session; * this means that {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK} * will be set for you to make it a new task. * * <p>The newly started activity will be displayed to the user in a special way, as * a layer under the voice interaction UI.</p> * * <p>As the voice activity runs, it can retrieve a {@link android.app.VoiceInteractor} * through which it can perform voice interactions through your session. These requests * for voice interactions will appear as callbacks on {@link #onGetSupportedCommands}, * {@link #onConfirm}, {@link #onCommand}, and {@link #onCancel}. * * <p>You will receive a call to {@link #onTaskStarted} when the task starts up * and {@link #onTaskFinished} when the last activity has finished. * * @param intent The Intent to start this voice interaction. The given Intent will * always have {@link Intent#CATEGORY_VOICE Intent.CATEGORY_VOICE} added to it, since * this is part of a voice interaction. */ public void startVoiceActivity(Intent intent) { if (mToken == null) { throw new IllegalStateException("Can't call before onCreate()"); Loading @@ -439,14 +528,23 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } } /** * Convenience for inflating views. */ public LayoutInflater getLayoutInflater() { return mInflater; } /** * Retrieve the window being used to show the session's UI. */ public Dialog getWindow() { return mWindow; } /** * Finish the session. */ public void finish() { if (mToken == null) { throw new IllegalStateException("Can't call before onCreate()"); Loading @@ -458,6 +556,12 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } } /** * Initiatize a new session. * * @param args The arguments that were supplied to * {@link VoiceInteractionService#startSession VoiceInteractionService.startSession}. */ public void onCreate(Bundle args) { mTheme = mTheme != 0 ? mTheme : com.android.internal.R.style.Theme_DeviceDefault_VoiceInteractionSession; Loading @@ -472,9 +576,15 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { mWindow.setToken(mToken); } /** * Last callback to the session as it is being finished. */ public void onDestroy() { } /** * Hook in which to create the session's UI. */ public View onCreateContentView() { return null; } Loading Loading @@ -507,6 +617,11 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { finish(); } /** * Sessions automatically watch for requests that all system UI be closed (such as when * the user presses HOME), which will appear here. The default implementation always * calls {@link #finish}. */ public void onCloseSystemDialogs() { finish(); } Loading @@ -530,15 +645,98 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { outInsets.touchableRegion.setEmpty(); } /** * Called when a task initiated by {@link #startVoiceActivity(android.content.Intent)} * has actually started. * * @param intent The original {@link Intent} supplied to * {@link #startVoiceActivity(android.content.Intent)}. * @param taskId Unique ID of the now running task. */ public void onTaskStarted(Intent intent, int taskId) { } /** * Called when the last activity of a task initiated by * {@link #startVoiceActivity(android.content.Intent)} has finished. The default * implementation calls {@link #finish()} on the assumption that this represents * the completion of a voice action. You can override the implementation if you would * like a different behavior. * * @param intent The original {@link Intent} supplied to * {@link #startVoiceActivity(android.content.Intent)}. * @param taskId Unique ID of the finished task. */ public void onTaskFinished(Intent intent, int taskId) { finish(); } public abstract boolean[] onGetSupportedCommands(Caller caller, String[] commands); public abstract void onConfirm(Caller caller, Request request, String prompt, Bundle extras); /** * Request to query for what extended commands the session supports. * * @param caller Who is making the request. * @param commands An array of commands that are being queried. * @return Return an array of booleans indicating which of each entry in the * command array is supported. A true entry in the array indicates the command * is supported; false indicates it is not. The default implementation returns * an array of all false entries. */ public boolean[] onGetSupportedCommands(Caller caller, String[] commands) { return new boolean[commands.length]; } /** * Request to confirm with the user before proceeding with an unrecoverable operation, * corresponding to a {@link android.app.VoiceInteractor.ConfirmationRequest * VoiceInteractor.ConfirmationRequest}. * * @param caller Who is making the request. * @param request The active request. * @param prompt The prompt informing the user of what will happen, as per * {@link android.app.VoiceInteractor.ConfirmationRequest VoiceInteractor.ConfirmationRequest}. * @param extras Any additional information, as per * {@link android.app.VoiceInteractor.ConfirmationRequest VoiceInteractor.ConfirmationRequest}. */ public abstract void onConfirm(Caller caller, Request request, CharSequence prompt, Bundle extras); /** * Request to abort the voice interaction session because the voice activity can not * complete its interaction using voice. Corresponds to * {@link android.app.VoiceInteractor.AbortVoiceRequest * VoiceInteractor.AbortVoiceRequest}. The default implementation just sends an empty * confirmation back to allow the activity to exit. * * @param caller Who is making the request. * @param request The active request. * @param message The message informing the user of the problem, as per * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}. * @param extras Any additional information, as per * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}. */ public void onAbortVoice(Caller caller, Request request, CharSequence message, Bundle extras) { request.sendAbortVoiceResult(null); } /** * Process an arbitrary extended command from the caller, * corresponding to a {@link android.app.VoiceInteractor.CommandRequest * VoiceInteractor.CommandRequest}. * * @param caller Who is making the request. * @param request The active request. * @param command The command that is being executed, as per * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}. * @param extras Any additional information, as per * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}. */ public abstract void onCommand(Caller caller, Request request, String command, Bundle extras); /** * Called when the {@link android.app.VoiceInteractor} has asked to cancel a {@link Request} * that was previously delivered to {@link #onConfirm} or {@link #onCommand}. * * @param request The request that is being canceled. */ public abstract void onCancel(Request request); } core/java/com/android/internal/app/IVoiceInteractor.aidl +3 −1 Original line number Diff line number Diff line Loading @@ -26,7 +26,9 @@ import com.android.internal.app.IVoiceInteractorRequest; */ interface IVoiceInteractor { IVoiceInteractorRequest startConfirmation(String callingPackage, IVoiceInteractorCallback callback, String prompt, in Bundle extras); IVoiceInteractorCallback callback, CharSequence prompt, in Bundle extras); IVoiceInteractorRequest startAbortVoice(String callingPackage, IVoiceInteractorCallback callback, CharSequence message, in Bundle extras); IVoiceInteractorRequest startCommand(String callingPackage, IVoiceInteractorCallback callback, String command, in Bundle extras); boolean[] supportsCommands(String callingPackage, in String[] commands); Loading core/java/com/android/internal/app/IVoiceInteractorCallback.aidl +1 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
api/current.txt +9 −2 Original line number Diff line number Diff line Loading @@ -5029,6 +5029,11 @@ package android.app { method public boolean[] supportsCommands(java.lang.String[]); } public static class VoiceInteractor.AbortVoiceRequest extends android.app.VoiceInteractor.Request { ctor public VoiceInteractor.AbortVoiceRequest(java.lang.CharSequence, android.os.Bundle); method public void onAbortResult(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); Loading Loading @@ -26195,16 +26200,17 @@ package android.service.voice { method public android.view.LayoutInflater getLayoutInflater(); method public android.app.Dialog getWindow(); method public void hideWindow(); method public void onAbortVoice(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.CharSequence, android.os.Bundle); method public void onBackPressed(); method public abstract void onCancel(android.service.voice.VoiceInteractionSession.Request); method public void onCloseSystemDialogs(); method public abstract void onCommand(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.String, android.os.Bundle); method public void onComputeInsets(android.service.voice.VoiceInteractionSession.Insets); method public abstract void onConfirm(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.String, android.os.Bundle); method public abstract void onConfirm(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.CharSequence, android.os.Bundle); method public void onCreate(android.os.Bundle); method public android.view.View onCreateContentView(); method public void onDestroy(); method public abstract boolean[] onGetSupportedCommands(android.service.voice.VoiceInteractionSession.Caller, java.lang.String[]); method public boolean[] onGetSupportedCommands(android.service.voice.VoiceInteractionSession.Caller, java.lang.String[]); method public boolean onKeyDown(int, android.view.KeyEvent); method public boolean onKeyLongPress(int, android.view.KeyEvent); method public boolean onKeyMultiple(int, int, android.view.KeyEvent); Loading @@ -26231,6 +26237,7 @@ package android.service.voice { } public static class VoiceInteractionSession.Request { method public void sendAbortVoiceResult(android.os.Bundle); method public void sendCancelResult(); method public void sendCommandResult(boolean, android.os.Bundle); method public void sendConfirmResult(boolean, android.os.Bundle);
core/java/android/app/VoiceInteractor.java +74 −5 Original line number Diff line number Diff line Loading @@ -33,7 +33,26 @@ import com.android.internal.os.SomeArgs; import java.util.ArrayList; /** * Interface for an {@link Activity} to interact with the user through voice. * Interface for an {@link Activity} to interact with the user through voice. Use * {@link android.app.Activity#getVoiceInteractor() Activity.getVoiceInteractor} * to retrieve the interface, if the activity is currently involved in a voice interaction. * * <p>The voice interactor revolves around submitting voice interaction requests to the * back-end voice interaction service that is working with the user. These requests are * submitted with {@link #submitRequest}, providing a new instance of a * {@link Request} subclass describing the type of operation to perform -- currently the * possible requests are {@link ConfirmationRequest} and {@link CommandRequest}. * * <p>Once a request is submitted, the voice system will process it and evetually deliver * the result to the request object. The application can cancel a pending request at any * time. * * <p>The VoiceInteractor is integrated with Activity's state saving mechanism, so that * if an activity is being restarted with retained state, it will retain the current * VoiceInteractor and any outstanding requests. Because of this, you should always use * {@link Request#getActivity() Request.getActivity} to get back to the activity of a * request, rather than holding on to the actvitity instance yourself, either explicitly * or implicitly through a non-static inner class. */ public class VoiceInteractor { static final String TAG = "VoiceInteractor"; Loading Loading @@ -62,6 +81,16 @@ public class VoiceInteractor { request.clear(); } break; case MSG_ABORT_VOICE_RESULT: request = pullRequest((IVoiceInteractorRequest)args.arg1, true); if (DEBUG) Log.d(TAG, "onAbortVoice: req=" + ((IVoiceInteractorRequest)args.arg1).asBinder() + "/" + request + " result=" + args.arg1); if (request != null) { ((AbortVoiceRequest)request).onAbortResult((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=" Loading Loading @@ -95,6 +124,12 @@ public class VoiceInteractor { MSG_CONFIRMATION_RESULT, confirmed ? 1 : 0, request, result)); } @Override public void deliverAbortVoiceResult(IVoiceInteractorRequest request, Bundle result) { mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOO( MSG_ABORT_VOICE_RESULT, request, result)); } @Override public void deliverCommandResult(IVoiceInteractorRequest request, boolean complete, Bundle result) { Loading @@ -112,8 +147,9 @@ public class VoiceInteractor { 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; static final int MSG_ABORT_VOICE_RESULT = 2; static final int MSG_COMMAND_RESULT = 3; static final int MSG_CANCEL_RESULT = 4; public static abstract class Request { IVoiceInteractorRequest mRequestInterface; Loading Loading @@ -188,7 +224,40 @@ public class VoiceInteractor { IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName, IVoiceInteractorCallback callback) throws RemoteException { return interactor.startConfirmation(packageName, callback, mPrompt.toString(), mExtras); return interactor.startConfirmation(packageName, callback, mPrompt, mExtras); } } public static class AbortVoiceRequest extends Request { final CharSequence mMessage; final Bundle mExtras; /** * Reports that the current interaction can not be complete with voice, so the * application will need to switch to a traditional input UI. Applications should * only use this when they need to completely bail out of the voice interaction * and switch to a traditional UI. When the resonsponse comes back, the voice * system has handled the request and is ready to switch; at that point the application * can start a new non-voice activity. Be sure when starting the new activity * to use {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK * Intent.FLAG_ACTIVITY_NEW_TASK} to keep the new activity out of the current voice * interaction task. * * @param message Optional message to tell user about not being able to complete * the interaction with voice. * @param extras Additional optional information. */ public AbortVoiceRequest(CharSequence message, Bundle extras) { mMessage = message; mExtras = extras; } public void onAbortResult(Bundle result) { } IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName, IVoiceInteractorCallback callback) throws RemoteException { return interactor.startAbortVoice(packageName, callback, mMessage, mExtras); } } Loading
core/java/android/service/voice/VoiceInteractionSession.java +220 −22 Original line number Diff line number Diff line Loading @@ -47,9 +47,22 @@ import com.android.internal.app.IVoiceInteractorRequest; import com.android.internal.os.HandlerCaller; import com.android.internal.os.SomeArgs; import java.lang.ref.WeakReference; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; /** * An active voice interaction session, providing a facility for the implementation * to interact with the user in the voice interaction layer. This interface is no shown * by default, but you can request that it be shown with {@link #showWindow()}, which * will result in a later call to {@link #onCreateContentView()} in which the UI can be * built * * <p>A voice interaction session can be self-contained, ultimately calling {@link #finish} * when done. It can also initiate voice interactions with applications by calling * {@link #startVoiceActivity}</p>. */ public abstract class VoiceInteractionSession implements KeyEvent.Callback { static final String TAG = "VoiceInteractionSession"; static final boolean DEBUG = true; Loading Loading @@ -80,21 +93,34 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { final Insets mTmpInsets = new Insets(); final int[] mTmpLocation = new int[2]; final WeakReference<VoiceInteractionSession> mWeakRef = new WeakReference<VoiceInteractionSession>(this); final IVoiceInteractor mInteractor = new IVoiceInteractor.Stub() { @Override public IVoiceInteractorRequest startConfirmation(String callingPackage, IVoiceInteractorCallback callback, String prompt, Bundle extras) { Request request = findRequest(callback, true); IVoiceInteractorCallback callback, CharSequence prompt, Bundle extras) { Request request = newRequest(callback); mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_CONFIRMATION, new Caller(callingPackage, Binder.getCallingUid()), request, prompt, extras)); return request.mInterface; } @Override public IVoiceInteractorRequest startAbortVoice(String callingPackage, IVoiceInteractorCallback callback, CharSequence message, Bundle extras) { Request request = newRequest(callback); mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_ABORT_VOICE, new Caller(callingPackage, Binder.getCallingUid()), request, message, extras)); return request.mInterface; } @Override public IVoiceInteractorRequest startCommand(String callingPackage, IVoiceInteractorCallback callback, String command, Bundle extras) { Request request = findRequest(callback, true); Request request = newRequest(callback); mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_COMMAND, new Caller(callingPackage, Binder.getCallingUid()), request, command, extras)); Loading Loading @@ -143,29 +169,60 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { final IVoiceInteractorRequest mInterface = new IVoiceInteractorRequest.Stub() { @Override public void cancel() throws RemoteException { mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_CANCEL, Request.this)); VoiceInteractionSession session = mSession.get(); if (session != null) { session.mHandlerCaller.sendMessage( session.mHandlerCaller.obtainMessageO(MSG_CANCEL, Request.this)); } } }; final IVoiceInteractorCallback mCallback; final HandlerCaller mHandlerCaller; Request(IVoiceInteractorCallback callback, HandlerCaller handlerCaller) { final WeakReference<VoiceInteractionSession> mSession; Request(IVoiceInteractorCallback callback, VoiceInteractionSession session) { mCallback = callback; mHandlerCaller = handlerCaller; mSession = session.mWeakRef; } void finishRequest() { VoiceInteractionSession session = mSession.get(); if (session == null) { throw new IllegalStateException("VoiceInteractionSession has been destroyed"); } Request req = session.removeRequest(mInterface.asBinder()); if (req == null) { throw new IllegalStateException("Request not active: " + this); } else if (req != this) { throw new IllegalStateException("Current active request " + req + " not same as calling request " + this); } } public void sendConfirmResult(boolean confirmed, Bundle result) { try { if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface + " confirmed=" + confirmed + " result=" + result); finishRequest(); mCallback.deliverConfirmationResult(mInterface, confirmed, result); } catch (RemoteException e) { } } public void sendAbortVoiceResult(Bundle result) { try { if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface + " result=" + result); finishRequest(); mCallback.deliverAbortVoiceResult(mInterface, result); } catch (RemoteException e) { } } public void sendCommandResult(boolean complete, Bundle result) { try { if (DEBUG) Log.d(TAG, "sendCommandResult: req=" + mInterface + " result=" + result); finishRequest(); mCallback.deliverCommandResult(mInterface, complete, result); } catch (RemoteException e) { } Loading @@ -174,6 +231,7 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { public void sendCancelResult() { try { if (DEBUG) Log.d(TAG, "sendCancelResult: req=" + mInterface); finishRequest(); mCallback.deliverCancel(mInterface); } catch (RemoteException e) { } Loading @@ -191,9 +249,10 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } static final int MSG_START_CONFIRMATION = 1; static final int MSG_START_COMMAND = 2; static final int MSG_SUPPORTS_COMMANDS = 3; static final int MSG_CANCEL = 4; static final int MSG_START_ABORT_VOICE = 2; static final int MSG_START_COMMAND = 3; static final int MSG_SUPPORTS_COMMANDS = 4; static final int MSG_CANCEL = 5; static final int MSG_TASK_STARTED = 100; static final int MSG_TASK_FINISHED = 101; Loading @@ -209,7 +268,14 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { args = (SomeArgs)msg.obj; if (DEBUG) Log.d(TAG, "onConfirm: req=" + ((Request) args.arg2).mInterface + " prompt=" + args.arg3 + " extras=" + args.arg4); onConfirm((Caller)args.arg1, (Request)args.arg2, (String)args.arg3, onConfirm((Caller)args.arg1, (Request)args.arg2, (CharSequence)args.arg3, (Bundle)args.arg4); break; case MSG_START_ABORT_VOICE: args = (SomeArgs)msg.obj; if (DEBUG) Log.d(TAG, "onAbortVoice: req=" + ((Request) args.arg2).mInterface + " message=" + args.arg3 + " extras=" + args.arg4); onAbortVoice((Caller) args.arg1, (Request) args.arg2, (CharSequence) args.arg3, (Bundle) args.arg4); break; case MSG_START_COMMAND: Loading Loading @@ -329,18 +395,20 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { mCallbacks, true); } Request findRequest(IVoiceInteractorCallback callback, boolean newRequest) { Request newRequest(IVoiceInteractorCallback callback) { synchronized (this) { Request req = mActiveRequests.get(callback.asBinder()); if (req != null) { if (newRequest) { throw new IllegalArgumentException("Given request callback " + callback + " is already active"); } Request req = new Request(callback, this); mActiveRequests.put(req.mInterface.asBinder(), req); return req; } req = new Request(callback, mHandlerCaller); mActiveRequests.put(callback.asBinder(), req); } Request removeRequest(IBinder reqInterface) { synchronized (this) { Request req = mActiveRequests.get(reqInterface); if (req != null) { mActiveRequests.remove(req); } return req; } } Loading Loading @@ -425,6 +493,27 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { mTheme = theme; } /** * Ask that a new activity be started for voice interaction. This will create a * new dedicated task in the activity manager for this voice interaction session; * this means that {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK} * will be set for you to make it a new task. * * <p>The newly started activity will be displayed to the user in a special way, as * a layer under the voice interaction UI.</p> * * <p>As the voice activity runs, it can retrieve a {@link android.app.VoiceInteractor} * through which it can perform voice interactions through your session. These requests * for voice interactions will appear as callbacks on {@link #onGetSupportedCommands}, * {@link #onConfirm}, {@link #onCommand}, and {@link #onCancel}. * * <p>You will receive a call to {@link #onTaskStarted} when the task starts up * and {@link #onTaskFinished} when the last activity has finished. * * @param intent The Intent to start this voice interaction. The given Intent will * always have {@link Intent#CATEGORY_VOICE Intent.CATEGORY_VOICE} added to it, since * this is part of a voice interaction. */ public void startVoiceActivity(Intent intent) { if (mToken == null) { throw new IllegalStateException("Can't call before onCreate()"); Loading @@ -439,14 +528,23 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } } /** * Convenience for inflating views. */ public LayoutInflater getLayoutInflater() { return mInflater; } /** * Retrieve the window being used to show the session's UI. */ public Dialog getWindow() { return mWindow; } /** * Finish the session. */ public void finish() { if (mToken == null) { throw new IllegalStateException("Can't call before onCreate()"); Loading @@ -458,6 +556,12 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } } /** * Initiatize a new session. * * @param args The arguments that were supplied to * {@link VoiceInteractionService#startSession VoiceInteractionService.startSession}. */ public void onCreate(Bundle args) { mTheme = mTheme != 0 ? mTheme : com.android.internal.R.style.Theme_DeviceDefault_VoiceInteractionSession; Loading @@ -472,9 +576,15 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { mWindow.setToken(mToken); } /** * Last callback to the session as it is being finished. */ public void onDestroy() { } /** * Hook in which to create the session's UI. */ public View onCreateContentView() { return null; } Loading Loading @@ -507,6 +617,11 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { finish(); } /** * Sessions automatically watch for requests that all system UI be closed (such as when * the user presses HOME), which will appear here. The default implementation always * calls {@link #finish}. */ public void onCloseSystemDialogs() { finish(); } Loading @@ -530,15 +645,98 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { outInsets.touchableRegion.setEmpty(); } /** * Called when a task initiated by {@link #startVoiceActivity(android.content.Intent)} * has actually started. * * @param intent The original {@link Intent} supplied to * {@link #startVoiceActivity(android.content.Intent)}. * @param taskId Unique ID of the now running task. */ public void onTaskStarted(Intent intent, int taskId) { } /** * Called when the last activity of a task initiated by * {@link #startVoiceActivity(android.content.Intent)} has finished. The default * implementation calls {@link #finish()} on the assumption that this represents * the completion of a voice action. You can override the implementation if you would * like a different behavior. * * @param intent The original {@link Intent} supplied to * {@link #startVoiceActivity(android.content.Intent)}. * @param taskId Unique ID of the finished task. */ public void onTaskFinished(Intent intent, int taskId) { finish(); } public abstract boolean[] onGetSupportedCommands(Caller caller, String[] commands); public abstract void onConfirm(Caller caller, Request request, String prompt, Bundle extras); /** * Request to query for what extended commands the session supports. * * @param caller Who is making the request. * @param commands An array of commands that are being queried. * @return Return an array of booleans indicating which of each entry in the * command array is supported. A true entry in the array indicates the command * is supported; false indicates it is not. The default implementation returns * an array of all false entries. */ public boolean[] onGetSupportedCommands(Caller caller, String[] commands) { return new boolean[commands.length]; } /** * Request to confirm with the user before proceeding with an unrecoverable operation, * corresponding to a {@link android.app.VoiceInteractor.ConfirmationRequest * VoiceInteractor.ConfirmationRequest}. * * @param caller Who is making the request. * @param request The active request. * @param prompt The prompt informing the user of what will happen, as per * {@link android.app.VoiceInteractor.ConfirmationRequest VoiceInteractor.ConfirmationRequest}. * @param extras Any additional information, as per * {@link android.app.VoiceInteractor.ConfirmationRequest VoiceInteractor.ConfirmationRequest}. */ public abstract void onConfirm(Caller caller, Request request, CharSequence prompt, Bundle extras); /** * Request to abort the voice interaction session because the voice activity can not * complete its interaction using voice. Corresponds to * {@link android.app.VoiceInteractor.AbortVoiceRequest * VoiceInteractor.AbortVoiceRequest}. The default implementation just sends an empty * confirmation back to allow the activity to exit. * * @param caller Who is making the request. * @param request The active request. * @param message The message informing the user of the problem, as per * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}. * @param extras Any additional information, as per * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}. */ public void onAbortVoice(Caller caller, Request request, CharSequence message, Bundle extras) { request.sendAbortVoiceResult(null); } /** * Process an arbitrary extended command from the caller, * corresponding to a {@link android.app.VoiceInteractor.CommandRequest * VoiceInteractor.CommandRequest}. * * @param caller Who is making the request. * @param request The active request. * @param command The command that is being executed, as per * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}. * @param extras Any additional information, as per * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}. */ public abstract void onCommand(Caller caller, Request request, String command, Bundle extras); /** * Called when the {@link android.app.VoiceInteractor} has asked to cancel a {@link Request} * that was previously delivered to {@link #onConfirm} or {@link #onCommand}. * * @param request The request that is being canceled. */ public abstract void onCancel(Request request); }
core/java/com/android/internal/app/IVoiceInteractor.aidl +3 −1 Original line number Diff line number Diff line Loading @@ -26,7 +26,9 @@ import com.android.internal.app.IVoiceInteractorRequest; */ interface IVoiceInteractor { IVoiceInteractorRequest startConfirmation(String callingPackage, IVoiceInteractorCallback callback, String prompt, in Bundle extras); IVoiceInteractorCallback callback, CharSequence prompt, in Bundle extras); IVoiceInteractorRequest startAbortVoice(String callingPackage, IVoiceInteractorCallback callback, CharSequence message, in Bundle extras); IVoiceInteractorRequest startCommand(String callingPackage, IVoiceInteractorCallback callback, String command, in Bundle extras); boolean[] supportsCommands(String callingPackage, in String[] commands); Loading
core/java/com/android/internal/app/IVoiceInteractorCallback.aidl +1 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes