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