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