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