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