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