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