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