VoiceInteractionSession.java revision 57dd737443a174379eb638450e4888500d8e4a23
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.annotation.Nullable;
20import android.app.Dialog;
21import android.app.Instrumentation;
22import android.app.VoiceInteractor;
23import android.app.assist.AssistContent;
24import android.app.assist.AssistStructure;
25import android.content.ComponentCallbacks2;
26import android.content.Context;
27import android.content.Intent;
28import android.content.res.Configuration;
29import android.content.res.TypedArray;
30import android.graphics.Bitmap;
31import android.graphics.Rect;
32import android.graphics.Region;
33import android.inputmethodservice.SoftInputWindow;
34import android.os.Binder;
35import android.os.Bundle;
36import android.os.Handler;
37import android.os.IBinder;
38import android.os.Message;
39import android.os.RemoteException;
40import android.os.UserHandle;
41import android.util.ArrayMap;
42import android.util.DebugUtils;
43import android.util.Log;
44import android.view.Gravity;
45import android.view.KeyEvent;
46import android.view.LayoutInflater;
47import android.view.View;
48import android.view.ViewGroup;
49import android.view.ViewTreeObserver;
50import android.view.WindowManager;
51import android.widget.FrameLayout;
52import com.android.internal.app.IVoiceInteractionManagerService;
53import com.android.internal.app.IVoiceInteractionSessionShowCallback;
54import com.android.internal.app.IVoiceInteractor;
55import com.android.internal.app.IVoiceInteractorCallback;
56import com.android.internal.app.IVoiceInteractorRequest;
57import com.android.internal.os.HandlerCaller;
58import com.android.internal.os.SomeArgs;
59
60import java.io.FileDescriptor;
61import java.io.PrintWriter;
62import java.lang.ref.WeakReference;
63
64import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
65
66/**
67 * An active voice interaction session, providing a facility for the implementation
68 * to interact with the user in the voice interaction layer.  The user interface is
69 * initially shown by default, and can be created be overriding {@link #onCreateContentView()}
70 * in which the UI can be built.
71 *
72 * <p>A voice interaction session can be self-contained, ultimately calling {@link #finish}
73 * when done.  It can also initiate voice interactions with applications by calling
74 * {@link #startVoiceActivity}</p>.
75 */
76public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCallbacks2 {
77    static final String TAG = "VoiceInteractionSession";
78    static final boolean DEBUG = false;
79
80    /**
81     * Flag received in {@link #onShow}: originator requested that the session be started with
82     * assist data from the currently focused activity.
83     */
84    public static final int SHOW_WITH_ASSIST = 1<<0;
85
86    /**
87     * Flag received in {@link #onShow}: originator requested that the session be started with
88     * a screen shot of the currently focused activity.
89     */
90    public static final int SHOW_WITH_SCREENSHOT = 1<<1;
91
92    /**
93     * Flag for use with {@link #onShow}: indicates that the session has been started from the
94     * system assist gesture.
95     */
96    public static final int SHOW_SOURCE_ASSIST_GESTURE = 1<<2;
97
98    /**
99     * Flag for use with {@link #onShow}: indicates that the application itself has invoked
100     * the assistant.
101     */
102    public static final int SHOW_SOURCE_APPLICATION = 1<<3;
103
104    final Context mContext;
105    final HandlerCaller mHandlerCaller;
106
107    final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState();
108
109    IVoiceInteractionManagerService mSystemService;
110    IBinder mToken;
111
112    int mTheme = 0;
113    LayoutInflater mInflater;
114    TypedArray mThemeAttrs;
115    View mRootView;
116    FrameLayout mContentFrame;
117    SoftInputWindow mWindow;
118
119    boolean mInitialized;
120    boolean mWindowAdded;
121    boolean mWindowVisible;
122    boolean mWindowWasVisible;
123    boolean mInShowWindow;
124
125    final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<IBinder, Request>();
126
127    final Insets mTmpInsets = new Insets();
128
129    final WeakReference<VoiceInteractionSession> mWeakRef
130            = new WeakReference<VoiceInteractionSession>(this);
131
132    final IVoiceInteractor mInteractor = new IVoiceInteractor.Stub() {
133        @Override
134        public IVoiceInteractorRequest startConfirmation(String callingPackage,
135                IVoiceInteractorCallback callback, VoiceInteractor.Prompt prompt, Bundle extras) {
136            ConfirmationRequest request = new ConfirmationRequest(callingPackage,
137                    Binder.getCallingUid(), callback, VoiceInteractionSession.this,
138                    prompt, extras);
139            addRequest(request);
140            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_CONFIRMATION,
141                    request));
142            return request.mInterface;
143        }
144
145        @Override
146        public IVoiceInteractorRequest startPickOption(String callingPackage,
147                IVoiceInteractorCallback callback, VoiceInteractor.Prompt prompt,
148                VoiceInteractor.PickOptionRequest.Option[] options, Bundle extras) {
149            PickOptionRequest request = new PickOptionRequest(callingPackage,
150                    Binder.getCallingUid(), callback, VoiceInteractionSession.this,
151                    prompt, options, extras);
152            addRequest(request);
153            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_PICK_OPTION,
154                    request));
155            return request.mInterface;
156        }
157
158        @Override
159        public IVoiceInteractorRequest startCompleteVoice(String callingPackage,
160                IVoiceInteractorCallback callback, VoiceInteractor.Prompt message, Bundle extras) {
161            CompleteVoiceRequest request = new CompleteVoiceRequest(callingPackage,
162                    Binder.getCallingUid(), callback, VoiceInteractionSession.this,
163                    message, extras);
164            addRequest(request);
165            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_COMPLETE_VOICE,
166                    request));
167            return request.mInterface;
168        }
169
170        @Override
171        public IVoiceInteractorRequest startAbortVoice(String callingPackage,
172                IVoiceInteractorCallback callback, VoiceInteractor.Prompt message, Bundle extras) {
173            AbortVoiceRequest request = new AbortVoiceRequest(callingPackage,
174                    Binder.getCallingUid(), callback, VoiceInteractionSession.this,
175                    message, extras);
176            addRequest(request);
177            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_ABORT_VOICE,
178                    request));
179            return request.mInterface;
180        }
181
182        @Override
183        public IVoiceInteractorRequest startCommand(String callingPackage,
184                IVoiceInteractorCallback callback, String command, Bundle extras) {
185            CommandRequest request = new CommandRequest(callingPackage,
186                    Binder.getCallingUid(), callback, VoiceInteractionSession.this,
187                    command, extras);
188            addRequest(request);
189            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_COMMAND,
190                    request));
191            return request.mInterface;
192        }
193
194        @Override
195        public boolean[] supportsCommands(String callingPackage, String[] commands) {
196            Message msg = mHandlerCaller.obtainMessageIOO(MSG_SUPPORTS_COMMANDS,
197                    0, commands, null);
198            SomeArgs args = mHandlerCaller.sendMessageAndWait(msg);
199            if (args != null) {
200                boolean[] res = (boolean[])args.arg1;
201                args.recycle();
202                return res;
203            }
204            return new boolean[commands.length];
205        }
206    };
207
208    final IVoiceInteractionSession mSession = new IVoiceInteractionSession.Stub() {
209        @Override
210        public void show(Bundle sessionArgs, int flags,
211                IVoiceInteractionSessionShowCallback showCallback) {
212            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOO(MSG_SHOW,
213                    flags, sessionArgs, showCallback));
214        }
215
216        @Override
217        public void hide() {
218            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_HIDE));
219        }
220
221        @Override
222        public void handleAssist(final Bundle data, final AssistStructure structure,
223                final AssistContent content) {
224            // We want to pre-warm the AssistStructure before handing it off to the main
225            // thread.  We also want to do this on a separate thread, so that if the app
226            // is for some reason slow (due to slow filling in of async children in the
227            // structure), we don't block other incoming IPCs (such as the screenshot) to
228            // us (since we are a oneway interface, they get serialized).  (Okay?)
229            Thread retriever = new Thread("AssistStructure retriever") {
230                @Override
231                public void run() {
232                    Throwable failure = null;
233                    if (structure != null) {
234                        try {
235                            structure.ensureData();
236                        } catch (Throwable e) {
237                            Log.w(TAG, "Failure retrieving AssistStructure", e);
238                            failure = e;
239                        }
240                    }
241                    mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_HANDLE_ASSIST,
242                            data, failure == null ? structure : null, failure, content));
243                }
244            };
245            retriever.start();
246        }
247
248        @Override
249        public void handleScreenshot(Bitmap screenshot) {
250            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_HANDLE_SCREENSHOT,
251                    screenshot));
252        }
253
254        @Override
255        public void taskStarted(Intent intent, int taskId) {
256            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_TASK_STARTED,
257                    taskId, intent));
258        }
259
260        @Override
261        public void taskFinished(Intent intent, int taskId) {
262            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_TASK_FINISHED,
263                    taskId, intent));
264        }
265
266        @Override
267        public void closeSystemDialogs() {
268            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_CLOSE_SYSTEM_DIALOGS));
269        }
270
271        @Override
272        public void onLockscreenShown() {
273            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_ON_LOCKSCREEN_SHOWN));
274        }
275
276        @Override
277        public void destroy() {
278            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_DESTROY));
279        }
280    };
281
282    /**
283     * Base class representing a request from a voice-driver app to perform a particular
284     * voice operation with the user.  See related subclasses for the types of requests
285     * that are possible.
286     */
287    public static class Request {
288        final IVoiceInteractorRequest mInterface = new IVoiceInteractorRequest.Stub() {
289            @Override
290            public void cancel() throws RemoteException {
291                VoiceInteractionSession session = mSession.get();
292                if (session != null) {
293                    session.mHandlerCaller.sendMessage(
294                            session.mHandlerCaller.obtainMessageO(MSG_CANCEL, Request.this));
295                }
296            }
297        };
298        final String mCallingPackage;
299        final int mCallingUid;
300        final IVoiceInteractorCallback mCallback;
301        final WeakReference<VoiceInteractionSession> mSession;
302        final Bundle mExtras;
303
304        Request(String packageName, int uid, IVoiceInteractorCallback callback,
305                VoiceInteractionSession session, Bundle extras) {
306            mCallingPackage = packageName;
307            mCallingUid = uid;
308            mCallback = callback;
309            mSession = session.mWeakRef;
310            mExtras = extras;
311        }
312
313        /**
314         * Return the uid of the application that initiated the request.
315         */
316        public int getCallingUid() {
317            return mCallingUid;
318        }
319
320        /**
321         * Return the package name of the application that initiated the request.
322         */
323        public String getCallingPackage() {
324            return mCallingPackage;
325        }
326
327        /**
328         * Return any additional extra information that was supplied as part of the request.
329         */
330        public Bundle getExtras() {
331            return mExtras;
332        }
333
334        /**
335         * Check whether this request is currently active.  A request becomes inactive after
336         * calling {@link #cancel} or a final result method that completes the request.  After
337         * this point, further interactions with the request will result in
338         * {@link java.lang.IllegalStateException} errors; you should not catch these errors,
339         * but can use this method if you need to determine the state of the request.  Returns
340         * true if the request is still active.
341         */
342        public boolean isActive() {
343            VoiceInteractionSession session = mSession.get();
344            if (session == null) {
345                return false;
346            }
347            return session.isRequestActive(mInterface.asBinder());
348        }
349
350        void finishRequest() {
351            VoiceInteractionSession session = mSession.get();
352            if (session == null) {
353                throw new IllegalStateException("VoiceInteractionSession has been destroyed");
354            }
355            Request req = session.removeRequest(mInterface.asBinder());
356            if (req == null) {
357                throw new IllegalStateException("Request not active: " + this);
358            } else if (req != this) {
359                throw new IllegalStateException("Current active request " + req
360                        + " not same as calling request " + this);
361            }
362        }
363
364        /**
365         * Ask the app to cancel this current request.
366         * This also finishes the request (it is no longer active).
367         */
368        public void cancel() {
369            try {
370                if (DEBUG) Log.d(TAG, "sendCancelResult: req=" + mInterface);
371                finishRequest();
372                mCallback.deliverCancel(mInterface);
373            } catch (RemoteException e) {
374            }
375        }
376
377        @Override
378        public String toString() {
379            StringBuilder sb = new StringBuilder(128);
380            DebugUtils.buildShortClassTag(this, sb);
381            sb.append(" ");
382            sb.append(mInterface.asBinder());
383            sb.append(" pkg=");
384            sb.append(mCallingPackage);
385            sb.append(" uid=");
386            UserHandle.formatUid(sb, mCallingUid);
387            sb.append('}');
388            return sb.toString();
389        }
390
391        void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
392            writer.print(prefix); writer.print("mInterface=");
393            writer.println(mInterface.asBinder());
394            writer.print(prefix); writer.print("mCallingPackage="); writer.print(mCallingPackage);
395            writer.print(" mCallingUid="); UserHandle.formatUid(writer, mCallingUid);
396            writer.println();
397            writer.print(prefix); writer.print("mCallback=");
398            writer.println(mCallback.asBinder());
399            if (mExtras != null) {
400                writer.print(prefix); writer.print("mExtras=");
401                writer.println(mExtras);
402            }
403        }
404    }
405
406    /**
407     * A request for confirmation from the user of an operation, as per
408     * {@link android.app.VoiceInteractor.ConfirmationRequest
409     * VoiceInteractor.ConfirmationRequest}.
410     */
411    public static final class ConfirmationRequest extends Request {
412        final VoiceInteractor.Prompt mPrompt;
413
414        ConfirmationRequest(String packageName, int uid, IVoiceInteractorCallback callback,
415                VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras) {
416            super(packageName, uid, callback, session, extras);
417            mPrompt = prompt;
418        }
419
420        /**
421         * Return the prompt informing the user of what will happen, as per
422         * {@link android.app.VoiceInteractor.ConfirmationRequest
423         * VoiceInteractor.ConfirmationRequest}.
424         */
425        @Nullable
426        public VoiceInteractor.Prompt getVoicePrompt() {
427            return mPrompt;
428        }
429
430        /**
431         * Return the prompt informing the user of what will happen, as per
432         * {@link android.app.VoiceInteractor.ConfirmationRequest
433         * VoiceInteractor.ConfirmationRequest}.
434         * @deprecated Prefer {@link #getVoicePrompt()} which allows multiple voice prompts.
435         */
436        @Nullable
437        public CharSequence getPrompt() {
438            return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null);
439        }
440
441        /**
442         * Report that the voice interactor has confirmed the operation with the user, resulting
443         * in a call to
444         * {@link android.app.VoiceInteractor.ConfirmationRequest#onConfirmationResult
445         * VoiceInteractor.ConfirmationRequest.onConfirmationResult}.
446         * This finishes the request (it is no longer active).
447         */
448        public void sendConfirmationResult(boolean confirmed, Bundle result) {
449            try {
450                if (DEBUG) Log.d(TAG, "sendConfirmationResult: req=" + mInterface
451                        + " confirmed=" + confirmed + " result=" + result);
452                finishRequest();
453                mCallback.deliverConfirmationResult(mInterface, confirmed, result);
454            } catch (RemoteException e) {
455            }
456        }
457
458        void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
459            super.dump(prefix, fd, writer, args);
460            writer.print(prefix); writer.print("mPrompt=");
461            writer.println(mPrompt);
462        }
463    }
464
465    /**
466     * A request for the user to pick from a set of option, as per
467     * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
468     */
469    public static final class PickOptionRequest extends Request {
470        final VoiceInteractor.Prompt mPrompt;
471        final VoiceInteractor.PickOptionRequest.Option[] mOptions;
472
473        PickOptionRequest(String packageName, int uid, IVoiceInteractorCallback callback,
474                VoiceInteractionSession session, VoiceInteractor.Prompt prompt,
475                VoiceInteractor.PickOptionRequest.Option[] options, Bundle extras) {
476            super(packageName, uid, callback, session, extras);
477            mPrompt = prompt;
478            mOptions = options;
479        }
480
481        /**
482         * Return the prompt informing the user of what they are picking, as per
483         * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
484         */
485        @Nullable
486        public VoiceInteractor.Prompt getVoicePrompt() {
487            return mPrompt;
488        }
489
490        /**
491         * Return the prompt informing the user of what they are picking, as per
492         * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
493         * @deprecated Prefer {@link #getVoicePrompt()} which allows multiple voice prompts.
494         */
495        @Nullable
496        public CharSequence getPrompt() {
497            return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null);
498        }
499
500        /**
501         * Return the set of options the user is picking from, as per
502         * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
503         */
504        public VoiceInteractor.PickOptionRequest.Option[] getOptions() {
505            return mOptions;
506        }
507
508        void sendPickOptionResult(boolean finished,
509                VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) {
510            try {
511                if (DEBUG) Log.d(TAG, "sendPickOptionResult: req=" + mInterface
512                        + " finished=" + finished + " selections=" + selections
513                        + " result=" + result);
514                if (finished) {
515                    finishRequest();
516                }
517                mCallback.deliverPickOptionResult(mInterface, finished, selections, result);
518            } catch (RemoteException e) {
519            }
520        }
521
522        /**
523         * Report an intermediate option selection from the request, without completing it (the
524         * request is still active and the app is waiting for the final option selection),
525         * resulting in a call to
526         * {@link android.app.VoiceInteractor.PickOptionRequest#onPickOptionResult
527         * VoiceInteractor.PickOptionRequest.onPickOptionResult} with false for finished.
528         */
529        public void sendIntermediatePickOptionResult(
530                VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) {
531            sendPickOptionResult(false, selections, result);
532        }
533
534        /**
535         * Report the final option selection for the request, completing the request
536         * and resulting in a call to
537         * {@link android.app.VoiceInteractor.PickOptionRequest#onPickOptionResult
538         * VoiceInteractor.PickOptionRequest.onPickOptionResult} with false for finished.
539         * This finishes the request (it is no longer active).
540         */
541        public void sendPickOptionResult(
542                VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) {
543            sendPickOptionResult(true, selections, result);
544        }
545
546        void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
547            super.dump(prefix, fd, writer, args);
548            writer.print(prefix); writer.print("mPrompt=");
549            writer.println(mPrompt);
550            if (mOptions != null) {
551                writer.print(prefix); writer.println("Options:");
552                for (int i=0; i<mOptions.length; i++) {
553                    VoiceInteractor.PickOptionRequest.Option op = mOptions[i];
554                    writer.print(prefix); writer.print("  #"); writer.print(i); writer.println(":");
555                    writer.print(prefix); writer.print("    mLabel=");
556                    writer.println(op.getLabel());
557                    writer.print(prefix); writer.print("    mIndex=");
558                    writer.println(op.getIndex());
559                    if (op.countSynonyms() > 0) {
560                        writer.print(prefix); writer.println("    Synonyms:");
561                        for (int j=0; j<op.countSynonyms(); j++) {
562                            writer.print(prefix); writer.print("      #"); writer.print(j);
563                            writer.print(": "); writer.println(op.getSynonymAt(j));
564                        }
565                    }
566                    if (op.getExtras() != null) {
567                        writer.print(prefix); writer.print("    mExtras=");
568                        writer.println(op.getExtras());
569                    }
570                }
571            }
572        }
573    }
574
575    /**
576     * A request to simply inform the user that the voice operation has completed, as per
577     * {@link android.app.VoiceInteractor.CompleteVoiceRequest
578     * VoiceInteractor.CompleteVoiceRequest}.
579     */
580    public static final class CompleteVoiceRequest extends Request {
581        final VoiceInteractor.Prompt mPrompt;
582
583        CompleteVoiceRequest(String packageName, int uid, IVoiceInteractorCallback callback,
584                VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras) {
585            super(packageName, uid, callback, session, extras);
586            mPrompt = prompt;
587        }
588
589        /**
590         * Return the message informing the user of the completion, as per
591         * {@link android.app.VoiceInteractor.CompleteVoiceRequest
592         * VoiceInteractor.CompleteVoiceRequest}.
593         */
594        @Nullable
595        public VoiceInteractor.Prompt getVoicePrompt() {
596            return mPrompt;
597        }
598
599        /**
600         * Return the message informing the user of the completion, as per
601         * {@link android.app.VoiceInteractor.CompleteVoiceRequest
602         * VoiceInteractor.CompleteVoiceRequest}.
603         * @deprecated Prefer {@link #getVoicePrompt()} which allows a separate visual message.
604         */
605        @Nullable
606        public CharSequence getMessage() {
607            return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null);
608        }
609
610        /**
611         * Report that the voice interactor has finished completing the voice operation, resulting
612         * in a call to
613         * {@link android.app.VoiceInteractor.CompleteVoiceRequest#onCompleteResult
614         * VoiceInteractor.CompleteVoiceRequest.onCompleteResult}.
615         * This finishes the request (it is no longer active).
616         */
617        public void sendCompleteResult(Bundle result) {
618            try {
619                if (DEBUG) Log.d(TAG, "sendCompleteVoiceResult: req=" + mInterface
620                        + " result=" + result);
621                finishRequest();
622                mCallback.deliverCompleteVoiceResult(mInterface, result);
623            } catch (RemoteException e) {
624            }
625        }
626
627        void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
628            super.dump(prefix, fd, writer, args);
629            writer.print(prefix); writer.print("mPrompt=");
630            writer.println(mPrompt);
631        }
632    }
633
634    /**
635     * A request to report that the current user interaction can not be completed with voice, as per
636     * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
637     */
638    public static final class AbortVoiceRequest extends Request {
639        final VoiceInteractor.Prompt mPrompt;
640
641        AbortVoiceRequest(String packageName, int uid, IVoiceInteractorCallback callback,
642                VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras) {
643            super(packageName, uid, callback, session, extras);
644            mPrompt = prompt;
645        }
646
647        /**
648         * Return the message informing the user of the problem, as per
649         * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
650         */
651        @Nullable
652        public VoiceInteractor.Prompt getVoicePrompt() {
653            return mPrompt;
654        }
655
656        /**
657         * Return the message informing the user of the problem, as per
658         * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
659         * @deprecated Prefer {@link #getVoicePrompt()} which allows a separate visual message.
660         */
661        @Nullable
662        public CharSequence getMessage() {
663            return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null);
664        }
665
666        /**
667         * Report that the voice interactor has finished aborting the voice operation, resulting
668         * in a call to
669         * {@link android.app.VoiceInteractor.AbortVoiceRequest#onAbortResult
670         * VoiceInteractor.AbortVoiceRequest.onAbortResult}.  This finishes the request (it
671         * is no longer active).
672         */
673        public void sendAbortResult(Bundle result) {
674            try {
675                if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface
676                        + " result=" + result);
677                finishRequest();
678                mCallback.deliverAbortVoiceResult(mInterface, result);
679            } catch (RemoteException e) {
680            }
681        }
682
683        void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
684            super.dump(prefix, fd, writer, args);
685            writer.print(prefix); writer.print("mPrompt=");
686            writer.println(mPrompt);
687        }
688    }
689
690    /**
691     * A generic vendor-specific request, as per
692     * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}.
693     */
694    public static final class CommandRequest extends Request {
695        final String mCommand;
696
697        CommandRequest(String packageName, int uid, IVoiceInteractorCallback callback,
698                VoiceInteractionSession session, String command, Bundle extras) {
699            super(packageName, uid, callback, session, extras);
700            mCommand = command;
701        }
702
703        /**
704         * Return the command that is being executed, as per
705         * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}.
706         */
707        public String getCommand() {
708            return mCommand;
709        }
710
711        void sendCommandResult(boolean finished, Bundle result) {
712            try {
713                if (DEBUG) Log.d(TAG, "sendCommandResult: req=" + mInterface
714                        + " result=" + result);
715                if (finished) {
716                    finishRequest();
717                }
718                mCallback.deliverCommandResult(mInterface, finished, result);
719            } catch (RemoteException e) {
720            }
721        }
722
723        /**
724         * Report an intermediate result of the request, without completing it (the request
725         * is still active and the app is waiting for the final result), resulting in a call to
726         * {@link android.app.VoiceInteractor.CommandRequest#onCommandResult
727         * VoiceInteractor.CommandRequest.onCommandResult} with false for isCompleted.
728         */
729        public void sendIntermediateResult(Bundle result) {
730            sendCommandResult(false, result);
731        }
732
733        /**
734         * Report the final result of the request, completing the request and resulting in a call to
735         * {@link android.app.VoiceInteractor.CommandRequest#onCommandResult
736         * VoiceInteractor.CommandRequest.onCommandResult} with true for isCompleted.
737         * This finishes the request (it is no longer active).
738         */
739        public void sendResult(Bundle result) {
740            sendCommandResult(true, result);
741        }
742
743        void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
744            super.dump(prefix, fd, writer, args);
745            writer.print(prefix); writer.print("mCommand=");
746            writer.println(mCommand);
747        }
748    }
749
750    static final int MSG_START_CONFIRMATION = 1;
751    static final int MSG_START_PICK_OPTION = 2;
752    static final int MSG_START_COMPLETE_VOICE = 3;
753    static final int MSG_START_ABORT_VOICE = 4;
754    static final int MSG_START_COMMAND = 5;
755    static final int MSG_SUPPORTS_COMMANDS = 6;
756    static final int MSG_CANCEL = 7;
757
758    static final int MSG_TASK_STARTED = 100;
759    static final int MSG_TASK_FINISHED = 101;
760    static final int MSG_CLOSE_SYSTEM_DIALOGS = 102;
761    static final int MSG_DESTROY = 103;
762    static final int MSG_HANDLE_ASSIST = 104;
763    static final int MSG_HANDLE_SCREENSHOT = 105;
764    static final int MSG_SHOW = 106;
765    static final int MSG_HIDE = 107;
766    static final int MSG_ON_LOCKSCREEN_SHOWN = 108;
767
768    class MyCallbacks implements HandlerCaller.Callback, SoftInputWindow.Callback {
769        @Override
770        public void executeMessage(Message msg) {
771            SomeArgs args = null;
772            switch (msg.what) {
773                case MSG_START_CONFIRMATION:
774                    if (DEBUG) Log.d(TAG, "onConfirm: req=" + msg.obj);
775                    onRequestConfirmation((ConfirmationRequest) msg.obj);
776                    break;
777                case MSG_START_PICK_OPTION:
778                    if (DEBUG) Log.d(TAG, "onPickOption: req=" + msg.obj);
779                    onRequestPickOption((PickOptionRequest) msg.obj);
780                    break;
781                case MSG_START_COMPLETE_VOICE:
782                    if (DEBUG) Log.d(TAG, "onCompleteVoice: req=" + msg.obj);
783                    onRequestCompleteVoice((CompleteVoiceRequest) msg.obj);
784                    break;
785                case MSG_START_ABORT_VOICE:
786                    if (DEBUG) Log.d(TAG, "onAbortVoice: req=" + msg.obj);
787                    onRequestAbortVoice((AbortVoiceRequest) msg.obj);
788                    break;
789                case MSG_START_COMMAND:
790                    if (DEBUG) Log.d(TAG, "onCommand: req=" + msg.obj);
791                    onRequestCommand((CommandRequest) msg.obj);
792                    break;
793                case MSG_SUPPORTS_COMMANDS:
794                    args = (SomeArgs)msg.obj;
795                    if (DEBUG) Log.d(TAG, "onGetSupportedCommands: cmds=" + args.arg1);
796                    args.arg1 = onGetSupportedCommands((String[]) args.arg1);
797                    args.complete();
798                    args = null;
799                    break;
800                case MSG_CANCEL:
801                    if (DEBUG) Log.d(TAG, "onCancel: req=" + ((Request)msg.obj));
802                    onCancelRequest((Request) msg.obj);
803                    break;
804                case MSG_TASK_STARTED:
805                    if (DEBUG) Log.d(TAG, "onTaskStarted: intent=" + msg.obj
806                            + " taskId=" + msg.arg1);
807                    onTaskStarted((Intent) msg.obj, msg.arg1);
808                    break;
809                case MSG_TASK_FINISHED:
810                    if (DEBUG) Log.d(TAG, "onTaskFinished: intent=" + msg.obj
811                            + " taskId=" + msg.arg1);
812                    onTaskFinished((Intent) msg.obj, msg.arg1);
813                    break;
814                case MSG_CLOSE_SYSTEM_DIALOGS:
815                    if (DEBUG) Log.d(TAG, "onCloseSystemDialogs");
816                    onCloseSystemDialogs();
817                    break;
818                case MSG_DESTROY:
819                    if (DEBUG) Log.d(TAG, "doDestroy");
820                    doDestroy();
821                    break;
822                case MSG_HANDLE_ASSIST:
823                    args = (SomeArgs)msg.obj;
824                    if (DEBUG) Log.d(TAG, "onHandleAssist: data=" + args.arg1
825                            + " structure=" + args.arg2 + " content=" + args.arg3);
826                    doOnHandleAssist((Bundle) args.arg1, (AssistStructure) args.arg2,
827                            (Throwable) args.arg3, (AssistContent) args.arg4);
828                    break;
829                case MSG_HANDLE_SCREENSHOT:
830                    if (DEBUG) Log.d(TAG, "onHandleScreenshot: " + msg.obj);
831                    onHandleScreenshot((Bitmap) msg.obj);
832                    break;
833                case MSG_SHOW:
834                    args = (SomeArgs)msg.obj;
835                    if (DEBUG) Log.d(TAG, "doShow: args=" + args.arg1
836                            + " flags=" + msg.arg1
837                            + " showCallback=" + args.arg2);
838                    doShow((Bundle) args.arg1, msg.arg1,
839                            (IVoiceInteractionSessionShowCallback) args.arg2);
840                    break;
841                case MSG_HIDE:
842                    if (DEBUG) Log.d(TAG, "doHide");
843                    doHide();
844                    break;
845                case MSG_ON_LOCKSCREEN_SHOWN:
846                    if (DEBUG) Log.d(TAG, "onLockscreenShown");
847                    onLockscreenShown();
848                    break;
849            }
850            if (args != null) {
851                args.recycle();
852            }
853        }
854
855        @Override
856        public void onBackPressed() {
857            VoiceInteractionSession.this.onBackPressed();
858        }
859    }
860
861    final MyCallbacks mCallbacks = new MyCallbacks();
862
863    /**
864     * Information about where interesting parts of the input method UI appear.
865     */
866    public static final class Insets {
867        /**
868         * This is the part of the UI that is the main content.  It is
869         * used to determine the basic space needed, to resize/pan the
870         * application behind.  It is assumed that this inset does not
871         * change very much, since any change will cause a full resize/pan
872         * of the application behind.  This value is relative to the top edge
873         * of the input method window.
874         */
875        public final Rect contentInsets = new Rect();
876
877        /**
878         * This is the region of the UI that is touchable.  It is used when
879         * {@link #touchableInsets} is set to {@link #TOUCHABLE_INSETS_REGION}.
880         * The region should be specified relative to the origin of the window frame.
881         */
882        public final Region touchableRegion = new Region();
883
884        /**
885         * Option for {@link #touchableInsets}: the entire window frame
886         * can be touched.
887         */
888        public static final int TOUCHABLE_INSETS_FRAME
889                = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
890
891        /**
892         * Option for {@link #touchableInsets}: the area inside of
893         * the content insets can be touched.
894         */
895        public static final int TOUCHABLE_INSETS_CONTENT
896                = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
897
898        /**
899         * Option for {@link #touchableInsets}: the region specified by
900         * {@link #touchableRegion} can be touched.
901         */
902        public static final int TOUCHABLE_INSETS_REGION
903                = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
904
905        /**
906         * Determine which area of the window is touchable by the user.  May
907         * be one of: {@link #TOUCHABLE_INSETS_FRAME},
908         * {@link #TOUCHABLE_INSETS_CONTENT}, or {@link #TOUCHABLE_INSETS_REGION}.
909         */
910        public int touchableInsets;
911    }
912
913    final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
914            new ViewTreeObserver.OnComputeInternalInsetsListener() {
915        public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
916            onComputeInsets(mTmpInsets);
917            info.contentInsets.set(mTmpInsets.contentInsets);
918            info.visibleInsets.set(mTmpInsets.contentInsets);
919            info.touchableRegion.set(mTmpInsets.touchableRegion);
920            info.setTouchableInsets(mTmpInsets.touchableInsets);
921        }
922    };
923
924    public VoiceInteractionSession(Context context) {
925        this(context, new Handler());
926    }
927
928    public VoiceInteractionSession(Context context, Handler handler) {
929        mContext = context;
930        mHandlerCaller = new HandlerCaller(context, handler.getLooper(),
931                mCallbacks, true);
932    }
933
934    public Context getContext() {
935        return mContext;
936    }
937
938    void addRequest(Request req) {
939        synchronized (this) {
940            mActiveRequests.put(req.mInterface.asBinder(), req);
941        }
942    }
943
944    boolean isRequestActive(IBinder reqInterface) {
945        synchronized (this) {
946            return mActiveRequests.containsKey(reqInterface);
947        }
948    }
949
950    Request removeRequest(IBinder reqInterface) {
951        synchronized (this) {
952            return mActiveRequests.remove(reqInterface);
953        }
954    }
955
956    void doCreate(IVoiceInteractionManagerService service, IBinder token) {
957        mSystemService = service;
958        mToken = token;
959        onCreate();
960    }
961
962    void doShow(Bundle args, int flags, final IVoiceInteractionSessionShowCallback showCallback) {
963        if (DEBUG) Log.v(TAG, "Showing window: mWindowAdded=" + mWindowAdded
964                + " mWindowVisible=" + mWindowVisible);
965
966        if (mInShowWindow) {
967            Log.w(TAG, "Re-entrance in to showWindow");
968            return;
969        }
970
971        try {
972            mInShowWindow = true;
973            if (!mWindowVisible) {
974                if (!mWindowAdded) {
975                    mWindowAdded = true;
976                    View v = onCreateContentView();
977                    if (v != null) {
978                        setContentView(v);
979                    }
980                }
981            }
982            onShow(args, flags);
983            if (!mWindowVisible) {
984                mWindowVisible = true;
985                mWindow.show();
986            }
987            if (showCallback != null) {
988                mRootView.invalidate();
989                mRootView.getViewTreeObserver().addOnPreDrawListener(
990                        new ViewTreeObserver.OnPreDrawListener() {
991                            @Override
992                            public boolean onPreDraw() {
993                                mRootView.getViewTreeObserver().removeOnPreDrawListener(this);
994                                try {
995                                    showCallback.onShown();
996                                } catch (RemoteException e) {
997                                    Log.w(TAG, "Error calling onShown", e);
998                                }
999                                return true;
1000                            }
1001                        });
1002            }
1003        } finally {
1004            mWindowWasVisible = true;
1005            mInShowWindow = false;
1006        }
1007    }
1008
1009    void doHide() {
1010        if (mWindowVisible) {
1011            mWindow.hide();
1012            mWindowVisible = false;
1013            onHide();
1014        }
1015    }
1016
1017    void doDestroy() {
1018        onDestroy();
1019        if (mInitialized) {
1020            mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
1021                    mInsetsComputer);
1022            if (mWindowAdded) {
1023                mWindow.dismiss();
1024                mWindowAdded = false;
1025            }
1026            mInitialized = false;
1027        }
1028    }
1029
1030    void initViews() {
1031        mInitialized = true;
1032
1033        mThemeAttrs = mContext.obtainStyledAttributes(android.R.styleable.VoiceInteractionSession);
1034        mRootView = mInflater.inflate(
1035                com.android.internal.R.layout.voice_interaction_session, null);
1036        mRootView.setSystemUiVisibility(
1037                View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
1038                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
1039        mWindow.setContentView(mRootView);
1040        mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
1041
1042        mContentFrame = (FrameLayout)mRootView.findViewById(android.R.id.content);
1043    }
1044
1045    /**
1046     * Equivalent to {@link VoiceInteractionService#setDisabledShowContext
1047     * VoiceInteractionService.setDisabledShowContext(int)}.
1048     */
1049    public void setDisabledShowContext(int flags) {
1050        try {
1051            mSystemService.setDisabledShowContext(flags);
1052        } catch (RemoteException e) {
1053        }
1054    }
1055
1056    /**
1057     * Equivalent to {@link VoiceInteractionService#getDisabledShowContext
1058     * VoiceInteractionService.getDisabledShowContext}.
1059     */
1060    public int getDisabledShowContext() {
1061        try {
1062            return mSystemService.getDisabledShowContext();
1063        } catch (RemoteException e) {
1064            return 0;
1065        }
1066    }
1067
1068    /**
1069     * Return which show context flags have been disabled by the user through the system
1070     * settings UI, so the session will never get this data.  Returned flags are any combination of
1071     * {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST} and
1072     * {@link VoiceInteractionSession#SHOW_WITH_SCREENSHOT
1073     * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}.  Note that this only tells you about
1074     * global user settings, not about restrictions that may be applied contextual based on
1075     * the current application the user is in or other transient states.
1076     */
1077    public int getUserDisabledShowContext() {
1078        try {
1079            return mSystemService.getUserDisabledShowContext();
1080        } catch (RemoteException e) {
1081            return 0;
1082        }
1083    }
1084
1085    /**
1086     * Show the UI for this session.  This asks the system to go through the process of showing
1087     * your UI, which will eventually culminate in {@link #onShow}.  This is similar to calling
1088     * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
1089     * @param args Arbitrary arguments that will be propagated {@link #onShow}.
1090     * @param flags Indicates additional optional behavior that should be performed.  May
1091     * be any combination of
1092     * {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST} and
1093     * {@link VoiceInteractionSession#SHOW_WITH_SCREENSHOT
1094     * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}
1095     * to request that the system generate and deliver assist data on the current foreground
1096     * app as part of showing the session UI.
1097     */
1098    public void show(Bundle args, int flags) {
1099        if (mToken == null) {
1100            throw new IllegalStateException("Can't call before onCreate()");
1101        }
1102        try {
1103            mSystemService.showSessionFromSession(mToken, args, flags);
1104        } catch (RemoteException e) {
1105        }
1106    }
1107
1108    /**
1109     * Hide the session's UI, if currently shown.  Call this when you are done with your
1110     * user interaction.
1111     */
1112    public void hide() {
1113        if (mToken == null) {
1114            throw new IllegalStateException("Can't call before onCreate()");
1115        }
1116        try {
1117            mSystemService.hideSessionFromSession(mToken);
1118        } catch (RemoteException e) {
1119        }
1120    }
1121
1122    /**
1123     * You can call this to customize the theme used by your IME's window.
1124     * This must be set before {@link #onCreate}, so you
1125     * will typically call it in your constructor with the resource ID
1126     * of your custom theme.
1127     */
1128    public void setTheme(int theme) {
1129        if (mWindow != null) {
1130            throw new IllegalStateException("Must be called before onCreate()");
1131        }
1132        mTheme = theme;
1133    }
1134
1135    /**
1136     * Ask that a new activity be started for voice interaction.  This will create a
1137     * new dedicated task in the activity manager for this voice interaction session;
1138     * this means that {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK}
1139     * will be set for you to make it a new task.
1140     *
1141     * <p>The newly started activity will be displayed to the user in a special way, as
1142     * a layer under the voice interaction UI.</p>
1143     *
1144     * <p>As the voice activity runs, it can retrieve a {@link android.app.VoiceInteractor}
1145     * through which it can perform voice interactions through your session.  These requests
1146     * for voice interactions will appear as callbacks on {@link #onGetSupportedCommands},
1147     * {@link #onRequestConfirmation}, {@link #onRequestPickOption},
1148     * {@link #onRequestCompleteVoice}, {@link #onRequestAbortVoice},
1149     * or {@link #onRequestCommand}
1150     *
1151     * <p>You will receive a call to {@link #onTaskStarted} when the task starts up
1152     * and {@link #onTaskFinished} when the last activity has finished.
1153     *
1154     * @param intent The Intent to start this voice interaction.  The given Intent will
1155     * always have {@link Intent#CATEGORY_VOICE Intent.CATEGORY_VOICE} added to it, since
1156     * this is part of a voice interaction.
1157     */
1158    public void startVoiceActivity(Intent intent) {
1159        if (mToken == null) {
1160            throw new IllegalStateException("Can't call before onCreate()");
1161        }
1162        try {
1163            intent.migrateExtraStreamToClipData();
1164            intent.prepareToLeaveProcess();
1165            int res = mSystemService.startVoiceActivity(mToken, intent,
1166                    intent.resolveType(mContext.getContentResolver()));
1167            Instrumentation.checkStartActivityResult(res, intent);
1168        } catch (RemoteException e) {
1169        }
1170    }
1171
1172    /**
1173     * Set whether this session will keep the device awake while it is running a voice
1174     * activity.  By default, the system holds a wake lock for it while in this state,
1175     * so that it can work even if the screen is off.  Setting this to false removes that
1176     * wake lock, allowing the CPU to go to sleep.  This is typically used if the
1177     * session decides it has been waiting too long for a response from the user and
1178     * doesn't want to let this continue to drain the battery.
1179     *
1180     * <p>Passing false here will release the wake lock, and you can call later with
1181     * true to re-acquire it.  It will also be automatically re-acquired for you each
1182     * time you start a new voice activity task -- that is when you call
1183     * {@link #startVoiceActivity}.</p>
1184     */
1185    public void setKeepAwake(boolean keepAwake) {
1186        if (mToken == null) {
1187            throw new IllegalStateException("Can't call before onCreate()");
1188        }
1189        try {
1190            mSystemService.setKeepAwake(mToken, keepAwake);
1191        } catch (RemoteException e) {
1192        }
1193    }
1194
1195    /**
1196     * Request that all system dialogs (and status bar shade etc) be closed, allowing
1197     * access to the session's UI.  This will <em>not</em> cause the lock screen to be
1198     * dismissed.
1199     */
1200    public void closeSystemDialogs() {
1201        if (mToken == null) {
1202            throw new IllegalStateException("Can't call before onCreate()");
1203        }
1204        try {
1205            mSystemService.closeSystemDialogs(mToken);
1206        } catch (RemoteException e) {
1207        }
1208    }
1209
1210    /**
1211     * Convenience for inflating views.
1212     */
1213    public LayoutInflater getLayoutInflater() {
1214        return mInflater;
1215    }
1216
1217    /**
1218     * Retrieve the window being used to show the session's UI.
1219     */
1220    public Dialog getWindow() {
1221        return mWindow;
1222    }
1223
1224    /**
1225     * Finish the session.  This completely destroys the session -- the next time it is shown,
1226     * an entirely new one will be created.  You do not normally call this function; instead,
1227     * use {@link #hide} and allow the system to destroy your session if it needs its RAM.
1228     */
1229    public void finish() {
1230        if (mToken == null) {
1231            throw new IllegalStateException("Can't call before onCreate()");
1232        }
1233        try {
1234            mSystemService.finish(mToken);
1235        } catch (RemoteException e) {
1236        }
1237    }
1238
1239    /**
1240     * Initiatize a new session.  At this point you don't know exactly what this
1241     * session will be used for; you will find that out in {@link #onShow}.
1242     */
1243    public void onCreate() {
1244        doOnCreate();
1245    }
1246
1247    private void doOnCreate() {
1248        mTheme = mTheme != 0 ? mTheme
1249                : com.android.internal.R.style.Theme_DeviceDefault_VoiceInteractionSession;
1250        mInflater = (LayoutInflater)mContext.getSystemService(
1251                Context.LAYOUT_INFLATER_SERVICE);
1252        mWindow = new SoftInputWindow(mContext, "VoiceInteractionSession", mTheme,
1253                mCallbacks, this, mDispatcherState,
1254                WindowManager.LayoutParams.TYPE_VOICE_INTERACTION, Gravity.BOTTOM, true);
1255        mWindow.getWindow().addFlags(
1256                WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED |
1257                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
1258                WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
1259        initViews();
1260        mWindow.getWindow().setLayout(MATCH_PARENT, MATCH_PARENT);
1261        mWindow.setToken(mToken);
1262    }
1263
1264    /**
1265     * Called when the session UI is going to be shown.  This is called after
1266     * {@link #onCreateContentView} (if the session's content UI needed to be created) and
1267     * immediately prior to the window being shown.  This may be called while the window
1268     * is already shown, if a show request has come in while it is shown, to allow you to
1269     * update the UI to match the new show arguments.
1270     *
1271     * @param args The arguments that were supplied to
1272     * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
1273     * @param showFlags The show flags originally provided to
1274     * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
1275     */
1276    public void onShow(Bundle args, int showFlags) {
1277    }
1278
1279    /**
1280     * Called immediately after stopping to show the session UI.
1281     */
1282    public void onHide() {
1283    }
1284
1285    /**
1286     * Last callback to the session as it is being finished.
1287     */
1288    public void onDestroy() {
1289    }
1290
1291    /**
1292     * Hook in which to create the session's UI.
1293     */
1294    public View onCreateContentView() {
1295        return null;
1296    }
1297
1298    public void setContentView(View view) {
1299        mContentFrame.removeAllViews();
1300        mContentFrame.addView(view, new FrameLayout.LayoutParams(
1301                ViewGroup.LayoutParams.MATCH_PARENT,
1302                ViewGroup.LayoutParams.MATCH_PARENT));
1303        mContentFrame.requestApplyInsets();
1304    }
1305
1306    void doOnHandleAssist(Bundle data, AssistStructure structure, Throwable failure,
1307            AssistContent content) {
1308        if (failure != null) {
1309            onAssistStructureFailure(failure);
1310        }
1311        onHandleAssist(data, structure, content);
1312    }
1313
1314    /**
1315     * Called when there has been a failure transferring the {@link AssistStructure} to
1316     * the assistant.  This may happen, for example, if the data is too large and results
1317     * in an out of memory exception, or the client has provided corrupt data.  This will
1318     * be called immediately before {@link #onHandleAssist} and the AssistStructure supplied
1319     * there afterwards will be null.
1320     *
1321     * @param failure The failure exception that was thrown when building the
1322     * {@link AssistStructure}.
1323     */
1324    public void onAssistStructureFailure(Throwable failure) {
1325    }
1326
1327    /**
1328     * Called to receive data from the application that the user was currently viewing when
1329     * an assist session is started.  If the original show request did not specify
1330     * {@link #SHOW_WITH_ASSIST}, this method will not be called.
1331     *
1332     * @param data Arbitrary data supplied by the app through
1333     * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}.
1334     * May be null if assist data has been disabled by the user or device policy.
1335     * @param structure If available, the structure definition of all windows currently
1336     * displayed by the app.  May be null if assist data has been disabled by the user
1337     * or device policy; will be an empty stub if the application has disabled assist
1338     * by marking its window as secure.
1339     * @param content Additional content data supplied by the app through
1340     * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}.
1341     * May be null if assist data has been disabled by the user or device policy; will
1342     * not be automatically filled in with data from the app if the app has marked its
1343     * window as secure.
1344     */
1345    public void onHandleAssist(@Nullable Bundle data, @Nullable AssistStructure structure,
1346            @Nullable AssistContent content) {
1347    }
1348
1349    /**
1350     * Called to receive a screenshot of what the user was currently viewing when an assist
1351     * session is started.  May be null if screenshots are disabled by the user, policy,
1352     * or application.  If the original show request did not specify
1353     * {@link #SHOW_WITH_SCREENSHOT}, this method will not be called.
1354     */
1355    public void onHandleScreenshot(@Nullable Bitmap screenshot) {
1356    }
1357
1358    public boolean onKeyDown(int keyCode, KeyEvent event) {
1359        return false;
1360    }
1361
1362    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
1363        return false;
1364    }
1365
1366    public boolean onKeyUp(int keyCode, KeyEvent event) {
1367        return false;
1368    }
1369
1370    public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
1371        return false;
1372    }
1373
1374    /**
1375     * Called when the user presses the back button while focus is in the session UI.  Note
1376     * that this will only happen if the session UI has requested input focus in its window;
1377     * otherwise, the back key will go to whatever window has focus and do whatever behavior
1378     * it normally has there.  The default implementation simply calls {@link #hide}.
1379     */
1380    public void onBackPressed() {
1381        hide();
1382    }
1383
1384    /**
1385     * Sessions automatically watch for requests that all system UI be closed (such as when
1386     * the user presses HOME), which will appear here.  The default implementation always
1387     * calls {@link #hide}.
1388     */
1389    public void onCloseSystemDialogs() {
1390        hide();
1391    }
1392
1393    /**
1394     * Called when the lockscreen was shown.
1395     */
1396    public void onLockscreenShown() {
1397        hide();
1398    }
1399
1400    @Override
1401    public void onConfigurationChanged(Configuration newConfig) {
1402    }
1403
1404    @Override
1405    public void onLowMemory() {
1406    }
1407
1408    @Override
1409    public void onTrimMemory(int level) {
1410    }
1411
1412    /**
1413     * Compute the interesting insets into your UI.  The default implementation
1414     * sets {@link Insets#contentInsets outInsets.contentInsets.top} to the height
1415     * of the window, meaning it should not adjust content underneath.  The default touchable
1416     * insets are {@link Insets#TOUCHABLE_INSETS_FRAME}, meaning it consumes all touch
1417     * events within its window frame.
1418     *
1419     * @param outInsets Fill in with the current UI insets.
1420     */
1421    public void onComputeInsets(Insets outInsets) {
1422        outInsets.contentInsets.left = 0;
1423        outInsets.contentInsets.bottom = 0;
1424        outInsets.contentInsets.right = 0;
1425        View decor = getWindow().getWindow().getDecorView();
1426        outInsets.contentInsets.top = decor.getHeight();
1427        outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_FRAME;
1428        outInsets.touchableRegion.setEmpty();
1429    }
1430
1431    /**
1432     * Called when a task initiated by {@link #startVoiceActivity(android.content.Intent)}
1433     * has actually started.
1434     *
1435     * @param intent The original {@link Intent} supplied to
1436     * {@link #startVoiceActivity(android.content.Intent)}.
1437     * @param taskId Unique ID of the now running task.
1438     */
1439    public void onTaskStarted(Intent intent, int taskId) {
1440    }
1441
1442    /**
1443     * Called when the last activity of a task initiated by
1444     * {@link #startVoiceActivity(android.content.Intent)} has finished.  The default
1445     * implementation calls {@link #finish()} on the assumption that this represents
1446     * the completion of a voice action.  You can override the implementation if you would
1447     * like a different behavior.
1448     *
1449     * @param intent The original {@link Intent} supplied to
1450     * {@link #startVoiceActivity(android.content.Intent)}.
1451     * @param taskId Unique ID of the finished task.
1452     */
1453    public void onTaskFinished(Intent intent, int taskId) {
1454        hide();
1455    }
1456
1457    /**
1458     * Request to query for what extended commands the session supports.
1459     *
1460     * @param commands An array of commands that are being queried.
1461     * @return Return an array of booleans indicating which of each entry in the
1462     * command array is supported.  A true entry in the array indicates the command
1463     * is supported; false indicates it is not.  The default implementation returns
1464     * an array of all false entries.
1465     */
1466    public boolean[] onGetSupportedCommands(String[] commands) {
1467        return new boolean[commands.length];
1468    }
1469
1470    /**
1471     * Request to confirm with the user before proceeding with an unrecoverable operation,
1472     * corresponding to a {@link android.app.VoiceInteractor.ConfirmationRequest
1473     * VoiceInteractor.ConfirmationRequest}.
1474     *
1475     * @param request The active request.
1476     */
1477    public void onRequestConfirmation(ConfirmationRequest request) {
1478    }
1479
1480    /**
1481     * Request for the user to pick one of N options, corresponding to a
1482     * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
1483     *
1484     * @param request The active request.
1485     */
1486    public void onRequestPickOption(PickOptionRequest request) {
1487    }
1488
1489    /**
1490     * Request to complete the voice interaction session because the voice activity successfully
1491     * completed its interaction using voice.  Corresponds to
1492     * {@link android.app.VoiceInteractor.CompleteVoiceRequest
1493     * VoiceInteractor.CompleteVoiceRequest}.  The default implementation just sends an empty
1494     * confirmation back to allow the activity to exit.
1495     *
1496     * @param request The active request.
1497     */
1498    public void onRequestCompleteVoice(CompleteVoiceRequest request) {
1499    }
1500
1501    /**
1502     * Request to abort the voice interaction session because the voice activity can not
1503     * complete its interaction using voice.  Corresponds to
1504     * {@link android.app.VoiceInteractor.AbortVoiceRequest
1505     * VoiceInteractor.AbortVoiceRequest}.  The default implementation just sends an empty
1506     * confirmation back to allow the activity to exit.
1507     *
1508     * @param request The active request.
1509     */
1510    public void onRequestAbortVoice(AbortVoiceRequest request) {
1511    }
1512
1513    /**
1514     * Process an arbitrary extended command from the caller,
1515     * corresponding to a {@link android.app.VoiceInteractor.CommandRequest
1516     * VoiceInteractor.CommandRequest}.
1517     *
1518     * @param request The active request.
1519     */
1520    public void onRequestCommand(CommandRequest request) {
1521    }
1522
1523    /**
1524     * Called when the {@link android.app.VoiceInteractor} has asked to cancel a {@link Request}
1525     * that was previously delivered to {@link #onRequestConfirmation},
1526     * {@link #onRequestPickOption}, {@link #onRequestCompleteVoice}, {@link #onRequestAbortVoice},
1527     * or {@link #onRequestCommand}.
1528     *
1529     * @param request The request that is being canceled.
1530     */
1531    public void onCancelRequest(Request request) {
1532    }
1533
1534    /**
1535     * Print the Service's state into the given stream.  This gets invoked by
1536     * {@link VoiceInteractionSessionService} when its Service
1537     * {@link android.app.Service#dump} method is called.
1538     *
1539     * @param prefix Text to print at the front of each line.
1540     * @param fd The raw file descriptor that the dump is being sent to.
1541     * @param writer The PrintWriter to which you should dump your state.  This will be
1542     * closed for you after you return.
1543     * @param args additional arguments to the dump request.
1544     */
1545    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
1546        writer.print(prefix); writer.print("mToken="); writer.println(mToken);
1547        writer.print(prefix); writer.print("mTheme=#"); writer.println(Integer.toHexString(mTheme));
1548        writer.print(prefix); writer.print("mInitialized="); writer.println(mInitialized);
1549        writer.print(prefix); writer.print("mWindowAdded="); writer.print(mWindowAdded);
1550        writer.print(" mWindowVisible="); writer.println(mWindowVisible);
1551        writer.print(prefix); writer.print("mWindowWasVisible="); writer.print(mWindowWasVisible);
1552        writer.print(" mInShowWindow="); writer.println(mInShowWindow);
1553        if (mActiveRequests.size() > 0) {
1554            writer.print(prefix); writer.println("Active requests:");
1555            String innerPrefix = prefix + "    ";
1556            for (int i=0; i<mActiveRequests.size(); i++) {
1557                Request req = mActiveRequests.valueAt(i);
1558                writer.print(prefix); writer.print("  #"); writer.print(i);
1559                writer.print(": ");
1560                writer.println(req);
1561                req.dump(innerPrefix, fd, writer, args);
1562
1563            }
1564        }
1565    }
1566}
1567