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