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