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