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