VoiceInteractionSession.java revision 16036f26847f3f1a88a093fb776bf081008ff8d8
1/**
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.service.voice;
18
19import android.annotation.Nullable;
20import android.app.Dialog;
21import android.app.Instrumentation;
22import android.app.VoiceInteractor;
23import android.app.assist.AssistContent;
24import android.app.assist.AssistStructure;
25import android.content.ComponentCallbacks2;
26import android.content.Context;
27import android.content.Intent;
28import android.content.res.Configuration;
29import android.content.res.TypedArray;
30import android.graphics.Bitmap;
31import android.graphics.Rect;
32import android.graphics.Region;
33import android.inputmethodservice.SoftInputWindow;
34import android.os.Binder;
35import android.os.Bundle;
36import android.os.Handler;
37import android.os.IBinder;
38import android.os.Message;
39import android.os.RemoteException;
40import android.util.ArrayMap;
41import android.util.Log;
42import android.view.Gravity;
43import android.view.KeyEvent;
44import android.view.LayoutInflater;
45import android.view.View;
46import android.view.ViewGroup;
47import android.view.ViewTreeObserver;
48import android.view.WindowManager;
49import android.widget.FrameLayout;
50import com.android.internal.app.IVoiceInteractionManagerService;
51import com.android.internal.app.IVoiceInteractionSessionShowCallback;
52import com.android.internal.app.IVoiceInteractor;
53import com.android.internal.app.IVoiceInteractorCallback;
54import com.android.internal.app.IVoiceInteractorRequest;
55import com.android.internal.os.HandlerCaller;
56import com.android.internal.os.SomeArgs;
57
58import java.lang.ref.WeakReference;
59
60import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
61
62/**
63 * An active voice interaction session, providing a facility for the implementation
64 * to interact with the user in the voice interaction layer.  The user interface is
65 * initially shown by default, and can be created be overriding {@link #onCreateContentView()}
66 * in which the UI can be built.
67 *
68 * <p>A voice interaction session can be self-contained, ultimately calling {@link #finish}
69 * when done.  It can also initiate voice interactions with applications by calling
70 * {@link #startVoiceActivity}</p>.
71 */
72public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCallbacks2 {
73    static final String TAG = "VoiceInteractionSession";
74    static final boolean DEBUG = true;
75
76    /**
77     * Flag received in {@link #onShow}: originator requested that the session be started with
78     * assist data from the currently focused activity.
79     */
80    public static final int SHOW_WITH_ASSIST = 1<<0;
81
82    /**
83     * Flag received in {@link #onShow}: originator requested that the session be started with
84     * a screen shot of the currently focused activity.
85     */
86    public static final int SHOW_WITH_SCREENSHOT = 1<<1;
87
88    /**
89     * Flag for use with {@link #onShow}: indicates that the session has been started from the
90     * system assist gesture.
91     */
92    public static final int SHOW_SOURCE_ASSIST_GESTURE = 1<<2;
93
94    final Context mContext;
95    final HandlerCaller mHandlerCaller;
96
97    final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState();
98
99    IVoiceInteractionManagerService mSystemService;
100    IBinder mToken;
101
102    int mTheme = 0;
103    LayoutInflater mInflater;
104    TypedArray mThemeAttrs;
105    View mRootView;
106    FrameLayout mContentFrame;
107    SoftInputWindow mWindow;
108
109    boolean mInitialized;
110    boolean mWindowAdded;
111    boolean mWindowVisible;
112    boolean mWindowWasVisible;
113    boolean mInShowWindow;
114
115    final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<IBinder, Request>();
116
117    final Insets mTmpInsets = new Insets();
118
119    final WeakReference<VoiceInteractionSession> mWeakRef
120            = new WeakReference<VoiceInteractionSession>(this);
121
122    final IVoiceInteractor mInteractor = new IVoiceInteractor.Stub() {
123        @Override
124        public IVoiceInteractorRequest startConfirmation(String callingPackage,
125                IVoiceInteractorCallback callback, VoiceInteractor.Prompt prompt, Bundle extras) {
126            ConfirmationRequest request = new ConfirmationRequest(callingPackage,
127                    Binder.getCallingUid(), callback, VoiceInteractionSession.this,
128                    prompt, extras);
129            addRequest(request);
130            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_CONFIRMATION,
131                    request));
132            return request.mInterface;
133        }
134
135        @Override
136        public IVoiceInteractorRequest startPickOption(String callingPackage,
137                IVoiceInteractorCallback callback, VoiceInteractor.Prompt prompt,
138                VoiceInteractor.PickOptionRequest.Option[] options, Bundle extras) {
139            PickOptionRequest request = new PickOptionRequest(callingPackage,
140                    Binder.getCallingUid(), callback, VoiceInteractionSession.this,
141                    prompt, options, extras);
142            addRequest(request);
143            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_PICK_OPTION,
144                    request));
145            return request.mInterface;
146        }
147
148        @Override
149        public IVoiceInteractorRequest startCompleteVoice(String callingPackage,
150                IVoiceInteractorCallback callback, VoiceInteractor.Prompt message, Bundle extras) {
151            CompleteVoiceRequest request = new CompleteVoiceRequest(callingPackage,
152                    Binder.getCallingUid(), callback, VoiceInteractionSession.this,
153                    message, extras);
154            addRequest(request);
155            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_COMPLETE_VOICE,
156                    request));
157            return request.mInterface;
158        }
159
160        @Override
161        public IVoiceInteractorRequest startAbortVoice(String callingPackage,
162                IVoiceInteractorCallback callback, VoiceInteractor.Prompt message, Bundle extras) {
163            AbortVoiceRequest request = new AbortVoiceRequest(callingPackage,
164                    Binder.getCallingUid(), callback, VoiceInteractionSession.this,
165                    message, extras);
166            addRequest(request);
167            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_ABORT_VOICE,
168                    request));
169            return request.mInterface;
170        }
171
172        @Override
173        public IVoiceInteractorRequest startCommand(String callingPackage,
174                IVoiceInteractorCallback callback, String command, Bundle extras) {
175            CommandRequest request = new CommandRequest(callingPackage,
176                    Binder.getCallingUid(), callback, VoiceInteractionSession.this,
177                    command, extras);
178            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_COMMAND,
179                    request));
180            return request.mInterface;
181        }
182
183        @Override
184        public boolean[] supportsCommands(String callingPackage, String[] commands) {
185            Message msg = mHandlerCaller.obtainMessageIOO(MSG_SUPPORTS_COMMANDS,
186                    0, commands, null);
187            SomeArgs args = mHandlerCaller.sendMessageAndWait(msg);
188            if (args != null) {
189                boolean[] res = (boolean[])args.arg1;
190                args.recycle();
191                return res;
192            }
193            return new boolean[commands.length];
194        }
195    };
196
197    final IVoiceInteractionSession mSession = new IVoiceInteractionSession.Stub() {
198        @Override
199        public void show(Bundle sessionArgs, int flags,
200                IVoiceInteractionSessionShowCallback showCallback) {
201            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOO(MSG_SHOW,
202                    flags, sessionArgs, showCallback));
203        }
204
205        @Override
206        public void hide() {
207            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_HIDE));
208        }
209
210        @Override
211        public void handleAssist(Bundle data, AssistStructure structure,
212                AssistContent content) {
213            // We want to pre-warm the AssistStructure before handing it off to the main
214            // thread.  There is a strong argument to be made that it should be handed
215            // through as a separate param rather than part of the assistBundle.
216            if (structure != null) {
217                structure.ensureData();
218            }
219            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOO(MSG_HANDLE_ASSIST,
220                    data, structure, content));
221        }
222
223        @Override
224        public void handleScreenshot(Bitmap screenshot) {
225            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_HANDLE_SCREENSHOT,
226                    screenshot));
227        }
228
229        @Override
230        public void taskStarted(Intent intent, int taskId) {
231            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_TASK_STARTED,
232                    taskId, intent));
233        }
234
235        @Override
236        public void taskFinished(Intent intent, int taskId) {
237            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_TASK_FINISHED,
238                    taskId, intent));
239        }
240
241        @Override
242        public void closeSystemDialogs() {
243            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_CLOSE_SYSTEM_DIALOGS));
244        }
245
246        @Override
247        public void destroy() {
248            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_DESTROY));
249        }
250    };
251
252    /** @hide */
253    public static class Caller {
254    }
255
256    /**
257     * Base class representing a request from a voice-driver app to perform a particular
258     * voice operation with the user.  See related subclasses for the types of requests
259     * that are possible.
260     */
261    public static class Request extends Caller {
262        final IVoiceInteractorRequest mInterface = new IVoiceInteractorRequest.Stub() {
263            @Override
264            public void cancel() throws RemoteException {
265                VoiceInteractionSession session = mSession.get();
266                if (session != null) {
267                    session.mHandlerCaller.sendMessage(
268                            session.mHandlerCaller.obtainMessageO(MSG_CANCEL, Request.this));
269                }
270            }
271        };
272        final String mCallingPackage;
273        final int mCallingUid;
274        final IVoiceInteractorCallback mCallback;
275        final WeakReference<VoiceInteractionSession> mSession;
276        final Bundle mExtras;
277
278        Request(String packageName, int uid, IVoiceInteractorCallback callback,
279                VoiceInteractionSession session, Bundle extras) {
280            mCallingPackage = packageName;
281            mCallingUid = uid;
282            mCallback = callback;
283            mSession = session.mWeakRef;
284            mExtras = extras;
285        }
286
287        /**
288         * Return the uid of the application that initiated the request.
289         */
290        public int getCallingUid() {
291            return mCallingUid;
292        }
293
294        /**
295         * Return the package name of the application that initiated the request.
296         */
297        public String getCallingPackage() {
298            return mCallingPackage;
299        }
300
301        /**
302         * Return any additional extra information that was supplied as part of the request.
303         */
304        public Bundle getExtras() {
305            return mExtras;
306        }
307
308        void finishRequest() {
309            VoiceInteractionSession session = mSession.get();
310            if (session == null) {
311                throw new IllegalStateException("VoiceInteractionSession has been destroyed");
312            }
313            Request req = session.removeRequest(mInterface.asBinder());
314            if (req == null) {
315                throw new IllegalStateException("Request not active: " + this);
316            } else if (req != this) {
317                throw new IllegalStateException("Current active request " + req
318                        + " not same as calling request " + this);
319            }
320        }
321
322        /** @hide */
323        public void sendConfirmResult(boolean confirmed, Bundle result) {
324            try {
325                if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface
326                        + " confirmed=" + confirmed + " result=" + result);
327                finishRequest();
328                mCallback.deliverConfirmationResult(mInterface, confirmed, result);
329            } catch (RemoteException e) {
330            }
331        }
332
333        /** @hide */
334        public void sendPickOptionResult(boolean finished,
335                VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) {
336            try {
337                if (DEBUG) Log.d(TAG, "sendPickOptionResult: req=" + mInterface
338                        + " finished=" + finished + " selections=" + selections
339                        + " result=" + result);
340                if (finished) {
341                    finishRequest();
342                }
343                mCallback.deliverPickOptionResult(mInterface, finished, selections, result);
344            } catch (RemoteException e) {
345            }
346        }
347
348        /** @hide */
349        public void sendCompleteVoiceResult(Bundle result) {
350            try {
351                if (DEBUG) Log.d(TAG, "sendCompleteVoiceResult: req=" + mInterface
352                        + " result=" + result);
353                finishRequest();
354                mCallback.deliverCompleteVoiceResult(mInterface, result);
355            } catch (RemoteException e) {
356            }
357        }
358
359        /** @hide */
360        public void sendAbortVoiceResult(Bundle result) {
361            try {
362                if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface
363                        + " result=" + result);
364                finishRequest();
365                mCallback.deliverAbortVoiceResult(mInterface, result);
366            } catch (RemoteException e) {
367            }
368        }
369
370        /** @hide */
371        public void sendCommandResult(boolean finished, Bundle result) {
372            try {
373                if (DEBUG) Log.d(TAG, "sendCommandResult: req=" + mInterface
374                        + " result=" + result);
375                if (finished) {
376                    finishRequest();
377                }
378                mCallback.deliverCommandResult(mInterface, finished, result);
379            } catch (RemoteException e) {
380            }
381        }
382
383        /** @hide */
384        public void sendCancelResult() {
385            cancel();
386        }
387
388        /**
389         * ASk the app to cancelLocked this current request.
390         */
391        public void cancel() {
392            try {
393                if (DEBUG) Log.d(TAG, "sendCancelResult: req=" + mInterface);
394                finishRequest();
395                mCallback.deliverCancel(mInterface);
396            } catch (RemoteException e) {
397            }
398        }
399    }
400
401    /**
402     * A request for confirmation from the user of an operation, as per
403     * {@link android.app.VoiceInteractor.ConfirmationRequest
404     * VoiceInteractor.ConfirmationRequest}.
405     */
406    public static final class ConfirmationRequest extends Request {
407        final VoiceInteractor.Prompt mPrompt;
408
409        ConfirmationRequest(String packageName, int uid, IVoiceInteractorCallback callback,
410                VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras) {
411            super(packageName, uid, callback, session, extras);
412            mPrompt = prompt;
413        }
414
415        /**
416         * Return the prompt informing the user of what will happen, as per
417         * {@link android.app.VoiceInteractor.ConfirmationRequest
418         * VoiceInteractor.ConfirmationRequest}.
419         */
420        @Nullable
421        public VoiceInteractor.Prompt getVoicePrompt() {
422            return mPrompt;
423        }
424
425        /**
426         * Return the prompt informing the user of what will happen, as per
427         * {@link android.app.VoiceInteractor.ConfirmationRequest
428         * VoiceInteractor.ConfirmationRequest}.
429         * @deprecated Prefer {@link #getVoicePrompt()} which allows multiple voice prompts.
430         */
431        @Nullable
432        public CharSequence getPrompt() {
433            return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null);
434        }
435
436        /**
437         * Report that the voice interactor has confirmed the operation with the user, resulting
438         * in a call to
439         * {@link android.app.VoiceInteractor.ConfirmationRequest#onConfirmationResult
440         * VoiceInteractor.ConfirmationRequest.onConfirmationResult}.
441         */
442        public void sendConfirmationResult(boolean confirmed, Bundle result) {
443            sendConfirmResult(confirmed, result);
444        }
445    }
446
447    /**
448     * A request for the user to pick from a set of option, as per
449     * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
450     */
451    public static final class PickOptionRequest extends Request {
452        final VoiceInteractor.Prompt mPrompt;
453        final VoiceInteractor.PickOptionRequest.Option[] mOptions;
454
455        PickOptionRequest(String packageName, int uid, IVoiceInteractorCallback callback,
456                VoiceInteractionSession session, VoiceInteractor.Prompt prompt,
457                VoiceInteractor.PickOptionRequest.Option[] options, Bundle extras) {
458            super(packageName, uid, callback, session, extras);
459            mPrompt = prompt;
460            mOptions = options;
461        }
462
463        /**
464         * Return the prompt informing the user of what they are picking, as per
465         * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
466         */
467        @Nullable
468        public VoiceInteractor.Prompt getVoicePrompt() {
469            return mPrompt;
470        }
471
472        /**
473         * Return the prompt informing the user of what they are picking, as per
474         * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
475         * @deprecated Prefer {@link #getVoicePrompt()} which allows multiple voice prompts.
476         */
477        @Nullable
478        public CharSequence getPrompt() {
479            return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null);
480        }
481
482        /**
483         * Return the set of options the user is picking from, as per
484         * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
485         */
486        public VoiceInteractor.PickOptionRequest.Option[] getOptions() {
487            return mOptions;
488        }
489
490        /**
491         * Report an intermediate option selection from the request, without completing it (the
492         * request is still active and the app is waiting for the final option selection),
493         * resulting in a call to
494         * {@link android.app.VoiceInteractor.PickOptionRequest#onPickOptionResult
495         * VoiceInteractor.PickOptionRequest.onPickOptionResult} with false for finished.
496         */
497        public void sendIntermediatePickOptionResult(
498                VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) {
499            sendPickOptionResult(false, selections, result);
500        }
501
502        /**
503         * Report the final option selection for the request, completing the request
504         * and resulting in a call to
505         * {@link android.app.VoiceInteractor.PickOptionRequest#onPickOptionResult
506         * VoiceInteractor.PickOptionRequest.onPickOptionResult} with false for finished.
507         */
508        public void sendPickOptionResult(
509                VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) {
510            sendPickOptionResult(true, selections, result);
511        }
512    }
513
514    /**
515     * A request to simply inform the user that the voice operation has completed, as per
516     * {@link android.app.VoiceInteractor.CompleteVoiceRequest
517     * VoiceInteractor.CompleteVoiceRequest}.
518     */
519    public static final class CompleteVoiceRequest extends Request {
520        final VoiceInteractor.Prompt mPrompt;
521
522        CompleteVoiceRequest(String packageName, int uid, IVoiceInteractorCallback callback,
523                VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras) {
524            super(packageName, uid, callback, session, extras);
525            mPrompt = prompt;
526        }
527
528        /**
529         * Return the message informing the user of the completion, as per
530         * {@link android.app.VoiceInteractor.CompleteVoiceRequest
531         * VoiceInteractor.CompleteVoiceRequest}.
532         */
533        @Nullable
534        public VoiceInteractor.Prompt getVoicePrompt() {
535            return mPrompt;
536        }
537
538        /**
539         * Return the message informing the user of the completion, as per
540         * {@link android.app.VoiceInteractor.CompleteVoiceRequest
541         * VoiceInteractor.CompleteVoiceRequest}.
542         * @deprecated Prefer {@link #getVoicePrompt()} which allows a separate visual message.
543         */
544        @Nullable
545        public CharSequence getMessage() {
546            return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null);
547        }
548
549        /**
550         * Report that the voice interactor has finished completing the voice operation, resulting
551         * in a call to
552         * {@link android.app.VoiceInteractor.CompleteVoiceRequest#onCompleteResult
553         * VoiceInteractor.CompleteVoiceRequest.onCompleteResult}.
554         */
555        public void sendCompleteResult(Bundle result) {
556            sendCompleteVoiceResult(result);
557        }
558    }
559
560    /**
561     * A request to report that the current user interaction can not be completed with voice, as per
562     * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
563     */
564    public static final class AbortVoiceRequest extends Request {
565        final VoiceInteractor.Prompt mPrompt;
566
567        AbortVoiceRequest(String packageName, int uid, IVoiceInteractorCallback callback,
568                VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras) {
569            super(packageName, uid, callback, session, extras);
570            mPrompt = prompt;
571        }
572
573        /**
574         * Return the message informing the user of the problem, as per
575         * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
576         */
577        @Nullable
578        public VoiceInteractor.Prompt getVoicePrompt() {
579            return mPrompt;
580        }
581
582        /**
583         * Return the message informing the user of the problem, as per
584         * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
585         * @deprecated Prefer {@link #getVoicePrompt()} which allows a separate visual message.
586         */
587        @Nullable
588        public CharSequence getMessage() {
589            return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null);
590        }
591
592        /**
593         * Report that the voice interactor has finished aborting the voice operation, resulting
594         * in a call to
595         * {@link android.app.VoiceInteractor.AbortVoiceRequest#onAbortResult
596         * VoiceInteractor.AbortVoiceRequest.onAbortResult}.
597         */
598        public void sendAbortResult(Bundle result) {
599            sendAbortVoiceResult(result);
600        }
601    }
602
603    /**
604     * A generic vendor-specific request, as per
605     * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}.
606     */
607    public static final class CommandRequest extends Request {
608        final String mCommand;
609
610        CommandRequest(String packageName, int uid, IVoiceInteractorCallback callback,
611                VoiceInteractionSession session, String command, Bundle extras) {
612            super(packageName, uid, callback, session, extras);
613            mCommand = command;
614        }
615
616        /**
617         * Return the command that is being executed, as per
618         * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}.
619         */
620        public String getCommand() {
621            return mCommand;
622        }
623
624        /**
625         * Report an intermediate result of the request, without completing it (the request
626         * is still active and the app is waiting for the final result), resulting in a call to
627         * {@link android.app.VoiceInteractor.CommandRequest#onCommandResult
628         * VoiceInteractor.CommandRequest.onCommandResult} with false for isCompleted.
629         */
630        public void sendIntermediateResult(Bundle result) {
631            sendCommandResult(false, result);
632        }
633
634        /**
635         * Report the final result of the request, completing the request and resulting in a call to
636         * {@link android.app.VoiceInteractor.CommandRequest#onCommandResult
637         * VoiceInteractor.CommandRequest.onCommandResult} with true for isCompleted.
638         */
639        public void sendResult(Bundle result) {
640            sendCommandResult(true, result);
641        }
642    }
643
644    static final int MSG_START_CONFIRMATION = 1;
645    static final int MSG_START_PICK_OPTION = 2;
646    static final int MSG_START_COMPLETE_VOICE = 3;
647    static final int MSG_START_ABORT_VOICE = 4;
648    static final int MSG_START_COMMAND = 5;
649    static final int MSG_SUPPORTS_COMMANDS = 6;
650    static final int MSG_CANCEL = 7;
651
652    static final int MSG_TASK_STARTED = 100;
653    static final int MSG_TASK_FINISHED = 101;
654    static final int MSG_CLOSE_SYSTEM_DIALOGS = 102;
655    static final int MSG_DESTROY = 103;
656    static final int MSG_HANDLE_ASSIST = 104;
657    static final int MSG_HANDLE_SCREENSHOT = 105;
658    static final int MSG_SHOW = 106;
659    static final int MSG_HIDE = 107;
660
661    class MyCallbacks implements HandlerCaller.Callback, SoftInputWindow.Callback {
662        @Override
663        public void executeMessage(Message msg) {
664            SomeArgs args;
665            switch (msg.what) {
666                case MSG_START_CONFIRMATION:
667                    if (DEBUG) Log.d(TAG, "onConfirm: req=" + msg.obj);
668                    onRequestConfirmation((ConfirmationRequest) msg.obj);
669                    break;
670                case MSG_START_PICK_OPTION:
671                    if (DEBUG) Log.d(TAG, "onPickOption: req=" + msg.obj);
672                    onRequestPickOption((PickOptionRequest) msg.obj);
673                    break;
674                case MSG_START_COMPLETE_VOICE:
675                    if (DEBUG) Log.d(TAG, "onCompleteVoice: req=" + msg.obj);
676                    onRequestCompleteVoice((CompleteVoiceRequest) msg.obj);
677                    break;
678                case MSG_START_ABORT_VOICE:
679                    if (DEBUG) Log.d(TAG, "onAbortVoice: req=" + msg.obj);
680                    onRequestAbortVoice((AbortVoiceRequest) msg.obj);
681                    break;
682                case MSG_START_COMMAND:
683                    if (DEBUG) Log.d(TAG, "onCommand: req=" + msg.obj);
684                    onRequestCommand((CommandRequest) msg.obj);
685                    break;
686                case MSG_SUPPORTS_COMMANDS:
687                    args = (SomeArgs)msg.obj;
688                    if (DEBUG) Log.d(TAG, "onGetSupportedCommands: cmds=" + args.arg1);
689                    args.arg1 = onGetSupportedCommands((String[]) args.arg1);
690                    break;
691                case MSG_CANCEL:
692                    if (DEBUG) Log.d(TAG, "onCancel: req=" + ((Request)msg.obj));
693                    onCancelRequest((Request) msg.obj);
694                    break;
695                case MSG_TASK_STARTED:
696                    if (DEBUG) Log.d(TAG, "onTaskStarted: intent=" + msg.obj
697                            + " taskId=" + msg.arg1);
698                    onTaskStarted((Intent) msg.obj, msg.arg1);
699                    break;
700                case MSG_TASK_FINISHED:
701                    if (DEBUG) Log.d(TAG, "onTaskFinished: intent=" + msg.obj
702                            + " taskId=" + msg.arg1);
703                    onTaskFinished((Intent) msg.obj, msg.arg1);
704                    break;
705                case MSG_CLOSE_SYSTEM_DIALOGS:
706                    if (DEBUG) Log.d(TAG, "onCloseSystemDialogs");
707                    onCloseSystemDialogs();
708                    break;
709                case MSG_DESTROY:
710                    if (DEBUG) Log.d(TAG, "doDestroy");
711                    doDestroy();
712                    break;
713                case MSG_HANDLE_ASSIST:
714                    args = (SomeArgs)msg.obj;
715                    if (DEBUG) Log.d(TAG, "onHandleAssist: data=" + args.arg1
716                            + " structure=" + args.arg2 + " content=" + args.arg3);
717                    onHandleAssist((Bundle) args.arg1, (AssistStructure) args.arg2,
718                            (AssistContent) args.arg3);
719                    break;
720                case MSG_HANDLE_SCREENSHOT:
721                    if (DEBUG) Log.d(TAG, "onHandleScreenshot: " + msg.obj);
722                    onHandleScreenshot((Bitmap) msg.obj);
723                    break;
724                case MSG_SHOW:
725                    args = (SomeArgs)msg.obj;
726                    if (DEBUG) Log.d(TAG, "doShow: args=" + args.arg1
727                            + " flags=" + msg.arg1
728                            + " showCallback=" + args.arg2);
729                    doShow((Bundle) args.arg1, msg.arg1,
730                            (IVoiceInteractionSessionShowCallback) args.arg2);
731                    break;
732                case MSG_HIDE:
733                    if (DEBUG) Log.d(TAG, "doHide");
734                    doHide();
735                    break;
736            }
737        }
738
739        @Override
740        public void onBackPressed() {
741            VoiceInteractionSession.this.onBackPressed();
742        }
743    }
744
745    final MyCallbacks mCallbacks = new MyCallbacks();
746
747    /**
748     * Information about where interesting parts of the input method UI appear.
749     */
750    public static final class Insets {
751        /**
752         * This is the part of the UI that is the main content.  It is
753         * used to determine the basic space needed, to resize/pan the
754         * application behind.  It is assumed that this inset does not
755         * change very much, since any change will cause a full resize/pan
756         * of the application behind.  This value is relative to the top edge
757         * of the input method window.
758         */
759        public final Rect contentInsets = new Rect();
760
761        /**
762         * This is the region of the UI that is touchable.  It is used when
763         * {@link #touchableInsets} is set to {@link #TOUCHABLE_INSETS_REGION}.
764         * The region should be specified relative to the origin of the window frame.
765         */
766        public final Region touchableRegion = new Region();
767
768        /**
769         * Option for {@link #touchableInsets}: the entire window frame
770         * can be touched.
771         */
772        public static final int TOUCHABLE_INSETS_FRAME
773                = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
774
775        /**
776         * Option for {@link #touchableInsets}: the area inside of
777         * the content insets can be touched.
778         */
779        public static final int TOUCHABLE_INSETS_CONTENT
780                = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
781
782        /**
783         * Option for {@link #touchableInsets}: the region specified by
784         * {@link #touchableRegion} can be touched.
785         */
786        public static final int TOUCHABLE_INSETS_REGION
787                = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
788
789        /**
790         * Determine which area of the window is touchable by the user.  May
791         * be one of: {@link #TOUCHABLE_INSETS_FRAME},
792         * {@link #TOUCHABLE_INSETS_CONTENT}, or {@link #TOUCHABLE_INSETS_REGION}.
793         */
794        public int touchableInsets;
795    }
796
797    final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
798            new ViewTreeObserver.OnComputeInternalInsetsListener() {
799        public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
800            onComputeInsets(mTmpInsets);
801            info.contentInsets.set(mTmpInsets.contentInsets);
802            info.visibleInsets.set(mTmpInsets.contentInsets);
803            info.touchableRegion.set(mTmpInsets.touchableRegion);
804            info.setTouchableInsets(mTmpInsets.touchableInsets);
805        }
806    };
807
808    public VoiceInteractionSession(Context context) {
809        this(context, new Handler());
810    }
811
812    public VoiceInteractionSession(Context context, Handler handler) {
813        mContext = context;
814        mHandlerCaller = new HandlerCaller(context, handler.getLooper(),
815                mCallbacks, true);
816    }
817
818    public Context getContext() {
819        return mContext;
820    }
821
822    void addRequest(Request req) {
823        mActiveRequests.put(req.mInterface.asBinder(), req);
824    }
825
826    Request removeRequest(IBinder reqInterface) {
827        synchronized (this) {
828            return mActiveRequests.remove(reqInterface);
829        }
830    }
831
832    void doCreate(IVoiceInteractionManagerService service, IBinder token, Bundle args,
833            int startFlags) {
834        mSystemService = service;
835        mToken = token;
836        onCreate(args, startFlags);
837    }
838
839    void doShow(Bundle args, int flags, final IVoiceInteractionSessionShowCallback showCallback) {
840        if (DEBUG) Log.v(TAG, "Showing window: mWindowAdded=" + mWindowAdded
841                + " mWindowVisible=" + mWindowVisible);
842
843        if (mInShowWindow) {
844            Log.w(TAG, "Re-entrance in to showWindow");
845            return;
846        }
847
848        try {
849            mInShowWindow = true;
850            if (!mWindowVisible) {
851                if (!mWindowAdded) {
852                    mWindowAdded = true;
853                    View v = onCreateContentView();
854                    if (v != null) {
855                        setContentView(v);
856                    }
857                }
858            }
859            onShow(args, flags);
860            if (!mWindowVisible) {
861                mWindowVisible = true;
862                mWindow.show();
863            }
864            if (showCallback != null) {
865                mRootView.invalidate();
866                mRootView.getViewTreeObserver().addOnPreDrawListener(
867                        new ViewTreeObserver.OnPreDrawListener() {
868                            @Override
869                            public boolean onPreDraw() {
870                                mRootView.getViewTreeObserver().removeOnPreDrawListener(this);
871                                try {
872                                    showCallback.onShown();
873                                } catch (RemoteException e) {
874                                    Log.w(TAG, "Error calling onShown", e);
875                                }
876                                return true;
877                            }
878                        });
879            }
880        } finally {
881            mWindowWasVisible = true;
882            mInShowWindow = false;
883        }
884    }
885
886    void doHide() {
887        if (mWindowVisible) {
888            mWindow.hide();
889            mWindowVisible = false;
890            onHide();
891        }
892    }
893
894    void doDestroy() {
895        onDestroy();
896        if (mInitialized) {
897            mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
898                    mInsetsComputer);
899            if (mWindowAdded) {
900                mWindow.dismiss();
901                mWindowAdded = false;
902            }
903            mInitialized = false;
904        }
905    }
906
907    void initViews() {
908        mInitialized = true;
909
910        mThemeAttrs = mContext.obtainStyledAttributes(android.R.styleable.VoiceInteractionSession);
911        mRootView = mInflater.inflate(
912                com.android.internal.R.layout.voice_interaction_session, null);
913        mRootView.setSystemUiVisibility(
914                View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
915                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
916        mWindow.setContentView(mRootView);
917        mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
918
919        mContentFrame = (FrameLayout)mRootView.findViewById(android.R.id.content);
920    }
921
922    /** @hide */
923    public void show() {
924        show(null, 0);
925    }
926
927    /**
928     * Show the UI for this session.  This asks the system to go through the process of showing
929     * your UI, which will eventually culminate in {@link #onShow}.  This is similar to calling
930     * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
931     * @param args Arbitrary arguments that will be propagated {@link #onShow}.
932     * @param flags Indicates additional optional behavior that should be performed.  May
933     * be {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST}
934     * to request that the system generate and deliver assist data on the current foreground
935     * app as part of showing the session UI.
936     */
937    public void show(Bundle args, int flags) {
938        if (mToken == null) {
939            throw new IllegalStateException("Can't call before onCreate()");
940        }
941        try {
942            mSystemService.showSessionFromSession(mToken, args, flags);
943        } catch (RemoteException e) {
944        }
945    }
946
947    /**
948     * Hide the session's UI, if currently shown.  Call this when you are done with your
949     * user interaction.
950     */
951    public void hide() {
952        if (mToken == null) {
953            throw new IllegalStateException("Can't call before onCreate()");
954        }
955        try {
956            mSystemService.hideSessionFromSession(mToken);
957        } catch (RemoteException e) {
958        }
959    }
960
961    /** @hide */
962    public void showWindow() {
963    }
964
965    /** @hide */
966    public void hideWindow() {
967    }
968
969    /**
970     * You can call this to customize the theme used by your IME's window.
971     * This must be set before {@link #onCreate}, so you
972     * will typically call it in your constructor with the resource ID
973     * of your custom theme.
974     */
975    public void setTheme(int theme) {
976        if (mWindow != null) {
977            throw new IllegalStateException("Must be called before onCreate()");
978        }
979        mTheme = theme;
980    }
981
982    /**
983     * Ask that a new activity be started for voice interaction.  This will create a
984     * new dedicated task in the activity manager for this voice interaction session;
985     * this means that {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK}
986     * will be set for you to make it a new task.
987     *
988     * <p>The newly started activity will be displayed to the user in a special way, as
989     * a layer under the voice interaction UI.</p>
990     *
991     * <p>As the voice activity runs, it can retrieve a {@link android.app.VoiceInteractor}
992     * through which it can perform voice interactions through your session.  These requests
993     * for voice interactions will appear as callbacks on {@link #onGetSupportedCommands},
994     * {@link #onRequestConfirmation}, {@link #onRequestPickOption},
995     * {@link #onRequestCompleteVoice}, {@link #onRequestAbortVoice},
996     * or {@link #onRequestCommand}
997     *
998     * <p>You will receive a call to {@link #onTaskStarted} when the task starts up
999     * and {@link #onTaskFinished} when the last activity has finished.
1000     *
1001     * @param intent The Intent to start this voice interaction.  The given Intent will
1002     * always have {@link Intent#CATEGORY_VOICE Intent.CATEGORY_VOICE} added to it, since
1003     * this is part of a voice interaction.
1004     */
1005    public void startVoiceActivity(Intent intent) {
1006        if (mToken == null) {
1007            throw new IllegalStateException("Can't call before onCreate()");
1008        }
1009        try {
1010            intent.migrateExtraStreamToClipData();
1011            intent.prepareToLeaveProcess();
1012            int res = mSystemService.startVoiceActivity(mToken, intent,
1013                    intent.resolveType(mContext.getContentResolver()));
1014            Instrumentation.checkStartActivityResult(res, intent);
1015        } catch (RemoteException e) {
1016        }
1017    }
1018
1019    /**
1020     * Set whether this session will keep the device awake while it is running a voice
1021     * activity.  By default, the system holds a wake lock for it while in this state,
1022     * so that it can work even if the screen is off.  Setting this to false removes that
1023     * wake lock, allowing the CPU to go to sleep.  This is typically used if the
1024     * session decides it has been waiting too long for a response from the user and
1025     * doesn't want to let this continue to drain the battery.
1026     *
1027     * <p>Passing false here will release the wake lock, and you can call later with
1028     * true to re-acquire it.  It will also be automatically re-acquired for you each
1029     * time you start a new voice activity task -- that is when you call
1030     * {@link #startVoiceActivity}.</p>
1031     */
1032    public void setKeepAwake(boolean keepAwake) {
1033        if (mToken == null) {
1034            throw new IllegalStateException("Can't call before onCreate()");
1035        }
1036        try {
1037            mSystemService.setKeepAwake(mToken, keepAwake);
1038        } catch (RemoteException e) {
1039        }
1040    }
1041
1042    /**
1043     * Convenience for inflating views.
1044     */
1045    public LayoutInflater getLayoutInflater() {
1046        return mInflater;
1047    }
1048
1049    /**
1050     * Retrieve the window being used to show the session's UI.
1051     */
1052    public Dialog getWindow() {
1053        return mWindow;
1054    }
1055
1056    /**
1057     * Finish the session.  This completely destroys the session -- the next time it is shown,
1058     * an entirely new one will be created.  You do not normally call this function; instead,
1059     * use {@link #hide} and allow the system to destroy your session if it needs its RAM.
1060     */
1061    public void finish() {
1062        if (mToken == null) {
1063            throw new IllegalStateException("Can't call before onCreate()");
1064        }
1065        hideWindow();
1066        try {
1067            mSystemService.finish(mToken);
1068        } catch (RemoteException e) {
1069        }
1070    }
1071
1072    /**
1073     * Initiatize a new session.  At this point you don't know exactly what this
1074     * session will be used for; you will find that out in {@link #onShow}.
1075     */
1076    public void onCreate() {
1077        doOnCreate();
1078    }
1079
1080    /** @hide */
1081    public void onCreate(Bundle args) {
1082        doOnCreate();
1083    }
1084
1085    /** @hide */
1086    public void onCreate(Bundle args, int showFlags) {
1087        doOnCreate();
1088    }
1089
1090    private void doOnCreate() {
1091        mTheme = mTheme != 0 ? mTheme
1092                : com.android.internal.R.style.Theme_DeviceDefault_VoiceInteractionSession;
1093        mInflater = (LayoutInflater)mContext.getSystemService(
1094                Context.LAYOUT_INFLATER_SERVICE);
1095        mWindow = new SoftInputWindow(mContext, "VoiceInteractionSession", mTheme,
1096                mCallbacks, this, mDispatcherState,
1097                WindowManager.LayoutParams.TYPE_VOICE_INTERACTION, Gravity.BOTTOM, true);
1098        mWindow.getWindow().addFlags(
1099                WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED |
1100                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
1101                WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
1102        initViews();
1103        mWindow.getWindow().setLayout(MATCH_PARENT, MATCH_PARENT);
1104        mWindow.setToken(mToken);
1105    }
1106
1107    /**
1108     * Called when the session UI is going to be shown.  This is called after
1109     * {@link #onCreateContentView} (if the session's content UI needed to be created) and
1110     * immediately prior to the window being shown.  This may be called while the window
1111     * is already shown, if a show request has come in while it is shown, to allow you to
1112     * update the UI to match the new show arguments.
1113     *
1114     * @param args The arguments that were supplied to
1115     * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
1116     * @param showFlags The show flags originally provided to
1117     * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
1118     */
1119    public void onShow(Bundle args, int showFlags) {
1120    }
1121
1122    /**
1123     * Called immediately after stopping to show the session UI.
1124     */
1125    public void onHide() {
1126    }
1127
1128    /**
1129     * Last callback to the session as it is being finished.
1130     */
1131    public void onDestroy() {
1132    }
1133
1134    /**
1135     * Hook in which to create the session's UI.
1136     */
1137    public View onCreateContentView() {
1138        return null;
1139    }
1140
1141    public void setContentView(View view) {
1142        mContentFrame.removeAllViews();
1143        mContentFrame.addView(view, new FrameLayout.LayoutParams(
1144                ViewGroup.LayoutParams.MATCH_PARENT,
1145                ViewGroup.LayoutParams.MATCH_PARENT));
1146        mContentFrame.requestApplyInsets();
1147    }
1148
1149    public void onHandleAssist(Bundle data, AssistStructure structure, AssistContent content) {
1150    }
1151
1152    public void onHandleScreenshot(Bitmap screenshot) {
1153    }
1154
1155    public boolean onKeyDown(int keyCode, KeyEvent event) {
1156        return false;
1157    }
1158
1159    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
1160        return false;
1161    }
1162
1163    public boolean onKeyUp(int keyCode, KeyEvent event) {
1164        return false;
1165    }
1166
1167    public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
1168        return false;
1169    }
1170
1171    /**
1172     * Called when the user presses the back button while focus is in the session UI.  Note
1173     * that this will only happen if the session UI has requested input focus in its window;
1174     * otherwise, the back key will go to whatever window has focus and do whatever behavior
1175     * it normally has there.  The default implementation simply calls {@link #hide}.
1176     */
1177    public void onBackPressed() {
1178        hide();
1179    }
1180
1181    /**
1182     * Sessions automatically watch for requests that all system UI be closed (such as when
1183     * the user presses HOME), which will appear here.  The default implementation always
1184     * calls {@link #hide}.
1185     */
1186    public void onCloseSystemDialogs() {
1187        hide();
1188    }
1189
1190    @Override
1191    public void onConfigurationChanged(Configuration newConfig) {
1192    }
1193
1194    @Override
1195    public void onLowMemory() {
1196    }
1197
1198    @Override
1199    public void onTrimMemory(int level) {
1200    }
1201
1202    /**
1203     * Compute the interesting insets into your UI.  The default implementation
1204     * sets {@link Insets#contentInsets outInsets.contentInsets.top} to the height
1205     * of the window, meaning it should not adjust content underneath.  The default touchable
1206     * insets are {@link Insets#TOUCHABLE_INSETS_FRAME}, meaning it consumes all touch
1207     * events within its window frame.
1208     *
1209     * @param outInsets Fill in with the current UI insets.
1210     */
1211    public void onComputeInsets(Insets outInsets) {
1212        outInsets.contentInsets.left = 0;
1213        outInsets.contentInsets.bottom = 0;
1214        outInsets.contentInsets.right = 0;
1215        View decor = getWindow().getWindow().getDecorView();
1216        outInsets.contentInsets.top = decor.getHeight();
1217        outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_FRAME;
1218        outInsets.touchableRegion.setEmpty();
1219    }
1220
1221    /**
1222     * Called when a task initiated by {@link #startVoiceActivity(android.content.Intent)}
1223     * has actually started.
1224     *
1225     * @param intent The original {@link Intent} supplied to
1226     * {@link #startVoiceActivity(android.content.Intent)}.
1227     * @param taskId Unique ID of the now running task.
1228     */
1229    public void onTaskStarted(Intent intent, int taskId) {
1230    }
1231
1232    /**
1233     * Called when the last activity of a task initiated by
1234     * {@link #startVoiceActivity(android.content.Intent)} has finished.  The default
1235     * implementation calls {@link #finish()} on the assumption that this represents
1236     * the completion of a voice action.  You can override the implementation if you would
1237     * like a different behavior.
1238     *
1239     * @param intent The original {@link Intent} supplied to
1240     * {@link #startVoiceActivity(android.content.Intent)}.
1241     * @param taskId Unique ID of the finished task.
1242     */
1243    public void onTaskFinished(Intent intent, int taskId) {
1244        hide();
1245    }
1246
1247    /** @hide */
1248    public boolean[] onGetSupportedCommands(Caller caller, String[] commands) {
1249        return new boolean[commands.length];
1250    }
1251    /** @hide */
1252    public void onConfirm(Caller caller, Request request, CharSequence prompt,
1253            Bundle extras) {
1254    }
1255    /** @hide */
1256    public void onPickOption(Caller caller, Request request, CharSequence prompt,
1257            VoiceInteractor.PickOptionRequest.Option[] options, Bundle extras) {
1258    }
1259    /** @hide */
1260    public void onCompleteVoice(Caller caller, Request request, CharSequence message,
1261           Bundle extras) {
1262        request.sendCompleteVoiceResult(null);
1263    }
1264    /** @hide */
1265    public void onAbortVoice(Caller caller, Request request, CharSequence message, Bundle extras) {
1266        request.sendAbortVoiceResult(null);
1267    }
1268    /** @hide */
1269    public void onCommand(Caller caller, Request request, String command, Bundle extras) {
1270    }
1271    /** @hide */
1272    public void onCancel(Request request) {
1273    }
1274
1275    /**
1276     * Request to query for what extended commands the session supports.
1277     *
1278     * @param commands An array of commands that are being queried.
1279     * @return Return an array of booleans indicating which of each entry in the
1280     * command array is supported.  A true entry in the array indicates the command
1281     * is supported; false indicates it is not.  The default implementation returns
1282     * an array of all false entries.
1283     */
1284    public boolean[] onGetSupportedCommands(String[] commands) {
1285        return onGetSupportedCommands(new Caller(), commands);
1286    }
1287
1288    /**
1289     * Request to confirm with the user before proceeding with an unrecoverable operation,
1290     * corresponding to a {@link android.app.VoiceInteractor.ConfirmationRequest
1291     * VoiceInteractor.ConfirmationRequest}.
1292     *
1293     * @param request The active request.
1294     */
1295    public void onRequestConfirmation(ConfirmationRequest request) {
1296        onConfirm(request, request, request.getPrompt(), request.getExtras());
1297    }
1298
1299    /**
1300     * Request for the user to pick one of N options, corresponding to a
1301     * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
1302     *
1303     * @param request The active request.
1304     */
1305    public void onRequestPickOption(PickOptionRequest request) {
1306        onPickOption(request, request, request.getPrompt(), request.getOptions(),
1307                request.getExtras());
1308    }
1309
1310    /**
1311     * Request to complete the voice interaction session because the voice activity successfully
1312     * completed its interaction using voice.  Corresponds to
1313     * {@link android.app.VoiceInteractor.CompleteVoiceRequest
1314     * VoiceInteractor.CompleteVoiceRequest}.  The default implementation just sends an empty
1315     * confirmation back to allow the activity to exit.
1316     *
1317     * @param request The active request.
1318     */
1319    public void onRequestCompleteVoice(CompleteVoiceRequest request) {
1320        onCompleteVoice(request, request, request.getMessage(), request.getExtras());
1321    }
1322
1323    /**
1324     * Request to abort the voice interaction session because the voice activity can not
1325     * complete its interaction using voice.  Corresponds to
1326     * {@link android.app.VoiceInteractor.AbortVoiceRequest
1327     * VoiceInteractor.AbortVoiceRequest}.  The default implementation just sends an empty
1328     * confirmation back to allow the activity to exit.
1329     *
1330     * @param request The active request.
1331     */
1332    public void onRequestAbortVoice(AbortVoiceRequest request) {
1333        onAbortVoice(request, request, request.getMessage(), request.getExtras());
1334    }
1335
1336    /**
1337     * Process an arbitrary extended command from the caller,
1338     * corresponding to a {@link android.app.VoiceInteractor.CommandRequest
1339     * VoiceInteractor.CommandRequest}.
1340     *
1341     * @param request The active request.
1342     */
1343    public void onRequestCommand(CommandRequest request) {
1344        onCommand(request, request, request.getCommand(), request.getExtras());
1345    }
1346
1347    /**
1348     * Called when the {@link android.app.VoiceInteractor} has asked to cancelLocked a {@link Request}
1349     * that was previously delivered to {@link #onRequestConfirmation},
1350     * {@link #onRequestPickOption}, {@link #onRequestCompleteVoice}, {@link #onRequestAbortVoice},
1351     * or {@link #onRequestCommand}.
1352     *
1353     * @param request The request that is being canceled.
1354     */
1355    public void onCancelRequest(Request request) {
1356        onCancel(request);
1357    }
1358}
1359