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().addFlags( 1141 WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED | 1142 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | 1143 WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR); 1144 1145 mThemeAttrs = mContext.obtainStyledAttributes(android.R.styleable.VoiceInteractionSession); 1146 mRootView = mInflater.inflate( 1147 com.android.internal.R.layout.voice_interaction_session, null); 1148 mRootView.setSystemUiVisibility( 1149 View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 1150 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); 1151 mWindow.setContentView(mRootView); 1152 mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer); 1153 1154 mContentFrame = (FrameLayout)mRootView.findViewById(android.R.id.content); 1155 1156 mWindow.getWindow().setLayout(MATCH_PARENT, MATCH_PARENT); 1157 mWindow.setToken(mToken); 1158 } 1159 ensureWindowAdded()1160 void ensureWindowAdded() { 1161 if (mUiEnabled && !mWindowAdded) { 1162 mWindowAdded = true; 1163 ensureWindowCreated(); 1164 View v = onCreateContentView(); 1165 if (v != null) { 1166 setContentView(v); 1167 } 1168 } 1169 } 1170 ensureWindowHidden()1171 void ensureWindowHidden() { 1172 if (mWindow != null) { 1173 mWindow.hide(); 1174 } 1175 } 1176 1177 /** 1178 * Equivalent to {@link VoiceInteractionService#setDisabledShowContext 1179 * VoiceInteractionService.setDisabledShowContext(int)}. 1180 */ setDisabledShowContext(int flags)1181 public void setDisabledShowContext(int flags) { 1182 try { 1183 mSystemService.setDisabledShowContext(flags); 1184 } catch (RemoteException e) { 1185 } 1186 } 1187 1188 /** 1189 * Equivalent to {@link VoiceInteractionService#getDisabledShowContext 1190 * VoiceInteractionService.getDisabledShowContext}. 1191 */ getDisabledShowContext()1192 public int getDisabledShowContext() { 1193 try { 1194 return mSystemService.getDisabledShowContext(); 1195 } catch (RemoteException e) { 1196 return 0; 1197 } 1198 } 1199 1200 /** 1201 * Return which show context flags have been disabled by the user through the system 1202 * settings UI, so the session will never get this data. Returned flags are any combination of 1203 * {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST} and 1204 * {@link VoiceInteractionSession#SHOW_WITH_SCREENSHOT 1205 * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}. Note that this only tells you about 1206 * global user settings, not about restrictions that may be applied contextual based on 1207 * the current application the user is in or other transient states. 1208 */ getUserDisabledShowContext()1209 public int getUserDisabledShowContext() { 1210 try { 1211 return mSystemService.getUserDisabledShowContext(); 1212 } catch (RemoteException e) { 1213 return 0; 1214 } 1215 } 1216 1217 /** 1218 * Show the UI for this session. This asks the system to go through the process of showing 1219 * your UI, which will eventually culminate in {@link #onShow}. This is similar to calling 1220 * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}. 1221 * @param args Arbitrary arguments that will be propagated {@link #onShow}. 1222 * @param flags Indicates additional optional behavior that should be performed. May 1223 * be any combination of 1224 * {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST} and 1225 * {@link VoiceInteractionSession#SHOW_WITH_SCREENSHOT 1226 * VoiceInteractionSession.SHOW_WITH_SCREENSHOT} 1227 * to request that the system generate and deliver assist data on the current foreground 1228 * app as part of showing the session UI. 1229 */ show(Bundle args, int flags)1230 public void show(Bundle args, int flags) { 1231 if (mToken == null) { 1232 throw new IllegalStateException("Can't call before onCreate()"); 1233 } 1234 try { 1235 mSystemService.showSessionFromSession(mToken, args, flags); 1236 } catch (RemoteException e) { 1237 } 1238 } 1239 1240 /** 1241 * Hide the session's UI, if currently shown. Call this when you are done with your 1242 * user interaction. 1243 */ hide()1244 public void hide() { 1245 if (mToken == null) { 1246 throw new IllegalStateException("Can't call before onCreate()"); 1247 } 1248 try { 1249 mSystemService.hideSessionFromSession(mToken); 1250 } catch (RemoteException e) { 1251 } 1252 } 1253 1254 /** 1255 * Control whether the UI layer for this session is enabled. It is enabled by default. 1256 * If set to false, you will not be able to provide a UI through {@link #onCreateContentView()}. 1257 */ setUiEnabled(boolean enabled)1258 public void setUiEnabled(boolean enabled) { 1259 if (mUiEnabled != enabled) { 1260 mUiEnabled = enabled; 1261 if (mWindowVisible) { 1262 if (enabled) { 1263 ensureWindowAdded(); 1264 mWindow.show(); 1265 } else { 1266 ensureWindowHidden(); 1267 } 1268 } 1269 } 1270 } 1271 1272 /** 1273 * You can call this to customize the theme used by your IME's window. 1274 * This must be set before {@link #onCreate}, so you 1275 * will typically call it in your constructor with the resource ID 1276 * of your custom theme. 1277 */ setTheme(int theme)1278 public void setTheme(int theme) { 1279 if (mWindow != null) { 1280 throw new IllegalStateException("Must be called before onCreate()"); 1281 } 1282 mTheme = theme; 1283 } 1284 1285 /** 1286 * Ask that a new activity be started for voice interaction. This will create a 1287 * new dedicated task in the activity manager for this voice interaction session; 1288 * this means that {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK} 1289 * will be set for you to make it a new task. 1290 * 1291 * <p>The newly started activity will be displayed to the user in a special way, as 1292 * a layer under the voice interaction UI.</p> 1293 * 1294 * <p>As the voice activity runs, it can retrieve a {@link android.app.VoiceInteractor} 1295 * through which it can perform voice interactions through your session. These requests 1296 * for voice interactions will appear as callbacks on {@link #onGetSupportedCommands}, 1297 * {@link #onRequestConfirmation}, {@link #onRequestPickOption}, 1298 * {@link #onRequestCompleteVoice}, {@link #onRequestAbortVoice}, 1299 * or {@link #onRequestCommand} 1300 * 1301 * <p>You will receive a call to {@link #onTaskStarted} when the task starts up 1302 * and {@link #onTaskFinished} when the last activity has finished. 1303 * 1304 * @param intent The Intent to start this voice interaction. The given Intent will 1305 * always have {@link Intent#CATEGORY_VOICE Intent.CATEGORY_VOICE} added to it, since 1306 * this is part of a voice interaction. 1307 */ startVoiceActivity(Intent intent)1308 public void startVoiceActivity(Intent intent) { 1309 if (mToken == null) { 1310 throw new IllegalStateException("Can't call before onCreate()"); 1311 } 1312 try { 1313 intent.migrateExtraStreamToClipData(); 1314 intent.prepareToLeaveProcess(mContext); 1315 int res = mSystemService.startVoiceActivity(mToken, intent, 1316 intent.resolveType(mContext.getContentResolver())); 1317 Instrumentation.checkStartActivityResult(res, intent); 1318 } catch (RemoteException e) { 1319 } 1320 } 1321 1322 /** 1323 * <p>Ask that a new assistant activity be started. This will create a new task in the 1324 * in activity manager: this means that 1325 * {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK} 1326 * will be set for you to make it a new task.</p> 1327 * 1328 * <p>The newly started activity will be displayed on top of other activities in the system 1329 * in a new layer that is not affected by multi-window mode. Tasks started from this activity 1330 * will go into the normal activity layer and not this new layer.</p> 1331 * 1332 * <p>By default, the system will create a window for the UI for this session. If you are using 1333 * an assistant activity instead, then you can disable the window creation by calling 1334 * {@link #setUiEnabled} in {@link #onPrepareShow(Bundle, int)}.</p> 1335 */ startAssistantActivity(Intent intent)1336 public void startAssistantActivity(Intent intent) { 1337 if (mToken == null) { 1338 throw new IllegalStateException("Can't call before onCreate()"); 1339 } 1340 try { 1341 intent.migrateExtraStreamToClipData(); 1342 intent.prepareToLeaveProcess(mContext); 1343 int res = mSystemService.startAssistantActivity(mToken, intent, 1344 intent.resolveType(mContext.getContentResolver())); 1345 Instrumentation.checkStartActivityResult(res, intent); 1346 } catch (RemoteException e) { 1347 } 1348 } 1349 1350 /** 1351 * Requests a list of supported actions from an app. 1352 * 1353 * @param activityId Ths activity id of the app to get the actions from. 1354 * @param resultExecutor The handler to receive the callback 1355 * @param cancellationSignal A signal to cancel the operation in progress, 1356 * or {@code null} if none. 1357 * @param callback The callback to receive the response 1358 */ requestDirectActions(@onNull ActivityId activityId, @Nullable CancellationSignal cancellationSignal, @NonNull @CallbackExecutor Executor resultExecutor, @NonNull Consumer<List<DirectAction>> callback)1359 public final void requestDirectActions(@NonNull ActivityId activityId, 1360 @Nullable CancellationSignal cancellationSignal, 1361 @NonNull @CallbackExecutor Executor resultExecutor, 1362 @NonNull Consumer<List<DirectAction>> callback) { 1363 Preconditions.checkNotNull(activityId); 1364 Preconditions.checkNotNull(resultExecutor); 1365 Preconditions.checkNotNull(callback); 1366 if (mToken == null) { 1367 throw new IllegalStateException("Can't call before onCreate()"); 1368 } 1369 1370 if (cancellationSignal != null) { 1371 cancellationSignal.throwIfCanceled(); 1372 } 1373 1374 final RemoteCallback cancellationCallback = (cancellationSignal != null) 1375 ? new RemoteCallback(b -> { 1376 if (b != null) { 1377 final IBinder cancellation = b.getBinder( 1378 VoiceInteractor.KEY_CANCELLATION_SIGNAL); 1379 if (cancellation != null) { 1380 cancellationSignal.setRemote(ICancellationSignal.Stub.asInterface( 1381 cancellation)); 1382 } 1383 } 1384 }) 1385 : null; 1386 1387 try { 1388 mSystemService.requestDirectActions(mToken, activityId.getTaskId(), 1389 activityId.getAssistToken(), cancellationCallback, 1390 new RemoteCallback(createSafeResultListener((result) -> { 1391 List<DirectAction> list; 1392 if (result == null) { 1393 list = Collections.emptyList(); 1394 } else { 1395 final ParceledListSlice<DirectAction> pls = result.getParcelable( 1396 DirectAction.KEY_ACTIONS_LIST); 1397 if (pls != null) { 1398 final List<DirectAction> receivedList = pls.getList(); 1399 list = (receivedList != null) ? receivedList : Collections.emptyList(); 1400 } else { 1401 list = Collections.emptyList(); 1402 } 1403 } 1404 resultExecutor.execute(() -> callback.accept(list)); 1405 }))); 1406 } catch (RemoteException e) { 1407 e.rethrowFromSystemServer(); 1408 } 1409 } 1410 1411 /** 1412 * Called when the direct actions are invalidated. 1413 */ onDirectActionsInvalidated(@onNull ActivityId activityId)1414 public void onDirectActionsInvalidated(@NonNull ActivityId activityId) { 1415 1416 } 1417 1418 /** 1419 * Asks that an action be performed by the app. This will send a request to the app which 1420 * provided this action. 1421 * 1422 * <p> An action could take time to execute and the result is provided asynchronously 1423 * via a callback. If the action is taking longer and you want to cancel its execution 1424 * you can pass in a cancellation signal through which to notify the app to abort the 1425 * action. 1426 * 1427 * @param action The action to be performed. 1428 * @param extras Any optional extras sent to the app as part of the request 1429 * @param cancellationSignal A signal to cancel the operation in progress, 1430 * or {@code null} if none. 1431 * @param resultExecutor The handler to receive the callback. 1432 * @param resultListener The callback to receive the response. 1433 * 1434 * @see #requestDirectActions(ActivityId, CancellationSignal, Executor, Consumer) 1435 * @see Activity#onGetDirectActions(CancellationSignal, Consumer) 1436 */ performDirectAction(@onNull DirectAction action, @Nullable Bundle extras, @Nullable CancellationSignal cancellationSignal, @NonNull @CallbackExecutor Executor resultExecutor, @NonNull Consumer<Bundle> resultListener)1437 public final void performDirectAction(@NonNull DirectAction action, @Nullable Bundle extras, 1438 @Nullable CancellationSignal cancellationSignal, 1439 @NonNull @CallbackExecutor Executor resultExecutor, 1440 @NonNull Consumer<Bundle> resultListener) { 1441 if (mToken == null) { 1442 throw new IllegalStateException("Can't call before onCreate()"); 1443 } 1444 Preconditions.checkNotNull(resultExecutor); 1445 Preconditions.checkNotNull(resultListener); 1446 1447 if (cancellationSignal != null) { 1448 cancellationSignal.throwIfCanceled(); 1449 } 1450 1451 final RemoteCallback cancellationCallback = (cancellationSignal != null) 1452 ? new RemoteCallback(createSafeResultListener(b -> { 1453 if (b != null) { 1454 final IBinder cancellation = b.getBinder( 1455 VoiceInteractor.KEY_CANCELLATION_SIGNAL); 1456 if (cancellation != null) { 1457 cancellationSignal.setRemote(ICancellationSignal.Stub.asInterface( 1458 cancellation)); 1459 } 1460 } 1461 })) 1462 : null; 1463 1464 final RemoteCallback resultCallback = new RemoteCallback(createSafeResultListener(b -> { 1465 if (b != null) { 1466 resultExecutor.execute(() -> resultListener.accept(b)); 1467 } else { 1468 resultExecutor.execute(() -> resultListener.accept(Bundle.EMPTY)); 1469 } 1470 })); 1471 1472 try { 1473 mSystemService.performDirectAction(mToken, action.getId(), extras, 1474 action.getTaskId(), action.getActivityId(), cancellationCallback, 1475 resultCallback); 1476 } catch (RemoteException e) { 1477 e.rethrowFromSystemServer(); 1478 } 1479 } 1480 1481 /** 1482 * Set whether this session will keep the device awake while it is running a voice 1483 * activity. By default, the system holds a wake lock for it while in this state, 1484 * so that it can work even if the screen is off. Setting this to false removes that 1485 * wake lock, allowing the CPU to go to sleep. This is typically used if the 1486 * session decides it has been waiting too long for a response from the user and 1487 * doesn't want to let this continue to drain the battery. 1488 * 1489 * <p>Passing false here will release the wake lock, and you can call later with 1490 * true to re-acquire it. It will also be automatically re-acquired for you each 1491 * time you start a new voice activity task -- that is when you call 1492 * {@link #startVoiceActivity}.</p> 1493 */ setKeepAwake(boolean keepAwake)1494 public void setKeepAwake(boolean keepAwake) { 1495 if (mToken == null) { 1496 throw new IllegalStateException("Can't call before onCreate()"); 1497 } 1498 try { 1499 mSystemService.setKeepAwake(mToken, keepAwake); 1500 } catch (RemoteException e) { 1501 } 1502 } 1503 1504 /** 1505 * Request that all system dialogs (and status bar shade etc) be closed, allowing 1506 * access to the session's UI. This will <em>not</em> cause the lock screen to be 1507 * dismissed. 1508 */ closeSystemDialogs()1509 public void closeSystemDialogs() { 1510 if (mToken == null) { 1511 throw new IllegalStateException("Can't call before onCreate()"); 1512 } 1513 try { 1514 mSystemService.closeSystemDialogs(mToken); 1515 } catch (RemoteException e) { 1516 } 1517 } 1518 1519 /** 1520 * Convenience for inflating views. 1521 */ getLayoutInflater()1522 public LayoutInflater getLayoutInflater() { 1523 ensureWindowCreated(); 1524 return mInflater; 1525 } 1526 1527 /** 1528 * Retrieve the window being used to show the session's UI. 1529 */ getWindow()1530 public Dialog getWindow() { 1531 ensureWindowCreated(); 1532 return mWindow; 1533 } 1534 1535 /** 1536 * Finish the session. This completely destroys the session -- the next time it is shown, 1537 * an entirely new one will be created. You do not normally call this function; instead, 1538 * use {@link #hide} and allow the system to destroy your session if it needs its RAM. 1539 */ finish()1540 public void finish() { 1541 if (mToken == null) { 1542 throw new IllegalStateException("Can't call before onCreate()"); 1543 } 1544 try { 1545 mSystemService.finish(mToken); 1546 } catch (RemoteException e) { 1547 } 1548 } 1549 1550 /** 1551 * Initiatize a new session. At this point you don't know exactly what this 1552 * session will be used for; you will find that out in {@link #onShow}. 1553 */ onCreate()1554 public void onCreate() { 1555 doOnCreate(); 1556 } 1557 doOnCreate()1558 private void doOnCreate() { 1559 mTheme = mTheme != 0 ? mTheme 1560 : com.android.internal.R.style.Theme_DeviceDefault_VoiceInteractionSession; 1561 } 1562 1563 /** 1564 * Called prior to {@link #onShow} before any UI setup has occurred. Not generally useful. 1565 * 1566 * @param args The arguments that were supplied to 1567 * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}. 1568 * @param showFlags The show flags originally provided to 1569 * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}. 1570 */ onPrepareShow(Bundle args, int showFlags)1571 public void onPrepareShow(Bundle args, int showFlags) { 1572 } 1573 1574 /** 1575 * Called when the session UI is going to be shown. This is called after 1576 * {@link #onCreateContentView} (if the session's content UI needed to be created) and 1577 * immediately prior to the window being shown. This may be called while the window 1578 * is already shown, if a show request has come in while it is shown, to allow you to 1579 * update the UI to match the new show arguments. 1580 * 1581 * @param args The arguments that were supplied to 1582 * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}. 1583 * @param showFlags The show flags originally provided to 1584 * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}. 1585 */ onShow(Bundle args, int showFlags)1586 public void onShow(Bundle args, int showFlags) { 1587 } 1588 1589 /** 1590 * Called immediately after stopping to show the session UI. 1591 */ onHide()1592 public void onHide() { 1593 } 1594 1595 /** 1596 * Last callback to the session as it is being finished. 1597 */ onDestroy()1598 public void onDestroy() { 1599 } 1600 1601 /** 1602 * Hook in which to create the session's UI. 1603 */ onCreateContentView()1604 public View onCreateContentView() { 1605 return null; 1606 } 1607 setContentView(View view)1608 public void setContentView(View view) { 1609 ensureWindowCreated(); 1610 mContentFrame.removeAllViews(); 1611 mContentFrame.addView(view, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)); 1612 mContentFrame.requestApplyInsets(); 1613 } 1614 doOnHandleAssist(int taskId, IBinder assistToken, Bundle data, AssistStructure structure, Throwable failure, AssistContent content, int index, int count)1615 void doOnHandleAssist(int taskId, IBinder assistToken, Bundle data, AssistStructure structure, 1616 Throwable failure, AssistContent content, int index, int count) { 1617 if (failure != null) { 1618 onAssistStructureFailure(failure); 1619 } 1620 AssistState assistState = new AssistState(new ActivityId(taskId, assistToken), 1621 data, structure, content, index, count); 1622 onHandleAssist(assistState); 1623 } 1624 1625 /** 1626 * Called when there has been a failure transferring the {@link AssistStructure} to 1627 * the assistant. This may happen, for example, if the data is too large and results 1628 * in an out of memory exception, or the client has provided corrupt data. This will 1629 * be called immediately before {@link #onHandleAssist} and the AssistStructure supplied 1630 * there afterwards will be null. 1631 * 1632 * @param failure The failure exception that was thrown when building the 1633 * {@link AssistStructure}. 1634 */ onAssistStructureFailure(Throwable failure)1635 public void onAssistStructureFailure(Throwable failure) { 1636 } 1637 1638 /** 1639 * Called to receive data from the application that the user was currently viewing when 1640 * an assist session is started. If the original show request did not specify 1641 * {@link #SHOW_WITH_ASSIST}, this method will not be called. 1642 * 1643 * @param data Arbitrary data supplied by the app through 1644 * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}. 1645 * May be null if assist data has been disabled by the user or device policy. 1646 * @param structure If available, the structure definition of all windows currently 1647 * displayed by the app. May be null if assist data has been disabled by the user 1648 * or device policy; will be an empty stub if the application has disabled assist 1649 * by marking its window as secure. 1650 * @param content Additional content data supplied by the app through 1651 * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}. 1652 * May be null if assist data has been disabled by the user or device policy; will 1653 * not be automatically filled in with data from the app if the app has marked its 1654 * window as secure. 1655 * 1656 * @deprecated use {@link #onHandleAssist(AssistState)} 1657 */ 1658 @Deprecated onHandleAssist(@ullable Bundle data, @Nullable AssistStructure structure, @Nullable AssistContent content)1659 public void onHandleAssist(@Nullable Bundle data, @Nullable AssistStructure structure, 1660 @Nullable AssistContent content) { 1661 } 1662 1663 /** 1664 * Called to receive data from the application that the user was currently viewing when 1665 * an assist session is started. If the original show request did not specify 1666 * {@link #SHOW_WITH_ASSIST}, this method will not be called. 1667 * 1668 * <p>This method is called for all activities along with an index and count that indicates 1669 * which activity the data is for. {@code index} will be between 0 and {@code count}-1 and 1670 * this method is called once for each activity in no particular order. The {@code count} 1671 * indicates how many activities to expect assist data for, including the top focused one. 1672 * The focused activity can be determined by calling {@link AssistState#isFocused()}. 1673 * 1674 * <p>To be responsive to assist requests, process assist data as soon as it is received, 1675 * without waiting for all queued activities to return assist data. 1676 * 1677 * @param state The state object capturing the state of an activity. 1678 */ onHandleAssist(@onNull AssistState state)1679 public void onHandleAssist(@NonNull AssistState state) { 1680 if (state.getIndex() == 0) { 1681 onHandleAssist(state.getAssistData(), state.getAssistStructure(), 1682 state.getAssistContent()); 1683 } else { 1684 onHandleAssistSecondary(state.getAssistData(), state.getAssistStructure(), 1685 state.getAssistContent(), state.getIndex(), state.getCount()); 1686 } 1687 } 1688 1689 /** 1690 * Called to receive data from other applications that the user was or is interacting with, 1691 * that are currently on the screen in a multi-window display environment, not including the 1692 * currently focused activity. This could be 1693 * a free-form window, a picture-in-picture window, or another window in a split-screen display. 1694 * <p> 1695 * This method is very similar to 1696 * {@link #onHandleAssist} except that it is called 1697 * for additional non-focused activities along with an index and count that indicates 1698 * which additional activity the data is for. {@code index} will be between 1 and 1699 * {@code count}-1 and this method is called once for each additional window, in no particular 1700 * order. The {@code count} indicates how many windows to expect assist data for, including the 1701 * top focused activity, which continues to be returned via {@link #onHandleAssist}. 1702 * <p> 1703 * To be responsive to assist requests, process assist data as soon as it is received, 1704 * without waiting for all queued activities to return assist data. 1705 * 1706 * @param data Arbitrary data supplied by the app through 1707 * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}. 1708 * May be null if assist data has been disabled by the user or device policy. 1709 * @param structure If available, the structure definition of all windows currently 1710 * displayed by the app. May be null if assist data has been disabled by the user 1711 * or device policy; will be an empty stub if the application has disabled assist 1712 * by marking its window as secure. 1713 * @param content Additional content data supplied by the app through 1714 * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}. 1715 * May be null if assist data has been disabled by the user or device policy; will 1716 * not be automatically filled in with data from the app if the app has marked its 1717 * window as secure. 1718 * @param index the index of the additional activity that this data 1719 * is for. 1720 * @param count the total number of additional activities for which the assist data is being 1721 * returned, including the focused activity that is returned via 1722 * {@link #onHandleAssist}. 1723 * 1724 * @deprecated use {@link #onHandleAssist(AssistState)} 1725 */ 1726 @Deprecated onHandleAssistSecondary(@ullable Bundle data, @Nullable AssistStructure structure, @Nullable AssistContent content, int index, int count)1727 public void onHandleAssistSecondary(@Nullable Bundle data, @Nullable AssistStructure structure, 1728 @Nullable AssistContent content, int index, int count) { 1729 } 1730 1731 /** 1732 * Called to receive a screenshot of what the user was currently viewing when an assist 1733 * session is started. May be null if screenshots are disabled by the user, policy, 1734 * or application. If the original show request did not specify 1735 * {@link #SHOW_WITH_SCREENSHOT}, this method will not be called. 1736 */ onHandleScreenshot(@ullable Bitmap screenshot)1737 public void onHandleScreenshot(@Nullable Bitmap screenshot) { 1738 } 1739 onKeyDown(int keyCode, KeyEvent event)1740 public boolean onKeyDown(int keyCode, KeyEvent event) { 1741 return false; 1742 } 1743 onKeyLongPress(int keyCode, KeyEvent event)1744 public boolean onKeyLongPress(int keyCode, KeyEvent event) { 1745 return false; 1746 } 1747 onKeyUp(int keyCode, KeyEvent event)1748 public boolean onKeyUp(int keyCode, KeyEvent event) { 1749 return false; 1750 } 1751 onKeyMultiple(int keyCode, int count, KeyEvent event)1752 public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) { 1753 return false; 1754 } 1755 1756 /** 1757 * Called when the user presses the back button while focus is in the session UI. Note 1758 * that this will only happen if the session UI has requested input focus in its window; 1759 * otherwise, the back key will go to whatever window has focus and do whatever behavior 1760 * it normally has there. The default implementation simply calls {@link #hide}. 1761 */ onBackPressed()1762 public void onBackPressed() { 1763 hide(); 1764 } 1765 1766 /** 1767 * Sessions automatically watch for requests that all system UI be closed (such as when 1768 * the user presses HOME), which will appear here. The default implementation always 1769 * calls {@link #hide}. 1770 */ onCloseSystemDialogs()1771 public void onCloseSystemDialogs() { 1772 hide(); 1773 } 1774 1775 /** 1776 * Called when the lockscreen was shown. 1777 */ onLockscreenShown()1778 public void onLockscreenShown() { 1779 hide(); 1780 } 1781 1782 @Override onConfigurationChanged(Configuration newConfig)1783 public void onConfigurationChanged(Configuration newConfig) { 1784 } 1785 1786 @Override onLowMemory()1787 public void onLowMemory() { 1788 } 1789 1790 @Override onTrimMemory(int level)1791 public void onTrimMemory(int level) { 1792 } 1793 1794 /** 1795 * Compute the interesting insets into your UI. The default implementation 1796 * sets {@link Insets#contentInsets outInsets.contentInsets.top} to the height 1797 * of the window, meaning it should not adjust content underneath. The default touchable 1798 * insets are {@link Insets#TOUCHABLE_INSETS_FRAME}, meaning it consumes all touch 1799 * events within its window frame. 1800 * 1801 * @param outInsets Fill in with the current UI insets. 1802 */ onComputeInsets(Insets outInsets)1803 public void onComputeInsets(Insets outInsets) { 1804 outInsets.contentInsets.left = 0; 1805 outInsets.contentInsets.bottom = 0; 1806 outInsets.contentInsets.right = 0; 1807 View decor = getWindow().getWindow().getDecorView(); 1808 outInsets.contentInsets.top = decor.getHeight(); 1809 outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_FRAME; 1810 outInsets.touchableRegion.setEmpty(); 1811 } 1812 1813 /** 1814 * Called when a task initiated by {@link #startVoiceActivity(android.content.Intent)} 1815 * has actually started. 1816 * 1817 * @param intent The original {@link Intent} supplied to 1818 * {@link #startVoiceActivity(android.content.Intent)}. 1819 * @param taskId Unique ID of the now running task. 1820 */ onTaskStarted(Intent intent, int taskId)1821 public void onTaskStarted(Intent intent, int taskId) { 1822 } 1823 1824 /** 1825 * Called when the last activity of a task initiated by 1826 * {@link #startVoiceActivity(android.content.Intent)} has finished. The default 1827 * implementation calls {@link #finish()} on the assumption that this represents 1828 * the completion of a voice action. You can override the implementation if you would 1829 * like a different behavior. 1830 * 1831 * @param intent The original {@link Intent} supplied to 1832 * {@link #startVoiceActivity(android.content.Intent)}. 1833 * @param taskId Unique ID of the finished task. 1834 */ onTaskFinished(Intent intent, int taskId)1835 public void onTaskFinished(Intent intent, int taskId) { 1836 hide(); 1837 } 1838 1839 /** 1840 * Request to query for what extended commands the session supports. 1841 * 1842 * @param commands An array of commands that are being queried. 1843 * @return Return an array of booleans indicating which of each entry in the 1844 * command array is supported. A true entry in the array indicates the command 1845 * is supported; false indicates it is not. The default implementation returns 1846 * an array of all false entries. 1847 */ onGetSupportedCommands(String[] commands)1848 public boolean[] onGetSupportedCommands(String[] commands) { 1849 return new boolean[commands.length]; 1850 } 1851 1852 /** 1853 * Request to confirm with the user before proceeding with an unrecoverable operation, 1854 * corresponding to a {@link android.app.VoiceInteractor.ConfirmationRequest 1855 * VoiceInteractor.ConfirmationRequest}. 1856 * 1857 * @param request The active request. 1858 */ onRequestConfirmation(ConfirmationRequest request)1859 public void onRequestConfirmation(ConfirmationRequest request) { 1860 } 1861 1862 /** 1863 * Request for the user to pick one of N options, corresponding to a 1864 * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}. 1865 * 1866 * @param request The active request. 1867 */ onRequestPickOption(PickOptionRequest request)1868 public void onRequestPickOption(PickOptionRequest request) { 1869 } 1870 1871 /** 1872 * Request to complete the voice interaction session because the voice activity successfully 1873 * completed its interaction using voice. Corresponds to 1874 * {@link android.app.VoiceInteractor.CompleteVoiceRequest 1875 * VoiceInteractor.CompleteVoiceRequest}. The default implementation just sends an empty 1876 * confirmation back to allow the activity to exit. 1877 * 1878 * @param request The active request. 1879 */ onRequestCompleteVoice(CompleteVoiceRequest request)1880 public void onRequestCompleteVoice(CompleteVoiceRequest request) { 1881 } 1882 1883 /** 1884 * Request to abort the voice interaction session because the voice activity can not 1885 * complete its interaction using voice. Corresponds to 1886 * {@link android.app.VoiceInteractor.AbortVoiceRequest 1887 * VoiceInteractor.AbortVoiceRequest}. The default implementation just sends an empty 1888 * confirmation back to allow the activity to exit. 1889 * 1890 * @param request The active request. 1891 */ onRequestAbortVoice(AbortVoiceRequest request)1892 public void onRequestAbortVoice(AbortVoiceRequest request) { 1893 } 1894 1895 /** 1896 * Process an arbitrary extended command from the caller, 1897 * corresponding to a {@link android.app.VoiceInteractor.CommandRequest 1898 * VoiceInteractor.CommandRequest}. 1899 * 1900 * @param request The active request. 1901 */ onRequestCommand(CommandRequest request)1902 public void onRequestCommand(CommandRequest request) { 1903 } 1904 1905 /** 1906 * Called when the {@link android.app.VoiceInteractor} has asked to cancel a {@link Request} 1907 * that was previously delivered to {@link #onRequestConfirmation}, 1908 * {@link #onRequestPickOption}, {@link #onRequestCompleteVoice}, {@link #onRequestAbortVoice}, 1909 * or {@link #onRequestCommand}. 1910 * 1911 * @param request The request that is being canceled. 1912 */ onCancelRequest(Request request)1913 public void onCancelRequest(Request request) { 1914 } 1915 1916 /** 1917 * Print the Service's state into the given stream. This gets invoked by 1918 * {@link VoiceInteractionSessionService} when its Service 1919 * {@link android.app.Service#dump} method is called. 1920 * 1921 * @param prefix Text to print at the front of each line. 1922 * @param fd The raw file descriptor that the dump is being sent to. 1923 * @param writer The PrintWriter to which you should dump your state. This will be 1924 * closed for you after you return. 1925 * @param args additional arguments to the dump request. 1926 */ dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)1927 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 1928 writer.print(prefix); writer.print("mToken="); writer.println(mToken); 1929 writer.print(prefix); writer.print("mTheme=#"); writer.println(Integer.toHexString(mTheme)); 1930 writer.print(prefix); writer.print("mUiEnabled="); writer.println(mUiEnabled); 1931 writer.print(" mInitialized="); writer.println(mInitialized); 1932 writer.print(prefix); writer.print("mWindowAdded="); writer.print(mWindowAdded); 1933 writer.print(" mWindowVisible="); writer.println(mWindowVisible); 1934 writer.print(prefix); writer.print("mWindowWasVisible="); writer.print(mWindowWasVisible); 1935 writer.print(" mInShowWindow="); writer.println(mInShowWindow); 1936 if (mActiveRequests.size() > 0) { 1937 writer.print(prefix); writer.println("Active requests:"); 1938 String innerPrefix = prefix + " "; 1939 for (int i=0; i<mActiveRequests.size(); i++) { 1940 Request req = mActiveRequests.valueAt(i); 1941 writer.print(prefix); writer.print(" #"); writer.print(i); 1942 writer.print(": "); 1943 writer.println(req); 1944 req.dump(innerPrefix, fd, writer, args); 1945 1946 } 1947 } 1948 } 1949 createSafeResultListener( @onNull Consumer<Bundle> consumer)1950 private SafeResultListener createSafeResultListener( 1951 @NonNull Consumer<Bundle> consumer) { 1952 synchronized (this) { 1953 final SafeResultListener listener = new SafeResultListener(consumer, this); 1954 mRemoteCallbacks.put(listener, consumer); 1955 return listener; 1956 } 1957 } 1958 removeSafeResultListener(@onNull SafeResultListener listener)1959 private Consumer<Bundle> removeSafeResultListener(@NonNull SafeResultListener listener) { 1960 synchronized (this) { 1961 return mRemoteCallbacks.remove(listener); 1962 } 1963 } 1964 1965 /** 1966 * Represents assist state captured when this session was started. 1967 * It contains the various assist data objects and a reference to 1968 * the source activity. 1969 */ 1970 @Immutable 1971 public static final class AssistState { 1972 private final @NonNull ActivityId mActivityId; 1973 private final int mIndex; 1974 private final int mCount; 1975 private final @Nullable Bundle mData; 1976 private final @Nullable AssistStructure mStructure; 1977 private final @Nullable AssistContent mContent; 1978 AssistState(@onNull ActivityId activityId, @Nullable Bundle data, @Nullable AssistStructure structure, @Nullable AssistContent content, int index, int count)1979 AssistState(@NonNull ActivityId activityId, @Nullable Bundle data, 1980 @Nullable AssistStructure structure, @Nullable AssistContent content, 1981 int index, int count) { 1982 mActivityId = activityId; 1983 mIndex = index; 1984 mCount = count; 1985 mData = data; 1986 mStructure = structure; 1987 mContent = content; 1988 } 1989 1990 /** 1991 * @return whether the source activity is focused. 1992 */ isFocused()1993 public boolean isFocused() { 1994 return mIndex == 0; 1995 } 1996 1997 /** 1998 * @return the index of the activity that this state is for or -1 1999 * if there was no assist data captured. 2000 */ getIndex()2001 public @IntRange(from = -1) int getIndex() { 2002 return mIndex; 2003 } 2004 2005 /**s 2006 * @return the total number of activities for which the assist data is 2007 * being returned. 2008 */ getCount()2009 public @IntRange(from = 0) int getCount() { 2010 return mCount; 2011 } 2012 2013 /** 2014 * @return the id of the source activity 2015 */ getActivityId()2016 public @NonNull ActivityId getActivityId() { 2017 return mActivityId; 2018 } 2019 2020 /** 2021 * @return Arbitrary data supplied by the app through 2022 * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}. 2023 * May be null if assist data has been disabled by the user or device policy. 2024 */ getAssistData()2025 public @Nullable Bundle getAssistData() { 2026 return mData; 2027 } 2028 2029 /** 2030 * @return If available, the structure definition of all windows currently 2031 * displayed by the app. May be null if assist data has been disabled by the user 2032 * or device policy; will be an empty stub if the application has disabled assist 2033 * by marking its window as secure. 2034 */ getAssistStructure()2035 public @Nullable AssistStructure getAssistStructure() { 2036 return mStructure; 2037 } 2038 2039 /** 2040 * @return Additional content data supplied by the app through 2041 * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}. 2042 * May be null if assist data has been disabled by the user or device policy; will 2043 * not be automatically filled in with data from the app if the app has marked its 2044 * window as secure. 2045 */ getAssistContent()2046 public @Nullable AssistContent getAssistContent() { 2047 return mContent; 2048 } 2049 } 2050 2051 /** 2052 * Represents the id of an assist source activity. You can use 2053 * {@link #equals(Object)} to compare instances of this class. 2054 */ 2055 public static class ActivityId { 2056 private final int mTaskId; 2057 private final IBinder mAssistToken; 2058 ActivityId(int taskId, IBinder assistToken)2059 ActivityId(int taskId, IBinder assistToken) { 2060 mTaskId = taskId; 2061 mAssistToken = assistToken; 2062 } 2063 getTaskId()2064 int getTaskId() { 2065 return mTaskId; 2066 } 2067 getAssistToken()2068 IBinder getAssistToken() { 2069 return mAssistToken; 2070 } 2071 2072 @Override equals(Object o)2073 public boolean equals(Object o) { 2074 if (this == o) { 2075 return true; 2076 } 2077 if (o == null || getClass() != o.getClass()) { 2078 return false; 2079 } 2080 2081 ActivityId that = (ActivityId) o; 2082 2083 if (mTaskId != that.mTaskId) { 2084 return false; 2085 } 2086 return mAssistToken != null 2087 ? mAssistToken.equals(that.mAssistToken) 2088 : that.mAssistToken == null; 2089 } 2090 2091 @Override hashCode()2092 public int hashCode() { 2093 int result = mTaskId; 2094 result = 31 * result + (mAssistToken != null ? mAssistToken.hashCode() : 0); 2095 return result; 2096 } 2097 } 2098 2099 private static class SafeResultListener implements RemoteCallback.OnResultListener { 2100 private final @NonNull WeakReference<VoiceInteractionSession> mWeakSession; 2101 SafeResultListener(@onNull Consumer<Bundle> action, @NonNull VoiceInteractionSession session)2102 SafeResultListener(@NonNull Consumer<Bundle> action, 2103 @NonNull VoiceInteractionSession session) { 2104 mWeakSession = new WeakReference<>(session); 2105 } 2106 2107 @Override onResult(Bundle result)2108 public void onResult(Bundle result) { 2109 final VoiceInteractionSession session = mWeakSession.get(); 2110 if (session != null) { 2111 final Consumer<Bundle> consumer = session.removeSafeResultListener(this); 2112 if (consumer != null) { 2113 consumer.accept(result); 2114 } 2115 } 2116 } 2117 } 2118 } 2119