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