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