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