VoiceInteractionSession.java revision 9a35d78cad1a4cf9639053c24139147e658ec055
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.app.Dialog;
20import android.app.Instrumentation;
21import android.content.Context;
22import android.content.Intent;
23import android.content.res.TypedArray;
24import android.graphics.Rect;
25import android.graphics.Region;
26import android.inputmethodservice.SoftInputWindow;
27import android.os.Binder;
28import android.os.Bundle;
29import android.os.Handler;
30import android.os.IBinder;
31import android.os.Message;
32import android.os.RemoteException;
33import android.util.ArrayMap;
34import android.util.Log;
35import android.view.Gravity;
36import android.view.KeyEvent;
37import android.view.LayoutInflater;
38import android.view.View;
39import android.view.ViewGroup;
40import android.view.ViewTreeObserver;
41import android.view.WindowManager;
42import android.widget.FrameLayout;
43import com.android.internal.app.IVoiceInteractionManagerService;
44import com.android.internal.app.IVoiceInteractor;
45import com.android.internal.app.IVoiceInteractorCallback;
46import com.android.internal.app.IVoiceInteractorRequest;
47import com.android.internal.os.HandlerCaller;
48import com.android.internal.os.SomeArgs;
49
50import java.lang.ref.WeakReference;
51
52import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
53import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
54
55/**
56 * An active voice interaction session, providing a facility for the implementation
57 * to interact with the user in the voice interaction layer.  This interface is no shown
58 * by default, but you can request that it be shown with {@link #showWindow()}, which
59 * will result in a later call to {@link #onCreateContentView()} in which the UI can be
60 * built
61 *
62 * <p>A voice interaction session can be self-contained, ultimately calling {@link #finish}
63 * when done.  It can also initiate voice interactions with applications by calling
64 * {@link #startVoiceActivity}</p>.
65 */
66public abstract class VoiceInteractionSession implements KeyEvent.Callback {
67    static final String TAG = "VoiceInteractionSession";
68    static final boolean DEBUG = true;
69
70    final Context mContext;
71    final HandlerCaller mHandlerCaller;
72
73    final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState();
74
75    IVoiceInteractionManagerService mSystemService;
76    IBinder mToken;
77
78    int mTheme = 0;
79    LayoutInflater mInflater;
80    TypedArray mThemeAttrs;
81    View mRootView;
82    FrameLayout mContentFrame;
83    SoftInputWindow mWindow;
84
85    boolean mInitialized;
86    boolean mWindowAdded;
87    boolean mWindowVisible;
88    boolean mWindowWasVisible;
89    boolean mInShowWindow;
90
91    final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<IBinder, Request>();
92
93    final Insets mTmpInsets = new Insets();
94    final int[] mTmpLocation = new int[2];
95
96    final WeakReference<VoiceInteractionSession> mWeakRef
97            = new WeakReference<VoiceInteractionSession>(this);
98
99    final IVoiceInteractor mInteractor = new IVoiceInteractor.Stub() {
100        @Override
101        public IVoiceInteractorRequest startConfirmation(String callingPackage,
102                IVoiceInteractorCallback callback, CharSequence prompt, Bundle extras) {
103            Request request = newRequest(callback);
104            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_CONFIRMATION,
105                    new Caller(callingPackage, Binder.getCallingUid()), request,
106                    prompt, extras));
107            return request.mInterface;
108        }
109
110        @Override
111        public IVoiceInteractorRequest startCompleteVoice(String callingPackage,
112                IVoiceInteractorCallback callback, CharSequence message, Bundle extras) {
113            Request request = newRequest(callback);
114            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_COMPLETE_VOICE,
115                    new Caller(callingPackage, Binder.getCallingUid()), request,
116                    message, extras));
117            return request.mInterface;
118        }
119
120        @Override
121        public IVoiceInteractorRequest startAbortVoice(String callingPackage,
122                IVoiceInteractorCallback callback, CharSequence message, Bundle extras) {
123            Request request = newRequest(callback);
124            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_ABORT_VOICE,
125                    new Caller(callingPackage, Binder.getCallingUid()), request,
126                    message, extras));
127            return request.mInterface;
128        }
129
130        @Override
131        public IVoiceInteractorRequest startCommand(String callingPackage,
132                IVoiceInteractorCallback callback, String command, Bundle extras) {
133            Request request = newRequest(callback);
134            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_COMMAND,
135                    new Caller(callingPackage, Binder.getCallingUid()), request,
136                    command, extras));
137            return request.mInterface;
138        }
139
140        @Override
141        public boolean[] supportsCommands(String callingPackage, String[] commands) {
142            Message msg = mHandlerCaller.obtainMessageIOO(MSG_SUPPORTS_COMMANDS,
143                    0, new Caller(callingPackage, Binder.getCallingUid()), commands);
144            SomeArgs args = mHandlerCaller.sendMessageAndWait(msg);
145            if (args != null) {
146                boolean[] res = (boolean[])args.arg1;
147                args.recycle();
148                return res;
149            }
150            return new boolean[commands.length];
151        }
152    };
153
154    final IVoiceInteractionSession mSession = new IVoiceInteractionSession.Stub() {
155        @Override
156        public void taskStarted(Intent intent, int taskId) {
157            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_TASK_STARTED,
158                    taskId, intent));
159        }
160
161        @Override
162        public void taskFinished(Intent intent, int taskId) {
163            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_TASK_FINISHED,
164                    taskId, intent));
165        }
166
167        @Override
168        public void closeSystemDialogs() {
169            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_CLOSE_SYSTEM_DIALOGS));
170        }
171
172        @Override
173        public void destroy() {
174            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_DESTROY));
175        }
176    };
177
178    public static class Request {
179        final IVoiceInteractorRequest mInterface = new IVoiceInteractorRequest.Stub() {
180            @Override
181            public void cancel() throws RemoteException {
182                VoiceInteractionSession session = mSession.get();
183                if (session != null) {
184                    session.mHandlerCaller.sendMessage(
185                            session.mHandlerCaller.obtainMessageO(MSG_CANCEL, Request.this));
186                }
187            }
188        };
189        final IVoiceInteractorCallback mCallback;
190        final WeakReference<VoiceInteractionSession> mSession;
191
192        Request(IVoiceInteractorCallback callback, VoiceInteractionSession session) {
193            mCallback = callback;
194            mSession = session.mWeakRef;
195        }
196
197        void finishRequest() {
198            VoiceInteractionSession session = mSession.get();
199            if (session == null) {
200                throw new IllegalStateException("VoiceInteractionSession has been destroyed");
201            }
202            Request req = session.removeRequest(mInterface.asBinder());
203            if (req == null) {
204                throw new IllegalStateException("Request not active: " + this);
205            } else if (req != this) {
206                throw new IllegalStateException("Current active request " + req
207                        + " not same as calling request " + this);
208            }
209        }
210
211        public void sendConfirmResult(boolean confirmed, Bundle result) {
212            try {
213                if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface
214                        + " confirmed=" + confirmed + " result=" + result);
215                finishRequest();
216                mCallback.deliverConfirmationResult(mInterface, confirmed, result);
217            } catch (RemoteException e) {
218            }
219        }
220
221        public void sendCompleteVoiceResult(Bundle result) {
222            try {
223                if (DEBUG) Log.d(TAG, "sendCompleteVoiceResult: req=" + mInterface
224                        + " result=" + result);
225                finishRequest();
226                mCallback.deliverCompleteVoiceResult(mInterface, result);
227            } catch (RemoteException e) {
228            }
229        }
230
231        public void sendAbortVoiceResult(Bundle result) {
232            try {
233                if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface
234                        + " result=" + result);
235                finishRequest();
236                mCallback.deliverAbortVoiceResult(mInterface, result);
237            } catch (RemoteException e) {
238            }
239        }
240
241        public void sendCommandResult(boolean complete, Bundle result) {
242            try {
243                if (DEBUG) Log.d(TAG, "sendCommandResult: req=" + mInterface
244                        + " result=" + result);
245                finishRequest();
246                mCallback.deliverCommandResult(mInterface, complete, result);
247            } catch (RemoteException e) {
248            }
249        }
250
251        public void sendCancelResult() {
252            try {
253                if (DEBUG) Log.d(TAG, "sendCancelResult: req=" + mInterface);
254                finishRequest();
255                mCallback.deliverCancel(mInterface);
256            } catch (RemoteException e) {
257            }
258        }
259    }
260
261    public static class Caller {
262        final String packageName;
263        final int uid;
264
265        Caller(String _packageName, int _uid) {
266            packageName = _packageName;
267            uid = _uid;
268        }
269    }
270
271    static final int MSG_START_CONFIRMATION = 1;
272    static final int MSG_START_COMPLETE_VOICE = 2;
273    static final int MSG_START_ABORT_VOICE = 3;
274    static final int MSG_START_COMMAND = 4;
275    static final int MSG_SUPPORTS_COMMANDS = 5;
276    static final int MSG_CANCEL = 6;
277
278    static final int MSG_TASK_STARTED = 100;
279    static final int MSG_TASK_FINISHED = 101;
280    static final int MSG_CLOSE_SYSTEM_DIALOGS = 102;
281    static final int MSG_DESTROY = 103;
282
283    class MyCallbacks implements HandlerCaller.Callback, SoftInputWindow.Callback {
284        @Override
285        public void executeMessage(Message msg) {
286            SomeArgs args;
287            switch (msg.what) {
288                case MSG_START_CONFIRMATION:
289                    args = (SomeArgs)msg.obj;
290                    if (DEBUG) Log.d(TAG, "onConfirm: req=" + ((Request) args.arg2).mInterface
291                            + " prompt=" + args.arg3 + " extras=" + args.arg4);
292                    onConfirm((Caller)args.arg1, (Request)args.arg2, (CharSequence)args.arg3,
293                            (Bundle)args.arg4);
294                    break;
295                case MSG_START_COMPLETE_VOICE:
296                    args = (SomeArgs)msg.obj;
297                    if (DEBUG) Log.d(TAG, "onCompleteVoice: req=" + ((Request) args.arg2).mInterface
298                            + " message=" + args.arg3 + " extras=" + args.arg4);
299                    onCompleteVoice((Caller) args.arg1, (Request) args.arg2,
300                            (CharSequence) args.arg3, (Bundle) args.arg4);
301                    break;
302                case MSG_START_ABORT_VOICE:
303                    args = (SomeArgs)msg.obj;
304                    if (DEBUG) Log.d(TAG, "onAbortVoice: req=" + ((Request) args.arg2).mInterface
305                            + " message=" + args.arg3 + " extras=" + args.arg4);
306                    onAbortVoice((Caller) args.arg1, (Request) args.arg2, (CharSequence) args.arg3,
307                            (Bundle) args.arg4);
308                    break;
309                case MSG_START_COMMAND:
310                    args = (SomeArgs)msg.obj;
311                    if (DEBUG) Log.d(TAG, "onCommand: req=" + ((Request) args.arg2).mInterface
312                            + " command=" + args.arg3 + " extras=" + args.arg4);
313                    onCommand((Caller) args.arg1, (Request) args.arg2, (String) args.arg3,
314                            (Bundle) args.arg4);
315                    break;
316                case MSG_SUPPORTS_COMMANDS:
317                    args = (SomeArgs)msg.obj;
318                    if (DEBUG) Log.d(TAG, "onGetSupportedCommands: cmds=" + args.arg2);
319                    args.arg1 = onGetSupportedCommands((Caller) args.arg1, (String[]) args.arg2);
320                    break;
321                case MSG_CANCEL:
322                    args = (SomeArgs)msg.obj;
323                    if (DEBUG) Log.d(TAG, "onCancel: req=" + ((Request) args.arg1).mInterface);
324                    onCancel((Request)args.arg1);
325                    break;
326                case MSG_TASK_STARTED:
327                    if (DEBUG) Log.d(TAG, "onTaskStarted: intent=" + msg.obj
328                            + " taskId=" + msg.arg1);
329                    onTaskStarted((Intent) msg.obj, msg.arg1);
330                    break;
331                case MSG_TASK_FINISHED:
332                    if (DEBUG) Log.d(TAG, "onTaskFinished: intent=" + msg.obj
333                            + " taskId=" + msg.arg1);
334                    onTaskFinished((Intent) msg.obj, msg.arg1);
335                    break;
336                case MSG_CLOSE_SYSTEM_DIALOGS:
337                    if (DEBUG) Log.d(TAG, "onCloseSystemDialogs");
338                    onCloseSystemDialogs();
339                    break;
340                case MSG_DESTROY:
341                    if (DEBUG) Log.d(TAG, "doDestroy");
342                    doDestroy();
343                    break;
344            }
345        }
346
347        @Override
348        public void onBackPressed() {
349            VoiceInteractionSession.this.onBackPressed();
350        }
351    }
352
353    final MyCallbacks mCallbacks = new MyCallbacks();
354
355    /**
356     * Information about where interesting parts of the input method UI appear.
357     */
358    public static final class Insets {
359        /**
360         * This is the part of the UI that is the main content.  It is
361         * used to determine the basic space needed, to resize/pan the
362         * application behind.  It is assumed that this inset does not
363         * change very much, since any change will cause a full resize/pan
364         * of the application behind.  This value is relative to the top edge
365         * of the input method window.
366         */
367        public final Rect contentInsets = new Rect();
368
369        /**
370         * This is the region of the UI that is touchable.  It is used when
371         * {@link #touchableInsets} is set to {@link #TOUCHABLE_INSETS_REGION}.
372         * The region should be specified relative to the origin of the window frame.
373         */
374        public final Region touchableRegion = new Region();
375
376        /**
377         * Option for {@link #touchableInsets}: the entire window frame
378         * can be touched.
379         */
380        public static final int TOUCHABLE_INSETS_FRAME
381                = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
382
383        /**
384         * Option for {@link #touchableInsets}: the area inside of
385         * the content insets can be touched.
386         */
387        public static final int TOUCHABLE_INSETS_CONTENT
388                = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
389
390        /**
391         * Option for {@link #touchableInsets}: the region specified by
392         * {@link #touchableRegion} can be touched.
393         */
394        public static final int TOUCHABLE_INSETS_REGION
395                = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
396
397        /**
398         * Determine which area of the window is touchable by the user.  May
399         * be one of: {@link #TOUCHABLE_INSETS_FRAME},
400         * {@link #TOUCHABLE_INSETS_CONTENT}, or {@link #TOUCHABLE_INSETS_REGION}.
401         */
402        public int touchableInsets;
403    }
404
405    final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
406            new ViewTreeObserver.OnComputeInternalInsetsListener() {
407        public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
408            onComputeInsets(mTmpInsets);
409            info.contentInsets.set(mTmpInsets.contentInsets);
410            info.visibleInsets.set(mTmpInsets.contentInsets);
411            info.touchableRegion.set(mTmpInsets.touchableRegion);
412            info.setTouchableInsets(mTmpInsets.touchableInsets);
413        }
414    };
415
416    public VoiceInteractionSession(Context context) {
417        this(context, new Handler());
418    }
419
420    public VoiceInteractionSession(Context context, Handler handler) {
421        mContext = context;
422        mHandlerCaller = new HandlerCaller(context, handler.getLooper(),
423                mCallbacks, true);
424    }
425
426    Request newRequest(IVoiceInteractorCallback callback) {
427        synchronized (this) {
428            Request req = new Request(callback, this);
429            mActiveRequests.put(req.mInterface.asBinder(), req);
430            return req;
431        }
432    }
433
434    Request removeRequest(IBinder reqInterface) {
435        synchronized (this) {
436            Request req = mActiveRequests.get(reqInterface);
437            if (req != null) {
438                mActiveRequests.remove(req);
439            }
440            return req;
441        }
442    }
443
444    void doCreate(IVoiceInteractionManagerService service, IBinder token, Bundle args) {
445        mSystemService = service;
446        mToken = token;
447        onCreate(args);
448    }
449
450    void doDestroy() {
451        onDestroy();
452        if (mInitialized) {
453            mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
454                    mInsetsComputer);
455            if (mWindowAdded) {
456                mWindow.dismiss();
457                mWindowAdded = false;
458            }
459            mInitialized = false;
460        }
461    }
462
463    void initViews() {
464        mInitialized = true;
465
466        mThemeAttrs = mContext.obtainStyledAttributes(android.R.styleable.VoiceInteractionSession);
467        mRootView = mInflater.inflate(
468                com.android.internal.R.layout.voice_interaction_session, null);
469        mRootView.setSystemUiVisibility(
470                View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
471        mWindow.setContentView(mRootView);
472        mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
473
474        mContentFrame = (FrameLayout)mRootView.findViewById(android.R.id.content);
475    }
476
477    public void showWindow() {
478        if (DEBUG) Log.v(TAG, "Showing window: mWindowAdded=" + mWindowAdded
479                + " mWindowVisible=" + mWindowVisible);
480
481        if (mInShowWindow) {
482            Log.w(TAG, "Re-entrance in to showWindow");
483            return;
484        }
485
486        try {
487            mInShowWindow = true;
488            if (!mWindowVisible) {
489                mWindowVisible = true;
490                if (!mWindowAdded) {
491                    mWindowAdded = true;
492                    View v = onCreateContentView();
493                    if (v != null) {
494                        setContentView(v);
495                    }
496                }
497                mWindow.show();
498            }
499        } finally {
500            mWindowWasVisible = true;
501            mInShowWindow = false;
502        }
503    }
504
505    public void hideWindow() {
506        if (mWindowVisible) {
507            mWindow.hide();
508            mWindowVisible = false;
509        }
510    }
511
512    /**
513     * You can call this to customize the theme used by your IME's window.
514     * This must be set before {@link #onCreate}, so you
515     * will typically call it in your constructor with the resource ID
516     * of your custom theme.
517     */
518    public void setTheme(int theme) {
519        if (mWindow != null) {
520            throw new IllegalStateException("Must be called before onCreate()");
521        }
522        mTheme = theme;
523    }
524
525    /**
526     * Ask that a new activity be started for voice interaction.  This will create a
527     * new dedicated task in the activity manager for this voice interaction session;
528     * this means that {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK}
529     * will be set for you to make it a new task.
530     *
531     * <p>The newly started activity will be displayed to the user in a special way, as
532     * a layer under the voice interaction UI.</p>
533     *
534     * <p>As the voice activity runs, it can retrieve a {@link android.app.VoiceInteractor}
535     * through which it can perform voice interactions through your session.  These requests
536     * for voice interactions will appear as callbacks on {@link #onGetSupportedCommands},
537     * {@link #onConfirm}, {@link #onCommand}, and {@link #onCancel}.
538     *
539     * <p>You will receive a call to {@link #onTaskStarted} when the task starts up
540     * and {@link #onTaskFinished} when the last activity has finished.
541     *
542     * @param intent The Intent to start this voice interaction.  The given Intent will
543     * always have {@link Intent#CATEGORY_VOICE Intent.CATEGORY_VOICE} added to it, since
544     * this is part of a voice interaction.
545     */
546    public void startVoiceActivity(Intent intent) {
547        if (mToken == null) {
548            throw new IllegalStateException("Can't call before onCreate()");
549        }
550        try {
551            intent.migrateExtraStreamToClipData();
552            intent.prepareToLeaveProcess();
553            int res = mSystemService.startVoiceActivity(mToken, intent,
554                    intent.resolveType(mContext.getContentResolver()));
555            Instrumentation.checkStartActivityResult(res, intent);
556        } catch (RemoteException e) {
557        }
558    }
559
560    /**
561     * Convenience for inflating views.
562     */
563    public LayoutInflater getLayoutInflater() {
564        return mInflater;
565    }
566
567    /**
568     * Retrieve the window being used to show the session's UI.
569     */
570    public Dialog getWindow() {
571        return mWindow;
572    }
573
574    /**
575     * Finish the session.
576     */
577    public void finish() {
578        if (mToken == null) {
579            throw new IllegalStateException("Can't call before onCreate()");
580        }
581        hideWindow();
582        try {
583            mSystemService.finish(mToken);
584        } catch (RemoteException e) {
585        }
586    }
587
588    /**
589     * Initiatize a new session.
590     *
591     * @param args The arguments that were supplied to
592     * {@link VoiceInteractionService#startSession VoiceInteractionService.startSession}.
593     */
594    public void onCreate(Bundle args) {
595        mTheme = mTheme != 0 ? mTheme
596                : com.android.internal.R.style.Theme_DeviceDefault_VoiceInteractionSession;
597        mInflater = (LayoutInflater)mContext.getSystemService(
598                Context.LAYOUT_INFLATER_SERVICE);
599        mWindow = new SoftInputWindow(mContext, "VoiceInteractionSession", mTheme,
600                mCallbacks, this, mDispatcherState,
601                WindowManager.LayoutParams.TYPE_VOICE_INTERACTION, Gravity.TOP, true);
602        mWindow.getWindow().addFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
603        initViews();
604        mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
605        mWindow.setToken(mToken);
606    }
607
608    /**
609     * Last callback to the session as it is being finished.
610     */
611    public void onDestroy() {
612    }
613
614    /**
615     * Hook in which to create the session's UI.
616     */
617    public View onCreateContentView() {
618        return null;
619    }
620
621    public void setContentView(View view) {
622        mContentFrame.removeAllViews();
623        mContentFrame.addView(view, new FrameLayout.LayoutParams(
624                ViewGroup.LayoutParams.MATCH_PARENT,
625                ViewGroup.LayoutParams.WRAP_CONTENT));
626
627    }
628
629    public boolean onKeyDown(int keyCode, KeyEvent event) {
630        return false;
631    }
632
633    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
634        return false;
635    }
636
637    public boolean onKeyUp(int keyCode, KeyEvent event) {
638        return false;
639    }
640
641    public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
642        return false;
643    }
644
645    public void onBackPressed() {
646        finish();
647    }
648
649    /**
650     * Sessions automatically watch for requests that all system UI be closed (such as when
651     * the user presses HOME), which will appear here.  The default implementation always
652     * calls {@link #finish}.
653     */
654    public void onCloseSystemDialogs() {
655        finish();
656    }
657
658    /**
659     * Compute the interesting insets into your UI.  The default implementation
660     * uses the entire window frame as the insets.  The default touchable
661     * insets are {@link Insets#TOUCHABLE_INSETS_FRAME}.
662     *
663     * @param outInsets Fill in with the current UI insets.
664     */
665    public void onComputeInsets(Insets outInsets) {
666        int[] loc = mTmpLocation;
667        View decor = getWindow().getWindow().getDecorView();
668        decor.getLocationInWindow(loc);
669        outInsets.contentInsets.top = 0;
670        outInsets.contentInsets.left = 0;
671        outInsets.contentInsets.right = 0;
672        outInsets.contentInsets.bottom = 0;
673        outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_FRAME;
674        outInsets.touchableRegion.setEmpty();
675    }
676
677    /**
678     * Called when a task initiated by {@link #startVoiceActivity(android.content.Intent)}
679     * has actually started.
680     *
681     * @param intent The original {@link Intent} supplied to
682     * {@link #startVoiceActivity(android.content.Intent)}.
683     * @param taskId Unique ID of the now running task.
684     */
685    public void onTaskStarted(Intent intent, int taskId) {
686    }
687
688    /**
689     * Called when the last activity of a task initiated by
690     * {@link #startVoiceActivity(android.content.Intent)} has finished.  The default
691     * implementation calls {@link #finish()} on the assumption that this represents
692     * the completion of a voice action.  You can override the implementation if you would
693     * like a different behavior.
694     *
695     * @param intent The original {@link Intent} supplied to
696     * {@link #startVoiceActivity(android.content.Intent)}.
697     * @param taskId Unique ID of the finished task.
698     */
699    public void onTaskFinished(Intent intent, int taskId) {
700        finish();
701    }
702
703    /**
704     * Request to query for what extended commands the session supports.
705     *
706     * @param caller Who is making the request.
707     * @param commands An array of commands that are being queried.
708     * @return Return an array of booleans indicating which of each entry in the
709     * command array is supported.  A true entry in the array indicates the command
710     * is supported; false indicates it is not.  The default implementation returns
711     * an array of all false entries.
712     */
713    public boolean[] onGetSupportedCommands(Caller caller, String[] commands) {
714        return new boolean[commands.length];
715    }
716
717    /**
718     * Request to confirm with the user before proceeding with an unrecoverable operation,
719     * corresponding to a {@link android.app.VoiceInteractor.ConfirmationRequest
720     * VoiceInteractor.ConfirmationRequest}.
721     *
722     * @param caller Who is making the request.
723     * @param request The active request.
724     * @param prompt The prompt informing the user of what will happen, as per
725     * {@link android.app.VoiceInteractor.ConfirmationRequest VoiceInteractor.ConfirmationRequest}.
726     * @param extras Any additional information, as per
727     * {@link android.app.VoiceInteractor.ConfirmationRequest VoiceInteractor.ConfirmationRequest}.
728     */
729    public abstract void onConfirm(Caller caller, Request request, CharSequence prompt,
730            Bundle extras);
731
732    /**
733     * Request to complete the voice interaction session because the voice activity successfully
734     * completed its interaction using voice.  Corresponds to
735     * {@link android.app.VoiceInteractor.CompleteVoiceRequest
736     * VoiceInteractor.CompleteVoiceRequest}.  The default implementation just sends an empty
737     * confirmation back to allow the activity to exit.
738     *
739     * @param caller Who is making the request.
740     * @param request The active request.
741     * @param message The message informing the user of the problem, as per
742     * {@link android.app.VoiceInteractor.CompleteVoiceRequest
743     * VoiceInteractor.CompleteVoiceRequest}.
744     * @param extras Any additional information, as per
745     * {@link android.app.VoiceInteractor.CompleteVoiceRequest
746     * VoiceInteractor.CompleteVoiceRequest}.
747     */
748    public void onCompleteVoice(Caller caller, Request request, CharSequence message,
749           Bundle extras) {
750        request.sendCompleteVoiceResult(null);
751    }
752
753    /**
754     * Request to abort the voice interaction session because the voice activity can not
755     * complete its interaction using voice.  Corresponds to
756     * {@link android.app.VoiceInteractor.AbortVoiceRequest
757     * VoiceInteractor.AbortVoiceRequest}.  The default implementation just sends an empty
758     * confirmation back to allow the activity to exit.
759     *
760     * @param caller Who is making the request.
761     * @param request The active request.
762     * @param message The message informing the user of the problem, as per
763     * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
764     * @param extras Any additional information, as per
765     * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
766     */
767    public void onAbortVoice(Caller caller, Request request, CharSequence message, Bundle extras) {
768        request.sendAbortVoiceResult(null);
769    }
770
771    /**
772     * Process an arbitrary extended command from the caller,
773     * corresponding to a {@link android.app.VoiceInteractor.CommandRequest
774     * VoiceInteractor.CommandRequest}.
775     *
776     * @param caller Who is making the request.
777     * @param request The active request.
778     * @param command The command that is being executed, as per
779     * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}.
780     * @param extras Any additional information, as per
781     * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}.
782     */
783    public abstract void onCommand(Caller caller, Request request, String command, Bundle extras);
784
785    /**
786     * Called when the {@link android.app.VoiceInteractor} has asked to cancel a {@link Request}
787     * that was previously delivered to {@link #onConfirm} or {@link #onCommand}.
788     *
789     * @param request The request that is being canceled.
790     */
791    public abstract void onCancel(Request request);
792}
793