VoiceInteractionSession.java revision 27eac1d58fe0b7ca3a2e27f5ed64eff232745f45
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.app.VoiceInteractor;
22import android.content.Context;
23import android.content.Intent;
24import android.content.res.TypedArray;
25import android.graphics.Bitmap;
26import android.graphics.Rect;
27import android.graphics.Region;
28import android.inputmethodservice.SoftInputWindow;
29import android.os.Binder;
30import android.os.Bundle;
31import android.os.Handler;
32import android.os.IBinder;
33import android.os.Message;
34import android.os.RemoteException;
35import android.util.ArrayMap;
36import android.util.Log;
37import android.view.Gravity;
38import android.view.KeyEvent;
39import android.view.LayoutInflater;
40import android.view.View;
41import android.view.ViewGroup;
42import android.view.ViewTreeObserver;
43import android.view.WindowManager;
44import android.widget.FrameLayout;
45import com.android.internal.app.IVoiceInteractionManagerService;
46import com.android.internal.app.IVoiceInteractor;
47import com.android.internal.app.IVoiceInteractorCallback;
48import com.android.internal.app.IVoiceInteractorRequest;
49import com.android.internal.os.HandlerCaller;
50import com.android.internal.os.SomeArgs;
51
52import java.lang.ref.WeakReference;
53
54import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
55
56/**
57 * An active voice interaction session, providing a facility for the implementation
58 * to interact with the user in the voice interaction layer.  The user interface is
59 * initially shown by default, and can be created be overriding {@link #onCreateContentView()}
60 * in which the UI can be 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
95    final WeakReference<VoiceInteractionSession> mWeakRef
96            = new WeakReference<VoiceInteractionSession>(this);
97
98    final IVoiceInteractor mInteractor = new IVoiceInteractor.Stub() {
99        @Override
100        public IVoiceInteractorRequest startConfirmation(String callingPackage,
101                IVoiceInteractorCallback callback, CharSequence prompt, Bundle extras) {
102            Request request = newRequest(callback);
103            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_CONFIRMATION,
104                    new Caller(callingPackage, Binder.getCallingUid()), request,
105                    prompt, extras));
106            return request.mInterface;
107        }
108
109        @Override
110        public IVoiceInteractorRequest startPickOption(String callingPackage,
111                IVoiceInteractorCallback callback, CharSequence prompt,
112                VoiceInteractor.PickOptionRequest.Option[] options, Bundle extras) {
113            Request request = newRequest(callback);
114            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOOO(MSG_START_PICK_OPTION,
115                    new Caller(callingPackage, Binder.getCallingUid()), request,
116                    prompt, options, extras));
117            return request.mInterface;
118        }
119
120        @Override
121        public IVoiceInteractorRequest startCompleteVoice(String callingPackage,
122                IVoiceInteractorCallback callback, CharSequence message, Bundle extras) {
123            Request request = newRequest(callback);
124            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_COMPLETE_VOICE,
125                    new Caller(callingPackage, Binder.getCallingUid()), request,
126                    message, extras));
127            return request.mInterface;
128        }
129
130        @Override
131        public IVoiceInteractorRequest startAbortVoice(String callingPackage,
132                IVoiceInteractorCallback callback, CharSequence message, Bundle extras) {
133            Request request = newRequest(callback);
134            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_ABORT_VOICE,
135                    new Caller(callingPackage, Binder.getCallingUid()), request,
136                    message, extras));
137            return request.mInterface;
138        }
139
140        @Override
141        public IVoiceInteractorRequest startCommand(String callingPackage,
142                IVoiceInteractorCallback callback, String command, Bundle extras) {
143            Request request = newRequest(callback);
144            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_COMMAND,
145                    new Caller(callingPackage, Binder.getCallingUid()), request,
146                    command, extras));
147            return request.mInterface;
148        }
149
150        @Override
151        public boolean[] supportsCommands(String callingPackage, String[] commands) {
152            Message msg = mHandlerCaller.obtainMessageIOO(MSG_SUPPORTS_COMMANDS,
153                    0, new Caller(callingPackage, Binder.getCallingUid()), commands);
154            SomeArgs args = mHandlerCaller.sendMessageAndWait(msg);
155            if (args != null) {
156                boolean[] res = (boolean[])args.arg1;
157                args.recycle();
158                return res;
159            }
160            return new boolean[commands.length];
161        }
162    };
163
164    final IVoiceInteractionSession mSession = new IVoiceInteractionSession.Stub() {
165        @Override
166        public void show(Bundle sessionArgs, int flags) {
167            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_SHOW,
168                    flags, sessionArgs));
169        }
170
171        @Override
172        public void hide() {
173            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_HIDE));
174        }
175
176        @Override
177        public void handleAssist(Bundle assistBundle) {
178            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_HANDLE_ASSIST,
179                    assistBundle));
180        }
181
182        @Override
183        public void handleScreenshot(Bitmap screenshot) {
184            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_HANDLE_SCREENSHOT,
185                    screenshot));
186        }
187
188        @Override
189        public void taskStarted(Intent intent, int taskId) {
190            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_TASK_STARTED,
191                    taskId, intent));
192        }
193
194        @Override
195        public void taskFinished(Intent intent, int taskId) {
196            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_TASK_FINISHED,
197                    taskId, intent));
198        }
199
200        @Override
201        public void closeSystemDialogs() {
202            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_CLOSE_SYSTEM_DIALOGS));
203        }
204
205        @Override
206        public void destroy() {
207            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_DESTROY));
208        }
209    };
210
211    public static class Request {
212        final IVoiceInteractorRequest mInterface = new IVoiceInteractorRequest.Stub() {
213            @Override
214            public void cancel() throws RemoteException {
215                VoiceInteractionSession session = mSession.get();
216                if (session != null) {
217                    session.mHandlerCaller.sendMessage(
218                            session.mHandlerCaller.obtainMessageO(MSG_CANCEL, Request.this));
219                }
220            }
221        };
222        final IVoiceInteractorCallback mCallback;
223        final WeakReference<VoiceInteractionSession> mSession;
224
225        Request(IVoiceInteractorCallback callback, VoiceInteractionSession session) {
226            mCallback = callback;
227            mSession = session.mWeakRef;
228        }
229
230        void finishRequest() {
231            VoiceInteractionSession session = mSession.get();
232            if (session == null) {
233                throw new IllegalStateException("VoiceInteractionSession has been destroyed");
234            }
235            Request req = session.removeRequest(mInterface.asBinder());
236            if (req == null) {
237                throw new IllegalStateException("Request not active: " + this);
238            } else if (req != this) {
239                throw new IllegalStateException("Current active request " + req
240                        + " not same as calling request " + this);
241            }
242        }
243
244        public void sendConfirmResult(boolean confirmed, Bundle result) {
245            try {
246                if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface
247                        + " confirmed=" + confirmed + " result=" + result);
248                finishRequest();
249                mCallback.deliverConfirmationResult(mInterface, confirmed, result);
250            } catch (RemoteException e) {
251            }
252        }
253
254        public void sendPickOptionResult(boolean finished,
255                VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) {
256            try {
257                if (DEBUG) Log.d(TAG, "sendPickOptionResult: req=" + mInterface
258                        + " finished=" + finished + " selections=" + selections
259                        + " result=" + result);
260                if (finished) {
261                    finishRequest();
262                }
263                mCallback.deliverPickOptionResult(mInterface, finished, selections, result);
264            } catch (RemoteException e) {
265            }
266        }
267
268        public void sendCompleteVoiceResult(Bundle result) {
269            try {
270                if (DEBUG) Log.d(TAG, "sendCompleteVoiceResult: req=" + mInterface
271                        + " result=" + result);
272                finishRequest();
273                mCallback.deliverCompleteVoiceResult(mInterface, result);
274            } catch (RemoteException e) {
275            }
276        }
277
278        public void sendAbortVoiceResult(Bundle result) {
279            try {
280                if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface
281                        + " result=" + result);
282                finishRequest();
283                mCallback.deliverAbortVoiceResult(mInterface, result);
284            } catch (RemoteException e) {
285            }
286        }
287
288        public void sendCommandResult(boolean finished, Bundle result) {
289            try {
290                if (DEBUG) Log.d(TAG, "sendCommandResult: req=" + mInterface
291                        + " result=" + result);
292                if (finished) {
293                    finishRequest();
294                }
295                mCallback.deliverCommandResult(mInterface, finished, result);
296            } catch (RemoteException e) {
297            }
298        }
299
300        public void sendCancelResult() {
301            try {
302                if (DEBUG) Log.d(TAG, "sendCancelResult: req=" + mInterface);
303                finishRequest();
304                mCallback.deliverCancel(mInterface);
305            } catch (RemoteException e) {
306            }
307        }
308    }
309
310    public static class Caller {
311        final String packageName;
312        final int uid;
313
314        Caller(String _packageName, int _uid) {
315            packageName = _packageName;
316            uid = _uid;
317        }
318    }
319
320    static final int MSG_START_CONFIRMATION = 1;
321    static final int MSG_START_PICK_OPTION = 2;
322    static final int MSG_START_COMPLETE_VOICE = 3;
323    static final int MSG_START_ABORT_VOICE = 4;
324    static final int MSG_START_COMMAND = 5;
325    static final int MSG_SUPPORTS_COMMANDS = 6;
326    static final int MSG_CANCEL = 7;
327
328    static final int MSG_TASK_STARTED = 100;
329    static final int MSG_TASK_FINISHED = 101;
330    static final int MSG_CLOSE_SYSTEM_DIALOGS = 102;
331    static final int MSG_DESTROY = 103;
332    static final int MSG_HANDLE_ASSIST = 104;
333    static final int MSG_HANDLE_SCREENSHOT = 105;
334    static final int MSG_SHOW = 106;
335    static final int MSG_HIDE = 107;
336
337    class MyCallbacks implements HandlerCaller.Callback, SoftInputWindow.Callback {
338        @Override
339        public void executeMessage(Message msg) {
340            SomeArgs args;
341            switch (msg.what) {
342                case MSG_START_CONFIRMATION:
343                    args = (SomeArgs)msg.obj;
344                    if (DEBUG) Log.d(TAG, "onConfirm: req=" + ((Request) args.arg2).mInterface
345                            + " prompt=" + args.arg3 + " extras=" + args.arg4);
346                    onConfirm((Caller)args.arg1, (Request)args.arg2, (CharSequence)args.arg3,
347                            (Bundle)args.arg4);
348                    break;
349                case MSG_START_PICK_OPTION:
350                    args = (SomeArgs)msg.obj;
351                    if (DEBUG) Log.d(TAG, "onPickOption: req=" + ((Request) args.arg2).mInterface
352                            + " prompt=" + args.arg3 + " options=" + args.arg4
353                            + " extras=" + args.arg5);
354                    onPickOption((Caller)args.arg1, (Request)args.arg2, (CharSequence)args.arg3,
355                            (VoiceInteractor.PickOptionRequest.Option[])args.arg4,
356                            (Bundle)args.arg5);
357                    break;
358                case MSG_START_COMPLETE_VOICE:
359                    args = (SomeArgs)msg.obj;
360                    if (DEBUG) Log.d(TAG, "onCompleteVoice: req=" + ((Request) args.arg2).mInterface
361                            + " message=" + args.arg3 + " extras=" + args.arg4);
362                    onCompleteVoice((Caller) args.arg1, (Request) args.arg2,
363                            (CharSequence) args.arg3, (Bundle) args.arg4);
364                    break;
365                case MSG_START_ABORT_VOICE:
366                    args = (SomeArgs)msg.obj;
367                    if (DEBUG) Log.d(TAG, "onAbortVoice: req=" + ((Request) args.arg2).mInterface
368                            + " message=" + args.arg3 + " extras=" + args.arg4);
369                    onAbortVoice((Caller) args.arg1, (Request) args.arg2, (CharSequence) args.arg3,
370                            (Bundle) args.arg4);
371                    break;
372                case MSG_START_COMMAND:
373                    args = (SomeArgs)msg.obj;
374                    if (DEBUG) Log.d(TAG, "onCommand: req=" + ((Request) args.arg2).mInterface
375                            + " command=" + args.arg3 + " extras=" + args.arg4);
376                    onCommand((Caller) args.arg1, (Request) args.arg2, (String) args.arg3,
377                            (Bundle) args.arg4);
378                    break;
379                case MSG_SUPPORTS_COMMANDS:
380                    args = (SomeArgs)msg.obj;
381                    if (DEBUG) Log.d(TAG, "onGetSupportedCommands: cmds=" + args.arg2);
382                    args.arg1 = onGetSupportedCommands((Caller) args.arg1, (String[]) args.arg2);
383                    break;
384                case MSG_CANCEL:
385                    if (DEBUG) Log.d(TAG, "onCancel: req=" + ((Request)msg.obj));
386                    onCancel((Request)msg.obj);
387                    break;
388                case MSG_TASK_STARTED:
389                    if (DEBUG) Log.d(TAG, "onTaskStarted: intent=" + msg.obj
390                            + " taskId=" + msg.arg1);
391                    onTaskStarted((Intent) msg.obj, msg.arg1);
392                    break;
393                case MSG_TASK_FINISHED:
394                    if (DEBUG) Log.d(TAG, "onTaskFinished: intent=" + msg.obj
395                            + " taskId=" + msg.arg1);
396                    onTaskFinished((Intent) msg.obj, msg.arg1);
397                    break;
398                case MSG_CLOSE_SYSTEM_DIALOGS:
399                    if (DEBUG) Log.d(TAG, "onCloseSystemDialogs");
400                    onCloseSystemDialogs();
401                    break;
402                case MSG_DESTROY:
403                    if (DEBUG) Log.d(TAG, "doDestroy");
404                    doDestroy();
405                    break;
406                case MSG_HANDLE_ASSIST:
407                    if (DEBUG) Log.d(TAG, "onHandleAssist: " + msg.obj);
408                    onHandleAssist((Bundle) msg.obj);
409                    break;
410                case MSG_HANDLE_SCREENSHOT:
411                    if (DEBUG) Log.d(TAG, "onHandleScreenshot: " + msg.obj);
412                    onHandleScreenshot((Bitmap) msg.obj);
413                    break;
414                case MSG_SHOW:
415                    if (DEBUG) Log.d(TAG, "doShow: args=" + msg.obj
416                            + " flags=" + msg.arg1);
417                    doShow((Bundle) msg.obj, msg.arg1);
418                    break;
419                case MSG_HIDE:
420                    if (DEBUG) Log.d(TAG, "doHide");
421                    doHide();
422                    break;
423            }
424        }
425
426        @Override
427        public void onBackPressed() {
428            VoiceInteractionSession.this.onBackPressed();
429        }
430    }
431
432    final MyCallbacks mCallbacks = new MyCallbacks();
433
434    /**
435     * Information about where interesting parts of the input method UI appear.
436     */
437    public static final class Insets {
438        /**
439         * This is the part of the UI that is the main content.  It is
440         * used to determine the basic space needed, to resize/pan the
441         * application behind.  It is assumed that this inset does not
442         * change very much, since any change will cause a full resize/pan
443         * of the application behind.  This value is relative to the top edge
444         * of the input method window.
445         */
446        public final Rect contentInsets = new Rect();
447
448        /**
449         * This is the region of the UI that is touchable.  It is used when
450         * {@link #touchableInsets} is set to {@link #TOUCHABLE_INSETS_REGION}.
451         * The region should be specified relative to the origin of the window frame.
452         */
453        public final Region touchableRegion = new Region();
454
455        /**
456         * Option for {@link #touchableInsets}: the entire window frame
457         * can be touched.
458         */
459        public static final int TOUCHABLE_INSETS_FRAME
460                = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
461
462        /**
463         * Option for {@link #touchableInsets}: the area inside of
464         * the content insets can be touched.
465         */
466        public static final int TOUCHABLE_INSETS_CONTENT
467                = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
468
469        /**
470         * Option for {@link #touchableInsets}: the region specified by
471         * {@link #touchableRegion} can be touched.
472         */
473        public static final int TOUCHABLE_INSETS_REGION
474                = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
475
476        /**
477         * Determine which area of the window is touchable by the user.  May
478         * be one of: {@link #TOUCHABLE_INSETS_FRAME},
479         * {@link #TOUCHABLE_INSETS_CONTENT}, or {@link #TOUCHABLE_INSETS_REGION}.
480         */
481        public int touchableInsets;
482    }
483
484    final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
485            new ViewTreeObserver.OnComputeInternalInsetsListener() {
486        public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
487            onComputeInsets(mTmpInsets);
488            info.contentInsets.set(mTmpInsets.contentInsets);
489            info.visibleInsets.set(mTmpInsets.contentInsets);
490            info.touchableRegion.set(mTmpInsets.touchableRegion);
491            info.setTouchableInsets(mTmpInsets.touchableInsets);
492        }
493    };
494
495    public VoiceInteractionSession(Context context) {
496        this(context, new Handler());
497    }
498
499    public VoiceInteractionSession(Context context, Handler handler) {
500        mContext = context;
501        mHandlerCaller = new HandlerCaller(context, handler.getLooper(),
502                mCallbacks, true);
503    }
504
505    Request newRequest(IVoiceInteractorCallback callback) {
506        synchronized (this) {
507            Request req = new Request(callback, this);
508            mActiveRequests.put(req.mInterface.asBinder(), req);
509            return req;
510        }
511    }
512
513    Request removeRequest(IBinder reqInterface) {
514        synchronized (this) {
515            Request req = mActiveRequests.get(reqInterface);
516            if (req != null) {
517                mActiveRequests.remove(req);
518            }
519            return req;
520        }
521    }
522
523    void doCreate(IVoiceInteractionManagerService service, IBinder token, Bundle args,
524            int startFlags) {
525        mSystemService = service;
526        mToken = token;
527        onCreate(args, startFlags);
528    }
529
530    void doShow(Bundle args, int flags) {
531        if (DEBUG) Log.v(TAG, "Showing window: mWindowAdded=" + mWindowAdded
532                + " mWindowVisible=" + mWindowVisible);
533
534        if (mInShowWindow) {
535            Log.w(TAG, "Re-entrance in to showWindow");
536            return;
537        }
538
539        try {
540            mInShowWindow = true;
541            if (!mWindowVisible) {
542                if (!mWindowAdded) {
543                    mWindowAdded = true;
544                    View v = onCreateContentView();
545                    if (v != null) {
546                        setContentView(v);
547                    }
548                }
549            }
550            onShow(args, flags);
551            if (!mWindowVisible) {
552                mWindowVisible = true;
553                mWindow.show();
554            }
555        } finally {
556            mWindowWasVisible = true;
557            mInShowWindow = false;
558        }
559    }
560
561    void doHide() {
562        if (mWindowVisible) {
563            mWindow.hide();
564            mWindowVisible = false;
565            onHide();
566        }
567    }
568
569    void doDestroy() {
570        onDestroy();
571        if (mInitialized) {
572            mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
573                    mInsetsComputer);
574            if (mWindowAdded) {
575                mWindow.dismiss();
576                mWindowAdded = false;
577            }
578            mInitialized = false;
579        }
580    }
581
582    void initViews() {
583        mInitialized = true;
584
585        mThemeAttrs = mContext.obtainStyledAttributes(android.R.styleable.VoiceInteractionSession);
586        mRootView = mInflater.inflate(
587                com.android.internal.R.layout.voice_interaction_session, null);
588        mRootView.setSystemUiVisibility(
589                View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
590        mWindow.setContentView(mRootView);
591        mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
592
593        mContentFrame = (FrameLayout)mRootView.findViewById(android.R.id.content);
594    }
595
596    public void show() {
597        try {
598            mSystemService.showSessionFromSession(mToken, null, 0);
599        } catch (RemoteException e) {
600        }
601    }
602
603    public void hide() {
604        try {
605            mSystemService.hideSessionFromSession(mToken);
606        } catch (RemoteException e) {
607        }
608    }
609
610    /** TODO: remove */
611    public void showWindow() {
612    }
613
614    /** TODO: remove */
615    public void hideWindow() {
616    }
617
618    /**
619     * You can call this to customize the theme used by your IME's window.
620     * This must be set before {@link #onCreate}, so you
621     * will typically call it in your constructor with the resource ID
622     * of your custom theme.
623     */
624    public void setTheme(int theme) {
625        if (mWindow != null) {
626            throw new IllegalStateException("Must be called before onCreate()");
627        }
628        mTheme = theme;
629    }
630
631    /**
632     * Ask that a new activity be started for voice interaction.  This will create a
633     * new dedicated task in the activity manager for this voice interaction session;
634     * this means that {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK}
635     * will be set for you to make it a new task.
636     *
637     * <p>The newly started activity will be displayed to the user in a special way, as
638     * a layer under the voice interaction UI.</p>
639     *
640     * <p>As the voice activity runs, it can retrieve a {@link android.app.VoiceInteractor}
641     * through which it can perform voice interactions through your session.  These requests
642     * for voice interactions will appear as callbacks on {@link #onGetSupportedCommands},
643     * {@link #onConfirm}, {@link #onCommand}, and {@link #onCancel}.
644     *
645     * <p>You will receive a call to {@link #onTaskStarted} when the task starts up
646     * and {@link #onTaskFinished} when the last activity has finished.
647     *
648     * @param intent The Intent to start this voice interaction.  The given Intent will
649     * always have {@link Intent#CATEGORY_VOICE Intent.CATEGORY_VOICE} added to it, since
650     * this is part of a voice interaction.
651     */
652    public void startVoiceActivity(Intent intent) {
653        if (mToken == null) {
654            throw new IllegalStateException("Can't call before onCreate()");
655        }
656        try {
657            intent.migrateExtraStreamToClipData();
658            intent.prepareToLeaveProcess();
659            int res = mSystemService.startVoiceActivity(mToken, intent,
660                    intent.resolveType(mContext.getContentResolver()));
661            Instrumentation.checkStartActivityResult(res, intent);
662        } catch (RemoteException e) {
663        }
664    }
665
666    /**
667     * Set whether this session will keep the device awake while it is running a voice
668     * activity.  By default, the system holds a wake lock for it while in this state,
669     * so that it can work even if the screen is off.  Setting this to false removes that
670     * wake lock, allowing the CPU to go to sleep.  This is typically used if the
671     * session decides it has been waiting too long for a response from the user and
672     * doesn't want to let this continue to drain the battery.
673     *
674     * <p>Passing false here will release the wake lock, and you can call later with
675     * true to re-acquire it.  It will also be automatically re-acquired for you each
676     * time you start a new voice activity task -- that is when you call
677     * {@link #startVoiceActivity}.</p>
678     */
679    public void setKeepAwake(boolean keepAwake) {
680        try {
681            mSystemService.setKeepAwake(mToken, keepAwake);
682        } catch (RemoteException e) {
683        }
684    }
685
686    /**
687     * Convenience for inflating views.
688     */
689    public LayoutInflater getLayoutInflater() {
690        return mInflater;
691    }
692
693    /**
694     * Retrieve the window being used to show the session's UI.
695     */
696    public Dialog getWindow() {
697        return mWindow;
698    }
699
700    /**
701     * Finish the session.
702     */
703    public void finish() {
704        if (mToken == null) {
705            throw new IllegalStateException("Can't call before onCreate()");
706        }
707        hideWindow();
708        try {
709            mSystemService.finish(mToken);
710        } catch (RemoteException e) {
711        }
712    }
713
714    /** @hide */
715    public void onCreate(Bundle args) {
716        mTheme = mTheme != 0 ? mTheme
717                : com.android.internal.R.style.Theme_DeviceDefault_VoiceInteractionSession;
718        mInflater = (LayoutInflater)mContext.getSystemService(
719                Context.LAYOUT_INFLATER_SERVICE);
720        mWindow = new SoftInputWindow(mContext, "VoiceInteractionSession", mTheme,
721                mCallbacks, this, mDispatcherState,
722                WindowManager.LayoutParams.TYPE_VOICE_INTERACTION, Gravity.BOTTOM, true);
723        mWindow.getWindow().addFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
724        initViews();
725        mWindow.getWindow().setLayout(MATCH_PARENT, MATCH_PARENT);
726        mWindow.setToken(mToken);
727    }
728
729    /**
730     * Initiatize a new session.  The given args and showFlags are the initial values
731     * passed to {@link VoiceInteractionService#showSession VoiceInteractionService.showSession},
732     * if possible.  Normally you should handle these in {@link #onShow}.
733     */
734    public void onCreate(Bundle args, int showFlags) {
735        onCreate(args);
736    }
737
738    /**
739     * Called when the session UI is going to be shown.  This is called after
740     * {@link #onCreateContentView} (if the session's content UI needed to be created) and
741     * immediately prior to the window being shown.  This may be called while the window
742     * is already shown, if a show request has come in while it is shown, to allow you to
743     * update the UI to match the new show arguments.
744     *
745     * @param args The arguments that were supplied to
746     * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
747     * @param showFlags The show flags originally provided to
748     * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
749     */
750    public void onShow(Bundle args, int showFlags) {
751    }
752
753    /**
754     * Called immediately after stopping to show the session UI.
755     */
756    public void onHide() {
757    }
758
759    /**
760     * Last callback to the session as it is being finished.
761     */
762    public void onDestroy() {
763    }
764
765    /**
766     * Hook in which to create the session's UI.
767     */
768    public View onCreateContentView() {
769        return null;
770    }
771
772    public void setContentView(View view) {
773        mContentFrame.removeAllViews();
774        mContentFrame.addView(view, new FrameLayout.LayoutParams(
775                ViewGroup.LayoutParams.MATCH_PARENT,
776                ViewGroup.LayoutParams.MATCH_PARENT));
777
778    }
779
780    public void onHandleAssist(Bundle assistBundle) {
781    }
782
783    public void onHandleScreenshot(Bitmap screenshot) {
784    }
785
786    public boolean onKeyDown(int keyCode, KeyEvent event) {
787        return false;
788    }
789
790    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
791        return false;
792    }
793
794    public boolean onKeyUp(int keyCode, KeyEvent event) {
795        return false;
796    }
797
798    public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
799        return false;
800    }
801
802    public void onBackPressed() {
803        hide();
804    }
805
806    /**
807     * Sessions automatically watch for requests that all system UI be closed (such as when
808     * the user presses HOME), which will appear here.  The default implementation always
809     * calls {@link #finish}.
810     */
811    public void onCloseSystemDialogs() {
812        hide();
813    }
814
815    /**
816     * Compute the interesting insets into your UI.  The default implementation
817     * sets {@link Insets#contentInsets outInsets.contentInsets.top} to the height
818     * of the window, meaning it should not adjust content underneath.  The default touchable
819     * insets are {@link Insets#TOUCHABLE_INSETS_FRAME}, meaning it consumes all touch
820     * events within its window frame.
821     *
822     * @param outInsets Fill in with the current UI insets.
823     */
824    public void onComputeInsets(Insets outInsets) {
825        outInsets.contentInsets.left = 0;
826        outInsets.contentInsets.bottom = 0;
827        outInsets.contentInsets.right = 0;
828        View decor = getWindow().getWindow().getDecorView();
829        outInsets.contentInsets.top = decor.getHeight();
830        outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_FRAME;
831        outInsets.touchableRegion.setEmpty();
832    }
833
834    /**
835     * Called when a task initiated by {@link #startVoiceActivity(android.content.Intent)}
836     * has actually started.
837     *
838     * @param intent The original {@link Intent} supplied to
839     * {@link #startVoiceActivity(android.content.Intent)}.
840     * @param taskId Unique ID of the now running task.
841     */
842    public void onTaskStarted(Intent intent, int taskId) {
843    }
844
845    /**
846     * Called when the last activity of a task initiated by
847     * {@link #startVoiceActivity(android.content.Intent)} has finished.  The default
848     * implementation calls {@link #finish()} on the assumption that this represents
849     * the completion of a voice action.  You can override the implementation if you would
850     * like a different behavior.
851     *
852     * @param intent The original {@link Intent} supplied to
853     * {@link #startVoiceActivity(android.content.Intent)}.
854     * @param taskId Unique ID of the finished task.
855     */
856    public void onTaskFinished(Intent intent, int taskId) {
857        hide();
858    }
859
860    /**
861     * Request to query for what extended commands the session supports.
862     *
863     * @param caller Who is making the request.
864     * @param commands An array of commands that are being queried.
865     * @return Return an array of booleans indicating which of each entry in the
866     * command array is supported.  A true entry in the array indicates the command
867     * is supported; false indicates it is not.  The default implementation returns
868     * an array of all false entries.
869     */
870    public boolean[] onGetSupportedCommands(Caller caller, String[] commands) {
871        return new boolean[commands.length];
872    }
873
874    /**
875     * Request to confirm with the user before proceeding with an unrecoverable operation,
876     * corresponding to a {@link android.app.VoiceInteractor.ConfirmationRequest
877     * VoiceInteractor.ConfirmationRequest}.
878     *
879     * @param caller Who is making the request.
880     * @param request The active request.
881     * @param prompt The prompt informing the user of what will happen, as per
882     * {@link android.app.VoiceInteractor.ConfirmationRequest VoiceInteractor.ConfirmationRequest}.
883     * @param extras Any additional information, as per
884     * {@link android.app.VoiceInteractor.ConfirmationRequest VoiceInteractor.ConfirmationRequest}.
885     */
886    public abstract void onConfirm(Caller caller, Request request, CharSequence prompt,
887            Bundle extras);
888
889    /**
890     * Request for the user to pick one of N options, corresponding to a
891     * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
892     *
893     * @param caller Who is making the request.
894     * @param request The active request.
895     * @param prompt The prompt informing the user of what they are picking, as per
896     * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
897     * @param options The set of options the user is picking from, as per
898     * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
899     * @param extras Any additional information, as per
900     * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
901     */
902    public abstract void onPickOption(Caller caller, Request request, CharSequence prompt,
903            VoiceInteractor.PickOptionRequest.Option[] options, Bundle extras);
904
905    /**
906     * Request to complete the voice interaction session because the voice activity successfully
907     * completed its interaction using voice.  Corresponds to
908     * {@link android.app.VoiceInteractor.CompleteVoiceRequest
909     * VoiceInteractor.CompleteVoiceRequest}.  The default implementation just sends an empty
910     * confirmation back to allow the activity to exit.
911     *
912     * @param caller Who is making the request.
913     * @param request The active request.
914     * @param message The message informing the user of the problem, as per
915     * {@link android.app.VoiceInteractor.CompleteVoiceRequest
916     * VoiceInteractor.CompleteVoiceRequest}.
917     * @param extras Any additional information, as per
918     * {@link android.app.VoiceInteractor.CompleteVoiceRequest
919     * VoiceInteractor.CompleteVoiceRequest}.
920     */
921    public void onCompleteVoice(Caller caller, Request request, CharSequence message,
922           Bundle extras) {
923        request.sendCompleteVoiceResult(null);
924    }
925
926    /**
927     * Request to abort the voice interaction session because the voice activity can not
928     * complete its interaction using voice.  Corresponds to
929     * {@link android.app.VoiceInteractor.AbortVoiceRequest
930     * VoiceInteractor.AbortVoiceRequest}.  The default implementation just sends an empty
931     * confirmation back to allow the activity to exit.
932     *
933     * @param caller Who is making the request.
934     * @param request The active request.
935     * @param message The message informing the user of the problem, as per
936     * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
937     * @param extras Any additional information, as per
938     * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
939     */
940    public void onAbortVoice(Caller caller, Request request, CharSequence message, Bundle extras) {
941        request.sendAbortVoiceResult(null);
942    }
943
944    /**
945     * Process an arbitrary extended command from the caller,
946     * corresponding to a {@link android.app.VoiceInteractor.CommandRequest
947     * VoiceInteractor.CommandRequest}.
948     *
949     * @param caller Who is making the request.
950     * @param request The active request.
951     * @param command The command that is being executed, as per
952     * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}.
953     * @param extras Any additional information, as per
954     * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}.
955     */
956    public abstract void onCommand(Caller caller, Request request, String command, Bundle extras);
957
958    /**
959     * Called when the {@link android.app.VoiceInteractor} has asked to cancel a {@link Request}
960     * that was previously delivered to {@link #onConfirm} or {@link #onCommand}.
961     *
962     * @param request The request that is being canceled.
963     */
964    public abstract void onCancel(Request request);
965}
966