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