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