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 Request(String packageName, int uid, IVoiceInteractorCallback callback, VoiceInteractionSession session, Bundle extras)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 */ getCallingUid()339 public int getCallingUid() { 340 return mCallingUid; 341 } 342 343 /** 344 * Return the package name of the application that initiated the request. 345 */ getCallingPackage()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 */ getExtras()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 */ isActive()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 finishRequest()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 */ cancel()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 toString()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 dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)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 ConfirmationRequest(String packageName, int uid, IVoiceInteractorCallback callback, VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras)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 getVoicePrompt()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 getPrompt()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 */ sendConfirmationResult(boolean confirmed, Bundle result)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 dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)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 PickOptionRequest(String packageName, int uid, IVoiceInteractorCallback callback, VoiceInteractionSession session, VoiceInteractor.Prompt prompt, VoiceInteractor.PickOptionRequest.Option[] options, Bundle extras)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 getVoicePrompt()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 getPrompt()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 */ getOptions()529 public VoiceInteractor.PickOptionRequest.Option[] getOptions() { 530 return mOptions; 531 } 532 sendPickOptionResult(boolean finished, VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result)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 */ sendIntermediatePickOptionResult( VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result)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 */ sendPickOptionResult( VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result)566 public void sendPickOptionResult( 567 VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) { 568 sendPickOptionResult(true, selections, result); 569 } 570 dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)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 CompleteVoiceRequest(String packageName, int uid, IVoiceInteractorCallback callback, VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras)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 getVoicePrompt()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 getMessage()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 */ sendCompleteResult(Bundle result)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 dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)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 AbortVoiceRequest(String packageName, int uid, IVoiceInteractorCallback callback, VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras)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 getVoicePrompt()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 getMessage()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 */ sendAbortResult(Bundle result)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 dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)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 CommandRequest(String packageName, int uid, IVoiceInteractorCallback callback, VoiceInteractionSession session, String command, Bundle extras)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 */ getCommand()734 public String getCommand() { 735 return mCommand; 736 } 737 sendCommandResult(boolean finished, Bundle result)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 */ sendIntermediateResult(Bundle result)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 */ sendResult(Bundle result)766 public void sendResult(Bundle result) { 767 sendCommandResult(true, result); 768 } 769 dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)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 executeMessage(Message msg)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 onBackPressed()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 VoiceInteractionSession(Context context)958 public VoiceInteractionSession(Context context) { 959 this(context, new Handler()); 960 } 961 VoiceInteractionSession(Context context, Handler handler)962 public VoiceInteractionSession(Context context, Handler handler) { 963 mContext = context; 964 mHandlerCaller = new HandlerCaller(context, handler.getLooper(), 965 mCallbacks, true); 966 } 967 getContext()968 public Context getContext() { 969 return mContext; 970 } 971 addRequest(Request req)972 void addRequest(Request req) { 973 synchronized (this) { 974 mActiveRequests.put(req.mInterface.asBinder(), req); 975 } 976 } 977 isRequestActive(IBinder reqInterface)978 boolean isRequestActive(IBinder reqInterface) { 979 synchronized (this) { 980 return mActiveRequests.containsKey(reqInterface); 981 } 982 } 983 removeRequest(IBinder reqInterface)984 Request removeRequest(IBinder reqInterface) { 985 synchronized (this) { 986 return mActiveRequests.remove(reqInterface); 987 } 988 } 989 doCreate(IVoiceInteractionManagerService service, IBinder token)990 void doCreate(IVoiceInteractionManagerService service, IBinder token) { 991 mSystemService = service; 992 mToken = token; 993 onCreate(); 994 } 995 doShow(Bundle args, int flags, final IVoiceInteractionSessionShowCallback showCallback)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 doHide()1048 void doHide() { 1049 if (mWindowVisible) { 1050 ensureWindowHidden(); 1051 mWindowVisible = false; 1052 onHide(); 1053 } 1054 } 1055 doDestroy()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 ensureWindowCreated()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 ensureWindowAdded()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 ensureWindowHidden()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 */ setDisabledShowContext(int flags)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 */ getDisabledShowContext()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 */ getUserDisabledShowContext()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 */ show(Bundle args, int flags)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 */ hide()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 */ setUiEnabled(boolean enabled)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 */ setTheme(int theme)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 */ startVoiceActivity(Intent intent)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 */ startAssistantActivity(Intent intent)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 */ setKeepAwake(boolean keepAwake)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 */ closeSystemDialogs()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 */ getLayoutInflater()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 */ getWindow()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 */ finish()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 */ onCreate()1369 public void onCreate() { 1370 doOnCreate(); 1371 } 1372 doOnCreate()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 */ onPrepareShow(Bundle args, int showFlags)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 */ onShow(Bundle args, int showFlags)1401 public void onShow(Bundle args, int showFlags) { 1402 } 1403 1404 /** 1405 * Called immediately after stopping to show the session UI. 1406 */ onHide()1407 public void onHide() { 1408 } 1409 1410 /** 1411 * Last callback to the session as it is being finished. 1412 */ onDestroy()1413 public void onDestroy() { 1414 } 1415 1416 /** 1417 * Hook in which to create the session's UI. 1418 */ onCreateContentView()1419 public View onCreateContentView() { 1420 return null; 1421 } 1422 setContentView(View view)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 doOnHandleAssist(Bundle data, AssistStructure structure, Throwable failure, AssistContent content)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 doOnHandleAssistSecondary(Bundle data, AssistStructure structure, Throwable failure, AssistContent content, int index, int count)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 */ onAssistStructureFailure(Throwable failure)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 */ onHandleAssist(@ullable Bundle data, @Nullable AssistStructure structure, @Nullable AssistContent content)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 */ onHandleAssistSecondary(@ullable Bundle data, @Nullable AssistStructure structure, @Nullable AssistContent content, int index, int count)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 */ onHandleScreenshot(@ullable Bitmap screenshot)1528 public void onHandleScreenshot(@Nullable Bitmap screenshot) { 1529 } 1530 onKeyDown(int keyCode, KeyEvent event)1531 public boolean onKeyDown(int keyCode, KeyEvent event) { 1532 return false; 1533 } 1534 onKeyLongPress(int keyCode, KeyEvent event)1535 public boolean onKeyLongPress(int keyCode, KeyEvent event) { 1536 return false; 1537 } 1538 onKeyUp(int keyCode, KeyEvent event)1539 public boolean onKeyUp(int keyCode, KeyEvent event) { 1540 return false; 1541 } 1542 onKeyMultiple(int keyCode, int count, KeyEvent event)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 */ onBackPressed()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 */ onCloseSystemDialogs()1562 public void onCloseSystemDialogs() { 1563 hide(); 1564 } 1565 1566 /** 1567 * Called when the lockscreen was shown. 1568 */ onLockscreenShown()1569 public void onLockscreenShown() { 1570 hide(); 1571 } 1572 1573 @Override onConfigurationChanged(Configuration newConfig)1574 public void onConfigurationChanged(Configuration newConfig) { 1575 } 1576 1577 @Override onLowMemory()1578 public void onLowMemory() { 1579 } 1580 1581 @Override onTrimMemory(int level)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 */ onComputeInsets(Insets outInsets)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 */ onTaskStarted(Intent intent, int taskId)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 */ onTaskFinished(Intent intent, int taskId)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 */ onGetSupportedCommands(String[] commands)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 */ onRequestConfirmation(ConfirmationRequest request)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 */ onRequestPickOption(PickOptionRequest request)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 */ onRequestCompleteVoice(CompleteVoiceRequest request)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 */ onRequestAbortVoice(AbortVoiceRequest request)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 */ onRequestCommand(CommandRequest request)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 */ onCancelRequest(Request request)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 */ dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)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