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