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