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