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