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