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