VoiceInteractionSession.java revision d3fdb8bed8e836786253f9cd5ab640c7c5ed8501
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        if (mInitialized) {
452            mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
453                    mInsetsComputer);
454            if (mWindowAdded) {
455                mWindow.dismiss();
456                mWindowAdded = false;
457            }
458            mInitialized = false;
459        }
460    }
461
462    void initViews() {
463        mInitialized = true;
464
465        mThemeAttrs = mContext.obtainStyledAttributes(android.R.styleable.VoiceInteractionSession);
466        mRootView = mInflater.inflate(
467                com.android.internal.R.layout.voice_interaction_session, null);
468        mRootView.setSystemUiVisibility(
469                View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
470        mWindow.setContentView(mRootView);
471        mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
472
473        mContentFrame = (FrameLayout)mRootView.findViewById(android.R.id.content);
474    }
475
476    public void showWindow() {
477        if (DEBUG) Log.v(TAG, "Showing window: mWindowAdded=" + mWindowAdded
478                + " mWindowVisible=" + mWindowVisible);
479
480        if (mInShowWindow) {
481            Log.w(TAG, "Re-entrance in to showWindow");
482            return;
483        }
484
485        try {
486            mInShowWindow = true;
487            if (!mWindowVisible) {
488                mWindowVisible = true;
489                if (!mWindowAdded) {
490                    mWindowAdded = true;
491                    View v = onCreateContentView();
492                    if (v != null) {
493                        setContentView(v);
494                    }
495                }
496                mWindow.show();
497            }
498        } finally {
499            mWindowWasVisible = true;
500            mInShowWindow = false;
501        }
502    }
503
504    public void hideWindow() {
505        if (mWindowVisible) {
506            mWindow.hide();
507            mWindowVisible = false;
508        }
509    }
510
511    /**
512     * You can call this to customize the theme used by your IME's window.
513     * This must be set before {@link #onCreate}, so you
514     * will typically call it in your constructor with the resource ID
515     * of your custom theme.
516     */
517    public void setTheme(int theme) {
518        if (mWindow != null) {
519            throw new IllegalStateException("Must be called before onCreate()");
520        }
521        mTheme = theme;
522    }
523
524    /**
525     * Ask that a new activity be started for voice interaction.  This will create a
526     * new dedicated task in the activity manager for this voice interaction session;
527     * this means that {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK}
528     * will be set for you to make it a new task.
529     *
530     * <p>The newly started activity will be displayed to the user in a special way, as
531     * a layer under the voice interaction UI.</p>
532     *
533     * <p>As the voice activity runs, it can retrieve a {@link android.app.VoiceInteractor}
534     * through which it can perform voice interactions through your session.  These requests
535     * for voice interactions will appear as callbacks on {@link #onGetSupportedCommands},
536     * {@link #onConfirm}, {@link #onCommand}, and {@link #onCancel}.
537     *
538     * <p>You will receive a call to {@link #onTaskStarted} when the task starts up
539     * and {@link #onTaskFinished} when the last activity has finished.
540     *
541     * @param intent The Intent to start this voice interaction.  The given Intent will
542     * always have {@link Intent#CATEGORY_VOICE Intent.CATEGORY_VOICE} added to it, since
543     * this is part of a voice interaction.
544     */
545    public void startVoiceActivity(Intent intent) {
546        if (mToken == null) {
547            throw new IllegalStateException("Can't call before onCreate()");
548        }
549        try {
550            intent.migrateExtraStreamToClipData();
551            intent.prepareToLeaveProcess();
552            int res = mSystemService.startVoiceActivity(mToken, intent,
553                    intent.resolveType(mContext.getContentResolver()));
554            Instrumentation.checkStartActivityResult(res, intent);
555        } catch (RemoteException e) {
556        }
557    }
558
559    /**
560     * Convenience for inflating views.
561     */
562    public LayoutInflater getLayoutInflater() {
563        return mInflater;
564    }
565
566    /**
567     * Retrieve the window being used to show the session's UI.
568     */
569    public Dialog getWindow() {
570        return mWindow;
571    }
572
573    /**
574     * Finish the session.
575     */
576    public void finish() {
577        if (mToken == null) {
578            throw new IllegalStateException("Can't call before onCreate()");
579        }
580        hideWindow();
581        try {
582            mSystemService.finish(mToken);
583        } catch (RemoteException e) {
584        }
585    }
586
587    /**
588     * Initiatize a new session.
589     *
590     * @param args The arguments that were supplied to
591     * {@link VoiceInteractionService#startSession VoiceInteractionService.startSession}.
592     */
593    public void onCreate(Bundle args) {
594        mTheme = mTheme != 0 ? mTheme
595                : com.android.internal.R.style.Theme_DeviceDefault_VoiceInteractionSession;
596        mInflater = (LayoutInflater)mContext.getSystemService(
597                Context.LAYOUT_INFLATER_SERVICE);
598        mWindow = new SoftInputWindow(mContext, "VoiceInteractionSession", mTheme,
599                mCallbacks, this, mDispatcherState,
600                WindowManager.LayoutParams.TYPE_VOICE_INTERACTION, Gravity.TOP, true);
601        mWindow.getWindow().addFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
602        initViews();
603        mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
604        mWindow.setToken(mToken);
605    }
606
607    /**
608     * Last callback to the session as it is being finished.
609     */
610    public void onDestroy() {
611    }
612
613    /**
614     * Hook in which to create the session's UI.
615     */
616    public View onCreateContentView() {
617        return null;
618    }
619
620    public void setContentView(View view) {
621        mContentFrame.removeAllViews();
622        mContentFrame.addView(view, new FrameLayout.LayoutParams(
623                ViewGroup.LayoutParams.MATCH_PARENT,
624                ViewGroup.LayoutParams.WRAP_CONTENT));
625
626    }
627
628    public boolean onKeyDown(int keyCode, KeyEvent event) {
629        return false;
630    }
631
632    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
633        return false;
634    }
635
636    public boolean onKeyUp(int keyCode, KeyEvent event) {
637        return false;
638    }
639
640    public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
641        return false;
642    }
643
644    public void onBackPressed() {
645        finish();
646    }
647
648    /**
649     * Sessions automatically watch for requests that all system UI be closed (such as when
650     * the user presses HOME), which will appear here.  The default implementation always
651     * calls {@link #finish}.
652     */
653    public void onCloseSystemDialogs() {
654        finish();
655    }
656
657    /**
658     * Compute the interesting insets into your UI.  The default implementation
659     * uses the entire window frame as the insets.  The default touchable
660     * insets are {@link Insets#TOUCHABLE_INSETS_FRAME}.
661     *
662     * @param outInsets Fill in with the current UI insets.
663     */
664    public void onComputeInsets(Insets outInsets) {
665        int[] loc = mTmpLocation;
666        View decor = getWindow().getWindow().getDecorView();
667        decor.getLocationInWindow(loc);
668        outInsets.contentInsets.top = 0;
669        outInsets.contentInsets.left = 0;
670        outInsets.contentInsets.right = 0;
671        outInsets.contentInsets.bottom = 0;
672        outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_FRAME;
673        outInsets.touchableRegion.setEmpty();
674    }
675
676    /**
677     * Called when a task initiated by {@link #startVoiceActivity(android.content.Intent)}
678     * has actually started.
679     *
680     * @param intent The original {@link Intent} supplied to
681     * {@link #startVoiceActivity(android.content.Intent)}.
682     * @param taskId Unique ID of the now running task.
683     */
684    public void onTaskStarted(Intent intent, int taskId) {
685    }
686
687    /**
688     * Called when the last activity of a task initiated by
689     * {@link #startVoiceActivity(android.content.Intent)} has finished.  The default
690     * implementation calls {@link #finish()} on the assumption that this represents
691     * the completion of a voice action.  You can override the implementation if you would
692     * like a different behavior.
693     *
694     * @param intent The original {@link Intent} supplied to
695     * {@link #startVoiceActivity(android.content.Intent)}.
696     * @param taskId Unique ID of the finished task.
697     */
698    public void onTaskFinished(Intent intent, int taskId) {
699        finish();
700    }
701
702    /**
703     * Request to query for what extended commands the session supports.
704     *
705     * @param caller Who is making the request.
706     * @param commands An array of commands that are being queried.
707     * @return Return an array of booleans indicating which of each entry in the
708     * command array is supported.  A true entry in the array indicates the command
709     * is supported; false indicates it is not.  The default implementation returns
710     * an array of all false entries.
711     */
712    public boolean[] onGetSupportedCommands(Caller caller, String[] commands) {
713        return new boolean[commands.length];
714    }
715
716    /**
717     * Request to confirm with the user before proceeding with an unrecoverable operation,
718     * corresponding to a {@link android.app.VoiceInteractor.ConfirmationRequest
719     * VoiceInteractor.ConfirmationRequest}.
720     *
721     * @param caller Who is making the request.
722     * @param request The active request.
723     * @param prompt The prompt informing the user of what will happen, as per
724     * {@link android.app.VoiceInteractor.ConfirmationRequest VoiceInteractor.ConfirmationRequest}.
725     * @param extras Any additional information, as per
726     * {@link android.app.VoiceInteractor.ConfirmationRequest VoiceInteractor.ConfirmationRequest}.
727     */
728    public abstract void onConfirm(Caller caller, Request request, CharSequence prompt,
729            Bundle extras);
730
731    /**
732     * Request to complete the voice interaction session because the voice activity successfully
733     * completed its interaction using voice.  Corresponds to
734     * {@link android.app.VoiceInteractor.CompleteVoiceRequest
735     * VoiceInteractor.CompleteVoiceRequest}.  The default implementation just sends an empty
736     * confirmation back to allow the activity to exit.
737     *
738     * @param caller Who is making the request.
739     * @param request The active request.
740     * @param message The message informing the user of the problem, as per
741     * {@link android.app.VoiceInteractor.CompleteVoiceRequest
742     * VoiceInteractor.CompleteVoiceRequest}.
743     * @param extras Any additional information, as per
744     * {@link android.app.VoiceInteractor.CompleteVoiceRequest
745     * VoiceInteractor.CompleteVoiceRequest}.
746     */
747    public void onCompleteVoice(Caller caller, Request request, CharSequence message,
748           Bundle extras) {
749        request.sendCompleteVoiceResult(null);
750    }
751
752    /**
753     * Request to abort the voice interaction session because the voice activity can not
754     * complete its interaction using voice.  Corresponds to
755     * {@link android.app.VoiceInteractor.AbortVoiceRequest
756     * VoiceInteractor.AbortVoiceRequest}.  The default implementation just sends an empty
757     * confirmation back to allow the activity to exit.
758     *
759     * @param caller Who is making the request.
760     * @param request The active request.
761     * @param message The message informing the user of the problem, as per
762     * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
763     * @param extras Any additional information, as per
764     * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
765     */
766    public void onAbortVoice(Caller caller, Request request, CharSequence message, Bundle extras) {
767        request.sendAbortVoiceResult(null);
768    }
769
770    /**
771     * Process an arbitrary extended command from the caller,
772     * corresponding to a {@link android.app.VoiceInteractor.CommandRequest
773     * VoiceInteractor.CommandRequest}.
774     *
775     * @param caller Who is making the request.
776     * @param request The active request.
777     * @param command The command that is being executed, as per
778     * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}.
779     * @param extras Any additional information, as per
780     * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}.
781     */
782    public abstract void onCommand(Caller caller, Request request, String command, Bundle extras);
783
784    /**
785     * Called when the {@link android.app.VoiceInteractor} has asked to cancel a {@link Request}
786     * that was previously delivered to {@link #onConfirm} or {@link #onCommand}.
787     *
788     * @param request The request that is being canceled.
789     */
790    public abstract void onCancel(Request request);
791}
792