VoiceInteractionSession.java revision 69c6adc96eecfde74ceb83cf9177428dc08b6067
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.app.Dialog; 20import android.app.Instrumentation; 21import android.app.VoiceInteractor; 22import android.app.assist.AssistContent; 23import android.app.assist.AssistStructure; 24import android.content.ComponentCallbacks2; 25import android.content.Context; 26import android.content.Intent; 27import android.content.res.Configuration; 28import android.content.res.TypedArray; 29import android.graphics.Bitmap; 30import android.graphics.Rect; 31import android.graphics.Region; 32import android.inputmethodservice.SoftInputWindow; 33import android.os.Binder; 34import android.os.Bundle; 35import android.os.Handler; 36import android.os.IBinder; 37import android.os.Message; 38import android.os.RemoteException; 39import android.util.ArrayMap; 40import android.util.Log; 41import android.view.Gravity; 42import android.view.KeyEvent; 43import android.view.LayoutInflater; 44import android.view.View; 45import android.view.ViewGroup; 46import android.view.ViewTreeObserver; 47import android.view.WindowManager; 48import android.widget.FrameLayout; 49import com.android.internal.app.IVoiceInteractionManagerService; 50import com.android.internal.app.IVoiceInteractionSessionShowCallback; 51import com.android.internal.app.IVoiceInteractor; 52import com.android.internal.app.IVoiceInteractorCallback; 53import com.android.internal.app.IVoiceInteractorRequest; 54import com.android.internal.os.HandlerCaller; 55import com.android.internal.os.SomeArgs; 56 57import java.lang.ref.WeakReference; 58 59import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 60 61/** 62 * An active voice interaction session, providing a facility for the implementation 63 * to interact with the user in the voice interaction layer. The user interface is 64 * initially shown by default, and can be created be overriding {@link #onCreateContentView()} 65 * in which the UI can be built. 66 * 67 * <p>A voice interaction session can be self-contained, ultimately calling {@link #finish} 68 * when done. It can also initiate voice interactions with applications by calling 69 * {@link #startVoiceActivity}</p>. 70 */ 71public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCallbacks2 { 72 static final String TAG = "VoiceInteractionSession"; 73 static final boolean DEBUG = true; 74 75 /** 76 * Flag received in {@link #onShow}: originator requested that the session be started with 77 * assist data from the currently focused activity. 78 */ 79 public static final int SHOW_WITH_ASSIST = 1<<0; 80 81 /** 82 * @hide 83 * Flag received in {@link #onShow}: originator requested that the session be started with 84 * a screen shot of the currently focused activity. 85 */ 86 public static final int SHOW_WITH_SCREENSHOT = 1<<1; 87 88 /** 89 * Flag for use with {@link #onShow}: indicates that the session has been started from the 90 * system assist gesture. 91 */ 92 public static final int SHOW_SOURCE_ASSIST_GESTURE = 1<<2; 93 94 final Context mContext; 95 final HandlerCaller mHandlerCaller; 96 97 final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState(); 98 99 IVoiceInteractionManagerService mSystemService; 100 IBinder mToken; 101 102 int mTheme = 0; 103 LayoutInflater mInflater; 104 TypedArray mThemeAttrs; 105 View mRootView; 106 FrameLayout mContentFrame; 107 SoftInputWindow mWindow; 108 109 boolean mInitialized; 110 boolean mWindowAdded; 111 boolean mWindowVisible; 112 boolean mWindowWasVisible; 113 boolean mInShowWindow; 114 115 final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<IBinder, Request>(); 116 117 final Insets mTmpInsets = new Insets(); 118 119 final WeakReference<VoiceInteractionSession> mWeakRef 120 = new WeakReference<VoiceInteractionSession>(this); 121 122 final IVoiceInteractor mInteractor = new IVoiceInteractor.Stub() { 123 @Override 124 public IVoiceInteractorRequest startConfirmation(String callingPackage, 125 IVoiceInteractorCallback callback, CharSequence prompt, Bundle extras) { 126 ConfirmationRequest request = new ConfirmationRequest(callingPackage, 127 Binder.getCallingUid(), callback, VoiceInteractionSession.this, 128 prompt, extras); 129 addRequest(request); 130 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_CONFIRMATION, 131 request)); 132 return request.mInterface; 133 } 134 135 @Override 136 public IVoiceInteractorRequest startPickOption(String callingPackage, 137 IVoiceInteractorCallback callback, CharSequence prompt, 138 VoiceInteractor.PickOptionRequest.Option[] options, Bundle extras) { 139 PickOptionRequest request = new PickOptionRequest(callingPackage, 140 Binder.getCallingUid(), callback, VoiceInteractionSession.this, 141 prompt, options, extras); 142 addRequest(request); 143 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_PICK_OPTION, 144 request)); 145 return request.mInterface; 146 } 147 148 @Override 149 public IVoiceInteractorRequest startCompleteVoice(String callingPackage, 150 IVoiceInteractorCallback callback, CharSequence message, Bundle extras) { 151 CompleteVoiceRequest request = new CompleteVoiceRequest(callingPackage, 152 Binder.getCallingUid(), callback, VoiceInteractionSession.this, 153 message, extras); 154 addRequest(request); 155 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_COMPLETE_VOICE, 156 request)); 157 return request.mInterface; 158 } 159 160 @Override 161 public IVoiceInteractorRequest startAbortVoice(String callingPackage, 162 IVoiceInteractorCallback callback, CharSequence message, Bundle extras) { 163 AbortVoiceRequest request = new AbortVoiceRequest(callingPackage, 164 Binder.getCallingUid(), callback, VoiceInteractionSession.this, 165 message, extras); 166 addRequest(request); 167 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_ABORT_VOICE, 168 request)); 169 return request.mInterface; 170 } 171 172 @Override 173 public IVoiceInteractorRequest startCommand(String callingPackage, 174 IVoiceInteractorCallback callback, String command, Bundle extras) { 175 CommandRequest request = new CommandRequest(callingPackage, 176 Binder.getCallingUid(), callback, VoiceInteractionSession.this, 177 command, extras); 178 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_COMMAND, 179 request)); 180 return request.mInterface; 181 } 182 183 @Override 184 public boolean[] supportsCommands(String callingPackage, String[] commands) { 185 Message msg = mHandlerCaller.obtainMessageIOO(MSG_SUPPORTS_COMMANDS, 186 0, commands, null); 187 SomeArgs args = mHandlerCaller.sendMessageAndWait(msg); 188 if (args != null) { 189 boolean[] res = (boolean[])args.arg1; 190 args.recycle(); 191 return res; 192 } 193 return new boolean[commands.length]; 194 } 195 }; 196 197 final IVoiceInteractionSession mSession = new IVoiceInteractionSession.Stub() { 198 @Override 199 public void show(Bundle sessionArgs, int flags, 200 IVoiceInteractionSessionShowCallback showCallback) { 201 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOO(MSG_SHOW, 202 flags, sessionArgs, showCallback)); 203 } 204 205 @Override 206 public void hide() { 207 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_HIDE)); 208 } 209 210 @Override 211 public void handleAssist(Bundle data, AssistStructure structure, 212 AssistContent content) { 213 // We want to pre-warm the AssistStructure before handing it off to the main 214 // thread. There is a strong argument to be made that it should be handed 215 // through as a separate param rather than part of the assistBundle. 216 if (structure != null) { 217 structure.ensureData(); 218 } 219 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOO(MSG_HANDLE_ASSIST, 220 data, structure, content)); 221 } 222 223 @Override 224 public void handleScreenshot(Bitmap screenshot) { 225 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_HANDLE_SCREENSHOT, 226 screenshot)); 227 } 228 229 @Override 230 public void taskStarted(Intent intent, int taskId) { 231 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_TASK_STARTED, 232 taskId, intent)); 233 } 234 235 @Override 236 public void taskFinished(Intent intent, int taskId) { 237 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_TASK_FINISHED, 238 taskId, intent)); 239 } 240 241 @Override 242 public void closeSystemDialogs() { 243 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_CLOSE_SYSTEM_DIALOGS)); 244 } 245 246 @Override 247 public void destroy() { 248 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_DESTROY)); 249 } 250 }; 251 252 /** @hide */ 253 public static class Caller { 254 } 255 256 /** 257 * Base class representing a request from a voice-driver app to perform a particular 258 * voice operation with the user. See related subclasses for the types of requests 259 * that are possible. 260 */ 261 public static class Request extends Caller { 262 final IVoiceInteractorRequest mInterface = new IVoiceInteractorRequest.Stub() { 263 @Override 264 public void cancel() throws RemoteException { 265 VoiceInteractionSession session = mSession.get(); 266 if (session != null) { 267 session.mHandlerCaller.sendMessage( 268 session.mHandlerCaller.obtainMessageO(MSG_CANCEL, Request.this)); 269 } 270 } 271 }; 272 final String mCallingPackage; 273 final int mCallingUid; 274 final IVoiceInteractorCallback mCallback; 275 final WeakReference<VoiceInteractionSession> mSession; 276 final Bundle mExtras; 277 278 Request(String packageName, int uid, IVoiceInteractorCallback callback, 279 VoiceInteractionSession session, Bundle extras) { 280 mCallingPackage = packageName; 281 mCallingUid = uid; 282 mCallback = callback; 283 mSession = session.mWeakRef; 284 mExtras = extras; 285 } 286 287 /** 288 * Return the uid of the application that initiated the request. 289 */ 290 public int getCallingUid() { 291 return mCallingUid; 292 } 293 294 /** 295 * Return the package name of the application that initiated the request. 296 */ 297 public String getCallingPackage() { 298 return mCallingPackage; 299 } 300 301 /** 302 * Return any additional extra information that was supplied as part of the request. 303 */ 304 public Bundle getExtras() { 305 return mExtras; 306 } 307 308 void finishRequest() { 309 VoiceInteractionSession session = mSession.get(); 310 if (session == null) { 311 throw new IllegalStateException("VoiceInteractionSession has been destroyed"); 312 } 313 Request req = session.removeRequest(mInterface.asBinder()); 314 if (req == null) { 315 throw new IllegalStateException("Request not active: " + this); 316 } else if (req != this) { 317 throw new IllegalStateException("Current active request " + req 318 + " not same as calling request " + this); 319 } 320 } 321 322 /** @hide */ 323 public void sendConfirmResult(boolean confirmed, Bundle result) { 324 try { 325 if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface 326 + " confirmed=" + confirmed + " result=" + result); 327 finishRequest(); 328 mCallback.deliverConfirmationResult(mInterface, confirmed, result); 329 } catch (RemoteException e) { 330 } 331 } 332 333 /** @hide */ 334 public void sendPickOptionResult(boolean finished, 335 VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) { 336 try { 337 if (DEBUG) Log.d(TAG, "sendPickOptionResult: req=" + mInterface 338 + " finished=" + finished + " selections=" + selections 339 + " result=" + result); 340 if (finished) { 341 finishRequest(); 342 } 343 mCallback.deliverPickOptionResult(mInterface, finished, selections, result); 344 } catch (RemoteException e) { 345 } 346 } 347 348 /** @hide */ 349 public void sendCompleteVoiceResult(Bundle result) { 350 try { 351 if (DEBUG) Log.d(TAG, "sendCompleteVoiceResult: req=" + mInterface 352 + " result=" + result); 353 finishRequest(); 354 mCallback.deliverCompleteVoiceResult(mInterface, result); 355 } catch (RemoteException e) { 356 } 357 } 358 359 /** @hide */ 360 public void sendAbortVoiceResult(Bundle result) { 361 try { 362 if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface 363 + " result=" + result); 364 finishRequest(); 365 mCallback.deliverAbortVoiceResult(mInterface, result); 366 } catch (RemoteException e) { 367 } 368 } 369 370 /** @hide */ 371 public void sendCommandResult(boolean finished, Bundle result) { 372 try { 373 if (DEBUG) Log.d(TAG, "sendCommandResult: req=" + mInterface 374 + " result=" + result); 375 if (finished) { 376 finishRequest(); 377 } 378 mCallback.deliverCommandResult(mInterface, finished, result); 379 } catch (RemoteException e) { 380 } 381 } 382 383 /** @hide */ 384 public void sendCancelResult() { 385 cancel(); 386 } 387 388 /** 389 * ASk the app to cancelLocked this current request. 390 */ 391 public void cancel() { 392 try { 393 if (DEBUG) Log.d(TAG, "sendCancelResult: req=" + mInterface); 394 finishRequest(); 395 mCallback.deliverCancel(mInterface); 396 } catch (RemoteException e) { 397 } 398 } 399 } 400 401 /** 402 * A request for confirmation from the user of an operation, as per 403 * {@link android.app.VoiceInteractor.ConfirmationRequest 404 * VoiceInteractor.ConfirmationRequest}. 405 */ 406 public static final class ConfirmationRequest extends Request { 407 final CharSequence mPrompt; 408 409 ConfirmationRequest(String packageName, int uid, IVoiceInteractorCallback callback, 410 VoiceInteractionSession session, CharSequence prompt, Bundle extras) { 411 super(packageName, uid, callback, session, extras); 412 mPrompt = prompt; 413 } 414 415 /** 416 * Return the prompt informing the user of what will happen, as per 417 * {@link android.app.VoiceInteractor.ConfirmationRequest 418 * VoiceInteractor.ConfirmationRequest}. 419 */ 420 public CharSequence getPrompt() { 421 return mPrompt; 422 } 423 424 /** 425 * Report that the voice interactor has confirmed the operation with the user, resulting 426 * in a call to 427 * {@link android.app.VoiceInteractor.ConfirmationRequest#onConfirmationResult 428 * VoiceInteractor.ConfirmationRequest.onConfirmationResult}. 429 */ 430 public void sendConfirmationResult(boolean confirmed, Bundle result) { 431 sendConfirmResult(confirmed, result); 432 } 433 } 434 435 /** 436 * A request for the user to pick from a set of option, as per 437 * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}. 438 */ 439 public static final class PickOptionRequest extends Request { 440 final CharSequence mPrompt; 441 final VoiceInteractor.PickOptionRequest.Option[] mOptions; 442 443 PickOptionRequest(String packageName, int uid, IVoiceInteractorCallback callback, 444 VoiceInteractionSession session, CharSequence prompt, 445 VoiceInteractor.PickOptionRequest.Option[] options, Bundle extras) { 446 super(packageName, uid, callback, session, extras); 447 mPrompt = prompt; 448 mOptions = options; 449 } 450 451 /** 452 * Return the prompt informing the user of what they are picking, as per 453 * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}. 454 */ 455 public CharSequence getPrompt() { 456 return mPrompt; 457 } 458 459 /** 460 * Return the set of options the user is picking from, as per 461 * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}. 462 */ 463 public VoiceInteractor.PickOptionRequest.Option[] getOptions() { 464 return mOptions; 465 } 466 467 /** 468 * Report an intermediate option selection from the request, without completing it (the 469 * request is still active and the app is waiting for the final option selection), 470 * resulting in a call to 471 * {@link android.app.VoiceInteractor.PickOptionRequest#onPickOptionResult 472 * VoiceInteractor.PickOptionRequest.onPickOptionResult} with false for finished. 473 */ 474 public void sendIntermediatePickOptionResult( 475 VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) { 476 sendPickOptionResult(false, selections, result); 477 } 478 479 /** 480 * Report the final option selection for the request, completing the request 481 * and resulting in a call to 482 * {@link android.app.VoiceInteractor.PickOptionRequest#onPickOptionResult 483 * VoiceInteractor.PickOptionRequest.onPickOptionResult} with false for finished. 484 */ 485 public void sendPickOptionResult( 486 VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) { 487 sendPickOptionResult(true, selections, result); 488 } 489 } 490 491 /** 492 * A request to simply inform the user that the voice operation has completed, as per 493 * {@link android.app.VoiceInteractor.CompleteVoiceRequest 494 * VoiceInteractor.CompleteVoiceRequest}. 495 */ 496 public static final class CompleteVoiceRequest extends Request { 497 final CharSequence mMessage; 498 499 CompleteVoiceRequest(String packageName, int uid, IVoiceInteractorCallback callback, 500 VoiceInteractionSession session, CharSequence message, Bundle extras) { 501 super(packageName, uid, callback, session, extras); 502 mMessage = message; 503 } 504 505 /** 506 * Return the message informing the user of the completion, as per 507 * {@link android.app.VoiceInteractor.CompleteVoiceRequest 508 * VoiceInteractor.CompleteVoiceRequest}. 509 */ 510 public CharSequence getMessage() { 511 return mMessage; 512 } 513 514 /** 515 * Report that the voice interactor has finished completing the voice operation, resulting 516 * in a call to 517 * {@link android.app.VoiceInteractor.CompleteVoiceRequest#onCompleteResult 518 * VoiceInteractor.CompleteVoiceRequest.onCompleteResult}. 519 */ 520 public void sendCompleteResult(Bundle result) { 521 sendCompleteVoiceResult(result); 522 } 523 } 524 525 /** 526 * A request to report that the current user interaction can not be completed with voice, as per 527 * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}. 528 */ 529 public static final class AbortVoiceRequest extends Request { 530 final CharSequence mMessage; 531 532 AbortVoiceRequest(String packageName, int uid, IVoiceInteractorCallback callback, 533 VoiceInteractionSession session, CharSequence message, Bundle extras) { 534 super(packageName, uid, callback, session, extras); 535 mMessage = message; 536 } 537 538 /** 539 * Return the message informing the user of the problem, as per 540 * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}. 541 */ 542 public CharSequence getMessage() { 543 return mMessage; 544 } 545 546 /** 547 * Report that the voice interactor has finished aborting the voice operation, resulting 548 * in a call to 549 * {@link android.app.VoiceInteractor.AbortVoiceRequest#onAbortResult 550 * VoiceInteractor.AbortVoiceRequest.onAbortResult}. 551 */ 552 public void sendAbortResult(Bundle result) { 553 sendAbortVoiceResult(result); 554 } 555 } 556 557 /** 558 * A generic vendor-specific request, as per 559 * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}. 560 */ 561 public static final class CommandRequest extends Request { 562 final String mCommand; 563 564 CommandRequest(String packageName, int uid, IVoiceInteractorCallback callback, 565 VoiceInteractionSession session, String command, Bundle extras) { 566 super(packageName, uid, callback, session, extras); 567 mCommand = command; 568 } 569 570 /** 571 * Return the command that is being executed, as per 572 * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}. 573 */ 574 public String getCommand() { 575 return mCommand; 576 } 577 578 /** 579 * Report an intermediate result of the request, without completing it (the request 580 * is still active and the app is waiting for the final result), resulting in a call to 581 * {@link android.app.VoiceInteractor.CommandRequest#onCommandResult 582 * VoiceInteractor.CommandRequest.onCommandResult} with false for isCompleted. 583 */ 584 public void sendIntermediateResult(Bundle result) { 585 sendCommandResult(false, result); 586 } 587 588 /** 589 * Report the final result of the request, completing the request and resulting in a call to 590 * {@link android.app.VoiceInteractor.CommandRequest#onCommandResult 591 * VoiceInteractor.CommandRequest.onCommandResult} with true for isCompleted. 592 */ 593 public void sendResult(Bundle result) { 594 sendCommandResult(true, result); 595 } 596 } 597 598 static final int MSG_START_CONFIRMATION = 1; 599 static final int MSG_START_PICK_OPTION = 2; 600 static final int MSG_START_COMPLETE_VOICE = 3; 601 static final int MSG_START_ABORT_VOICE = 4; 602 static final int MSG_START_COMMAND = 5; 603 static final int MSG_SUPPORTS_COMMANDS = 6; 604 static final int MSG_CANCEL = 7; 605 606 static final int MSG_TASK_STARTED = 100; 607 static final int MSG_TASK_FINISHED = 101; 608 static final int MSG_CLOSE_SYSTEM_DIALOGS = 102; 609 static final int MSG_DESTROY = 103; 610 static final int MSG_HANDLE_ASSIST = 104; 611 static final int MSG_HANDLE_SCREENSHOT = 105; 612 static final int MSG_SHOW = 106; 613 static final int MSG_HIDE = 107; 614 615 class MyCallbacks implements HandlerCaller.Callback, SoftInputWindow.Callback { 616 @Override 617 public void executeMessage(Message msg) { 618 SomeArgs args; 619 switch (msg.what) { 620 case MSG_START_CONFIRMATION: 621 if (DEBUG) Log.d(TAG, "onConfirm: req=" + msg.obj); 622 onRequestConfirmation((ConfirmationRequest) msg.obj); 623 break; 624 case MSG_START_PICK_OPTION: 625 if (DEBUG) Log.d(TAG, "onPickOption: req=" + msg.obj); 626 onRequestPickOption((PickOptionRequest) msg.obj); 627 break; 628 case MSG_START_COMPLETE_VOICE: 629 if (DEBUG) Log.d(TAG, "onCompleteVoice: req=" + msg.obj); 630 onRequestCompleteVoice((CompleteVoiceRequest) msg.obj); 631 break; 632 case MSG_START_ABORT_VOICE: 633 if (DEBUG) Log.d(TAG, "onAbortVoice: req=" + msg.obj); 634 onRequestAbortVoice((AbortVoiceRequest) msg.obj); 635 break; 636 case MSG_START_COMMAND: 637 if (DEBUG) Log.d(TAG, "onCommand: req=" + msg.obj); 638 onRequestCommand((CommandRequest) msg.obj); 639 break; 640 case MSG_SUPPORTS_COMMANDS: 641 args = (SomeArgs)msg.obj; 642 if (DEBUG) Log.d(TAG, "onGetSupportedCommands: cmds=" + args.arg1); 643 args.arg1 = onGetSupportedCommands((String[]) args.arg1); 644 break; 645 case MSG_CANCEL: 646 if (DEBUG) Log.d(TAG, "onCancel: req=" + ((Request)msg.obj)); 647 onCancelRequest((Request) msg.obj); 648 break; 649 case MSG_TASK_STARTED: 650 if (DEBUG) Log.d(TAG, "onTaskStarted: intent=" + msg.obj 651 + " taskId=" + msg.arg1); 652 onTaskStarted((Intent) msg.obj, msg.arg1); 653 break; 654 case MSG_TASK_FINISHED: 655 if (DEBUG) Log.d(TAG, "onTaskFinished: intent=" + msg.obj 656 + " taskId=" + msg.arg1); 657 onTaskFinished((Intent) msg.obj, msg.arg1); 658 break; 659 case MSG_CLOSE_SYSTEM_DIALOGS: 660 if (DEBUG) Log.d(TAG, "onCloseSystemDialogs"); 661 onCloseSystemDialogs(); 662 break; 663 case MSG_DESTROY: 664 if (DEBUG) Log.d(TAG, "doDestroy"); 665 doDestroy(); 666 break; 667 case MSG_HANDLE_ASSIST: 668 args = (SomeArgs)msg.obj; 669 if (DEBUG) Log.d(TAG, "onHandleAssist: data=" + args.arg1 670 + " structure=" + args.arg2 + " content=" + args.arg3); 671 onHandleAssist((Bundle) args.arg1, (AssistStructure) args.arg2, 672 (AssistContent) args.arg3); 673 break; 674 case MSG_HANDLE_SCREENSHOT: 675 if (DEBUG) Log.d(TAG, "onHandleScreenshot: " + msg.obj); 676 onHandleScreenshot((Bitmap) msg.obj); 677 break; 678 case MSG_SHOW: 679 args = (SomeArgs)msg.obj; 680 if (DEBUG) Log.d(TAG, "doShow: args=" + args.arg1 681 + " flags=" + msg.arg1 682 + " showCallback=" + args.arg2); 683 doShow((Bundle) args.arg1, msg.arg1, 684 (IVoiceInteractionSessionShowCallback) args.arg2); 685 break; 686 case MSG_HIDE: 687 if (DEBUG) Log.d(TAG, "doHide"); 688 doHide(); 689 break; 690 } 691 } 692 693 @Override 694 public void onBackPressed() { 695 VoiceInteractionSession.this.onBackPressed(); 696 } 697 } 698 699 final MyCallbacks mCallbacks = new MyCallbacks(); 700 701 /** 702 * Information about where interesting parts of the input method UI appear. 703 */ 704 public static final class Insets { 705 /** 706 * This is the part of the UI that is the main content. It is 707 * used to determine the basic space needed, to resize/pan the 708 * application behind. It is assumed that this inset does not 709 * change very much, since any change will cause a full resize/pan 710 * of the application behind. This value is relative to the top edge 711 * of the input method window. 712 */ 713 public final Rect contentInsets = new Rect(); 714 715 /** 716 * This is the region of the UI that is touchable. It is used when 717 * {@link #touchableInsets} is set to {@link #TOUCHABLE_INSETS_REGION}. 718 * The region should be specified relative to the origin of the window frame. 719 */ 720 public final Region touchableRegion = new Region(); 721 722 /** 723 * Option for {@link #touchableInsets}: the entire window frame 724 * can be touched. 725 */ 726 public static final int TOUCHABLE_INSETS_FRAME 727 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME; 728 729 /** 730 * Option for {@link #touchableInsets}: the area inside of 731 * the content insets can be touched. 732 */ 733 public static final int TOUCHABLE_INSETS_CONTENT 734 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT; 735 736 /** 737 * Option for {@link #touchableInsets}: the region specified by 738 * {@link #touchableRegion} can be touched. 739 */ 740 public static final int TOUCHABLE_INSETS_REGION 741 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION; 742 743 /** 744 * Determine which area of the window is touchable by the user. May 745 * be one of: {@link #TOUCHABLE_INSETS_FRAME}, 746 * {@link #TOUCHABLE_INSETS_CONTENT}, or {@link #TOUCHABLE_INSETS_REGION}. 747 */ 748 public int touchableInsets; 749 } 750 751 final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = 752 new ViewTreeObserver.OnComputeInternalInsetsListener() { 753 public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) { 754 onComputeInsets(mTmpInsets); 755 info.contentInsets.set(mTmpInsets.contentInsets); 756 info.visibleInsets.set(mTmpInsets.contentInsets); 757 info.touchableRegion.set(mTmpInsets.touchableRegion); 758 info.setTouchableInsets(mTmpInsets.touchableInsets); 759 } 760 }; 761 762 public VoiceInteractionSession(Context context) { 763 this(context, new Handler()); 764 } 765 766 public VoiceInteractionSession(Context context, Handler handler) { 767 mContext = context; 768 mHandlerCaller = new HandlerCaller(context, handler.getLooper(), 769 mCallbacks, true); 770 } 771 772 public Context getContext() { 773 return mContext; 774 } 775 776 void addRequest(Request req) { 777 mActiveRequests.put(req.mInterface.asBinder(), req); 778 } 779 780 Request removeRequest(IBinder reqInterface) { 781 synchronized (this) { 782 return mActiveRequests.remove(reqInterface); 783 } 784 } 785 786 void doCreate(IVoiceInteractionManagerService service, IBinder token, Bundle args, 787 int startFlags) { 788 mSystemService = service; 789 mToken = token; 790 onCreate(args, startFlags); 791 } 792 793 void doShow(Bundle args, int flags, final IVoiceInteractionSessionShowCallback showCallback) { 794 if (DEBUG) Log.v(TAG, "Showing window: mWindowAdded=" + mWindowAdded 795 + " mWindowVisible=" + mWindowVisible); 796 797 if (mInShowWindow) { 798 Log.w(TAG, "Re-entrance in to showWindow"); 799 return; 800 } 801 802 try { 803 mInShowWindow = true; 804 if (!mWindowVisible) { 805 if (!mWindowAdded) { 806 mWindowAdded = true; 807 View v = onCreateContentView(); 808 if (v != null) { 809 setContentView(v); 810 } 811 } 812 } 813 onShow(args, flags); 814 if (!mWindowVisible) { 815 mWindowVisible = true; 816 mWindow.show(); 817 } 818 if (showCallback != null) { 819 mRootView.invalidate(); 820 mRootView.getViewTreeObserver().addOnPreDrawListener( 821 new ViewTreeObserver.OnPreDrawListener() { 822 @Override 823 public boolean onPreDraw() { 824 mRootView.getViewTreeObserver().removeOnPreDrawListener(this); 825 try { 826 showCallback.onShown(); 827 } catch (RemoteException e) { 828 Log.w(TAG, "Error calling onShown", e); 829 } 830 return true; 831 } 832 }); 833 } 834 } finally { 835 mWindowWasVisible = true; 836 mInShowWindow = false; 837 } 838 } 839 840 void doHide() { 841 if (mWindowVisible) { 842 mWindow.hide(); 843 mWindowVisible = false; 844 onHide(); 845 } 846 } 847 848 void doDestroy() { 849 onDestroy(); 850 if (mInitialized) { 851 mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener( 852 mInsetsComputer); 853 if (mWindowAdded) { 854 mWindow.dismiss(); 855 mWindowAdded = false; 856 } 857 mInitialized = false; 858 } 859 } 860 861 void initViews() { 862 mInitialized = true; 863 864 mThemeAttrs = mContext.obtainStyledAttributes(android.R.styleable.VoiceInteractionSession); 865 mRootView = mInflater.inflate( 866 com.android.internal.R.layout.voice_interaction_session, null); 867 mRootView.setSystemUiVisibility( 868 View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 869 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); 870 mWindow.setContentView(mRootView); 871 mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer); 872 873 mContentFrame = (FrameLayout)mRootView.findViewById(android.R.id.content); 874 } 875 876 /** @hide */ 877 public void show() { 878 show(null, 0); 879 } 880 881 /** 882 * Show the UI for this session. This asks the system to go through the process of showing 883 * your UI, which will eventually culminate in {@link #onShow}. This is similar to calling 884 * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}. 885 * @param args Arbitrary arguments that will be propagated {@link #onShow}. 886 * @param flags Indicates additional optional behavior that should be performed. May 887 * be {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST} 888 * to request that the system generate and deliver assist data on the current foreground 889 * app as part of showing the session UI. 890 */ 891 public void show(Bundle args, int flags) { 892 if (mToken == null) { 893 throw new IllegalStateException("Can't call before onCreate()"); 894 } 895 try { 896 mSystemService.showSessionFromSession(mToken, args, flags); 897 } catch (RemoteException e) { 898 } 899 } 900 901 /** 902 * Hide the session's UI, if currently shown. Call this when you are done with your 903 * user interaction. 904 */ 905 public void hide() { 906 if (mToken == null) { 907 throw new IllegalStateException("Can't call before onCreate()"); 908 } 909 try { 910 mSystemService.hideSessionFromSession(mToken); 911 } catch (RemoteException e) { 912 } 913 } 914 915 /** @hide */ 916 public void showWindow() { 917 } 918 919 /** @hide */ 920 public void hideWindow() { 921 } 922 923 /** 924 * You can call this to customize the theme used by your IME's window. 925 * This must be set before {@link #onCreate}, so you 926 * will typically call it in your constructor with the resource ID 927 * of your custom theme. 928 */ 929 public void setTheme(int theme) { 930 if (mWindow != null) { 931 throw new IllegalStateException("Must be called before onCreate()"); 932 } 933 mTheme = theme; 934 } 935 936 /** 937 * Ask that a new activity be started for voice interaction. This will create a 938 * new dedicated task in the activity manager for this voice interaction session; 939 * this means that {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK} 940 * will be set for you to make it a new task. 941 * 942 * <p>The newly started activity will be displayed to the user in a special way, as 943 * a layer under the voice interaction UI.</p> 944 * 945 * <p>As the voice activity runs, it can retrieve a {@link android.app.VoiceInteractor} 946 * through which it can perform voice interactions through your session. These requests 947 * for voice interactions will appear as callbacks on {@link #onGetSupportedCommands}, 948 * {@link #onRequestConfirmation}, {@link #onRequestPickOption}, 949 * {@link #onRequestCompleteVoice}, {@link #onRequestAbortVoice}, 950 * or {@link #onRequestCommand} 951 * 952 * <p>You will receive a call to {@link #onTaskStarted} when the task starts up 953 * and {@link #onTaskFinished} when the last activity has finished. 954 * 955 * @param intent The Intent to start this voice interaction. The given Intent will 956 * always have {@link Intent#CATEGORY_VOICE Intent.CATEGORY_VOICE} added to it, since 957 * this is part of a voice interaction. 958 */ 959 public void startVoiceActivity(Intent intent) { 960 if (mToken == null) { 961 throw new IllegalStateException("Can't call before onCreate()"); 962 } 963 try { 964 intent.migrateExtraStreamToClipData(); 965 intent.prepareToLeaveProcess(); 966 int res = mSystemService.startVoiceActivity(mToken, intent, 967 intent.resolveType(mContext.getContentResolver())); 968 Instrumentation.checkStartActivityResult(res, intent); 969 } catch (RemoteException e) { 970 } 971 } 972 973 /** 974 * Set whether this session will keep the device awake while it is running a voice 975 * activity. By default, the system holds a wake lock for it while in this state, 976 * so that it can work even if the screen is off. Setting this to false removes that 977 * wake lock, allowing the CPU to go to sleep. This is typically used if the 978 * session decides it has been waiting too long for a response from the user and 979 * doesn't want to let this continue to drain the battery. 980 * 981 * <p>Passing false here will release the wake lock, and you can call later with 982 * true to re-acquire it. It will also be automatically re-acquired for you each 983 * time you start a new voice activity task -- that is when you call 984 * {@link #startVoiceActivity}.</p> 985 */ 986 public void setKeepAwake(boolean keepAwake) { 987 if (mToken == null) { 988 throw new IllegalStateException("Can't call before onCreate()"); 989 } 990 try { 991 mSystemService.setKeepAwake(mToken, keepAwake); 992 } catch (RemoteException e) { 993 } 994 } 995 996 /** 997 * Convenience for inflating views. 998 */ 999 public LayoutInflater getLayoutInflater() { 1000 return mInflater; 1001 } 1002 1003 /** 1004 * Retrieve the window being used to show the session's UI. 1005 */ 1006 public Dialog getWindow() { 1007 return mWindow; 1008 } 1009 1010 /** 1011 * Finish the session. This completely destroys the session -- the next time it is shown, 1012 * an entirely new one will be created. You do not normally call this function; instead, 1013 * use {@link #hide} and allow the system to destroy your session if it needs its RAM. 1014 */ 1015 public void finish() { 1016 if (mToken == null) { 1017 throw new IllegalStateException("Can't call before onCreate()"); 1018 } 1019 hideWindow(); 1020 try { 1021 mSystemService.finish(mToken); 1022 } catch (RemoteException e) { 1023 } 1024 } 1025 1026 /** 1027 * Initiatize a new session. At this point you don't know exactly what this 1028 * session will be used for; you will find that out in {@link #onShow}. 1029 */ 1030 public void onCreate() { 1031 doOnCreate(); 1032 } 1033 1034 /** @hide */ 1035 public void onCreate(Bundle args) { 1036 doOnCreate(); 1037 } 1038 1039 /** @hide */ 1040 public void onCreate(Bundle args, int showFlags) { 1041 doOnCreate(); 1042 } 1043 1044 private void doOnCreate() { 1045 mTheme = mTheme != 0 ? mTheme 1046 : com.android.internal.R.style.Theme_DeviceDefault_VoiceInteractionSession; 1047 mInflater = (LayoutInflater)mContext.getSystemService( 1048 Context.LAYOUT_INFLATER_SERVICE); 1049 mWindow = new SoftInputWindow(mContext, "VoiceInteractionSession", mTheme, 1050 mCallbacks, this, mDispatcherState, 1051 WindowManager.LayoutParams.TYPE_VOICE_INTERACTION, Gravity.BOTTOM, true); 1052 mWindow.getWindow().addFlags( 1053 WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED | 1054 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN); 1055 initViews(); 1056 mWindow.getWindow().setLayout(MATCH_PARENT, MATCH_PARENT); 1057 mWindow.setToken(mToken); 1058 } 1059 1060 /** 1061 * Called when the session UI is going to be shown. This is called after 1062 * {@link #onCreateContentView} (if the session's content UI needed to be created) and 1063 * immediately prior to the window being shown. This may be called while the window 1064 * is already shown, if a show request has come in while it is shown, to allow you to 1065 * update the UI to match the new show arguments. 1066 * 1067 * @param args The arguments that were supplied to 1068 * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}. 1069 * @param showFlags The show flags originally provided to 1070 * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}. 1071 */ 1072 public void onShow(Bundle args, int showFlags) { 1073 } 1074 1075 /** 1076 * Called immediately after stopping to show the session UI. 1077 */ 1078 public void onHide() { 1079 } 1080 1081 /** 1082 * Last callback to the session as it is being finished. 1083 */ 1084 public void onDestroy() { 1085 } 1086 1087 /** 1088 * Hook in which to create the session's UI. 1089 */ 1090 public View onCreateContentView() { 1091 return null; 1092 } 1093 1094 public void setContentView(View view) { 1095 mContentFrame.removeAllViews(); 1096 mContentFrame.addView(view, new FrameLayout.LayoutParams( 1097 ViewGroup.LayoutParams.MATCH_PARENT, 1098 ViewGroup.LayoutParams.MATCH_PARENT)); 1099 1100 } 1101 1102 /** @hide */ 1103 public void onHandleAssist(Bundle assistBundle) { 1104 } 1105 1106 public void onHandleAssist(Bundle data, AssistStructure structure, AssistContent content) { 1107 if (data != null) { 1108 Bundle assistContext = data.getBundle(Intent.EXTRA_ASSIST_CONTEXT); 1109 if (assistContext != null) { 1110 assistContext.putParcelable(AssistStructure.ASSIST_KEY, structure); 1111 assistContext.putParcelable(AssistContent.ASSIST_KEY, content); 1112 data.putBundle(Intent.EXTRA_ASSIST_CONTEXT, assistContext); 1113 } 1114 } 1115 onHandleAssist(data); 1116 } 1117 1118 /** @hide */ 1119 public void onHandleScreenshot(Bitmap screenshot) { 1120 } 1121 1122 public boolean onKeyDown(int keyCode, KeyEvent event) { 1123 return false; 1124 } 1125 1126 public boolean onKeyLongPress(int keyCode, KeyEvent event) { 1127 return false; 1128 } 1129 1130 public boolean onKeyUp(int keyCode, KeyEvent event) { 1131 return false; 1132 } 1133 1134 public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) { 1135 return false; 1136 } 1137 1138 /** 1139 * Called when the user presses the back button while focus is in the session UI. Note 1140 * that this will only happen if the session UI has requested input focus in its window; 1141 * otherwise, the back key will go to whatever window has focus and do whatever behavior 1142 * it normally has there. The default implementation simply calls {@link #hide}. 1143 */ 1144 public void onBackPressed() { 1145 hide(); 1146 } 1147 1148 /** 1149 * Sessions automatically watch for requests that all system UI be closed (such as when 1150 * the user presses HOME), which will appear here. The default implementation always 1151 * calls {@link #hide}. 1152 */ 1153 public void onCloseSystemDialogs() { 1154 hide(); 1155 } 1156 1157 @Override 1158 public void onConfigurationChanged(Configuration newConfig) { 1159 } 1160 1161 @Override 1162 public void onLowMemory() { 1163 } 1164 1165 @Override 1166 public void onTrimMemory(int level) { 1167 } 1168 1169 /** 1170 * Compute the interesting insets into your UI. The default implementation 1171 * sets {@link Insets#contentInsets outInsets.contentInsets.top} to the height 1172 * of the window, meaning it should not adjust content underneath. The default touchable 1173 * insets are {@link Insets#TOUCHABLE_INSETS_FRAME}, meaning it consumes all touch 1174 * events within its window frame. 1175 * 1176 * @param outInsets Fill in with the current UI insets. 1177 */ 1178 public void onComputeInsets(Insets outInsets) { 1179 outInsets.contentInsets.left = 0; 1180 outInsets.contentInsets.bottom = 0; 1181 outInsets.contentInsets.right = 0; 1182 View decor = getWindow().getWindow().getDecorView(); 1183 outInsets.contentInsets.top = decor.getHeight(); 1184 outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_FRAME; 1185 outInsets.touchableRegion.setEmpty(); 1186 } 1187 1188 /** 1189 * Called when a task initiated by {@link #startVoiceActivity(android.content.Intent)} 1190 * has actually started. 1191 * 1192 * @param intent The original {@link Intent} supplied to 1193 * {@link #startVoiceActivity(android.content.Intent)}. 1194 * @param taskId Unique ID of the now running task. 1195 */ 1196 public void onTaskStarted(Intent intent, int taskId) { 1197 } 1198 1199 /** 1200 * Called when the last activity of a task initiated by 1201 * {@link #startVoiceActivity(android.content.Intent)} has finished. The default 1202 * implementation calls {@link #finish()} on the assumption that this represents 1203 * the completion of a voice action. You can override the implementation if you would 1204 * like a different behavior. 1205 * 1206 * @param intent The original {@link Intent} supplied to 1207 * {@link #startVoiceActivity(android.content.Intent)}. 1208 * @param taskId Unique ID of the finished task. 1209 */ 1210 public void onTaskFinished(Intent intent, int taskId) { 1211 hide(); 1212 } 1213 1214 /** @hide */ 1215 public boolean[] onGetSupportedCommands(Caller caller, String[] commands) { 1216 return new boolean[commands.length]; 1217 } 1218 /** @hide */ 1219 public void onConfirm(Caller caller, Request request, CharSequence prompt, 1220 Bundle extras) { 1221 } 1222 /** @hide */ 1223 public void onPickOption(Caller caller, Request request, CharSequence prompt, 1224 VoiceInteractor.PickOptionRequest.Option[] options, Bundle extras) { 1225 } 1226 /** @hide */ 1227 public void onCompleteVoice(Caller caller, Request request, CharSequence message, 1228 Bundle extras) { 1229 request.sendCompleteVoiceResult(null); 1230 } 1231 /** @hide */ 1232 public void onAbortVoice(Caller caller, Request request, CharSequence message, Bundle extras) { 1233 request.sendAbortVoiceResult(null); 1234 } 1235 /** @hide */ 1236 public void onCommand(Caller caller, Request request, String command, Bundle extras) { 1237 } 1238 /** @hide */ 1239 public void onCancel(Request request) { 1240 } 1241 1242 /** 1243 * Request to query for what extended commands the session supports. 1244 * 1245 * @param commands An array of commands that are being queried. 1246 * @return Return an array of booleans indicating which of each entry in the 1247 * command array is supported. A true entry in the array indicates the command 1248 * is supported; false indicates it is not. The default implementation returns 1249 * an array of all false entries. 1250 */ 1251 public boolean[] onGetSupportedCommands(String[] commands) { 1252 return onGetSupportedCommands(new Caller(), commands); 1253 } 1254 1255 /** 1256 * Request to confirm with the user before proceeding with an unrecoverable operation, 1257 * corresponding to a {@link android.app.VoiceInteractor.ConfirmationRequest 1258 * VoiceInteractor.ConfirmationRequest}. 1259 * 1260 * @param request The active request. 1261 */ 1262 public void onRequestConfirmation(ConfirmationRequest request) { 1263 onConfirm(request, request, request.getPrompt(), request.getExtras()); 1264 } 1265 1266 /** 1267 * Request for the user to pick one of N options, corresponding to a 1268 * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}. 1269 * 1270 * @param request The active request. 1271 */ 1272 public void onRequestPickOption(PickOptionRequest request) { 1273 onPickOption(request, request, request.getPrompt(), request.getOptions(), 1274 request.getExtras()); 1275 } 1276 1277 /** 1278 * Request to complete the voice interaction session because the voice activity successfully 1279 * completed its interaction using voice. Corresponds to 1280 * {@link android.app.VoiceInteractor.CompleteVoiceRequest 1281 * VoiceInteractor.CompleteVoiceRequest}. The default implementation just sends an empty 1282 * confirmation back to allow the activity to exit. 1283 * 1284 * @param request The active request. 1285 */ 1286 public void onRequestCompleteVoice(CompleteVoiceRequest request) { 1287 onCompleteVoice(request, request, request.getMessage(), request.getExtras()); 1288 } 1289 1290 /** 1291 * Request to abort the voice interaction session because the voice activity can not 1292 * complete its interaction using voice. Corresponds to 1293 * {@link android.app.VoiceInteractor.AbortVoiceRequest 1294 * VoiceInteractor.AbortVoiceRequest}. The default implementation just sends an empty 1295 * confirmation back to allow the activity to exit. 1296 * 1297 * @param request The active request. 1298 */ 1299 public void onRequestAbortVoice(AbortVoiceRequest request) { 1300 onAbortVoice(request, request, request.getMessage(), request.getExtras()); 1301 } 1302 1303 /** 1304 * Process an arbitrary extended command from the caller, 1305 * corresponding to a {@link android.app.VoiceInteractor.CommandRequest 1306 * VoiceInteractor.CommandRequest}. 1307 * 1308 * @param request The active request. 1309 */ 1310 public void onRequestCommand(CommandRequest request) { 1311 onCommand(request, request, request.getCommand(), request.getExtras()); 1312 } 1313 1314 /** 1315 * Called when the {@link android.app.VoiceInteractor} has asked to cancelLocked a {@link Request} 1316 * that was previously delivered to {@link #onRequestConfirmation}, 1317 * {@link #onRequestPickOption}, {@link #onRequestCompleteVoice}, {@link #onRequestAbortVoice}, 1318 * or {@link #onRequestCommand}. 1319 * 1320 * @param request The request that is being canceled. 1321 */ 1322 public void onCancelRequest(Request request) { 1323 onCancel(request); 1324 } 1325} 1326