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