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