VoiceInteractionSession.java revision a2c076d54048258cf88ab14551ce5fdf5a09c6e8
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.content.Context; 22import android.content.Intent; 23import android.content.res.TypedArray; 24import android.graphics.Rect; 25import android.graphics.Region; 26import android.inputmethodservice.SoftInputWindow; 27import android.os.Binder; 28import android.os.Bundle; 29import android.os.Handler; 30import android.os.IBinder; 31import android.os.Message; 32import android.os.RemoteException; 33import android.util.ArrayMap; 34import android.util.Log; 35import android.view.Gravity; 36import android.view.KeyEvent; 37import android.view.LayoutInflater; 38import android.view.View; 39import android.view.ViewGroup; 40import android.view.ViewTreeObserver; 41import android.view.WindowManager; 42import android.widget.FrameLayout; 43import com.android.internal.app.IVoiceInteractionManagerService; 44import com.android.internal.app.IVoiceInteractor; 45import com.android.internal.app.IVoiceInteractorCallback; 46import com.android.internal.app.IVoiceInteractorRequest; 47import com.android.internal.os.HandlerCaller; 48import com.android.internal.os.SomeArgs; 49 50import java.lang.ref.WeakReference; 51 52import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 53import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; 54 55/** 56 * An active voice interaction session, providing a facility for the implementation 57 * to interact with the user in the voice interaction layer. This interface is no shown 58 * by default, but you can request that it be shown with {@link #showWindow()}, which 59 * will result in a later call to {@link #onCreateContentView()} in which the UI can be 60 * built 61 * 62 * <p>A voice interaction session can be self-contained, ultimately calling {@link #finish} 63 * when done. It can also initiate voice interactions with applications by calling 64 * {@link #startVoiceActivity}</p>. 65 */ 66public abstract class VoiceInteractionSession implements KeyEvent.Callback { 67 static final String TAG = "VoiceInteractionSession"; 68 static final boolean DEBUG = true; 69 70 final Context mContext; 71 final HandlerCaller mHandlerCaller; 72 73 final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState(); 74 75 IVoiceInteractionManagerService mSystemService; 76 IBinder mToken; 77 78 int mTheme = 0; 79 LayoutInflater mInflater; 80 TypedArray mThemeAttrs; 81 View mRootView; 82 FrameLayout mContentFrame; 83 SoftInputWindow mWindow; 84 85 boolean mInitialized; 86 boolean mWindowAdded; 87 boolean mWindowVisible; 88 boolean mWindowWasVisible; 89 boolean mInShowWindow; 90 91 final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<IBinder, Request>(); 92 93 final Insets mTmpInsets = new Insets(); 94 final int[] mTmpLocation = new int[2]; 95 96 final WeakReference<VoiceInteractionSession> mWeakRef 97 = new WeakReference<VoiceInteractionSession>(this); 98 99 final IVoiceInteractor mInteractor = new IVoiceInteractor.Stub() { 100 @Override 101 public IVoiceInteractorRequest startConfirmation(String callingPackage, 102 IVoiceInteractorCallback callback, CharSequence prompt, Bundle extras) { 103 Request request = newRequest(callback); 104 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_CONFIRMATION, 105 new Caller(callingPackage, Binder.getCallingUid()), request, 106 prompt, extras)); 107 return request.mInterface; 108 } 109 110 @Override 111 public IVoiceInteractorRequest startAbortVoice(String callingPackage, 112 IVoiceInteractorCallback callback, CharSequence message, Bundle extras) { 113 Request request = newRequest(callback); 114 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_ABORT_VOICE, 115 new Caller(callingPackage, Binder.getCallingUid()), request, 116 message, extras)); 117 return request.mInterface; 118 } 119 120 @Override 121 public IVoiceInteractorRequest startCommand(String callingPackage, 122 IVoiceInteractorCallback callback, String command, Bundle extras) { 123 Request request = newRequest(callback); 124 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_COMMAND, 125 new Caller(callingPackage, Binder.getCallingUid()), request, 126 command, extras)); 127 return request.mInterface; 128 } 129 130 @Override 131 public boolean[] supportsCommands(String callingPackage, String[] commands) { 132 Message msg = mHandlerCaller.obtainMessageIOO(MSG_SUPPORTS_COMMANDS, 133 0, new Caller(callingPackage, Binder.getCallingUid()), commands); 134 SomeArgs args = mHandlerCaller.sendMessageAndWait(msg); 135 if (args != null) { 136 boolean[] res = (boolean[])args.arg1; 137 args.recycle(); 138 return res; 139 } 140 return new boolean[commands.length]; 141 } 142 }; 143 144 final IVoiceInteractionSession mSession = new IVoiceInteractionSession.Stub() { 145 @Override 146 public void taskStarted(Intent intent, int taskId) { 147 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_TASK_STARTED, 148 taskId, intent)); 149 } 150 151 @Override 152 public void taskFinished(Intent intent, int taskId) { 153 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_TASK_FINISHED, 154 taskId, intent)); 155 } 156 157 @Override 158 public void closeSystemDialogs() { 159 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_CLOSE_SYSTEM_DIALOGS)); 160 } 161 162 @Override 163 public void destroy() { 164 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_DESTROY)); 165 } 166 }; 167 168 public static class Request { 169 final IVoiceInteractorRequest mInterface = new IVoiceInteractorRequest.Stub() { 170 @Override 171 public void cancel() throws RemoteException { 172 VoiceInteractionSession session = mSession.get(); 173 if (session != null) { 174 session.mHandlerCaller.sendMessage( 175 session.mHandlerCaller.obtainMessageO(MSG_CANCEL, Request.this)); 176 } 177 } 178 }; 179 final IVoiceInteractorCallback mCallback; 180 final WeakReference<VoiceInteractionSession> mSession; 181 182 Request(IVoiceInteractorCallback callback, VoiceInteractionSession session) { 183 mCallback = callback; 184 mSession = session.mWeakRef; 185 } 186 187 void finishRequest() { 188 VoiceInteractionSession session = mSession.get(); 189 if (session == null) { 190 throw new IllegalStateException("VoiceInteractionSession has been destroyed"); 191 } 192 Request req = session.removeRequest(mInterface.asBinder()); 193 if (req == null) { 194 throw new IllegalStateException("Request not active: " + this); 195 } else if (req != this) { 196 throw new IllegalStateException("Current active request " + req 197 + " not same as calling request " + this); 198 } 199 } 200 201 public void sendConfirmResult(boolean confirmed, Bundle result) { 202 try { 203 if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface 204 + " confirmed=" + confirmed + " result=" + result); 205 finishRequest(); 206 mCallback.deliverConfirmationResult(mInterface, confirmed, result); 207 } catch (RemoteException e) { 208 } 209 } 210 211 public void sendAbortVoiceResult(Bundle result) { 212 try { 213 if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface 214 + " result=" + result); 215 finishRequest(); 216 mCallback.deliverAbortVoiceResult(mInterface, result); 217 } catch (RemoteException e) { 218 } 219 } 220 221 public void sendCommandResult(boolean complete, Bundle result) { 222 try { 223 if (DEBUG) Log.d(TAG, "sendCommandResult: req=" + mInterface 224 + " result=" + result); 225 finishRequest(); 226 mCallback.deliverCommandResult(mInterface, complete, result); 227 } catch (RemoteException e) { 228 } 229 } 230 231 public void sendCancelResult() { 232 try { 233 if (DEBUG) Log.d(TAG, "sendCancelResult: req=" + mInterface); 234 finishRequest(); 235 mCallback.deliverCancel(mInterface); 236 } catch (RemoteException e) { 237 } 238 } 239 } 240 241 public static class Caller { 242 final String packageName; 243 final int uid; 244 245 Caller(String _packageName, int _uid) { 246 packageName = _packageName; 247 uid = _uid; 248 } 249 } 250 251 static final int MSG_START_CONFIRMATION = 1; 252 static final int MSG_START_ABORT_VOICE = 2; 253 static final int MSG_START_COMMAND = 3; 254 static final int MSG_SUPPORTS_COMMANDS = 4; 255 static final int MSG_CANCEL = 5; 256 257 static final int MSG_TASK_STARTED = 100; 258 static final int MSG_TASK_FINISHED = 101; 259 static final int MSG_CLOSE_SYSTEM_DIALOGS = 102; 260 static final int MSG_DESTROY = 103; 261 262 class MyCallbacks implements HandlerCaller.Callback, SoftInputWindow.Callback { 263 @Override 264 public void executeMessage(Message msg) { 265 SomeArgs args; 266 switch (msg.what) { 267 case MSG_START_CONFIRMATION: 268 args = (SomeArgs)msg.obj; 269 if (DEBUG) Log.d(TAG, "onConfirm: req=" + ((Request) args.arg2).mInterface 270 + " prompt=" + args.arg3 + " extras=" + args.arg4); 271 onConfirm((Caller)args.arg1, (Request)args.arg2, (CharSequence)args.arg3, 272 (Bundle)args.arg4); 273 break; 274 case MSG_START_ABORT_VOICE: 275 args = (SomeArgs)msg.obj; 276 if (DEBUG) Log.d(TAG, "onAbortVoice: req=" + ((Request) args.arg2).mInterface 277 + " message=" + args.arg3 + " extras=" + args.arg4); 278 onAbortVoice((Caller) args.arg1, (Request) args.arg2, (CharSequence) args.arg3, 279 (Bundle) args.arg4); 280 break; 281 case MSG_START_COMMAND: 282 args = (SomeArgs)msg.obj; 283 if (DEBUG) Log.d(TAG, "onCommand: req=" + ((Request) args.arg2).mInterface 284 + " command=" + args.arg3 + " extras=" + args.arg4); 285 onCommand((Caller) args.arg1, (Request) args.arg2, (String) args.arg3, 286 (Bundle) args.arg4); 287 break; 288 case MSG_SUPPORTS_COMMANDS: 289 args = (SomeArgs)msg.obj; 290 if (DEBUG) Log.d(TAG, "onGetSupportedCommands: cmds=" + args.arg2); 291 args.arg1 = onGetSupportedCommands((Caller) args.arg1, (String[]) args.arg2); 292 break; 293 case MSG_CANCEL: 294 args = (SomeArgs)msg.obj; 295 if (DEBUG) Log.d(TAG, "onCancel: req=" + ((Request) args.arg1).mInterface); 296 onCancel((Request)args.arg1); 297 break; 298 case MSG_TASK_STARTED: 299 if (DEBUG) Log.d(TAG, "onTaskStarted: intent=" + msg.obj 300 + " taskId=" + msg.arg1); 301 onTaskStarted((Intent) msg.obj, msg.arg1); 302 break; 303 case MSG_TASK_FINISHED: 304 if (DEBUG) Log.d(TAG, "onTaskFinished: intent=" + msg.obj 305 + " taskId=" + msg.arg1); 306 onTaskFinished((Intent) msg.obj, msg.arg1); 307 break; 308 case MSG_CLOSE_SYSTEM_DIALOGS: 309 if (DEBUG) Log.d(TAG, "onCloseSystemDialogs"); 310 onCloseSystemDialogs(); 311 break; 312 case MSG_DESTROY: 313 if (DEBUG) Log.d(TAG, "doDestroy"); 314 doDestroy(); 315 break; 316 } 317 } 318 319 @Override 320 public void onBackPressed() { 321 VoiceInteractionSession.this.onBackPressed(); 322 } 323 } 324 325 final MyCallbacks mCallbacks = new MyCallbacks(); 326 327 /** 328 * Information about where interesting parts of the input method UI appear. 329 */ 330 public static final class Insets { 331 /** 332 * This is the part of the UI that is the main content. It is 333 * used to determine the basic space needed, to resize/pan the 334 * application behind. It is assumed that this inset does not 335 * change very much, since any change will cause a full resize/pan 336 * of the application behind. This value is relative to the top edge 337 * of the input method window. 338 */ 339 public final Rect contentInsets = new Rect(); 340 341 /** 342 * This is the region of the UI that is touchable. It is used when 343 * {@link #touchableInsets} is set to {@link #TOUCHABLE_INSETS_REGION}. 344 * The region should be specified relative to the origin of the window frame. 345 */ 346 public final Region touchableRegion = new Region(); 347 348 /** 349 * Option for {@link #touchableInsets}: the entire window frame 350 * can be touched. 351 */ 352 public static final int TOUCHABLE_INSETS_FRAME 353 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME; 354 355 /** 356 * Option for {@link #touchableInsets}: the area inside of 357 * the content insets can be touched. 358 */ 359 public static final int TOUCHABLE_INSETS_CONTENT 360 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT; 361 362 /** 363 * Option for {@link #touchableInsets}: the region specified by 364 * {@link #touchableRegion} can be touched. 365 */ 366 public static final int TOUCHABLE_INSETS_REGION 367 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION; 368 369 /** 370 * Determine which area of the window is touchable by the user. May 371 * be one of: {@link #TOUCHABLE_INSETS_FRAME}, 372 * {@link #TOUCHABLE_INSETS_CONTENT}, or {@link #TOUCHABLE_INSETS_REGION}. 373 */ 374 public int touchableInsets; 375 } 376 377 final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = 378 new ViewTreeObserver.OnComputeInternalInsetsListener() { 379 public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) { 380 onComputeInsets(mTmpInsets); 381 info.contentInsets.set(mTmpInsets.contentInsets); 382 info.visibleInsets.set(mTmpInsets.contentInsets); 383 info.touchableRegion.set(mTmpInsets.touchableRegion); 384 info.setTouchableInsets(mTmpInsets.touchableInsets); 385 } 386 }; 387 388 public VoiceInteractionSession(Context context) { 389 this(context, new Handler()); 390 } 391 392 public VoiceInteractionSession(Context context, Handler handler) { 393 mContext = context; 394 mHandlerCaller = new HandlerCaller(context, handler.getLooper(), 395 mCallbacks, true); 396 } 397 398 Request newRequest(IVoiceInteractorCallback callback) { 399 synchronized (this) { 400 Request req = new Request(callback, this); 401 mActiveRequests.put(req.mInterface.asBinder(), req); 402 return req; 403 } 404 } 405 406 Request removeRequest(IBinder reqInterface) { 407 synchronized (this) { 408 Request req = mActiveRequests.get(reqInterface); 409 if (req != null) { 410 mActiveRequests.remove(req); 411 } 412 return req; 413 } 414 } 415 416 void doCreate(IVoiceInteractionManagerService service, IBinder token, Bundle args) { 417 mSystemService = service; 418 mToken = token; 419 onCreate(args); 420 } 421 422 void doDestroy() { 423 if (mInitialized) { 424 mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener( 425 mInsetsComputer); 426 if (mWindowAdded) { 427 mWindow.dismiss(); 428 mWindowAdded = false; 429 } 430 mInitialized = false; 431 } 432 } 433 434 void initViews() { 435 mInitialized = true; 436 437 mThemeAttrs = mContext.obtainStyledAttributes(android.R.styleable.VoiceInteractionSession); 438 mRootView = mInflater.inflate( 439 com.android.internal.R.layout.voice_interaction_session, null); 440 mRootView.setSystemUiVisibility( 441 View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); 442 mWindow.setContentView(mRootView); 443 mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer); 444 445 mContentFrame = (FrameLayout)mRootView.findViewById(android.R.id.content); 446 } 447 448 public void showWindow() { 449 if (DEBUG) Log.v(TAG, "Showing window: mWindowAdded=" + mWindowAdded 450 + " mWindowVisible=" + mWindowVisible); 451 452 if (mInShowWindow) { 453 Log.w(TAG, "Re-entrance in to showWindow"); 454 return; 455 } 456 457 try { 458 mInShowWindow = true; 459 if (!mWindowVisible) { 460 mWindowVisible = true; 461 if (!mWindowAdded) { 462 mWindowAdded = true; 463 View v = onCreateContentView(); 464 if (v != null) { 465 setContentView(v); 466 } 467 } 468 mWindow.show(); 469 } 470 } finally { 471 mWindowWasVisible = true; 472 mInShowWindow = false; 473 } 474 } 475 476 public void hideWindow() { 477 if (mWindowVisible) { 478 mWindow.hide(); 479 mWindowVisible = false; 480 } 481 } 482 483 /** 484 * You can call this to customize the theme used by your IME's window. 485 * This must be set before {@link #onCreate}, so you 486 * will typically call it in your constructor with the resource ID 487 * of your custom theme. 488 */ 489 public void setTheme(int theme) { 490 if (mWindow != null) { 491 throw new IllegalStateException("Must be called before onCreate()"); 492 } 493 mTheme = theme; 494 } 495 496 /** 497 * Ask that a new activity be started for voice interaction. This will create a 498 * new dedicated task in the activity manager for this voice interaction session; 499 * this means that {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK} 500 * will be set for you to make it a new task. 501 * 502 * <p>The newly started activity will be displayed to the user in a special way, as 503 * a layer under the voice interaction UI.</p> 504 * 505 * <p>As the voice activity runs, it can retrieve a {@link android.app.VoiceInteractor} 506 * through which it can perform voice interactions through your session. These requests 507 * for voice interactions will appear as callbacks on {@link #onGetSupportedCommands}, 508 * {@link #onConfirm}, {@link #onCommand}, and {@link #onCancel}. 509 * 510 * <p>You will receive a call to {@link #onTaskStarted} when the task starts up 511 * and {@link #onTaskFinished} when the last activity has finished. 512 * 513 * @param intent The Intent to start this voice interaction. The given Intent will 514 * always have {@link Intent#CATEGORY_VOICE Intent.CATEGORY_VOICE} added to it, since 515 * this is part of a voice interaction. 516 */ 517 public void startVoiceActivity(Intent intent) { 518 if (mToken == null) { 519 throw new IllegalStateException("Can't call before onCreate()"); 520 } 521 try { 522 intent.migrateExtraStreamToClipData(); 523 intent.prepareToLeaveProcess(); 524 int res = mSystemService.startVoiceActivity(mToken, intent, 525 intent.resolveType(mContext.getContentResolver())); 526 Instrumentation.checkStartActivityResult(res, intent); 527 } catch (RemoteException e) { 528 } 529 } 530 531 /** 532 * Convenience for inflating views. 533 */ 534 public LayoutInflater getLayoutInflater() { 535 return mInflater; 536 } 537 538 /** 539 * Retrieve the window being used to show the session's UI. 540 */ 541 public Dialog getWindow() { 542 return mWindow; 543 } 544 545 /** 546 * Finish the session. 547 */ 548 public void finish() { 549 if (mToken == null) { 550 throw new IllegalStateException("Can't call before onCreate()"); 551 } 552 hideWindow(); 553 try { 554 mSystemService.finish(mToken); 555 } catch (RemoteException e) { 556 } 557 } 558 559 /** 560 * Initiatize a new session. 561 * 562 * @param args The arguments that were supplied to 563 * {@link VoiceInteractionService#startSession VoiceInteractionService.startSession}. 564 */ 565 public void onCreate(Bundle args) { 566 mTheme = mTheme != 0 ? mTheme 567 : com.android.internal.R.style.Theme_DeviceDefault_VoiceInteractionSession; 568 mInflater = (LayoutInflater)mContext.getSystemService( 569 Context.LAYOUT_INFLATER_SERVICE); 570 mWindow = new SoftInputWindow(mContext, "VoiceInteractionSession", mTheme, 571 mCallbacks, this, mDispatcherState, 572 WindowManager.LayoutParams.TYPE_VOICE_INTERACTION, Gravity.TOP, true); 573 mWindow.getWindow().addFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); 574 initViews(); 575 mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT); 576 mWindow.setToken(mToken); 577 } 578 579 /** 580 * Last callback to the session as it is being finished. 581 */ 582 public void onDestroy() { 583 } 584 585 /** 586 * Hook in which to create the session's UI. 587 */ 588 public View onCreateContentView() { 589 return null; 590 } 591 592 public void setContentView(View view) { 593 mContentFrame.removeAllViews(); 594 mContentFrame.addView(view, new FrameLayout.LayoutParams( 595 ViewGroup.LayoutParams.MATCH_PARENT, 596 ViewGroup.LayoutParams.WRAP_CONTENT)); 597 598 } 599 600 public boolean onKeyDown(int keyCode, KeyEvent event) { 601 return false; 602 } 603 604 public boolean onKeyLongPress(int keyCode, KeyEvent event) { 605 return false; 606 } 607 608 public boolean onKeyUp(int keyCode, KeyEvent event) { 609 return false; 610 } 611 612 public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) { 613 return false; 614 } 615 616 public void onBackPressed() { 617 finish(); 618 } 619 620 /** 621 * Sessions automatically watch for requests that all system UI be closed (such as when 622 * the user presses HOME), which will appear here. The default implementation always 623 * calls {@link #finish}. 624 */ 625 public void onCloseSystemDialogs() { 626 finish(); 627 } 628 629 /** 630 * Compute the interesting insets into your UI. The default implementation 631 * uses the entire window frame as the insets. The default touchable 632 * insets are {@link Insets#TOUCHABLE_INSETS_FRAME}. 633 * 634 * @param outInsets Fill in with the current UI insets. 635 */ 636 public void onComputeInsets(Insets outInsets) { 637 int[] loc = mTmpLocation; 638 View decor = getWindow().getWindow().getDecorView(); 639 decor.getLocationInWindow(loc); 640 outInsets.contentInsets.top = 0; 641 outInsets.contentInsets.left = 0; 642 outInsets.contentInsets.right = 0; 643 outInsets.contentInsets.bottom = 0; 644 outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_FRAME; 645 outInsets.touchableRegion.setEmpty(); 646 } 647 648 /** 649 * Called when a task initiated by {@link #startVoiceActivity(android.content.Intent)} 650 * has actually started. 651 * 652 * @param intent The original {@link Intent} supplied to 653 * {@link #startVoiceActivity(android.content.Intent)}. 654 * @param taskId Unique ID of the now running task. 655 */ 656 public void onTaskStarted(Intent intent, int taskId) { 657 } 658 659 /** 660 * Called when the last activity of a task initiated by 661 * {@link #startVoiceActivity(android.content.Intent)} has finished. The default 662 * implementation calls {@link #finish()} on the assumption that this represents 663 * the completion of a voice action. You can override the implementation if you would 664 * like a different behavior. 665 * 666 * @param intent The original {@link Intent} supplied to 667 * {@link #startVoiceActivity(android.content.Intent)}. 668 * @param taskId Unique ID of the finished task. 669 */ 670 public void onTaskFinished(Intent intent, int taskId) { 671 finish(); 672 } 673 674 /** 675 * Request to query for what extended commands the session supports. 676 * 677 * @param caller Who is making the request. 678 * @param commands An array of commands that are being queried. 679 * @return Return an array of booleans indicating which of each entry in the 680 * command array is supported. A true entry in the array indicates the command 681 * is supported; false indicates it is not. The default implementation returns 682 * an array of all false entries. 683 */ 684 public boolean[] onGetSupportedCommands(Caller caller, String[] commands) { 685 return new boolean[commands.length]; 686 } 687 688 /** 689 * Request to confirm with the user before proceeding with an unrecoverable operation, 690 * corresponding to a {@link android.app.VoiceInteractor.ConfirmationRequest 691 * VoiceInteractor.ConfirmationRequest}. 692 * 693 * @param caller Who is making the request. 694 * @param request The active request. 695 * @param prompt The prompt informing the user of what will happen, as per 696 * {@link android.app.VoiceInteractor.ConfirmationRequest VoiceInteractor.ConfirmationRequest}. 697 * @param extras Any additional information, as per 698 * {@link android.app.VoiceInteractor.ConfirmationRequest VoiceInteractor.ConfirmationRequest}. 699 */ 700 public abstract void onConfirm(Caller caller, Request request, CharSequence prompt, 701 Bundle extras); 702 703 /** 704 * Request to abort the voice interaction session because the voice activity can not 705 * complete its interaction using voice. Corresponds to 706 * {@link android.app.VoiceInteractor.AbortVoiceRequest 707 * VoiceInteractor.AbortVoiceRequest}. The default implementation just sends an empty 708 * confirmation back to allow the activity to exit. 709 * 710 * @param caller Who is making the request. 711 * @param request The active request. 712 * @param message The message informing the user of the problem, as per 713 * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}. 714 * @param extras Any additional information, as per 715 * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}. 716 */ 717 public void onAbortVoice(Caller caller, Request request, CharSequence message, Bundle extras) { 718 request.sendAbortVoiceResult(null); 719 } 720 721 /** 722 * Process an arbitrary extended command from the caller, 723 * corresponding to a {@link android.app.VoiceInteractor.CommandRequest 724 * VoiceInteractor.CommandRequest}. 725 * 726 * @param caller Who is making the request. 727 * @param request The active request. 728 * @param command The command that is being executed, as per 729 * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}. 730 * @param extras Any additional information, as per 731 * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}. 732 */ 733 public abstract void onCommand(Caller caller, Request request, String command, Bundle extras); 734 735 /** 736 * Called when the {@link android.app.VoiceInteractor} has asked to cancel a {@link Request} 737 * that was previously delivered to {@link #onConfirm} or {@link #onCommand}. 738 * 739 * @param request The request that is being canceled. 740 */ 741 public abstract void onCancel(Request request); 742} 743