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 static android.view.Display.DEFAULT_DISPLAY; 20 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 21 22 import android.annotation.CallbackExecutor; 23 import android.annotation.IntDef; 24 import android.annotation.IntRange; 25 import android.annotation.NonNull; 26 import android.annotation.Nullable; 27 import android.annotation.TestApi; 28 import android.app.Activity; 29 import android.app.ActivityOptions; 30 import android.app.Dialog; 31 import android.app.DirectAction; 32 import android.app.Instrumentation; 33 import android.app.VoiceInteractor; 34 import android.app.assist.AssistContent; 35 import android.app.assist.AssistStructure; 36 import android.content.ComponentCallbacks2; 37 import android.content.Context; 38 import android.content.Intent; 39 import android.content.pm.ParceledListSlice; 40 import android.content.res.Configuration; 41 import android.content.res.TypedArray; 42 import android.graphics.Bitmap; 43 import android.graphics.Rect; 44 import android.graphics.Region; 45 import android.hardware.display.DisplayManager; 46 import android.os.Binder; 47 import android.os.Bundle; 48 import android.os.CancellationSignal; 49 import android.os.Handler; 50 import android.os.IBinder; 51 import android.os.ICancellationSignal; 52 import android.os.Message; 53 import android.os.RemoteCallback; 54 import android.os.RemoteException; 55 import android.os.UserHandle; 56 import android.util.ArrayMap; 57 import android.util.DebugUtils; 58 import android.util.Log; 59 import android.view.Gravity; 60 import android.view.KeyEvent; 61 import android.view.LayoutInflater; 62 import android.view.View; 63 import android.view.ViewTreeObserver; 64 import android.view.WindowManager; 65 import android.widget.FrameLayout; 66 67 import com.android.internal.annotations.Immutable; 68 import com.android.internal.app.IVoiceInteractionManagerService; 69 import com.android.internal.app.IVoiceInteractionSessionShowCallback; 70 import com.android.internal.app.IVoiceInteractor; 71 import com.android.internal.app.IVoiceInteractorCallback; 72 import com.android.internal.app.IVoiceInteractorRequest; 73 import com.android.internal.os.HandlerCaller; 74 import com.android.internal.os.SomeArgs; 75 import com.android.internal.util.function.pooled.PooledLambda; 76 77 import java.io.FileDescriptor; 78 import java.io.PrintWriter; 79 import java.lang.annotation.Retention; 80 import java.lang.annotation.RetentionPolicy; 81 import java.lang.ref.WeakReference; 82 import java.util.ArrayList; 83 import java.util.Arrays; 84 import java.util.Collections; 85 import java.util.List; 86 import java.util.Map; 87 import java.util.Objects; 88 import java.util.concurrent.Executor; 89 import java.util.function.Consumer; 90 91 /** 92 * An active voice interaction session, providing a facility for the implementation 93 * to interact with the user in the voice interaction layer. The user interface is 94 * initially shown by default, and can be created by overriding {@link #onCreateContentView()} 95 * in which the UI can be built. 96 * 97 * <p>A voice interaction session can be self-contained, ultimately calling {@link #finish} 98 * when done. It can also initiate voice interactions with applications by calling 99 * {@link #startVoiceActivity}</p>. 100 */ 101 public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCallbacks2 { 102 static final String TAG = "VoiceInteractionSession"; 103 static final boolean DEBUG = false; 104 105 /** 106 * Flag received in {@link #onShow}: originator requested that the session be started with 107 * assist data from the currently focused activity. 108 */ 109 public static final int SHOW_WITH_ASSIST = 1<<0; 110 111 /** 112 * Flag received in {@link #onShow}: originator requested that the session be started with 113 * a screen shot of the currently focused activity. 114 */ 115 public static final int SHOW_WITH_SCREENSHOT = 1<<1; 116 117 /** 118 * Flag for use with {@link #onShow}: indicates that the session has been started from the 119 * system assist gesture. 120 */ 121 public static final int SHOW_SOURCE_ASSIST_GESTURE = 1<<2; 122 123 /** 124 * Flag for use with {@link #onShow}: indicates that the application itself has invoked 125 * the assistant. 126 */ 127 public static final int SHOW_SOURCE_APPLICATION = 1<<3; 128 129 /** 130 * Flag for use with {@link #onShow}: indicates that an Activity has invoked the voice 131 * interaction service for a local interaction using 132 * {@link Activity#startLocalVoiceInteraction(Bundle)}. 133 */ 134 public static final int SHOW_SOURCE_ACTIVITY = 1<<4; 135 136 /** 137 * Flag for use with {@link #onShow}: indicates that the voice interaction service was invoked 138 * from a physical button. 139 */ 140 public static final int SHOW_SOURCE_PUSH_TO_TALK = 1 << 5; 141 142 /** 143 * Flag for use with {@link #onShow}: indicates that the voice interaction service was invoked 144 * from a notification. 145 */ 146 public static final int SHOW_SOURCE_NOTIFICATION = 1 << 6; 147 148 /** 149 * Flag for use with {@link #onShow}: indicates that the voice interaction service was invoked 150 * from an Android automotive system UI. 151 */ 152 public static final int SHOW_SOURCE_AUTOMOTIVE_SYSTEM_UI = 1 << 7; 153 154 /** @hide */ 155 public static final int VOICE_INTERACTION_ACTIVITY_EVENT_START = 1; 156 /** @hide */ 157 public static final int VOICE_INTERACTION_ACTIVITY_EVENT_RESUME = 2; 158 /** @hide */ 159 public static final int VOICE_INTERACTION_ACTIVITY_EVENT_PAUSE = 3; 160 /** @hide */ 161 public static final int VOICE_INTERACTION_ACTIVITY_EVENT_STOP = 4; 162 163 /** @hide */ 164 @IntDef(prefix = { "VOICE_INTERACTION_ACTIVITY_EVENT_" }, value = { 165 VOICE_INTERACTION_ACTIVITY_EVENT_START, 166 VOICE_INTERACTION_ACTIVITY_EVENT_RESUME, 167 VOICE_INTERACTION_ACTIVITY_EVENT_PAUSE, 168 VOICE_INTERACTION_ACTIVITY_EVENT_STOP 169 }) 170 @Retention(RetentionPolicy.SOURCE) 171 public @interface VoiceInteractionActivityEventType{} 172 173 /** 174 * Bundle key used to specify the id when the system prepares to show session. It increases for 175 * each request. 176 * <p> 177 * Type: int 178 * </p> 179 * @see VoiceInteractionService#showSession(Bundle, int) 180 * @see VoiceInteractionService#onPrepareToShowSession(Bundle, int) 181 * @see VoiceInteractionService#onShowSessionFailed(Bundle) 182 * @see #onShow(Bundle, int) 183 * @see #show(Bundle, int) 184 */ 185 public static final String KEY_SHOW_SESSION_ID = "android.service.voice.SHOW_SESSION_ID"; 186 187 final Context mContext; 188 final HandlerCaller mHandlerCaller; 189 190 final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState(); 191 192 IVoiceInteractionManagerService mSystemService; 193 IBinder mToken; 194 195 int mTheme = 0; 196 LayoutInflater mInflater; 197 TypedArray mThemeAttrs; 198 View mRootView; 199 FrameLayout mContentFrame; 200 VoiceInteractionWindow mWindow; 201 202 boolean mUiEnabled = true; 203 boolean mInitialized; 204 boolean mWindowAdded; 205 boolean mWindowVisible; 206 boolean mWindowWasVisible; 207 boolean mInShowWindow; 208 209 final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<IBinder, Request>(); 210 211 final Insets mTmpInsets = new Insets(); 212 213 final WeakReference<VoiceInteractionSession> mWeakRef 214 = new WeakReference<VoiceInteractionSession>(this); 215 216 // Registry of remote callbacks pending a reply with reply handles. 217 final Map<SafeResultListener, Consumer<Bundle>> mRemoteCallbacks = new ArrayMap<>(); 218 219 ICancellationSignal mKillCallback; 220 221 private final Map<VisibleActivityCallback, Executor> mVisibleActivityCallbacks = 222 new ArrayMap<>(); 223 private final List<VisibleActivityInfo> mVisibleActivityInfos = new ArrayList<>(); 224 225 final IVoiceInteractor mInteractor = new IVoiceInteractor.Stub() { 226 @Override 227 public IVoiceInteractorRequest startConfirmation(String callingPackage, 228 IVoiceInteractorCallback callback, VoiceInteractor.Prompt prompt, Bundle extras) { 229 ConfirmationRequest request = new ConfirmationRequest(callingPackage, 230 Binder.getCallingUid(), callback, VoiceInteractionSession.this, 231 prompt, extras); 232 addRequest(request); 233 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_CONFIRMATION, 234 request)); 235 return request.mInterface; 236 } 237 238 @Override 239 public IVoiceInteractorRequest startPickOption(String callingPackage, 240 IVoiceInteractorCallback callback, VoiceInteractor.Prompt prompt, 241 VoiceInteractor.PickOptionRequest.Option[] options, Bundle extras) { 242 PickOptionRequest request = new PickOptionRequest(callingPackage, 243 Binder.getCallingUid(), callback, VoiceInteractionSession.this, 244 prompt, options, extras); 245 addRequest(request); 246 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_PICK_OPTION, 247 request)); 248 return request.mInterface; 249 } 250 251 @Override 252 public IVoiceInteractorRequest startCompleteVoice(String callingPackage, 253 IVoiceInteractorCallback callback, VoiceInteractor.Prompt message, Bundle extras) { 254 CompleteVoiceRequest request = new CompleteVoiceRequest(callingPackage, 255 Binder.getCallingUid(), callback, VoiceInteractionSession.this, 256 message, extras); 257 addRequest(request); 258 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_COMPLETE_VOICE, 259 request)); 260 return request.mInterface; 261 } 262 263 @Override 264 public IVoiceInteractorRequest startAbortVoice(String callingPackage, 265 IVoiceInteractorCallback callback, VoiceInteractor.Prompt message, Bundle extras) { 266 AbortVoiceRequest request = new AbortVoiceRequest(callingPackage, 267 Binder.getCallingUid(), callback, VoiceInteractionSession.this, 268 message, extras); 269 addRequest(request); 270 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_ABORT_VOICE, 271 request)); 272 return request.mInterface; 273 } 274 275 @Override 276 public IVoiceInteractorRequest startCommand(String callingPackage, 277 IVoiceInteractorCallback callback, String command, Bundle extras) { 278 CommandRequest request = new CommandRequest(callingPackage, 279 Binder.getCallingUid(), callback, VoiceInteractionSession.this, 280 command, extras); 281 addRequest(request); 282 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_COMMAND, 283 request)); 284 return request.mInterface; 285 } 286 287 @Override 288 public boolean[] supportsCommands(String callingPackage, String[] commands) { 289 Message msg = mHandlerCaller.obtainMessageIOO(MSG_SUPPORTS_COMMANDS, 290 0, commands, null); 291 SomeArgs args = mHandlerCaller.sendMessageAndWait(msg); 292 if (args != null) { 293 boolean[] res = (boolean[])args.arg1; 294 args.recycle(); 295 return res; 296 } 297 return new boolean[commands.length]; 298 } 299 300 @Override 301 public void notifyDirectActionsChanged(int taskId, IBinder assistToken) { 302 mHandlerCaller.getHandler().sendMessage(PooledLambda.obtainMessage( 303 VoiceInteractionSession::onDirectActionsInvalidated, 304 VoiceInteractionSession.this, new ActivityId(taskId, assistToken)) 305 ); 306 } 307 308 @Override 309 public void setKillCallback(ICancellationSignal callback) { 310 mKillCallback = callback; 311 } 312 }; 313 314 final IVoiceInteractionSession mSession = new IVoiceInteractionSession.Stub() { 315 @Override 316 public void show(Bundle sessionArgs, int flags, 317 IVoiceInteractionSessionShowCallback showCallback) { 318 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOO(MSG_SHOW, 319 flags, sessionArgs, showCallback)); 320 } 321 322 @Override 323 public void hide() { 324 // Remove any pending messages to show the session 325 mHandlerCaller.removeMessages(MSG_SHOW); 326 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_HIDE)); 327 } 328 329 @Override 330 public void handleAssist(final int taskId, final IBinder assistToken, final Bundle data, 331 final AssistStructure structure, final AssistContent content, final int index, 332 final int count) { 333 // We want to pre-warm the AssistStructure before handing it off to the main 334 // thread. We also want to do this on a separate thread, so that if the app 335 // is for some reason slow (due to slow filling in of async children in the 336 // structure), we don't block other incoming IPCs (such as the screenshot) to 337 // us (since we are a oneway interface, they get serialized). (Okay?) 338 Thread retriever = new Thread("AssistStructure retriever") { 339 @Override 340 public void run() { 341 Throwable failure = null; 342 if (structure != null) { 343 try { 344 structure.ensureData(); 345 } catch (Throwable e) { 346 Log.w(TAG, "Failure retrieving AssistStructure", e); 347 failure = e; 348 } 349 } 350 351 SomeArgs args = SomeArgs.obtain(); 352 args.argi1 = taskId; 353 args.arg1 = data; 354 args.arg2 = (failure == null) ? structure : null; 355 args.arg3 = failure; 356 args.arg4 = content; 357 args.arg5 = assistToken; 358 args.argi5 = index; 359 args.argi6 = count; 360 361 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO( 362 MSG_HANDLE_ASSIST, args)); 363 } 364 }; 365 retriever.start(); 366 } 367 368 @Override 369 public void handleScreenshot(Bitmap screenshot) { 370 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_HANDLE_SCREENSHOT, 371 screenshot)); 372 } 373 374 @Override 375 public void taskStarted(Intent intent, int taskId) { 376 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_TASK_STARTED, 377 taskId, intent)); 378 } 379 380 @Override 381 public void taskFinished(Intent intent, int taskId) { 382 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_TASK_FINISHED, 383 taskId, intent)); 384 } 385 386 @Override 387 public void closeSystemDialogs() { 388 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_CLOSE_SYSTEM_DIALOGS)); 389 } 390 391 @Override 392 public void onLockscreenShown() { 393 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_ON_LOCKSCREEN_SHOWN)); 394 } 395 396 @Override 397 public void destroy() { 398 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_DESTROY)); 399 } 400 401 @Override 402 public void notifyVisibleActivityInfoChanged(VisibleActivityInfo visibleActivityInfo, 403 int type) { 404 mHandlerCaller.sendMessage( 405 mHandlerCaller.obtainMessageIO(MSG_NOTIFY_VISIBLE_ACTIVITY_INFO_CHANGED, type, 406 visibleActivityInfo)); 407 } 408 }; 409 410 /** 411 * Base class representing a request from a voice-driver app to perform a particular 412 * voice operation with the user. See related subclasses for the types of requests 413 * that are possible. 414 */ 415 public static class Request { 416 final IVoiceInteractorRequest mInterface = new IVoiceInteractorRequest.Stub() { 417 @Override 418 public void cancel() throws RemoteException { 419 VoiceInteractionSession session = mSession.get(); 420 if (session != null) { 421 session.mHandlerCaller.sendMessage( 422 session.mHandlerCaller.obtainMessageO(MSG_CANCEL, Request.this)); 423 } 424 } 425 }; 426 final String mCallingPackage; 427 final int mCallingUid; 428 final IVoiceInteractorCallback mCallback; 429 final WeakReference<VoiceInteractionSession> mSession; 430 final Bundle mExtras; 431 Request(String packageName, int uid, IVoiceInteractorCallback callback, VoiceInteractionSession session, Bundle extras)432 Request(String packageName, int uid, IVoiceInteractorCallback callback, 433 VoiceInteractionSession session, Bundle extras) { 434 mCallingPackage = packageName; 435 mCallingUid = uid; 436 mCallback = callback; 437 mSession = session.mWeakRef; 438 mExtras = extras; 439 } 440 441 /** 442 * Return the uid of the application that initiated the request. 443 */ getCallingUid()444 public int getCallingUid() { 445 return mCallingUid; 446 } 447 448 /** 449 * Return the package name of the application that initiated the request. 450 */ getCallingPackage()451 public String getCallingPackage() { 452 return mCallingPackage; 453 } 454 455 /** 456 * Return any additional extra information that was supplied as part of the request. 457 */ getExtras()458 public Bundle getExtras() { 459 return mExtras; 460 } 461 462 /** 463 * Check whether this request is currently active. A request becomes inactive after 464 * calling {@link #cancel} or a final result method that completes the request. After 465 * this point, further interactions with the request will result in 466 * {@link java.lang.IllegalStateException} errors; you should not catch these errors, 467 * but can use this method if you need to determine the state of the request. Returns 468 * true if the request is still active. 469 */ isActive()470 public boolean isActive() { 471 VoiceInteractionSession session = mSession.get(); 472 if (session == null) { 473 return false; 474 } 475 return session.isRequestActive(mInterface.asBinder()); 476 } 477 finishRequest()478 void finishRequest() { 479 VoiceInteractionSession session = mSession.get(); 480 if (session == null) { 481 throw new IllegalStateException("VoiceInteractionSession has been destroyed"); 482 } 483 Request req = session.removeRequest(mInterface.asBinder()); 484 if (req == null) { 485 throw new IllegalStateException("Request not active: " + this); 486 } else if (req != this) { 487 throw new IllegalStateException("Current active request " + req 488 + " not same as calling request " + this); 489 } 490 } 491 492 /** 493 * Ask the app to cancel this current request. 494 * This also finishes the request (it is no longer active). 495 */ cancel()496 public void cancel() { 497 try { 498 if (DEBUG) Log.d(TAG, "sendCancelResult: req=" + mInterface); 499 finishRequest(); 500 mCallback.deliverCancel(mInterface); 501 } catch (RemoteException e) { 502 } 503 } 504 505 @Override toString()506 public String toString() { 507 StringBuilder sb = new StringBuilder(128); 508 DebugUtils.buildShortClassTag(this, sb); 509 sb.append(" "); 510 sb.append(mInterface.asBinder()); 511 sb.append(" pkg="); 512 sb.append(mCallingPackage); 513 sb.append(" uid="); 514 UserHandle.formatUid(sb, mCallingUid); 515 sb.append('}'); 516 return sb.toString(); 517 } 518 dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)519 void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 520 writer.print(prefix); writer.print("mInterface="); 521 writer.println(mInterface.asBinder()); 522 writer.print(prefix); writer.print("mCallingPackage="); writer.print(mCallingPackage); 523 writer.print(" mCallingUid="); UserHandle.formatUid(writer, mCallingUid); 524 writer.println(); 525 writer.print(prefix); writer.print("mCallback="); 526 writer.println(mCallback.asBinder()); 527 if (mExtras != null) { 528 writer.print(prefix); writer.print("mExtras="); 529 writer.println(mExtras); 530 } 531 } 532 } 533 534 /** 535 * A request for confirmation from the user of an operation, as per 536 * {@link android.app.VoiceInteractor.ConfirmationRequest 537 * VoiceInteractor.ConfirmationRequest}. 538 */ 539 public static final class ConfirmationRequest extends Request { 540 final VoiceInteractor.Prompt mPrompt; 541 ConfirmationRequest(String packageName, int uid, IVoiceInteractorCallback callback, VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras)542 ConfirmationRequest(String packageName, int uid, IVoiceInteractorCallback callback, 543 VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras) { 544 super(packageName, uid, callback, session, extras); 545 mPrompt = prompt; 546 } 547 548 /** 549 * Return the prompt informing the user of what will happen, as per 550 * {@link android.app.VoiceInteractor.ConfirmationRequest 551 * VoiceInteractor.ConfirmationRequest}. 552 */ 553 @Nullable getVoicePrompt()554 public VoiceInteractor.Prompt getVoicePrompt() { 555 return mPrompt; 556 } 557 558 /** 559 * Return the prompt informing the user of what will happen, as per 560 * {@link android.app.VoiceInteractor.ConfirmationRequest 561 * VoiceInteractor.ConfirmationRequest}. 562 * @deprecated Prefer {@link #getVoicePrompt()} which allows multiple voice prompts. 563 */ 564 @Deprecated 565 @Nullable getPrompt()566 public CharSequence getPrompt() { 567 return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null); 568 } 569 570 /** 571 * Report that the voice interactor has confirmed the operation with the user, resulting 572 * in a call to 573 * {@link android.app.VoiceInteractor.ConfirmationRequest#onConfirmationResult 574 * VoiceInteractor.ConfirmationRequest.onConfirmationResult}. 575 * This finishes the request (it is no longer active). 576 */ sendConfirmationResult(boolean confirmed, Bundle result)577 public void sendConfirmationResult(boolean confirmed, Bundle result) { 578 try { 579 if (DEBUG) Log.d(TAG, "sendConfirmationResult: req=" + mInterface 580 + " confirmed=" + confirmed + " result=" + result); 581 finishRequest(); 582 mCallback.deliverConfirmationResult(mInterface, confirmed, result); 583 } catch (RemoteException e) { 584 } 585 } 586 dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)587 void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 588 super.dump(prefix, fd, writer, args); 589 writer.print(prefix); writer.print("mPrompt="); 590 writer.println(mPrompt); 591 } 592 } 593 594 /** 595 * A request for the user to pick from a set of option, as per 596 * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}. 597 */ 598 public static final class PickOptionRequest extends Request { 599 final VoiceInteractor.Prompt mPrompt; 600 final VoiceInteractor.PickOptionRequest.Option[] mOptions; 601 PickOptionRequest(String packageName, int uid, IVoiceInteractorCallback callback, VoiceInteractionSession session, VoiceInteractor.Prompt prompt, VoiceInteractor.PickOptionRequest.Option[] options, Bundle extras)602 PickOptionRequest(String packageName, int uid, IVoiceInteractorCallback callback, 603 VoiceInteractionSession session, VoiceInteractor.Prompt prompt, 604 VoiceInteractor.PickOptionRequest.Option[] options, Bundle extras) { 605 super(packageName, uid, callback, session, extras); 606 mPrompt = prompt; 607 mOptions = options; 608 } 609 610 /** 611 * Return the prompt informing the user of what they are picking, as per 612 * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}. 613 */ 614 @Nullable getVoicePrompt()615 public VoiceInteractor.Prompt getVoicePrompt() { 616 return mPrompt; 617 } 618 619 /** 620 * Return the prompt informing the user of what they are picking, as per 621 * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}. 622 * @deprecated Prefer {@link #getVoicePrompt()} which allows multiple voice prompts. 623 */ 624 @Deprecated 625 @Nullable getPrompt()626 public CharSequence getPrompt() { 627 return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null); 628 } 629 630 /** 631 * Return the set of options the user is picking from, as per 632 * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}. 633 */ getOptions()634 public VoiceInteractor.PickOptionRequest.Option[] getOptions() { 635 return mOptions; 636 } 637 sendPickOptionResult(boolean finished, VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result)638 void sendPickOptionResult(boolean finished, 639 VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) { 640 try { 641 if (DEBUG) Log.d(TAG, "sendPickOptionResult: req=" + mInterface 642 + " finished=" + finished + " selections=" + Arrays.toString(selections) 643 + " result=" + result); 644 if (finished) { 645 finishRequest(); 646 } 647 mCallback.deliverPickOptionResult(mInterface, finished, selections, result); 648 } catch (RemoteException e) { 649 } 650 } 651 652 /** 653 * Report an intermediate option selection from the request, without completing it (the 654 * request is still active and the app is waiting for the final option selection), 655 * resulting in a call to 656 * {@link android.app.VoiceInteractor.PickOptionRequest#onPickOptionResult 657 * VoiceInteractor.PickOptionRequest.onPickOptionResult} with false for finished. 658 */ sendIntermediatePickOptionResult( VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result)659 public void sendIntermediatePickOptionResult( 660 VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) { 661 sendPickOptionResult(false, selections, result); 662 } 663 664 /** 665 * Report the final option selection for the request, completing the request 666 * and resulting in a call to 667 * {@link android.app.VoiceInteractor.PickOptionRequest#onPickOptionResult 668 * VoiceInteractor.PickOptionRequest.onPickOptionResult} with false for finished. 669 * This finishes the request (it is no longer active). 670 */ sendPickOptionResult( VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result)671 public void sendPickOptionResult( 672 VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) { 673 sendPickOptionResult(true, selections, result); 674 } 675 dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)676 void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 677 super.dump(prefix, fd, writer, args); 678 writer.print(prefix); writer.print("mPrompt="); 679 writer.println(mPrompt); 680 if (mOptions != null) { 681 writer.print(prefix); writer.println("Options:"); 682 for (int i=0; i<mOptions.length; i++) { 683 VoiceInteractor.PickOptionRequest.Option op = mOptions[i]; 684 writer.print(prefix); writer.print(" #"); writer.print(i); writer.println(":"); 685 writer.print(prefix); writer.print(" mLabel="); 686 writer.println(op.getLabel()); 687 writer.print(prefix); writer.print(" mIndex="); 688 writer.println(op.getIndex()); 689 if (op.countSynonyms() > 0) { 690 writer.print(prefix); writer.println(" Synonyms:"); 691 for (int j=0; j<op.countSynonyms(); j++) { 692 writer.print(prefix); writer.print(" #"); writer.print(j); 693 writer.print(": "); writer.println(op.getSynonymAt(j)); 694 } 695 } 696 if (op.getExtras() != null) { 697 writer.print(prefix); writer.print(" mExtras="); 698 writer.println(op.getExtras()); 699 } 700 } 701 } 702 } 703 } 704 705 /** 706 * A request to simply inform the user that the voice operation has completed, as per 707 * {@link android.app.VoiceInteractor.CompleteVoiceRequest 708 * VoiceInteractor.CompleteVoiceRequest}. 709 */ 710 public static final class CompleteVoiceRequest extends Request { 711 final VoiceInteractor.Prompt mPrompt; 712 CompleteVoiceRequest(String packageName, int uid, IVoiceInteractorCallback callback, VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras)713 CompleteVoiceRequest(String packageName, int uid, IVoiceInteractorCallback callback, 714 VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras) { 715 super(packageName, uid, callback, session, extras); 716 mPrompt = prompt; 717 } 718 719 /** 720 * Return the message informing the user of the completion, as per 721 * {@link android.app.VoiceInteractor.CompleteVoiceRequest 722 * VoiceInteractor.CompleteVoiceRequest}. 723 */ 724 @Nullable getVoicePrompt()725 public VoiceInteractor.Prompt getVoicePrompt() { 726 return mPrompt; 727 } 728 729 /** 730 * Return the message informing the user of the completion, as per 731 * {@link android.app.VoiceInteractor.CompleteVoiceRequest 732 * VoiceInteractor.CompleteVoiceRequest}. 733 * @deprecated Prefer {@link #getVoicePrompt()} which allows a separate visual message. 734 */ 735 @Deprecated 736 @Nullable getMessage()737 public CharSequence getMessage() { 738 return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null); 739 } 740 741 /** 742 * Report that the voice interactor has finished completing the voice operation, resulting 743 * in a call to 744 * {@link android.app.VoiceInteractor.CompleteVoiceRequest#onCompleteResult 745 * VoiceInteractor.CompleteVoiceRequest.onCompleteResult}. 746 * This finishes the request (it is no longer active). 747 */ sendCompleteResult(Bundle result)748 public void sendCompleteResult(Bundle result) { 749 try { 750 if (DEBUG) Log.d(TAG, "sendCompleteVoiceResult: req=" + mInterface 751 + " result=" + result); 752 finishRequest(); 753 mCallback.deliverCompleteVoiceResult(mInterface, result); 754 } catch (RemoteException e) { 755 } 756 } 757 dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)758 void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 759 super.dump(prefix, fd, writer, args); 760 writer.print(prefix); writer.print("mPrompt="); 761 writer.println(mPrompt); 762 } 763 } 764 765 /** 766 * A request to report that the current user interaction can not be completed with voice, as per 767 * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}. 768 */ 769 public static final class AbortVoiceRequest extends Request { 770 final VoiceInteractor.Prompt mPrompt; 771 AbortVoiceRequest(String packageName, int uid, IVoiceInteractorCallback callback, VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras)772 AbortVoiceRequest(String packageName, int uid, IVoiceInteractorCallback callback, 773 VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras) { 774 super(packageName, uid, callback, session, extras); 775 mPrompt = prompt; 776 } 777 778 /** 779 * Return the message informing the user of the problem, as per 780 * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}. 781 */ 782 @Nullable getVoicePrompt()783 public VoiceInteractor.Prompt getVoicePrompt() { 784 return mPrompt; 785 } 786 787 /** 788 * Return the message informing the user of the problem, as per 789 * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}. 790 * @deprecated Prefer {@link #getVoicePrompt()} which allows a separate visual message. 791 */ 792 @Deprecated 793 @Nullable getMessage()794 public CharSequence getMessage() { 795 return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null); 796 } 797 798 /** 799 * Report that the voice interactor has finished aborting the voice operation, resulting 800 * in a call to 801 * {@link android.app.VoiceInteractor.AbortVoiceRequest#onAbortResult 802 * VoiceInteractor.AbortVoiceRequest.onAbortResult}. This finishes the request (it 803 * is no longer active). 804 */ sendAbortResult(Bundle result)805 public void sendAbortResult(Bundle result) { 806 try { 807 if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface 808 + " result=" + result); 809 finishRequest(); 810 mCallback.deliverAbortVoiceResult(mInterface, result); 811 } catch (RemoteException e) { 812 } 813 } 814 dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)815 void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 816 super.dump(prefix, fd, writer, args); 817 writer.print(prefix); writer.print("mPrompt="); 818 writer.println(mPrompt); 819 } 820 } 821 822 /** 823 * A generic vendor-specific request, as per 824 * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}. 825 */ 826 public static final class CommandRequest extends Request { 827 final String mCommand; 828 CommandRequest(String packageName, int uid, IVoiceInteractorCallback callback, VoiceInteractionSession session, String command, Bundle extras)829 CommandRequest(String packageName, int uid, IVoiceInteractorCallback callback, 830 VoiceInteractionSession session, String command, Bundle extras) { 831 super(packageName, uid, callback, session, extras); 832 mCommand = command; 833 } 834 835 /** 836 * Return the command that is being executed, as per 837 * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}. 838 */ getCommand()839 public String getCommand() { 840 return mCommand; 841 } 842 sendCommandResult(boolean finished, Bundle result)843 void sendCommandResult(boolean finished, Bundle result) { 844 try { 845 if (DEBUG) Log.d(TAG, "sendCommandResult: req=" + mInterface 846 + " result=" + result); 847 if (finished) { 848 finishRequest(); 849 } 850 mCallback.deliverCommandResult(mInterface, finished, result); 851 } catch (RemoteException e) { 852 } 853 } 854 855 /** 856 * Report an intermediate result of the request, without completing it (the request 857 * is still active and the app is waiting for the final result), resulting in a call to 858 * {@link android.app.VoiceInteractor.CommandRequest#onCommandResult 859 * VoiceInteractor.CommandRequest.onCommandResult} with false for isCompleted. 860 */ sendIntermediateResult(Bundle result)861 public void sendIntermediateResult(Bundle result) { 862 sendCommandResult(false, result); 863 } 864 865 /** 866 * Report the final result of the request, completing the request and resulting in a call to 867 * {@link android.app.VoiceInteractor.CommandRequest#onCommandResult 868 * VoiceInteractor.CommandRequest.onCommandResult} with true for isCompleted. 869 * This finishes the request (it is no longer active). 870 */ sendResult(Bundle result)871 public void sendResult(Bundle result) { 872 sendCommandResult(true, result); 873 } 874 dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)875 void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 876 super.dump(prefix, fd, writer, args); 877 writer.print(prefix); writer.print("mCommand="); 878 writer.println(mCommand); 879 } 880 } 881 882 static final int MSG_START_CONFIRMATION = 1; 883 static final int MSG_START_PICK_OPTION = 2; 884 static final int MSG_START_COMPLETE_VOICE = 3; 885 static final int MSG_START_ABORT_VOICE = 4; 886 static final int MSG_START_COMMAND = 5; 887 static final int MSG_SUPPORTS_COMMANDS = 6; 888 static final int MSG_CANCEL = 7; 889 890 static final int MSG_TASK_STARTED = 100; 891 static final int MSG_TASK_FINISHED = 101; 892 static final int MSG_CLOSE_SYSTEM_DIALOGS = 102; 893 static final int MSG_DESTROY = 103; 894 static final int MSG_HANDLE_ASSIST = 104; 895 static final int MSG_HANDLE_SCREENSHOT = 105; 896 static final int MSG_SHOW = 106; 897 static final int MSG_HIDE = 107; 898 static final int MSG_ON_LOCKSCREEN_SHOWN = 108; 899 static final int MSG_NOTIFY_VISIBLE_ACTIVITY_INFO_CHANGED = 109; 900 static final int MSG_REGISTER_VISIBLE_ACTIVITY_CALLBACK = 110; 901 static final int MSG_UNREGISTER_VISIBLE_ACTIVITY_CALLBACK = 111; 902 903 class MyCallbacks implements HandlerCaller.Callback, VoiceInteractionWindow.Callback { 904 @Override executeMessage(Message msg)905 public void executeMessage(Message msg) { 906 SomeArgs args = null; 907 switch (msg.what) { 908 case MSG_START_CONFIRMATION: 909 if (DEBUG) Log.d(TAG, "onConfirm: req=" + msg.obj); 910 onRequestConfirmation((ConfirmationRequest) msg.obj); 911 break; 912 case MSG_START_PICK_OPTION: 913 if (DEBUG) Log.d(TAG, "onPickOption: req=" + msg.obj); 914 onRequestPickOption((PickOptionRequest) msg.obj); 915 break; 916 case MSG_START_COMPLETE_VOICE: 917 if (DEBUG) Log.d(TAG, "onCompleteVoice: req=" + msg.obj); 918 onRequestCompleteVoice((CompleteVoiceRequest) msg.obj); 919 break; 920 case MSG_START_ABORT_VOICE: 921 if (DEBUG) Log.d(TAG, "onAbortVoice: req=" + msg.obj); 922 onRequestAbortVoice((AbortVoiceRequest) msg.obj); 923 break; 924 case MSG_START_COMMAND: 925 if (DEBUG) Log.d(TAG, "onCommand: req=" + msg.obj); 926 onRequestCommand((CommandRequest) msg.obj); 927 break; 928 case MSG_SUPPORTS_COMMANDS: 929 args = (SomeArgs)msg.obj; 930 if (DEBUG) Log.d(TAG, "onGetSupportedCommands: cmds=" + args.arg1); 931 args.arg1 = onGetSupportedCommands((String[]) args.arg1); 932 args.complete(); 933 args = null; 934 break; 935 case MSG_CANCEL: 936 if (DEBUG) Log.d(TAG, "onCancel: req=" + ((Request)msg.obj)); 937 onCancelRequest((Request) msg.obj); 938 break; 939 case MSG_TASK_STARTED: 940 if (DEBUG) Log.d(TAG, "onTaskStarted: intent=" + msg.obj 941 + " taskId=" + msg.arg1); 942 onTaskStarted((Intent) msg.obj, msg.arg1); 943 break; 944 case MSG_TASK_FINISHED: 945 if (DEBUG) Log.d(TAG, "onTaskFinished: intent=" + msg.obj 946 + " taskId=" + msg.arg1); 947 onTaskFinished((Intent) msg.obj, msg.arg1); 948 break; 949 case MSG_CLOSE_SYSTEM_DIALOGS: 950 if (DEBUG) Log.d(TAG, "onCloseSystemDialogs"); 951 onCloseSystemDialogs(); 952 break; 953 case MSG_DESTROY: 954 if (DEBUG) Log.d(TAG, "doDestroy"); 955 doDestroy(); 956 break; 957 case MSG_HANDLE_ASSIST: 958 args = (SomeArgs)msg.obj; 959 if (DEBUG) Log.d(TAG, "onHandleAssist: taskId=" + args.argi1 960 + "assistToken=" + args.arg5 + " data=" + args.arg1 961 + " structure=" + args.arg2 + " content=" + args.arg3 962 + " activityIndex=" + args.argi5 + " activityCount=" + args.argi6); 963 doOnHandleAssist(args.argi1, (IBinder) args.arg5, (Bundle) args.arg1, 964 (AssistStructure) args.arg2, (Throwable) args.arg3, 965 (AssistContent) args.arg4, args.argi5, args.argi6); 966 break; 967 case MSG_HANDLE_SCREENSHOT: 968 if (DEBUG) Log.d(TAG, "onHandleScreenshot: " + msg.obj); 969 onHandleScreenshot((Bitmap) msg.obj); 970 break; 971 case MSG_SHOW: 972 args = (SomeArgs)msg.obj; 973 if (DEBUG) Log.d(TAG, "doShow: args=" + args.arg1 974 + " flags=" + msg.arg1 975 + " showCallback=" + args.arg2); 976 doShow((Bundle) args.arg1, msg.arg1, 977 (IVoiceInteractionSessionShowCallback) args.arg2); 978 break; 979 case MSG_HIDE: 980 if (DEBUG) Log.d(TAG, "doHide"); 981 doHide(); 982 break; 983 case MSG_ON_LOCKSCREEN_SHOWN: 984 if (DEBUG) Log.d(TAG, "onLockscreenShown"); 985 onLockscreenShown(); 986 break; 987 case MSG_NOTIFY_VISIBLE_ACTIVITY_INFO_CHANGED: 988 if (DEBUG) { 989 Log.d(TAG, 990 "doNotifyVisibleActivityInfoChanged: visibleActivityInfo=" + msg.obj 991 + " type=" + msg.arg1); 992 } 993 doNotifyVisibleActivityInfoChanged((VisibleActivityInfo) msg.obj, msg.arg1); 994 break; 995 case MSG_REGISTER_VISIBLE_ACTIVITY_CALLBACK: 996 if (DEBUG) { 997 Log.d(TAG, "doRegisterVisibleActivityCallback"); 998 } 999 args = (SomeArgs) msg.obj; 1000 doRegisterVisibleActivityCallback((Executor) args.arg1, 1001 (VisibleActivityCallback) args.arg2); 1002 break; 1003 case MSG_UNREGISTER_VISIBLE_ACTIVITY_CALLBACK: 1004 if (DEBUG) { 1005 Log.d(TAG, "doUnregisterVisibleActivityCallback"); 1006 } 1007 doUnregisterVisibleActivityCallback((VisibleActivityCallback) msg.obj); 1008 break; 1009 } 1010 if (args != null) { 1011 args.recycle(); 1012 } 1013 } 1014 1015 @Override onBackPressed()1016 public void onBackPressed() { 1017 VoiceInteractionSession.this.onBackPressed(); 1018 } 1019 } 1020 1021 final MyCallbacks mCallbacks = new MyCallbacks(); 1022 1023 /** 1024 * Information about where interesting parts of the input method UI appear. 1025 */ 1026 public static final class Insets { 1027 /** 1028 * This is the part of the UI that is the main content. It is 1029 * used to determine the basic space needed, to resize/pan the 1030 * application behind. It is assumed that this inset does not 1031 * change very much, since any change will cause a full resize/pan 1032 * of the application behind. This value is relative to the top edge 1033 * of the input method window. 1034 */ 1035 public final Rect contentInsets = new Rect(); 1036 1037 /** 1038 * This is the region of the UI that is touchable. It is used when 1039 * {@link #touchableInsets} is set to {@link #TOUCHABLE_INSETS_REGION}. 1040 * The region should be specified relative to the origin of the window frame. 1041 */ 1042 public final Region touchableRegion = new Region(); 1043 1044 /** 1045 * Option for {@link #touchableInsets}: the entire window frame 1046 * can be touched. 1047 */ 1048 public static final int TOUCHABLE_INSETS_FRAME 1049 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME; 1050 1051 /** 1052 * Option for {@link #touchableInsets}: the area inside of 1053 * the content insets can be touched. 1054 */ 1055 public static final int TOUCHABLE_INSETS_CONTENT 1056 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT; 1057 1058 /** 1059 * Option for {@link #touchableInsets}: the region specified by 1060 * {@link #touchableRegion} can be touched. 1061 */ 1062 public static final int TOUCHABLE_INSETS_REGION 1063 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION; 1064 1065 /** 1066 * Determine which area of the window is touchable by the user. May 1067 * be one of: {@link #TOUCHABLE_INSETS_FRAME}, 1068 * {@link #TOUCHABLE_INSETS_CONTENT}, or {@link #TOUCHABLE_INSETS_REGION}. 1069 */ 1070 public int touchableInsets; 1071 } 1072 1073 final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = 1074 new ViewTreeObserver.OnComputeInternalInsetsListener() { 1075 public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) { 1076 onComputeInsets(mTmpInsets); 1077 info.contentInsets.set(mTmpInsets.contentInsets); 1078 info.visibleInsets.set(mTmpInsets.contentInsets); 1079 info.touchableRegion.set(mTmpInsets.touchableRegion); 1080 info.setTouchableInsets(mTmpInsets.touchableInsets); 1081 } 1082 }; 1083 VoiceInteractionSession(Context context)1084 public VoiceInteractionSession(Context context) { 1085 this(context, new Handler()); 1086 } 1087 VoiceInteractionSession(Context context, Handler handler)1088 public VoiceInteractionSession(Context context, Handler handler) { 1089 mHandlerCaller = new HandlerCaller(context, handler.getLooper(), 1090 mCallbacks, true); 1091 mContext = createWindowContextIfNeeded(context); 1092 } 1093 getContext()1094 public Context getContext() { 1095 return mContext; 1096 } 1097 createWindowContextIfNeeded(Context context)1098 private Context createWindowContextIfNeeded(Context context) { 1099 try { 1100 if (!context.isUiContext()) { 1101 DisplayManager displayManager = context.getSystemService(DisplayManager.class); 1102 if (displayManager != null) { 1103 return context.createWindowContext( 1104 displayManager.getDisplay(DEFAULT_DISPLAY), 1105 WindowManager.LayoutParams.TYPE_VOICE_INTERACTION, 1106 /* options= */ null); 1107 } 1108 } 1109 return context; 1110 } catch (RuntimeException e) { 1111 Log.w(TAG, "Fail to createWindowContext, Exception = " + e); 1112 return context; 1113 } 1114 } 1115 addRequest(Request req)1116 void addRequest(Request req) { 1117 synchronized (this) { 1118 mActiveRequests.put(req.mInterface.asBinder(), req); 1119 } 1120 } 1121 isRequestActive(IBinder reqInterface)1122 boolean isRequestActive(IBinder reqInterface) { 1123 synchronized (this) { 1124 return mActiveRequests.containsKey(reqInterface); 1125 } 1126 } 1127 removeRequest(IBinder reqInterface)1128 Request removeRequest(IBinder reqInterface) { 1129 synchronized (this) { 1130 return mActiveRequests.remove(reqInterface); 1131 } 1132 } 1133 doCreate(IVoiceInteractionManagerService service, IBinder token)1134 void doCreate(IVoiceInteractionManagerService service, IBinder token) { 1135 mSystemService = service; 1136 mToken = token; 1137 onCreate(); 1138 } 1139 doShow(Bundle args, int flags, final IVoiceInteractionSessionShowCallback showCallback)1140 void doShow(Bundle args, int flags, final IVoiceInteractionSessionShowCallback showCallback) { 1141 if (DEBUG) Log.v(TAG, "Showing window: mWindowAdded=" + mWindowAdded 1142 + " mWindowVisible=" + mWindowVisible); 1143 1144 if (mInShowWindow) { 1145 Log.w(TAG, "Re-entrance in to showWindow"); 1146 return; 1147 } 1148 1149 try { 1150 mInShowWindow = true; 1151 onPrepareShow(args, flags); 1152 if (!mWindowVisible) { 1153 ensureWindowAdded(); 1154 } 1155 onShow(args, flags); 1156 if (!mWindowVisible) { 1157 mWindowVisible = true; 1158 if (mUiEnabled) { 1159 showWindow(); 1160 } 1161 } 1162 if (showCallback != null) { 1163 if (mUiEnabled) { 1164 mRootView.invalidate(); 1165 mRootView.getViewTreeObserver().addOnPreDrawListener( 1166 new ViewTreeObserver.OnPreDrawListener() { 1167 @Override 1168 public boolean onPreDraw() { 1169 mRootView.getViewTreeObserver().removeOnPreDrawListener(this); 1170 try { 1171 showCallback.onShown(); 1172 } catch (RemoteException e) { 1173 Log.w(TAG, "Error calling onShown", e); 1174 } 1175 return true; 1176 } 1177 }); 1178 } else { 1179 try { 1180 showCallback.onShown(); 1181 } catch (RemoteException e) { 1182 Log.w(TAG, "Error calling onShown", e); 1183 } 1184 } 1185 } 1186 } finally { 1187 mWindowWasVisible = true; 1188 mInShowWindow = false; 1189 } 1190 } 1191 doHide()1192 void doHide() { 1193 if (mWindowVisible) { 1194 ensureWindowHidden(); 1195 mWindowVisible = false; 1196 onHide(); 1197 } 1198 } 1199 doDestroy()1200 void doDestroy() { 1201 onDestroy(); 1202 if (mKillCallback != null) { 1203 try { 1204 mKillCallback.cancel(); 1205 } catch (RemoteException e) { 1206 /* ignore */ 1207 } 1208 mKillCallback = null; 1209 } 1210 if (mInitialized) { 1211 mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener( 1212 mInsetsComputer); 1213 if (mWindowAdded) { 1214 mWindow.dismiss(); 1215 mWindowAdded = false; 1216 } 1217 mInitialized = false; 1218 } 1219 } 1220 doNotifyVisibleActivityInfoChanged(VisibleActivityInfo visibleActivityInfo, int type)1221 private void doNotifyVisibleActivityInfoChanged(VisibleActivityInfo visibleActivityInfo, 1222 int type) { 1223 1224 if (mVisibleActivityCallbacks.isEmpty()) { 1225 return; 1226 } 1227 1228 switch (type) { 1229 case VisibleActivityInfo.TYPE_ACTIVITY_ADDED: 1230 notifyVisibleActivityChanged(visibleActivityInfo, type); 1231 mVisibleActivityInfos.add(visibleActivityInfo); 1232 break; 1233 case VisibleActivityInfo.TYPE_ACTIVITY_REMOVED: 1234 notifyVisibleActivityChanged(visibleActivityInfo, type); 1235 mVisibleActivityInfos.remove(visibleActivityInfo); 1236 break; 1237 } 1238 } 1239 doRegisterVisibleActivityCallback(@onNull @allbackExecutor Executor executor, @NonNull VisibleActivityCallback callback)1240 private void doRegisterVisibleActivityCallback(@NonNull @CallbackExecutor Executor executor, 1241 @NonNull VisibleActivityCallback callback) { 1242 if (mVisibleActivityCallbacks.containsKey(callback)) { 1243 if (DEBUG) { 1244 Log.d(TAG, "doRegisterVisibleActivityCallback: callback has registered"); 1245 } 1246 return; 1247 } 1248 1249 int preCallbackCount = mVisibleActivityCallbacks.size(); 1250 mVisibleActivityCallbacks.put(callback, executor); 1251 1252 if (preCallbackCount == 0) { 1253 try { 1254 mSystemService.startListeningVisibleActivityChanged(mToken); 1255 } catch (RemoteException e) { 1256 e.rethrowFromSystemServer(); 1257 } 1258 } else { 1259 for (int i = 0; i < mVisibleActivityInfos.size(); i++) { 1260 final VisibleActivityInfo visibleActivityInfo = mVisibleActivityInfos.get(i); 1261 executor.execute(() -> callback.onVisible(visibleActivityInfo)); 1262 } 1263 } 1264 } 1265 doUnregisterVisibleActivityCallback(@onNull VisibleActivityCallback callback)1266 private void doUnregisterVisibleActivityCallback(@NonNull VisibleActivityCallback callback) { 1267 mVisibleActivityCallbacks.remove(callback); 1268 1269 if (mVisibleActivityCallbacks.size() == 0) { 1270 mVisibleActivityInfos.clear(); 1271 try { 1272 mSystemService.stopListeningVisibleActivityChanged(mToken); 1273 } catch (RemoteException e) { 1274 e.rethrowFromSystemServer(); 1275 } 1276 } 1277 } 1278 notifyVisibleActivityChanged(VisibleActivityInfo visibleActivityInfo, int type)1279 private void notifyVisibleActivityChanged(VisibleActivityInfo visibleActivityInfo, int type) { 1280 for (Map.Entry<VisibleActivityCallback, Executor> e : 1281 mVisibleActivityCallbacks.entrySet()) { 1282 final Executor executor = e.getValue(); 1283 final VisibleActivityCallback visibleActivityCallback = e.getKey(); 1284 1285 switch (type) { 1286 case VisibleActivityInfo.TYPE_ACTIVITY_ADDED: 1287 Binder.withCleanCallingIdentity(() -> { 1288 executor.execute( 1289 () -> visibleActivityCallback.onVisible(visibleActivityInfo)); 1290 }); 1291 break; 1292 case VisibleActivityInfo.TYPE_ACTIVITY_REMOVED: 1293 Binder.withCleanCallingIdentity(() -> { 1294 executor.execute(() -> visibleActivityCallback.onInvisible( 1295 visibleActivityInfo.getActivityId())); 1296 }); 1297 break; 1298 } 1299 } 1300 } 1301 ensureWindowCreated()1302 void ensureWindowCreated() { 1303 if (mInitialized) { 1304 return; 1305 } 1306 1307 if (!mUiEnabled) { 1308 throw new IllegalStateException("setUiEnabled is false"); 1309 } 1310 1311 mInitialized = true; 1312 mInflater = (LayoutInflater)mContext.getSystemService( 1313 Context.LAYOUT_INFLATER_SERVICE); 1314 mWindow = new VoiceInteractionWindow(mContext, "VoiceInteractionSession", mTheme, 1315 mCallbacks, this, mDispatcherState, 1316 WindowManager.LayoutParams.TYPE_VOICE_INTERACTION, Gravity.BOTTOM, true); 1317 mWindow.getWindow().getAttributes().setFitInsetsTypes(0 /* types */); 1318 mWindow.getWindow().addFlags( 1319 WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED | 1320 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | 1321 WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR); 1322 1323 mThemeAttrs = mContext.obtainStyledAttributes(android.R.styleable.VoiceInteractionSession); 1324 mRootView = mInflater.inflate( 1325 com.android.internal.R.layout.voice_interaction_session, null); 1326 mRootView.setSystemUiVisibility( 1327 View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 1328 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); 1329 mWindow.setContentView(mRootView); 1330 mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer); 1331 1332 mContentFrame = (FrameLayout)mRootView.findViewById(android.R.id.content); 1333 1334 mWindow.getWindow().setLayout(MATCH_PARENT, MATCH_PARENT); 1335 mWindow.setToken(mToken); 1336 } 1337 ensureWindowAdded()1338 void ensureWindowAdded() { 1339 if (mUiEnabled && !mWindowAdded) { 1340 mWindowAdded = true; 1341 ensureWindowCreated(); 1342 View v = onCreateContentView(); 1343 if (v != null) { 1344 setContentView(v); 1345 } 1346 } 1347 } 1348 showWindow()1349 void showWindow() { 1350 if (mWindow != null) { 1351 mWindow.show(); 1352 try { 1353 mSystemService.setSessionWindowVisible(mToken, true); 1354 } catch (RemoteException e) { 1355 Log.w(TAG, "Failed to notify session window shown", e); 1356 } 1357 } 1358 } 1359 ensureWindowHidden()1360 void ensureWindowHidden() { 1361 if (mWindow != null) { 1362 mWindow.hide(); 1363 try { 1364 mSystemService.setSessionWindowVisible(mToken, false); 1365 } catch (RemoteException e) { 1366 Log.w(TAG, "Failed to notify session window hidden", e); 1367 } 1368 } 1369 } 1370 1371 /** 1372 * Equivalent to {@link VoiceInteractionService#setDisabledShowContext 1373 * VoiceInteractionService.setDisabledShowContext(int)}. 1374 */ setDisabledShowContext(int flags)1375 public void setDisabledShowContext(int flags) { 1376 try { 1377 mSystemService.setDisabledShowContext(flags); 1378 } catch (RemoteException e) { 1379 } 1380 } 1381 1382 /** 1383 * Equivalent to {@link VoiceInteractionService#getDisabledShowContext 1384 * VoiceInteractionService.getDisabledShowContext}. 1385 */ getDisabledShowContext()1386 public int getDisabledShowContext() { 1387 try { 1388 return mSystemService.getDisabledShowContext(); 1389 } catch (RemoteException e) { 1390 return 0; 1391 } 1392 } 1393 1394 /** 1395 * Return which show context flags have been disabled by the user through the system 1396 * settings UI, so the session will never get this data. Returned flags are any combination of 1397 * {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST} and 1398 * {@link VoiceInteractionSession#SHOW_WITH_SCREENSHOT 1399 * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}. Note that this only tells you about 1400 * global user settings, not about restrictions that may be applied contextual based on 1401 * the current application the user is in or other transient states. 1402 */ getUserDisabledShowContext()1403 public int getUserDisabledShowContext() { 1404 try { 1405 return mSystemService.getUserDisabledShowContext(); 1406 } catch (RemoteException e) { 1407 return 0; 1408 } 1409 } 1410 1411 /** 1412 * Show the UI for this session. This asks the system to go through the process of showing 1413 * your UI, which will eventually culminate in {@link #onShow}. This is similar to calling 1414 * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}. 1415 * @param args Arbitrary arguments that will be propagated {@link #onShow}. 1416 * @param flags Indicates additional optional behavior that should be performed. May 1417 * be any combination of 1418 * {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST} and 1419 * {@link VoiceInteractionSession#SHOW_WITH_SCREENSHOT 1420 * VoiceInteractionSession.SHOW_WITH_SCREENSHOT} 1421 * to request that the system generate and deliver assist data on the current foreground 1422 * app as part of showing the session UI. 1423 */ show(Bundle args, int flags)1424 public void show(Bundle args, int flags) { 1425 if (mToken == null) { 1426 throw new IllegalStateException("Can't call before onCreate()"); 1427 } 1428 try { 1429 mSystemService.showSessionFromSession(mToken, args, flags, 1430 mContext.getAttributionTag()); 1431 } catch (RemoteException e) { 1432 } 1433 } 1434 1435 /** 1436 * Hide the session's UI, if currently shown. Call this when you are done with your 1437 * user interaction. 1438 */ hide()1439 public void hide() { 1440 if (mToken == null) { 1441 throw new IllegalStateException("Can't call before onCreate()"); 1442 } 1443 try { 1444 mSystemService.hideSessionFromSession(mToken); 1445 } catch (RemoteException e) { 1446 } 1447 } 1448 1449 /** 1450 * Control whether the UI layer for this session is enabled. It is enabled by default. 1451 * If set to false, you will not be able to provide a UI through {@link #onCreateContentView()}. 1452 */ setUiEnabled(boolean enabled)1453 public void setUiEnabled(boolean enabled) { 1454 if (mUiEnabled != enabled) { 1455 mUiEnabled = enabled; 1456 if (mWindowVisible) { 1457 if (enabled) { 1458 ensureWindowAdded(); 1459 showWindow(); 1460 } else { 1461 ensureWindowHidden(); 1462 } 1463 } 1464 } 1465 } 1466 1467 /** 1468 * You can call this to customize the theme used by your IME's window. 1469 * This must be set before {@link #onCreate}, so you 1470 * will typically call it in your constructor with the resource ID 1471 * of your custom theme. 1472 */ setTheme(int theme)1473 public void setTheme(int theme) { 1474 if (mWindow != null) { 1475 throw new IllegalStateException("Must be called before onCreate()"); 1476 } 1477 mTheme = theme; 1478 } 1479 1480 /** 1481 * Ask that a new activity be started for voice interaction. This will create a 1482 * new dedicated task in the activity manager for this voice interaction session; 1483 * this means that {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK} 1484 * will be set for you to make it a new task. 1485 * 1486 * <p>The newly started activity will be displayed to the user in a special way, as 1487 * a layer under the voice interaction UI.</p> 1488 * 1489 * <p>As the voice activity runs, it can retrieve a {@link android.app.VoiceInteractor} 1490 * through which it can perform voice interactions through your session. These requests 1491 * for voice interactions will appear as callbacks on {@link #onGetSupportedCommands}, 1492 * {@link #onRequestConfirmation}, {@link #onRequestPickOption}, 1493 * {@link #onRequestCompleteVoice}, {@link #onRequestAbortVoice}, 1494 * or {@link #onRequestCommand} 1495 * 1496 * <p>You will receive a call to {@link #onTaskStarted} when the task starts up 1497 * and {@link #onTaskFinished} when the last activity has finished. 1498 * 1499 * @param intent The Intent to start this voice interaction. The given Intent will 1500 * always have {@link Intent#CATEGORY_VOICE Intent.CATEGORY_VOICE} added to it, since 1501 * this is part of a voice interaction. 1502 */ startVoiceActivity(Intent intent)1503 public void startVoiceActivity(Intent intent) { 1504 if (mToken == null) { 1505 throw new IllegalStateException("Can't call before onCreate()"); 1506 } 1507 try { 1508 intent.migrateExtraStreamToClipData(mContext); 1509 intent.prepareToLeaveProcess(mContext); 1510 int res = mSystemService.startVoiceActivity(mToken, intent, 1511 intent.resolveType(mContext.getContentResolver()), 1512 mContext.getAttributionTag()); 1513 Instrumentation.checkStartActivityResult(res, intent); 1514 } catch (RemoteException e) { 1515 } 1516 } 1517 1518 /** 1519 * <p>Ask that a new assistant activity be started. This will create a new task in the 1520 * in activity manager: this means that 1521 * {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK} 1522 * will be set for you to make it a new task.</p> 1523 * 1524 * <p>The newly started activity will be displayed on top of other activities in the system 1525 * in a new layer that is not affected by multi-window mode. Tasks started from this activity 1526 * will go into the normal activity layer and not this new layer.</p> 1527 * 1528 * <p>By default, the system will create a window for the UI for this session. If you are using 1529 * an assistant activity instead, then you can disable the window creation by calling 1530 * {@link #setUiEnabled} in {@link #onPrepareShow(Bundle, int)}.</p> 1531 * 1532 * NOTE: if the app would like to override some options to start the Activity, 1533 * use {@link #startAssistantActivity(Intent, Bundle)} instead. 1534 */ startAssistantActivity(Intent intent)1535 public void startAssistantActivity(Intent intent) { 1536 startAssistantActivity(intent, ActivityOptions.makeBasic().toBundle()); 1537 } 1538 1539 /** 1540 * <p>Ask that a new assistant activity be started. This will create a new task in the 1541 * in activity manager: this means that 1542 * {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK} 1543 * will be set for you to make it a new task.</p> 1544 * 1545 * <p>The newly started activity will be displayed on top of other activities in the system 1546 * in a new layer that is not affected by multi-window mode. Tasks started from this activity 1547 * will go into the normal activity layer and not this new layer.</p> 1548 * 1549 * <p>By default, the system will create a window for the UI for this session. If you are using 1550 * an assistant activity instead, then you can disable the window creation by calling 1551 * {@link #setUiEnabled} in {@link #onPrepareShow(Bundle, int)}.</p> 1552 * 1553 * @param intent the intent used to start an assistant activity 1554 * @param bundle Additional options for how the Activity should be started. See 1555 * {@link ActivityOptions} for how to build the Bundle supplied here. 1556 */ startAssistantActivity(@onNull Intent intent, @NonNull Bundle bundle)1557 public void startAssistantActivity(@NonNull Intent intent, @NonNull Bundle bundle) { 1558 Objects.requireNonNull(bundle); 1559 if (mToken == null) { 1560 throw new IllegalStateException("Can't call before onCreate()"); 1561 } 1562 try { 1563 intent.migrateExtraStreamToClipData(mContext); 1564 intent.prepareToLeaveProcess(mContext); 1565 int res = mSystemService.startAssistantActivity(mToken, intent, 1566 intent.resolveType(mContext.getContentResolver()), 1567 mContext.getAttributionTag(), bundle); 1568 Instrumentation.checkStartActivityResult(res, intent); 1569 } catch (RemoteException e) { 1570 } 1571 } 1572 1573 /** 1574 * Requests a list of supported actions from an app. 1575 * 1576 * @param activityId Ths activity id of the app to get the actions from. 1577 * @param cancellationSignal A signal to cancel the operation in progress, 1578 * or {@code null} if none. 1579 * @param resultExecutor The handler to receive the callback. 1580 * @param callback The callback to receive the response. 1581 */ requestDirectActions(@onNull ActivityId activityId, @Nullable CancellationSignal cancellationSignal, @NonNull @CallbackExecutor Executor resultExecutor, @NonNull Consumer<List<DirectAction>> callback)1582 public final void requestDirectActions(@NonNull ActivityId activityId, 1583 @Nullable CancellationSignal cancellationSignal, 1584 @NonNull @CallbackExecutor Executor resultExecutor, 1585 @NonNull Consumer<List<DirectAction>> callback) { 1586 Objects.requireNonNull(activityId); 1587 Objects.requireNonNull(resultExecutor); 1588 Objects.requireNonNull(callback); 1589 if (mToken == null) { 1590 throw new IllegalStateException("Can't call before onCreate()"); 1591 } 1592 1593 if (cancellationSignal != null) { 1594 cancellationSignal.throwIfCanceled(); 1595 } 1596 1597 final RemoteCallback cancellationCallback = (cancellationSignal != null) 1598 ? new RemoteCallback(b -> { 1599 if (b != null) { 1600 final IBinder cancellation = b.getBinder( 1601 VoiceInteractor.KEY_CANCELLATION_SIGNAL); 1602 if (cancellation != null) { 1603 cancellationSignal.setRemote(ICancellationSignal.Stub.asInterface( 1604 cancellation)); 1605 } 1606 } 1607 }) 1608 : null; 1609 1610 try { 1611 mSystemService.requestDirectActions(mToken, activityId.getTaskId(), 1612 activityId.getAssistToken(), cancellationCallback, 1613 new RemoteCallback(createSafeResultListener((result) -> { 1614 List<DirectAction> list; 1615 if (result == null) { 1616 list = Collections.emptyList(); 1617 } else { 1618 final ParceledListSlice<DirectAction> pls = result.getParcelable( 1619 DirectAction.KEY_ACTIONS_LIST, android.content.pm.ParceledListSlice.class); 1620 if (pls != null) { 1621 final List<DirectAction> receivedList = pls.getList(); 1622 list = (receivedList != null) ? receivedList : Collections.emptyList(); 1623 } else { 1624 list = Collections.emptyList(); 1625 } 1626 } 1627 resultExecutor.execute(() -> callback.accept(list)); 1628 }))); 1629 } catch (RemoteException e) { 1630 e.rethrowFromSystemServer(); 1631 } 1632 } 1633 1634 /** 1635 * Called when the direct actions are invalidated. 1636 */ onDirectActionsInvalidated(@onNull ActivityId activityId)1637 public void onDirectActionsInvalidated(@NonNull ActivityId activityId) { 1638 1639 } 1640 1641 /** 1642 * Asks that an action be performed by the app. This will send a request to the app which 1643 * provided this action. 1644 * 1645 * <p> An action could take time to execute and the result is provided asynchronously 1646 * via a callback. If the action is taking longer and you want to cancel its execution 1647 * you can pass in a cancellation signal through which to notify the app to abort the 1648 * action. 1649 * 1650 * @param action The action to be performed. 1651 * @param extras Any optional extras sent to the app as part of the request 1652 * @param cancellationSignal A signal to cancel the operation in progress, 1653 * or {@code null} if none. 1654 * @param resultExecutor The handler to receive the callback. 1655 * @param resultListener The callback to receive the response. 1656 * 1657 * @see #requestDirectActions(ActivityId, CancellationSignal, Executor, Consumer) 1658 * @see Activity#onGetDirectActions(CancellationSignal, Consumer) 1659 */ performDirectAction(@onNull DirectAction action, @Nullable Bundle extras, @Nullable CancellationSignal cancellationSignal, @NonNull @CallbackExecutor Executor resultExecutor, @NonNull Consumer<Bundle> resultListener)1660 public final void performDirectAction(@NonNull DirectAction action, @Nullable Bundle extras, 1661 @Nullable CancellationSignal cancellationSignal, 1662 @NonNull @CallbackExecutor Executor resultExecutor, 1663 @NonNull Consumer<Bundle> resultListener) { 1664 if (mToken == null) { 1665 throw new IllegalStateException("Can't call before onCreate()"); 1666 } 1667 Objects.requireNonNull(resultExecutor); 1668 Objects.requireNonNull(resultListener); 1669 1670 if (cancellationSignal != null) { 1671 cancellationSignal.throwIfCanceled(); 1672 } 1673 1674 final RemoteCallback cancellationCallback = (cancellationSignal != null) 1675 ? new RemoteCallback(createSafeResultListener(b -> { 1676 if (b != null) { 1677 final IBinder cancellation = b.getBinder( 1678 VoiceInteractor.KEY_CANCELLATION_SIGNAL); 1679 if (cancellation != null) { 1680 cancellationSignal.setRemote(ICancellationSignal.Stub.asInterface( 1681 cancellation)); 1682 } 1683 } 1684 })) 1685 : null; 1686 1687 final RemoteCallback resultCallback = new RemoteCallback(createSafeResultListener(b -> { 1688 if (b != null) { 1689 resultExecutor.execute(() -> resultListener.accept(b)); 1690 } else { 1691 resultExecutor.execute(() -> resultListener.accept(Bundle.EMPTY)); 1692 } 1693 })); 1694 1695 try { 1696 mSystemService.performDirectAction(mToken, action.getId(), extras, 1697 action.getTaskId(), action.getActivityId(), cancellationCallback, 1698 resultCallback); 1699 } catch (RemoteException e) { 1700 e.rethrowFromSystemServer(); 1701 } 1702 } 1703 1704 /** 1705 * Set whether this session will keep the device awake while it is running a voice 1706 * activity. By default, the system holds a wake lock for it while in this state, 1707 * so that it can work even if the screen is off. Setting this to false removes that 1708 * wake lock, allowing the CPU to go to sleep. This is typically used if the 1709 * session decides it has been waiting too long for a response from the user and 1710 * doesn't want to let this continue to drain the battery. 1711 * 1712 * <p>Passing false here will release the wake lock, and you can call later with 1713 * true to re-acquire it. It will also be automatically re-acquired for you each 1714 * time you start a new voice activity task -- that is when you call 1715 * {@link #startVoiceActivity}.</p> 1716 */ setKeepAwake(boolean keepAwake)1717 public void setKeepAwake(boolean keepAwake) { 1718 if (mToken == null) { 1719 throw new IllegalStateException("Can't call before onCreate()"); 1720 } 1721 try { 1722 mSystemService.setKeepAwake(mToken, keepAwake); 1723 } catch (RemoteException e) { 1724 } 1725 } 1726 1727 /** 1728 * Request that all system dialogs (and status bar shade etc) be closed, allowing 1729 * access to the session's UI. This will <em>not</em> cause the lock screen to be 1730 * dismissed. 1731 */ closeSystemDialogs()1732 public void closeSystemDialogs() { 1733 if (mToken == null) { 1734 throw new IllegalStateException("Can't call before onCreate()"); 1735 } 1736 try { 1737 mSystemService.closeSystemDialogs(mToken); 1738 } catch (RemoteException e) { 1739 } 1740 } 1741 1742 /** 1743 * Convenience for inflating views. 1744 */ getLayoutInflater()1745 public LayoutInflater getLayoutInflater() { 1746 ensureWindowCreated(); 1747 return mInflater; 1748 } 1749 1750 /** 1751 * Retrieve the window being used to show the session's UI. 1752 */ getWindow()1753 public Dialog getWindow() { 1754 ensureWindowCreated(); 1755 return mWindow; 1756 } 1757 1758 /** 1759 * Finish the session. This completely destroys the session -- the next time it is shown, 1760 * an entirely new one will be created. You do not normally call this function; instead, 1761 * use {@link #hide} and allow the system to destroy your session if it needs its RAM. 1762 */ finish()1763 public void finish() { 1764 if (mToken == null) { 1765 throw new IllegalStateException("Can't call before onCreate()"); 1766 } 1767 try { 1768 mSystemService.finish(mToken); 1769 } catch (RemoteException e) { 1770 } 1771 } 1772 1773 /** 1774 * Initiatize a new session. At this point you don't know exactly what this 1775 * session will be used for; you will find that out in {@link #onShow}. 1776 */ onCreate()1777 public void onCreate() { 1778 doOnCreate(); 1779 } 1780 doOnCreate()1781 private void doOnCreate() { 1782 mTheme = mTheme != 0 ? mTheme 1783 : com.android.internal.R.style.Theme_DeviceDefault_VoiceInteractionSession; 1784 } 1785 1786 /** 1787 * Called prior to {@link #onShow} before any UI setup has occurred. Not generally useful. 1788 * 1789 * @param args The arguments that were supplied to 1790 * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}. 1791 * @param showFlags The show flags originally provided to 1792 * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}. 1793 */ onPrepareShow(Bundle args, int showFlags)1794 public void onPrepareShow(Bundle args, int showFlags) { 1795 } 1796 1797 /** 1798 * Called when the session UI is going to be shown. This is called after 1799 * {@link #onCreateContentView} (if the session's content UI needed to be created) and 1800 * immediately prior to the window being shown. This may be called while the window 1801 * is already shown, if a show request has come in while it is shown, to allow you to 1802 * update the UI to match the new show arguments. 1803 * 1804 * @param args The arguments that were supplied to 1805 * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}. 1806 * Some example keys include : "invocation_type", "invocation_phone_state", 1807 * {@link #KEY_SHOW_SESSION_ID}, "invocation_time_ms", 1808 * Intent.EXTRA_TIME ("android.intent.extra.TIME") indicating timing 1809 * in milliseconds of the KeyEvent that triggered Assistant and 1810 * Intent.EXTRA_ASSIST_INPUT_DEVICE_ID (android.intent.extra.ASSIST_INPUT_DEVICE_ID) 1811 * referring to the device that sent the request. Starting from Android 14, the system will 1812 * add {@link VoiceInteractionService#KEY_SHOW_SESSION_ID}, the Bundle is not null. But the 1813 * application should handle null case before Android 14. 1814 * @param showFlags The show flags originally provided to 1815 * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}. 1816 */ onShow(@ullable Bundle args, int showFlags)1817 public void onShow(@Nullable Bundle args, int showFlags) { 1818 } 1819 1820 /** 1821 * Called immediately after stopping to show the session UI. 1822 */ onHide()1823 public void onHide() { 1824 } 1825 1826 /** 1827 * Last callback to the session as it is being finished. 1828 */ onDestroy()1829 public void onDestroy() { 1830 } 1831 1832 /** 1833 * Hook in which to create the session's UI. 1834 */ onCreateContentView()1835 public View onCreateContentView() { 1836 return null; 1837 } 1838 setContentView(View view)1839 public void setContentView(View view) { 1840 ensureWindowCreated(); 1841 mContentFrame.removeAllViews(); 1842 mContentFrame.addView(view, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)); 1843 mContentFrame.requestApplyInsets(); 1844 } 1845 doOnHandleAssist(int taskId, IBinder assistToken, Bundle data, AssistStructure structure, Throwable failure, AssistContent content, int index, int count)1846 void doOnHandleAssist(int taskId, IBinder assistToken, Bundle data, AssistStructure structure, 1847 Throwable failure, AssistContent content, int index, int count) { 1848 if (failure != null) { 1849 onAssistStructureFailure(failure); 1850 } 1851 AssistState assistState = new AssistState(new ActivityId(taskId, assistToken), 1852 data, structure, content, index, count); 1853 onHandleAssist(assistState); 1854 } 1855 1856 /** 1857 * Called when there has been a failure transferring the {@link AssistStructure} to 1858 * the assistant. This may happen, for example, if the data is too large and results 1859 * in an out of memory exception, the data has been cleared during transferring due to 1860 * the new incoming assist data, or the client has provided corrupt data. This will be 1861 * called immediately before {@link #onHandleAssist} and the AssistStructure supplied 1862 * there afterwards will be null. 1863 * 1864 * @param failure The failure exception that was thrown when building the 1865 * {@link AssistStructure}. 1866 */ onAssistStructureFailure(Throwable failure)1867 public void onAssistStructureFailure(Throwable failure) { 1868 } 1869 1870 /** 1871 * Called to receive data from the application that the user was currently viewing when 1872 - * an assist session is started. If the original show request did not specify 1873 * {@link #SHOW_WITH_ASSIST}, this method will not be called. 1874 * 1875 * @param data Arbitrary data supplied by the app through 1876 * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}. 1877 * May be null if assist data has been disabled by the user or device policy. 1878 * @param structure If available, the structure definition of all windows currently 1879 * displayed by the app. May be null if assist data has been disabled by the user 1880 * or device policy; will be an empty stub if the application has disabled assist 1881 * by marking its window as secure. 1882 * @param content Additional content data supplied by the app through 1883 * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}. 1884 * May be null if assist data has been disabled by the user or device policy; will 1885 * not be automatically filled in with data from the app if the app has marked its 1886 * window as secure. 1887 * 1888 * @deprecated use {@link #onHandleAssist(AssistState)} 1889 */ 1890 @Deprecated onHandleAssist(@ullable Bundle data, @Nullable AssistStructure structure, @Nullable AssistContent content)1891 public void onHandleAssist(@Nullable Bundle data, @Nullable AssistStructure structure, 1892 @Nullable AssistContent content) { 1893 } 1894 1895 /** 1896 * Called to receive data from the application that the user was currently viewing when 1897 * an assist session is started. If the original show request did not specify 1898 * {@link #SHOW_WITH_ASSIST}, {@link AssistState} parameter will only provide 1899 * {@link ActivityId}. If there was a failure to write the assist data to 1900 * {@link AssistStructure}, the {@link AssistState#getAssistStructure()} will return null. 1901 * 1902 * <p>This method is called for all activities along with an index and count that indicates 1903 * which activity the data is for. {@code index} will be between 0 and {@code count}-1 and 1904 * this method is called once for each activity in no particular order. The {@code count} 1905 * indicates how many activities to expect assist data for, including the top focused one. 1906 * The focused activity can be determined by calling {@link AssistState#isFocused()}. 1907 * 1908 * <p>To be responsive to assist requests, process assist data as soon as it is received, 1909 * without waiting for all queued activities to return assist data. 1910 * 1911 * @param state The state object capturing the state of an activity. 1912 */ onHandleAssist(@onNull AssistState state)1913 public void onHandleAssist(@NonNull AssistState state) { 1914 if (state.getAssistData() == null && state.getAssistStructure() == null 1915 && state.getAssistContent() == null) { 1916 return; 1917 } else if (state.getIndex() == 0) { 1918 onHandleAssist(state.getAssistData(), state.getAssistStructure(), 1919 state.getAssistContent()); 1920 } else { 1921 onHandleAssistSecondary(state.getAssistData(), state.getAssistStructure(), 1922 state.getAssistContent(), state.getIndex(), state.getCount()); 1923 } 1924 } 1925 1926 /** 1927 * Called to receive data from other applications that the user was or is interacting with, 1928 * that are currently on the screen in a multi-window display environment, not including the 1929 * currently focused activity. This could be 1930 * a free-form window, a picture-in-picture window, or another window in a split-screen display. 1931 * <p> 1932 * This method is very similar to 1933 * {@link #onHandleAssist} except that it is called 1934 * for additional non-focused activities along with an index and count that indicates 1935 * which additional activity the data is for. {@code index} will be between 1 and 1936 * {@code count}-1 and this method is called once for each additional window, in no particular 1937 * order. The {@code count} indicates how many windows to expect assist data for, including the 1938 * top focused activity, which continues to be returned via {@link #onHandleAssist}. 1939 * <p> 1940 * To be responsive to assist requests, process assist data as soon as it is received, 1941 * without waiting for all queued activities to return assist data. 1942 * 1943 * @param data Arbitrary data supplied by the app through 1944 * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}. 1945 * May be null if assist data has been disabled by the user or device policy. 1946 * @param structure If available, the structure definition of all windows currently 1947 * displayed by the app. May be null if assist data has been disabled by the user 1948 * or device policy; will be an empty stub if the application has disabled assist 1949 * by marking its window as secure. 1950 * @param content Additional content data supplied by the app through 1951 * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}. 1952 * May be null if assist data has been disabled by the user or device policy; will 1953 * not be automatically filled in with data from the app if the app has marked its 1954 * window as secure. 1955 * @param index the index of the additional activity that this data 1956 * is for. 1957 * @param count the total number of additional activities for which the assist data is being 1958 * returned, including the focused activity that is returned via 1959 * {@link #onHandleAssist}. 1960 * 1961 * @deprecated use {@link #onHandleAssist(AssistState)} 1962 */ 1963 @Deprecated onHandleAssistSecondary(@ullable Bundle data, @Nullable AssistStructure structure, @Nullable AssistContent content, int index, int count)1964 public void onHandleAssistSecondary(@Nullable Bundle data, @Nullable AssistStructure structure, 1965 @Nullable AssistContent content, int index, int count) { 1966 } 1967 1968 /** 1969 * Called to receive a screenshot of what the user was currently viewing when an assist 1970 * session is started. May be null if screenshots are disabled by the user, policy, 1971 * or application. If the original show request did not specify 1972 * {@link #SHOW_WITH_SCREENSHOT}, this method will not be called. 1973 */ onHandleScreenshot(@ullable Bitmap screenshot)1974 public void onHandleScreenshot(@Nullable Bitmap screenshot) { 1975 } 1976 onKeyDown(int keyCode, KeyEvent event)1977 public boolean onKeyDown(int keyCode, KeyEvent event) { 1978 return false; 1979 } 1980 onKeyLongPress(int keyCode, KeyEvent event)1981 public boolean onKeyLongPress(int keyCode, KeyEvent event) { 1982 return false; 1983 } 1984 onKeyUp(int keyCode, KeyEvent event)1985 public boolean onKeyUp(int keyCode, KeyEvent event) { 1986 return false; 1987 } 1988 onKeyMultiple(int keyCode, int count, KeyEvent event)1989 public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) { 1990 return false; 1991 } 1992 1993 /** 1994 * Called when the user presses the back button while focus is in the session UI. Note 1995 * that this will only happen if the session UI has requested input focus in its window; 1996 * otherwise, the back key will go to whatever window has focus and do whatever behavior 1997 * it normally has there. The default implementation simply calls {@link #hide}. 1998 */ onBackPressed()1999 public void onBackPressed() { 2000 hide(); 2001 } 2002 2003 /** 2004 * Sessions automatically watch for requests that all system UI be closed (such as when 2005 * the user presses HOME), which will appear here. The default implementation always 2006 * calls {@link #hide}. 2007 */ onCloseSystemDialogs()2008 public void onCloseSystemDialogs() { 2009 hide(); 2010 } 2011 2012 /** 2013 * Called when the lockscreen was shown. 2014 */ onLockscreenShown()2015 public void onLockscreenShown() { 2016 hide(); 2017 } 2018 2019 @Override onConfigurationChanged(Configuration newConfig)2020 public void onConfigurationChanged(Configuration newConfig) { 2021 } 2022 2023 @Override onLowMemory()2024 public void onLowMemory() { 2025 } 2026 2027 @Override onTrimMemory(int level)2028 public void onTrimMemory(int level) { 2029 } 2030 2031 /** 2032 * Compute the interesting insets into your UI. The default implementation 2033 * sets {@link Insets#contentInsets outInsets.contentInsets.top} to the height 2034 * of the window, meaning it should not adjust content underneath. The default touchable 2035 * insets are {@link Insets#TOUCHABLE_INSETS_FRAME}, meaning it consumes all touch 2036 * events within its window frame. 2037 * 2038 * @param outInsets Fill in with the current UI insets. 2039 */ onComputeInsets(Insets outInsets)2040 public void onComputeInsets(Insets outInsets) { 2041 outInsets.contentInsets.left = 0; 2042 outInsets.contentInsets.bottom = 0; 2043 outInsets.contentInsets.right = 0; 2044 View decor = getWindow().getWindow().getDecorView(); 2045 outInsets.contentInsets.top = decor.getHeight(); 2046 outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_FRAME; 2047 outInsets.touchableRegion.setEmpty(); 2048 } 2049 2050 /** 2051 * Called when a task initiated by {@link #startVoiceActivity(android.content.Intent)} 2052 * has actually started. 2053 * 2054 * @param intent The original {@link Intent} supplied to 2055 * {@link #startVoiceActivity(android.content.Intent)}. 2056 * @param taskId Unique ID of the now running task. 2057 */ onTaskStarted(Intent intent, int taskId)2058 public void onTaskStarted(Intent intent, int taskId) { 2059 } 2060 2061 /** 2062 * Called when the last activity of a task initiated by 2063 * {@link #startVoiceActivity(android.content.Intent)} has finished. The default 2064 * implementation calls {@link #finish()} on the assumption that this represents 2065 * the completion of a voice action. You can override the implementation if you would 2066 * like a different behavior. 2067 * 2068 * @param intent The original {@link Intent} supplied to 2069 * {@link #startVoiceActivity(android.content.Intent)}. 2070 * @param taskId Unique ID of the finished task. 2071 */ onTaskFinished(Intent intent, int taskId)2072 public void onTaskFinished(Intent intent, int taskId) { 2073 hide(); 2074 } 2075 2076 /** 2077 * Request to query for what extended commands the session supports. 2078 * 2079 * @param commands An array of commands that are being queried. 2080 * @return Return an array of booleans indicating which of each entry in the 2081 * command array is supported. A true entry in the array indicates the command 2082 * is supported; false indicates it is not. The default implementation returns 2083 * an array of all false entries. 2084 */ onGetSupportedCommands(String[] commands)2085 public boolean[] onGetSupportedCommands(String[] commands) { 2086 return new boolean[commands.length]; 2087 } 2088 2089 /** 2090 * Request to confirm with the user before proceeding with an unrecoverable operation, 2091 * corresponding to a {@link android.app.VoiceInteractor.ConfirmationRequest 2092 * VoiceInteractor.ConfirmationRequest}. 2093 * 2094 * @param request The active request. 2095 */ onRequestConfirmation(ConfirmationRequest request)2096 public void onRequestConfirmation(ConfirmationRequest request) { 2097 } 2098 2099 /** 2100 * Request for the user to pick one of N options, corresponding to a 2101 * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}. 2102 * 2103 * @param request The active request. 2104 */ onRequestPickOption(PickOptionRequest request)2105 public void onRequestPickOption(PickOptionRequest request) { 2106 } 2107 2108 /** 2109 * Request to complete the voice interaction session because the voice activity successfully 2110 * completed its interaction using voice. Corresponds to 2111 * {@link android.app.VoiceInteractor.CompleteVoiceRequest 2112 * VoiceInteractor.CompleteVoiceRequest}. The default implementation just sends an empty 2113 * confirmation back to allow the activity to exit. 2114 * 2115 * @param request The active request. 2116 */ onRequestCompleteVoice(CompleteVoiceRequest request)2117 public void onRequestCompleteVoice(CompleteVoiceRequest request) { 2118 } 2119 2120 /** 2121 * Request to abort the voice interaction session because the voice activity can not 2122 * complete its interaction using voice. Corresponds to 2123 * {@link android.app.VoiceInteractor.AbortVoiceRequest 2124 * VoiceInteractor.AbortVoiceRequest}. The default implementation just sends an empty 2125 * confirmation back to allow the activity to exit. 2126 * 2127 * @param request The active request. 2128 */ onRequestAbortVoice(AbortVoiceRequest request)2129 public void onRequestAbortVoice(AbortVoiceRequest request) { 2130 } 2131 2132 /** 2133 * Process an arbitrary extended command from the caller, 2134 * corresponding to a {@link android.app.VoiceInteractor.CommandRequest 2135 * VoiceInteractor.CommandRequest}. 2136 * 2137 * @param request The active request. 2138 */ onRequestCommand(CommandRequest request)2139 public void onRequestCommand(CommandRequest request) { 2140 } 2141 2142 /** 2143 * Called when the {@link android.app.VoiceInteractor} has asked to cancel a {@link Request} 2144 * that was previously delivered to {@link #onRequestConfirmation}, 2145 * {@link #onRequestPickOption}, {@link #onRequestCompleteVoice}, {@link #onRequestAbortVoice}, 2146 * or {@link #onRequestCommand}. 2147 * 2148 * @param request The request that is being canceled. 2149 */ onCancelRequest(Request request)2150 public void onCancelRequest(Request request) { 2151 } 2152 2153 /** 2154 * Registers a callback that will be notified when visible activities have been changed. 2155 * 2156 * Note: The {@link VisibleActivityCallback#onVisible(VisibleActivityInfo)} will be called 2157 * immediately with current visible activities when the callback is registered for the first 2158 * time. If the callback is already registered, this method does nothing. 2159 * 2160 * @param executor The executor which will be used to invoke the callback. 2161 * @param callback The callback to receive the response. 2162 * 2163 * @throws IllegalStateException if calling this method before onCreate(). 2164 */ registerVisibleActivityCallback(@onNull @allbackExecutor Executor executor, @NonNull VisibleActivityCallback callback)2165 public final void registerVisibleActivityCallback(@NonNull @CallbackExecutor Executor executor, 2166 @NonNull VisibleActivityCallback callback) { 2167 if (DEBUG) { 2168 Log.d(TAG, "registerVisibleActivityCallback"); 2169 } 2170 if (mToken == null) { 2171 throw new IllegalStateException("Can't call before onCreate()"); 2172 } 2173 Objects.requireNonNull(executor); 2174 Objects.requireNonNull(callback); 2175 2176 mHandlerCaller.sendMessage( 2177 mHandlerCaller.obtainMessageOO(MSG_REGISTER_VISIBLE_ACTIVITY_CALLBACK, executor, 2178 callback)); 2179 } 2180 2181 /** 2182 * Unregisters the callback. 2183 * 2184 * @param callback The callback to receive the response. 2185 */ unregisterVisibleActivityCallback(@onNull VisibleActivityCallback callback)2186 public final void unregisterVisibleActivityCallback(@NonNull VisibleActivityCallback callback) { 2187 if (DEBUG) { 2188 Log.d(TAG, "unregisterVisibleActivityCallback"); 2189 } 2190 Objects.requireNonNull(callback); 2191 2192 mHandlerCaller.sendMessage( 2193 mHandlerCaller.obtainMessageO(MSG_UNREGISTER_VISIBLE_ACTIVITY_CALLBACK, callback)); 2194 } 2195 2196 /** 2197 * Print the Service's state into the given stream. This gets invoked by 2198 * {@link VoiceInteractionSessionService} when its Service 2199 * {@link android.app.Service#dump} method is called. 2200 * 2201 * @param prefix Text to print at the front of each line. 2202 * @param fd The raw file descriptor that the dump is being sent to. 2203 * @param writer The PrintWriter to which you should dump your state. This will be 2204 * closed for you after you return. 2205 * @param args additional arguments to the dump request. 2206 */ dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)2207 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 2208 writer.print(prefix); writer.print("mToken="); writer.println(mToken); 2209 writer.print(prefix); writer.print("mTheme=#"); writer.println(Integer.toHexString(mTheme)); 2210 writer.print(prefix); writer.print("mUiEnabled="); writer.println(mUiEnabled); 2211 writer.print(" mInitialized="); writer.println(mInitialized); 2212 writer.print(prefix); writer.print("mWindowAdded="); writer.print(mWindowAdded); 2213 writer.print(" mWindowVisible="); writer.println(mWindowVisible); 2214 writer.print(prefix); writer.print("mWindowWasVisible="); writer.print(mWindowWasVisible); 2215 writer.print(" mInShowWindow="); writer.println(mInShowWindow); 2216 if (mActiveRequests.size() > 0) { 2217 writer.print(prefix); writer.println("Active requests:"); 2218 String innerPrefix = prefix + " "; 2219 for (int i=0; i<mActiveRequests.size(); i++) { 2220 Request req = mActiveRequests.valueAt(i); 2221 writer.print(prefix); writer.print(" #"); writer.print(i); 2222 writer.print(": "); 2223 writer.println(req); 2224 req.dump(innerPrefix, fd, writer, args); 2225 2226 } 2227 } 2228 } 2229 createSafeResultListener( @onNull Consumer<Bundle> consumer)2230 private SafeResultListener createSafeResultListener( 2231 @NonNull Consumer<Bundle> consumer) { 2232 synchronized (this) { 2233 final SafeResultListener listener = new SafeResultListener(consumer, this); 2234 mRemoteCallbacks.put(listener, consumer); 2235 return listener; 2236 } 2237 } 2238 removeSafeResultListener(@onNull SafeResultListener listener)2239 private Consumer<Bundle> removeSafeResultListener(@NonNull SafeResultListener listener) { 2240 synchronized (this) { 2241 return mRemoteCallbacks.remove(listener); 2242 } 2243 } 2244 2245 /** 2246 * Callback interface for receiving visible activity changes used for assistant usage. 2247 */ 2248 public interface VisibleActivityCallback { 2249 /** Callback to inform that an activity has become visible. */ onVisible(@onNull VisibleActivityInfo activityInfo)2250 default void onVisible(@NonNull VisibleActivityInfo activityInfo) {} 2251 2252 /** Callback to inform that a visible activity has gone. */ onInvisible(@onNull ActivityId activityId)2253 default void onInvisible(@NonNull ActivityId activityId) {} 2254 } 2255 2256 /** 2257 * Represents assist state captured when this session was started. 2258 * It contains the various assist data objects and a reference to 2259 * the source activity. 2260 */ 2261 @Immutable 2262 public static final class AssistState { 2263 private final @NonNull ActivityId mActivityId; 2264 private final int mIndex; 2265 private final int mCount; 2266 private final @Nullable Bundle mData; 2267 private final @Nullable AssistStructure mStructure; 2268 private final @Nullable AssistContent mContent; 2269 AssistState(@onNull ActivityId activityId, @Nullable Bundle data, @Nullable AssistStructure structure, @Nullable AssistContent content, int index, int count)2270 AssistState(@NonNull ActivityId activityId, @Nullable Bundle data, 2271 @Nullable AssistStructure structure, @Nullable AssistContent content, 2272 int index, int count) { 2273 mActivityId = activityId; 2274 mIndex = index; 2275 mCount = count; 2276 mData = data; 2277 mStructure = structure; 2278 mContent = content; 2279 } 2280 2281 /** 2282 * @return whether the source activity is focused. 2283 */ isFocused()2284 public boolean isFocused() { 2285 return mIndex == 0; 2286 } 2287 2288 /** 2289 * @return the index of the activity that this state is for or -1 2290 * if there was no assist data captured. 2291 */ getIndex()2292 public @IntRange(from = -1) int getIndex() { 2293 return mIndex; 2294 } 2295 2296 /** 2297 * @return the total number of activities for which the assist data is 2298 * being returned. 2299 */ getCount()2300 public @IntRange(from = 0) int getCount() { 2301 return mCount; 2302 } 2303 2304 /** 2305 * @return the id of the source activity 2306 */ getActivityId()2307 public @NonNull ActivityId getActivityId() { 2308 return mActivityId; 2309 } 2310 2311 /** 2312 * @return Arbitrary data supplied by the app through 2313 * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}. 2314 * May be null if assist data has been disabled by the user or device policy; will be null 2315 * if the original show request did not specify {@link #SHOW_WITH_ASSIST}. 2316 */ getAssistData()2317 public @Nullable Bundle getAssistData() { 2318 return mData; 2319 } 2320 2321 /** 2322 * @return If available, the structure definition of all windows currently 2323 * displayed by the app. May be null if assist data has been disabled by the user 2324 * or device policy; will be null if the original show request did not specify 2325 * {@link #SHOW_WITH_ASSIST} or the assist data has been corrupt when writing the data to 2326 * {@link AssistStructure}; will be an empty stub if the application has disabled assist 2327 * by marking its window as secure. 2328 */ getAssistStructure()2329 public @Nullable AssistStructure getAssistStructure() { 2330 return mStructure; 2331 } 2332 2333 /** 2334 * @return Additional content data supplied by the app through 2335 * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}. 2336 * May be null if assist data has been disabled by the user or device policy; will be null 2337 * if the original show request did not specify {@link #SHOW_WITH_ASSIST}. Will not be 2338 * automatically filled in with data from the app if the app has marked its window as 2339 * secure. 2340 */ getAssistContent()2341 public @Nullable AssistContent getAssistContent() { 2342 return mContent; 2343 } 2344 } 2345 2346 /** 2347 * Represents the id of an assist source activity. You can use 2348 * {@link #equals(Object)} to compare instances of this class. 2349 */ 2350 public static class ActivityId { 2351 private final int mTaskId; 2352 private final IBinder mAssistToken; 2353 ActivityId(int taskId, IBinder assistToken)2354 ActivityId(int taskId, IBinder assistToken) { 2355 mTaskId = taskId; 2356 mAssistToken = assistToken; 2357 } 2358 2359 /** @hide */ 2360 @TestApi getTaskId()2361 public int getTaskId() { 2362 return mTaskId; 2363 } 2364 2365 /** @hide */ 2366 @TestApi getAssistToken()2367 @NonNull public IBinder getAssistToken() { 2368 return mAssistToken; 2369 } 2370 2371 @Override equals(@ullable Object o)2372 public boolean equals(@Nullable Object o) { 2373 if (this == o) { 2374 return true; 2375 } 2376 if (o == null || getClass() != o.getClass()) { 2377 return false; 2378 } 2379 2380 ActivityId that = (ActivityId) o; 2381 2382 if (mTaskId != that.mTaskId) { 2383 return false; 2384 } 2385 return mAssistToken != null 2386 ? mAssistToken.equals(that.mAssistToken) 2387 : that.mAssistToken == null; 2388 } 2389 2390 @Override hashCode()2391 public int hashCode() { 2392 int result = mTaskId; 2393 result = 31 * result + (mAssistToken != null ? mAssistToken.hashCode() : 0); 2394 return result; 2395 } 2396 } 2397 2398 private static class SafeResultListener implements RemoteCallback.OnResultListener { 2399 private final @NonNull WeakReference<VoiceInteractionSession> mWeakSession; 2400 SafeResultListener(@onNull Consumer<Bundle> action, @NonNull VoiceInteractionSession session)2401 SafeResultListener(@NonNull Consumer<Bundle> action, 2402 @NonNull VoiceInteractionSession session) { 2403 mWeakSession = new WeakReference<>(session); 2404 } 2405 2406 @Override onResult(Bundle result)2407 public void onResult(Bundle result) { 2408 final VoiceInteractionSession session = mWeakSession.get(); 2409 if (session != null) { 2410 final Consumer<Bundle> consumer = session.removeSafeResultListener(this); 2411 if (consumer != null) { 2412 consumer.accept(result); 2413 } 2414 } 2415 } 2416 } 2417 } 2418