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