VoiceInteractionSession.java revision 344744b49633a7f8aa0c8e43469bbfcf9d8e2cab
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.annotation.Nullable; 20import android.app.Activity; 21import android.app.Dialog; 22import android.app.Instrumentation; 23import android.app.VoiceInteractor; 24import android.app.assist.AssistContent; 25import android.app.assist.AssistStructure; 26import android.content.ComponentCallbacks2; 27import android.content.Context; 28import android.content.Intent; 29import android.content.res.Configuration; 30import android.content.res.TypedArray; 31import android.graphics.Bitmap; 32import android.graphics.Rect; 33import android.graphics.Region; 34import android.inputmethodservice.SoftInputWindow; 35import android.os.Binder; 36import android.os.Bundle; 37import android.os.Handler; 38import android.os.IBinder; 39import android.os.Message; 40import android.os.RemoteException; 41import android.os.UserHandle; 42import android.util.ArrayMap; 43import android.util.DebugUtils; 44import android.util.Log; 45import android.view.Gravity; 46import android.view.KeyEvent; 47import android.view.LayoutInflater; 48import android.view.View; 49import android.view.ViewGroup; 50import android.view.ViewTreeObserver; 51import android.view.WindowManager; 52import android.widget.FrameLayout; 53 54import com.android.internal.app.IVoiceInteractionManagerService; 55import com.android.internal.app.IVoiceInteractionSessionShowCallback; 56import com.android.internal.app.IVoiceInteractor; 57import com.android.internal.app.IVoiceInteractorCallback; 58import com.android.internal.app.IVoiceInteractorRequest; 59import com.android.internal.os.HandlerCaller; 60import com.android.internal.os.SomeArgs; 61 62import java.io.FileDescriptor; 63import java.io.PrintWriter; 64import java.lang.ref.WeakReference; 65 66import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 67 68/** 69 * An active voice interaction session, providing a facility for the implementation 70 * to interact with the user in the voice interaction layer. The user interface is 71 * initially shown by default, and can be created be overriding {@link #onCreateContentView()} 72 * in which the UI can be built. 73 * 74 * <p>A voice interaction session can be self-contained, ultimately calling {@link #finish} 75 * when done. It can also initiate voice interactions with applications by calling 76 * {@link #startVoiceActivity}</p>. 77 */ 78public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCallbacks2 { 79 static final String TAG = "VoiceInteractionSession"; 80 static final boolean DEBUG = true; 81 82 /** 83 * Flag received in {@link #onShow}: originator requested that the session be started with 84 * assist data from the currently focused activity. 85 */ 86 public static final int SHOW_WITH_ASSIST = 1<<0; 87 88 /** 89 * Flag received in {@link #onShow}: originator requested that the session be started with 90 * a screen shot of the currently focused activity. 91 */ 92 public static final int SHOW_WITH_SCREENSHOT = 1<<1; 93 94 /** 95 * Flag for use with {@link #onShow}: indicates that the session has been started from the 96 * system assist gesture. 97 */ 98 public static final int SHOW_SOURCE_ASSIST_GESTURE = 1<<2; 99 100 /** 101 * Flag for use with {@link #onShow}: indicates that the application itself has invoked 102 * the assistant. 103 */ 104 public static final int SHOW_SOURCE_APPLICATION = 1<<3; 105 106 /** 107 * Flag for use with {@link #onShow}: indicates that an Activity has invoked the voice 108 * interaction service for a local interaction using 109 * {@link Activity#startLocalVoiceInteraction(Bundle)}. 110 */ 111 public static final int SHOW_SOURCE_ACTIVITY = 1<<4; 112 113 final Context mContext; 114 final HandlerCaller mHandlerCaller; 115 116 final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState(); 117 118 IVoiceInteractionManagerService mSystemService; 119 IBinder mToken; 120 121 int mTheme = 0; 122 LayoutInflater mInflater; 123 TypedArray mThemeAttrs; 124 View mRootView; 125 FrameLayout mContentFrame; 126 SoftInputWindow mWindow; 127 128 boolean mInitialized; 129 boolean mWindowAdded; 130 boolean mWindowVisible; 131 boolean mWindowWasVisible; 132 boolean mInShowWindow; 133 134 final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<IBinder, Request>(); 135 136 final Insets mTmpInsets = new Insets(); 137 138 final WeakReference<VoiceInteractionSession> mWeakRef 139 = new WeakReference<VoiceInteractionSession>(this); 140 141 final IVoiceInteractor mInteractor = new IVoiceInteractor.Stub() { 142 @Override 143 public IVoiceInteractorRequest startConfirmation(String callingPackage, 144 IVoiceInteractorCallback callback, VoiceInteractor.Prompt prompt, Bundle extras) { 145 ConfirmationRequest request = new ConfirmationRequest(callingPackage, 146 Binder.getCallingUid(), callback, VoiceInteractionSession.this, 147 prompt, extras); 148 addRequest(request); 149 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_CONFIRMATION, 150 request)); 151 return request.mInterface; 152 } 153 154 @Override 155 public IVoiceInteractorRequest startPickOption(String callingPackage, 156 IVoiceInteractorCallback callback, VoiceInteractor.Prompt prompt, 157 VoiceInteractor.PickOptionRequest.Option[] options, Bundle extras) { 158 PickOptionRequest request = new PickOptionRequest(callingPackage, 159 Binder.getCallingUid(), callback, VoiceInteractionSession.this, 160 prompt, options, extras); 161 addRequest(request); 162 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_PICK_OPTION, 163 request)); 164 return request.mInterface; 165 } 166 167 @Override 168 public IVoiceInteractorRequest startCompleteVoice(String callingPackage, 169 IVoiceInteractorCallback callback, VoiceInteractor.Prompt message, Bundle extras) { 170 CompleteVoiceRequest request = new CompleteVoiceRequest(callingPackage, 171 Binder.getCallingUid(), callback, VoiceInteractionSession.this, 172 message, extras); 173 addRequest(request); 174 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_COMPLETE_VOICE, 175 request)); 176 return request.mInterface; 177 } 178 179 @Override 180 public IVoiceInteractorRequest startAbortVoice(String callingPackage, 181 IVoiceInteractorCallback callback, VoiceInteractor.Prompt message, Bundle extras) { 182 AbortVoiceRequest request = new AbortVoiceRequest(callingPackage, 183 Binder.getCallingUid(), callback, VoiceInteractionSession.this, 184 message, extras); 185 addRequest(request); 186 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_ABORT_VOICE, 187 request)); 188 return request.mInterface; 189 } 190 191 @Override 192 public IVoiceInteractorRequest startCommand(String callingPackage, 193 IVoiceInteractorCallback callback, String command, Bundle extras) { 194 CommandRequest request = new CommandRequest(callingPackage, 195 Binder.getCallingUid(), callback, VoiceInteractionSession.this, 196 command, extras); 197 addRequest(request); 198 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_COMMAND, 199 request)); 200 return request.mInterface; 201 } 202 203 @Override 204 public boolean[] supportsCommands(String callingPackage, String[] commands) { 205 Message msg = mHandlerCaller.obtainMessageIOO(MSG_SUPPORTS_COMMANDS, 206 0, commands, null); 207 SomeArgs args = mHandlerCaller.sendMessageAndWait(msg); 208 if (args != null) { 209 boolean[] res = (boolean[])args.arg1; 210 args.recycle(); 211 return res; 212 } 213 return new boolean[commands.length]; 214 } 215 }; 216 217 final IVoiceInteractionSession mSession = new IVoiceInteractionSession.Stub() { 218 @Override 219 public void show(Bundle sessionArgs, int flags, 220 IVoiceInteractionSessionShowCallback showCallback) { 221 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOO(MSG_SHOW, 222 flags, sessionArgs, showCallback)); 223 } 224 225 @Override 226 public void hide() { 227 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_HIDE)); 228 } 229 230 @Override 231 public void handleAssist(final Bundle data, final AssistStructure structure, 232 final AssistContent content) { 233 // We want to pre-warm the AssistStructure before handing it off to the main 234 // thread. We also want to do this on a separate thread, so that if the app 235 // is for some reason slow (due to slow filling in of async children in the 236 // structure), we don't block other incoming IPCs (such as the screenshot) to 237 // us (since we are a oneway interface, they get serialized). (Okay?) 238 Thread retriever = new Thread("AssistStructure retriever") { 239 @Override 240 public void run() { 241 Throwable failure = null; 242 if (structure != null) { 243 try { 244 structure.ensureData(); 245 } catch (Throwable e) { 246 Log.w(TAG, "Failure retrieving AssistStructure", e); 247 failure = e; 248 } 249 } 250 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_HANDLE_ASSIST, 251 data, failure == null ? structure : null, failure, content)); 252 } 253 }; 254 retriever.start(); 255 } 256 257 @Override 258 public void handleScreenshot(Bitmap screenshot) { 259 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_HANDLE_SCREENSHOT, 260 screenshot)); 261 } 262 263 @Override 264 public void taskStarted(Intent intent, int taskId) { 265 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_TASK_STARTED, 266 taskId, intent)); 267 } 268 269 @Override 270 public void taskFinished(Intent intent, int taskId) { 271 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_TASK_FINISHED, 272 taskId, intent)); 273 } 274 275 @Override 276 public void closeSystemDialogs() { 277 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_CLOSE_SYSTEM_DIALOGS)); 278 } 279 280 @Override 281 public void onLockscreenShown() { 282 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_ON_LOCKSCREEN_SHOWN)); 283 } 284 285 @Override 286 public void destroy() { 287 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_DESTROY)); 288 } 289 }; 290 291 /** 292 * Base class representing a request from a voice-driver app to perform a particular 293 * voice operation with the user. See related subclasses for the types of requests 294 * that are possible. 295 */ 296 public static class Request { 297 final IVoiceInteractorRequest mInterface = new IVoiceInteractorRequest.Stub() { 298 @Override 299 public void cancel() throws RemoteException { 300 VoiceInteractionSession session = mSession.get(); 301 if (session != null) { 302 session.mHandlerCaller.sendMessage( 303 session.mHandlerCaller.obtainMessageO(MSG_CANCEL, Request.this)); 304 } 305 } 306 }; 307 final String mCallingPackage; 308 final int mCallingUid; 309 final IVoiceInteractorCallback mCallback; 310 final WeakReference<VoiceInteractionSession> mSession; 311 final Bundle mExtras; 312 313 Request(String packageName, int uid, IVoiceInteractorCallback callback, 314 VoiceInteractionSession session, Bundle extras) { 315 mCallingPackage = packageName; 316 mCallingUid = uid; 317 mCallback = callback; 318 mSession = session.mWeakRef; 319 mExtras = extras; 320 } 321 322 /** 323 * Return the uid of the application that initiated the request. 324 */ 325 public int getCallingUid() { 326 return mCallingUid; 327 } 328 329 /** 330 * Return the package name of the application that initiated the request. 331 */ 332 public String getCallingPackage() { 333 return mCallingPackage; 334 } 335 336 /** 337 * Return any additional extra information that was supplied as part of the request. 338 */ 339 public Bundle getExtras() { 340 return mExtras; 341 } 342 343 /** 344 * Check whether this request is currently active. A request becomes inactive after 345 * calling {@link #cancel} or a final result method that completes the request. After 346 * this point, further interactions with the request will result in 347 * {@link java.lang.IllegalStateException} errors; you should not catch these errors, 348 * but can use this method if you need to determine the state of the request. Returns 349 * true if the request is still active. 350 */ 351 public boolean isActive() { 352 VoiceInteractionSession session = mSession.get(); 353 if (session == null) { 354 return false; 355 } 356 return session.isRequestActive(mInterface.asBinder()); 357 } 358 359 void finishRequest() { 360 VoiceInteractionSession session = mSession.get(); 361 if (session == null) { 362 throw new IllegalStateException("VoiceInteractionSession has been destroyed"); 363 } 364 Request req = session.removeRequest(mInterface.asBinder()); 365 if (req == null) { 366 throw new IllegalStateException("Request not active: " + this); 367 } else if (req != this) { 368 throw new IllegalStateException("Current active request " + req 369 + " not same as calling request " + this); 370 } 371 } 372 373 /** 374 * Ask the app to cancel this current request. 375 * This also finishes the request (it is no longer active). 376 */ 377 public void cancel() { 378 try { 379 if (DEBUG) Log.d(TAG, "sendCancelResult: req=" + mInterface); 380 finishRequest(); 381 mCallback.deliverCancel(mInterface); 382 } catch (RemoteException e) { 383 } 384 } 385 386 @Override 387 public String toString() { 388 StringBuilder sb = new StringBuilder(128); 389 DebugUtils.buildShortClassTag(this, sb); 390 sb.append(" "); 391 sb.append(mInterface.asBinder()); 392 sb.append(" pkg="); 393 sb.append(mCallingPackage); 394 sb.append(" uid="); 395 UserHandle.formatUid(sb, mCallingUid); 396 sb.append('}'); 397 return sb.toString(); 398 } 399 400 void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 401 writer.print(prefix); writer.print("mInterface="); 402 writer.println(mInterface.asBinder()); 403 writer.print(prefix); writer.print("mCallingPackage="); writer.print(mCallingPackage); 404 writer.print(" mCallingUid="); UserHandle.formatUid(writer, mCallingUid); 405 writer.println(); 406 writer.print(prefix); writer.print("mCallback="); 407 writer.println(mCallback.asBinder()); 408 if (mExtras != null) { 409 writer.print(prefix); writer.print("mExtras="); 410 writer.println(mExtras); 411 } 412 } 413 } 414 415 /** 416 * A request for confirmation from the user of an operation, as per 417 * {@link android.app.VoiceInteractor.ConfirmationRequest 418 * VoiceInteractor.ConfirmationRequest}. 419 */ 420 public static final class ConfirmationRequest extends Request { 421 final VoiceInteractor.Prompt mPrompt; 422 423 ConfirmationRequest(String packageName, int uid, IVoiceInteractorCallback callback, 424 VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras) { 425 super(packageName, uid, callback, session, extras); 426 mPrompt = prompt; 427 } 428 429 /** 430 * Return the prompt informing the user of what will happen, as per 431 * {@link android.app.VoiceInteractor.ConfirmationRequest 432 * VoiceInteractor.ConfirmationRequest}. 433 */ 434 @Nullable 435 public VoiceInteractor.Prompt getVoicePrompt() { 436 return mPrompt; 437 } 438 439 /** 440 * Return the prompt informing the user of what will happen, as per 441 * {@link android.app.VoiceInteractor.ConfirmationRequest 442 * VoiceInteractor.ConfirmationRequest}. 443 * @deprecated Prefer {@link #getVoicePrompt()} which allows multiple voice prompts. 444 */ 445 @Nullable 446 public CharSequence getPrompt() { 447 return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null); 448 } 449 450 /** 451 * Report that the voice interactor has confirmed the operation with the user, resulting 452 * in a call to 453 * {@link android.app.VoiceInteractor.ConfirmationRequest#onConfirmationResult 454 * VoiceInteractor.ConfirmationRequest.onConfirmationResult}. 455 * This finishes the request (it is no longer active). 456 */ 457 public void sendConfirmationResult(boolean confirmed, Bundle result) { 458 try { 459 if (DEBUG) Log.d(TAG, "sendConfirmationResult: req=" + mInterface 460 + " confirmed=" + confirmed + " result=" + result); 461 finishRequest(); 462 mCallback.deliverConfirmationResult(mInterface, confirmed, result); 463 } catch (RemoteException e) { 464 } 465 } 466 467 void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 468 super.dump(prefix, fd, writer, args); 469 writer.print(prefix); writer.print("mPrompt="); 470 writer.println(mPrompt); 471 } 472 } 473 474 /** 475 * A request for the user to pick from a set of option, as per 476 * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}. 477 */ 478 public static final class PickOptionRequest extends Request { 479 final VoiceInteractor.Prompt mPrompt; 480 final VoiceInteractor.PickOptionRequest.Option[] mOptions; 481 482 PickOptionRequest(String packageName, int uid, IVoiceInteractorCallback callback, 483 VoiceInteractionSession session, VoiceInteractor.Prompt prompt, 484 VoiceInteractor.PickOptionRequest.Option[] options, Bundle extras) { 485 super(packageName, uid, callback, session, extras); 486 mPrompt = prompt; 487 mOptions = options; 488 } 489 490 /** 491 * Return the prompt informing the user of what they are picking, as per 492 * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}. 493 */ 494 @Nullable 495 public VoiceInteractor.Prompt getVoicePrompt() { 496 return mPrompt; 497 } 498 499 /** 500 * Return the prompt informing the user of what they are picking, as per 501 * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}. 502 * @deprecated Prefer {@link #getVoicePrompt()} which allows multiple voice prompts. 503 */ 504 @Nullable 505 public CharSequence getPrompt() { 506 return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null); 507 } 508 509 /** 510 * Return the set of options the user is picking from, as per 511 * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}. 512 */ 513 public VoiceInteractor.PickOptionRequest.Option[] getOptions() { 514 return mOptions; 515 } 516 517 void sendPickOptionResult(boolean finished, 518 VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) { 519 try { 520 if (DEBUG) Log.d(TAG, "sendPickOptionResult: req=" + mInterface 521 + " finished=" + finished + " selections=" + selections 522 + " result=" + result); 523 if (finished) { 524 finishRequest(); 525 } 526 mCallback.deliverPickOptionResult(mInterface, finished, selections, result); 527 } catch (RemoteException e) { 528 } 529 } 530 531 /** 532 * Report an intermediate option selection from the request, without completing it (the 533 * request is still active and the app is waiting for the final option selection), 534 * resulting in a call to 535 * {@link android.app.VoiceInteractor.PickOptionRequest#onPickOptionResult 536 * VoiceInteractor.PickOptionRequest.onPickOptionResult} with false for finished. 537 */ 538 public void sendIntermediatePickOptionResult( 539 VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) { 540 sendPickOptionResult(false, selections, result); 541 } 542 543 /** 544 * Report the final option selection for the request, completing the request 545 * and resulting in a call to 546 * {@link android.app.VoiceInteractor.PickOptionRequest#onPickOptionResult 547 * VoiceInteractor.PickOptionRequest.onPickOptionResult} with false for finished. 548 * This finishes the request (it is no longer active). 549 */ 550 public void sendPickOptionResult( 551 VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) { 552 sendPickOptionResult(true, selections, result); 553 } 554 555 void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 556 super.dump(prefix, fd, writer, args); 557 writer.print(prefix); writer.print("mPrompt="); 558 writer.println(mPrompt); 559 if (mOptions != null) { 560 writer.print(prefix); writer.println("Options:"); 561 for (int i=0; i<mOptions.length; i++) { 562 VoiceInteractor.PickOptionRequest.Option op = mOptions[i]; 563 writer.print(prefix); writer.print(" #"); writer.print(i); writer.println(":"); 564 writer.print(prefix); writer.print(" mLabel="); 565 writer.println(op.getLabel()); 566 writer.print(prefix); writer.print(" mIndex="); 567 writer.println(op.getIndex()); 568 if (op.countSynonyms() > 0) { 569 writer.print(prefix); writer.println(" Synonyms:"); 570 for (int j=0; j<op.countSynonyms(); j++) { 571 writer.print(prefix); writer.print(" #"); writer.print(j); 572 writer.print(": "); writer.println(op.getSynonymAt(j)); 573 } 574 } 575 if (op.getExtras() != null) { 576 writer.print(prefix); writer.print(" mExtras="); 577 writer.println(op.getExtras()); 578 } 579 } 580 } 581 } 582 } 583 584 /** 585 * A request to simply inform the user that the voice operation has completed, as per 586 * {@link android.app.VoiceInteractor.CompleteVoiceRequest 587 * VoiceInteractor.CompleteVoiceRequest}. 588 */ 589 public static final class CompleteVoiceRequest extends Request { 590 final VoiceInteractor.Prompt mPrompt; 591 592 CompleteVoiceRequest(String packageName, int uid, IVoiceInteractorCallback callback, 593 VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras) { 594 super(packageName, uid, callback, session, extras); 595 mPrompt = prompt; 596 } 597 598 /** 599 * Return the message informing the user of the completion, as per 600 * {@link android.app.VoiceInteractor.CompleteVoiceRequest 601 * VoiceInteractor.CompleteVoiceRequest}. 602 */ 603 @Nullable 604 public VoiceInteractor.Prompt getVoicePrompt() { 605 return mPrompt; 606 } 607 608 /** 609 * Return the message informing the user of the completion, as per 610 * {@link android.app.VoiceInteractor.CompleteVoiceRequest 611 * VoiceInteractor.CompleteVoiceRequest}. 612 * @deprecated Prefer {@link #getVoicePrompt()} which allows a separate visual message. 613 */ 614 @Nullable 615 public CharSequence getMessage() { 616 return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null); 617 } 618 619 /** 620 * Report that the voice interactor has finished completing the voice operation, resulting 621 * in a call to 622 * {@link android.app.VoiceInteractor.CompleteVoiceRequest#onCompleteResult 623 * VoiceInteractor.CompleteVoiceRequest.onCompleteResult}. 624 * This finishes the request (it is no longer active). 625 */ 626 public void sendCompleteResult(Bundle result) { 627 try { 628 if (DEBUG) Log.d(TAG, "sendCompleteVoiceResult: req=" + mInterface 629 + " result=" + result); 630 finishRequest(); 631 mCallback.deliverCompleteVoiceResult(mInterface, result); 632 } catch (RemoteException e) { 633 } 634 } 635 636 void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 637 super.dump(prefix, fd, writer, args); 638 writer.print(prefix); writer.print("mPrompt="); 639 writer.println(mPrompt); 640 } 641 } 642 643 /** 644 * A request to report that the current user interaction can not be completed with voice, as per 645 * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}. 646 */ 647 public static final class AbortVoiceRequest extends Request { 648 final VoiceInteractor.Prompt mPrompt; 649 650 AbortVoiceRequest(String packageName, int uid, IVoiceInteractorCallback callback, 651 VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras) { 652 super(packageName, uid, callback, session, extras); 653 mPrompt = prompt; 654 } 655 656 /** 657 * Return the message informing the user of the problem, as per 658 * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}. 659 */ 660 @Nullable 661 public VoiceInteractor.Prompt getVoicePrompt() { 662 return mPrompt; 663 } 664 665 /** 666 * Return the message informing the user of the problem, as per 667 * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}. 668 * @deprecated Prefer {@link #getVoicePrompt()} which allows a separate visual message. 669 */ 670 @Nullable 671 public CharSequence getMessage() { 672 return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null); 673 } 674 675 /** 676 * Report that the voice interactor has finished aborting the voice operation, resulting 677 * in a call to 678 * {@link android.app.VoiceInteractor.AbortVoiceRequest#onAbortResult 679 * VoiceInteractor.AbortVoiceRequest.onAbortResult}. This finishes the request (it 680 * is no longer active). 681 */ 682 public void sendAbortResult(Bundle result) { 683 try { 684 if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface 685 + " result=" + result); 686 finishRequest(); 687 mCallback.deliverAbortVoiceResult(mInterface, result); 688 } catch (RemoteException e) { 689 } 690 } 691 692 void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 693 super.dump(prefix, fd, writer, args); 694 writer.print(prefix); writer.print("mPrompt="); 695 writer.println(mPrompt); 696 } 697 } 698 699 /** 700 * A generic vendor-specific request, as per 701 * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}. 702 */ 703 public static final class CommandRequest extends Request { 704 final String mCommand; 705 706 CommandRequest(String packageName, int uid, IVoiceInteractorCallback callback, 707 VoiceInteractionSession session, String command, Bundle extras) { 708 super(packageName, uid, callback, session, extras); 709 mCommand = command; 710 } 711 712 /** 713 * Return the command that is being executed, as per 714 * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}. 715 */ 716 public String getCommand() { 717 return mCommand; 718 } 719 720 void sendCommandResult(boolean finished, Bundle result) { 721 try { 722 if (DEBUG) Log.d(TAG, "sendCommandResult: req=" + mInterface 723 + " result=" + result); 724 if (finished) { 725 finishRequest(); 726 } 727 mCallback.deliverCommandResult(mInterface, finished, result); 728 } catch (RemoteException e) { 729 } 730 } 731 732 /** 733 * Report an intermediate result of the request, without completing it (the request 734 * is still active and the app is waiting for the final result), resulting in a call to 735 * {@link android.app.VoiceInteractor.CommandRequest#onCommandResult 736 * VoiceInteractor.CommandRequest.onCommandResult} with false for isCompleted. 737 */ 738 public void sendIntermediateResult(Bundle result) { 739 sendCommandResult(false, result); 740 } 741 742 /** 743 * Report the final result of the request, completing the request and resulting in a call to 744 * {@link android.app.VoiceInteractor.CommandRequest#onCommandResult 745 * VoiceInteractor.CommandRequest.onCommandResult} with true for isCompleted. 746 * This finishes the request (it is no longer active). 747 */ 748 public void sendResult(Bundle result) { 749 sendCommandResult(true, result); 750 } 751 752 void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 753 super.dump(prefix, fd, writer, args); 754 writer.print(prefix); writer.print("mCommand="); 755 writer.println(mCommand); 756 } 757 } 758 759 static final int MSG_START_CONFIRMATION = 1; 760 static final int MSG_START_PICK_OPTION = 2; 761 static final int MSG_START_COMPLETE_VOICE = 3; 762 static final int MSG_START_ABORT_VOICE = 4; 763 static final int MSG_START_COMMAND = 5; 764 static final int MSG_SUPPORTS_COMMANDS = 6; 765 static final int MSG_CANCEL = 7; 766 767 static final int MSG_TASK_STARTED = 100; 768 static final int MSG_TASK_FINISHED = 101; 769 static final int MSG_CLOSE_SYSTEM_DIALOGS = 102; 770 static final int MSG_DESTROY = 103; 771 static final int MSG_HANDLE_ASSIST = 104; 772 static final int MSG_HANDLE_SCREENSHOT = 105; 773 static final int MSG_SHOW = 106; 774 static final int MSG_HIDE = 107; 775 static final int MSG_ON_LOCKSCREEN_SHOWN = 108; 776 777 class MyCallbacks implements HandlerCaller.Callback, SoftInputWindow.Callback { 778 @Override 779 public void executeMessage(Message msg) { 780 SomeArgs args = null; 781 switch (msg.what) { 782 case MSG_START_CONFIRMATION: 783 if (DEBUG) Log.d(TAG, "onConfirm: req=" + msg.obj); 784 onRequestConfirmation((ConfirmationRequest) msg.obj); 785 break; 786 case MSG_START_PICK_OPTION: 787 if (DEBUG) Log.d(TAG, "onPickOption: req=" + msg.obj); 788 onRequestPickOption((PickOptionRequest) msg.obj); 789 break; 790 case MSG_START_COMPLETE_VOICE: 791 if (DEBUG) Log.d(TAG, "onCompleteVoice: req=" + msg.obj); 792 onRequestCompleteVoice((CompleteVoiceRequest) msg.obj); 793 break; 794 case MSG_START_ABORT_VOICE: 795 if (DEBUG) Log.d(TAG, "onAbortVoice: req=" + msg.obj); 796 onRequestAbortVoice((AbortVoiceRequest) msg.obj); 797 break; 798 case MSG_START_COMMAND: 799 if (DEBUG) Log.d(TAG, "onCommand: req=" + msg.obj); 800 onRequestCommand((CommandRequest) msg.obj); 801 break; 802 case MSG_SUPPORTS_COMMANDS: 803 args = (SomeArgs)msg.obj; 804 if (DEBUG) Log.d(TAG, "onGetSupportedCommands: cmds=" + args.arg1); 805 args.arg1 = onGetSupportedCommands((String[]) args.arg1); 806 args.complete(); 807 args = null; 808 break; 809 case MSG_CANCEL: 810 if (DEBUG) Log.d(TAG, "onCancel: req=" + ((Request)msg.obj)); 811 onCancelRequest((Request) msg.obj); 812 break; 813 case MSG_TASK_STARTED: 814 if (DEBUG) Log.d(TAG, "onTaskStarted: intent=" + msg.obj 815 + " taskId=" + msg.arg1); 816 onTaskStarted((Intent) msg.obj, msg.arg1); 817 break; 818 case MSG_TASK_FINISHED: 819 if (DEBUG) Log.d(TAG, "onTaskFinished: intent=" + msg.obj 820 + " taskId=" + msg.arg1); 821 onTaskFinished((Intent) msg.obj, msg.arg1); 822 break; 823 case MSG_CLOSE_SYSTEM_DIALOGS: 824 if (DEBUG) Log.d(TAG, "onCloseSystemDialogs"); 825 onCloseSystemDialogs(); 826 break; 827 case MSG_DESTROY: 828 if (DEBUG) Log.d(TAG, "doDestroy"); 829 doDestroy(); 830 break; 831 case MSG_HANDLE_ASSIST: 832 args = (SomeArgs)msg.obj; 833 if (DEBUG) Log.d(TAG, "onHandleAssist: data=" + args.arg1 834 + " structure=" + args.arg2 + " content=" + args.arg3); 835 doOnHandleAssist((Bundle) args.arg1, (AssistStructure) args.arg2, 836 (Throwable) args.arg3, (AssistContent) args.arg4); 837 break; 838 case MSG_HANDLE_SCREENSHOT: 839 if (DEBUG) Log.d(TAG, "onHandleScreenshot: " + msg.obj); 840 onHandleScreenshot((Bitmap) msg.obj); 841 break; 842 case MSG_SHOW: 843 args = (SomeArgs)msg.obj; 844 if (DEBUG) Log.d(TAG, "doShow: args=" + args.arg1 845 + " flags=" + msg.arg1 846 + " showCallback=" + args.arg2); 847 doShow((Bundle) args.arg1, msg.arg1, 848 (IVoiceInteractionSessionShowCallback) args.arg2); 849 break; 850 case MSG_HIDE: 851 if (DEBUG) Log.d(TAG, "doHide"); 852 doHide(); 853 break; 854 case MSG_ON_LOCKSCREEN_SHOWN: 855 if (DEBUG) Log.d(TAG, "onLockscreenShown"); 856 onLockscreenShown(); 857 break; 858 } 859 if (args != null) { 860 args.recycle(); 861 } 862 } 863 864 @Override 865 public void onBackPressed() { 866 VoiceInteractionSession.this.onBackPressed(); 867 } 868 } 869 870 final MyCallbacks mCallbacks = new MyCallbacks(); 871 872 /** 873 * Information about where interesting parts of the input method UI appear. 874 */ 875 public static final class Insets { 876 /** 877 * This is the part of the UI that is the main content. It is 878 * used to determine the basic space needed, to resize/pan the 879 * application behind. It is assumed that this inset does not 880 * change very much, since any change will cause a full resize/pan 881 * of the application behind. This value is relative to the top edge 882 * of the input method window. 883 */ 884 public final Rect contentInsets = new Rect(); 885 886 /** 887 * This is the region of the UI that is touchable. It is used when 888 * {@link #touchableInsets} is set to {@link #TOUCHABLE_INSETS_REGION}. 889 * The region should be specified relative to the origin of the window frame. 890 */ 891 public final Region touchableRegion = new Region(); 892 893 /** 894 * Option for {@link #touchableInsets}: the entire window frame 895 * can be touched. 896 */ 897 public static final int TOUCHABLE_INSETS_FRAME 898 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME; 899 900 /** 901 * Option for {@link #touchableInsets}: the area inside of 902 * the content insets can be touched. 903 */ 904 public static final int TOUCHABLE_INSETS_CONTENT 905 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT; 906 907 /** 908 * Option for {@link #touchableInsets}: the region specified by 909 * {@link #touchableRegion} can be touched. 910 */ 911 public static final int TOUCHABLE_INSETS_REGION 912 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION; 913 914 /** 915 * Determine which area of the window is touchable by the user. May 916 * be one of: {@link #TOUCHABLE_INSETS_FRAME}, 917 * {@link #TOUCHABLE_INSETS_CONTENT}, or {@link #TOUCHABLE_INSETS_REGION}. 918 */ 919 public int touchableInsets; 920 } 921 922 final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = 923 new ViewTreeObserver.OnComputeInternalInsetsListener() { 924 public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) { 925 onComputeInsets(mTmpInsets); 926 info.contentInsets.set(mTmpInsets.contentInsets); 927 info.visibleInsets.set(mTmpInsets.contentInsets); 928 info.touchableRegion.set(mTmpInsets.touchableRegion); 929 info.setTouchableInsets(mTmpInsets.touchableInsets); 930 } 931 }; 932 933 public VoiceInteractionSession(Context context) { 934 this(context, new Handler()); 935 } 936 937 public VoiceInteractionSession(Context context, Handler handler) { 938 mContext = context; 939 mHandlerCaller = new HandlerCaller(context, handler.getLooper(), 940 mCallbacks, true); 941 } 942 943 public Context getContext() { 944 return mContext; 945 } 946 947 void addRequest(Request req) { 948 synchronized (this) { 949 mActiveRequests.put(req.mInterface.asBinder(), req); 950 } 951 } 952 953 boolean isRequestActive(IBinder reqInterface) { 954 synchronized (this) { 955 return mActiveRequests.containsKey(reqInterface); 956 } 957 } 958 959 Request removeRequest(IBinder reqInterface) { 960 synchronized (this) { 961 return mActiveRequests.remove(reqInterface); 962 } 963 } 964 965 void doCreate(IVoiceInteractionManagerService service, IBinder token) { 966 mSystemService = service; 967 mToken = token; 968 onCreate(); 969 } 970 971 void doShow(Bundle args, int flags, final IVoiceInteractionSessionShowCallback showCallback) { 972 if (DEBUG) Log.v(TAG, "Showing window: mWindowAdded=" + mWindowAdded 973 + " mWindowVisible=" + mWindowVisible); 974 975 if (mInShowWindow) { 976 Log.w(TAG, "Re-entrance in to showWindow"); 977 return; 978 } 979 980 try { 981 mInShowWindow = true; 982 if (!mWindowVisible) { 983 if (!mWindowAdded) { 984 mWindowAdded = true; 985 View v = onCreateContentView(); 986 if (v != null) { 987 setContentView(v); 988 } 989 } 990 } 991 onShow(args, flags); 992 if (!mWindowVisible) { 993 mWindowVisible = true; 994 mWindow.show(); 995 } 996 if (showCallback != null) { 997 mRootView.invalidate(); 998 mRootView.getViewTreeObserver().addOnPreDrawListener( 999 new ViewTreeObserver.OnPreDrawListener() { 1000 @Override 1001 public boolean onPreDraw() { 1002 mRootView.getViewTreeObserver().removeOnPreDrawListener(this); 1003 try { 1004 showCallback.onShown(); 1005 } catch (RemoteException e) { 1006 Log.w(TAG, "Error calling onShown", e); 1007 } 1008 return true; 1009 } 1010 }); 1011 } 1012 } finally { 1013 mWindowWasVisible = true; 1014 mInShowWindow = false; 1015 } 1016 } 1017 1018 void doHide() { 1019 if (mWindowVisible) { 1020 mWindow.hide(); 1021 mWindowVisible = false; 1022 onHide(); 1023 } 1024 } 1025 1026 void doDestroy() { 1027 onDestroy(); 1028 if (mInitialized) { 1029 mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener( 1030 mInsetsComputer); 1031 if (mWindowAdded) { 1032 mWindow.dismiss(); 1033 mWindowAdded = false; 1034 } 1035 mInitialized = false; 1036 } 1037 } 1038 1039 void initViews() { 1040 mInitialized = true; 1041 1042 mThemeAttrs = mContext.obtainStyledAttributes(android.R.styleable.VoiceInteractionSession); 1043 mRootView = mInflater.inflate( 1044 com.android.internal.R.layout.voice_interaction_session, null); 1045 mRootView.setSystemUiVisibility( 1046 View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 1047 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); 1048 mWindow.setContentView(mRootView); 1049 mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer); 1050 1051 mContentFrame = (FrameLayout)mRootView.findViewById(android.R.id.content); 1052 } 1053 1054 /** 1055 * Equivalent to {@link VoiceInteractionService#setDisabledShowContext 1056 * VoiceInteractionService.setDisabledShowContext(int)}. 1057 */ 1058 public void setDisabledShowContext(int flags) { 1059 try { 1060 mSystemService.setDisabledShowContext(flags); 1061 } catch (RemoteException e) { 1062 } 1063 } 1064 1065 /** 1066 * Equivalent to {@link VoiceInteractionService#getDisabledShowContext 1067 * VoiceInteractionService.getDisabledShowContext}. 1068 */ 1069 public int getDisabledShowContext() { 1070 try { 1071 return mSystemService.getDisabledShowContext(); 1072 } catch (RemoteException e) { 1073 return 0; 1074 } 1075 } 1076 1077 /** 1078 * Return which show context flags have been disabled by the user through the system 1079 * settings UI, so the session will never get this data. Returned flags are any combination of 1080 * {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST} and 1081 * {@link VoiceInteractionSession#SHOW_WITH_SCREENSHOT 1082 * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}. Note that this only tells you about 1083 * global user settings, not about restrictions that may be applied contextual based on 1084 * the current application the user is in or other transient states. 1085 */ 1086 public int getUserDisabledShowContext() { 1087 try { 1088 return mSystemService.getUserDisabledShowContext(); 1089 } catch (RemoteException e) { 1090 return 0; 1091 } 1092 } 1093 1094 /** 1095 * Show the UI for this session. This asks the system to go through the process of showing 1096 * your UI, which will eventually culminate in {@link #onShow}. This is similar to calling 1097 * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}. 1098 * @param args Arbitrary arguments that will be propagated {@link #onShow}. 1099 * @param flags Indicates additional optional behavior that should be performed. May 1100 * be any combination of 1101 * {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST} and 1102 * {@link VoiceInteractionSession#SHOW_WITH_SCREENSHOT 1103 * VoiceInteractionSession.SHOW_WITH_SCREENSHOT} 1104 * to request that the system generate and deliver assist data on the current foreground 1105 * app as part of showing the session UI. 1106 */ 1107 public void show(Bundle args, int flags) { 1108 if (mToken == null) { 1109 throw new IllegalStateException("Can't call before onCreate()"); 1110 } 1111 try { 1112 mSystemService.showSessionFromSession(mToken, args, flags); 1113 } catch (RemoteException e) { 1114 } 1115 } 1116 1117 /** 1118 * Hide the session's UI, if currently shown. Call this when you are done with your 1119 * user interaction. 1120 */ 1121 public void hide() { 1122 if (mToken == null) { 1123 throw new IllegalStateException("Can't call before onCreate()"); 1124 } 1125 try { 1126 mSystemService.hideSessionFromSession(mToken); 1127 } catch (RemoteException e) { 1128 } 1129 } 1130 1131 /** 1132 * You can call this to customize the theme used by your IME's window. 1133 * This must be set before {@link #onCreate}, so you 1134 * will typically call it in your constructor with the resource ID 1135 * of your custom theme. 1136 */ 1137 public void setTheme(int theme) { 1138 if (mWindow != null) { 1139 throw new IllegalStateException("Must be called before onCreate()"); 1140 } 1141 mTheme = theme; 1142 } 1143 1144 /** 1145 * Ask that a new activity be started for voice interaction. This will create a 1146 * new dedicated task in the activity manager for this voice interaction session; 1147 * this means that {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK} 1148 * will be set for you to make it a new task. 1149 * 1150 * <p>The newly started activity will be displayed to the user in a special way, as 1151 * a layer under the voice interaction UI.</p> 1152 * 1153 * <p>As the voice activity runs, it can retrieve a {@link android.app.VoiceInteractor} 1154 * through which it can perform voice interactions through your session. These requests 1155 * for voice interactions will appear as callbacks on {@link #onGetSupportedCommands}, 1156 * {@link #onRequestConfirmation}, {@link #onRequestPickOption}, 1157 * {@link #onRequestCompleteVoice}, {@link #onRequestAbortVoice}, 1158 * or {@link #onRequestCommand} 1159 * 1160 * <p>You will receive a call to {@link #onTaskStarted} when the task starts up 1161 * and {@link #onTaskFinished} when the last activity has finished. 1162 * 1163 * @param intent The Intent to start this voice interaction. The given Intent will 1164 * always have {@link Intent#CATEGORY_VOICE Intent.CATEGORY_VOICE} added to it, since 1165 * this is part of a voice interaction. 1166 */ 1167 public void startVoiceActivity(Intent intent) { 1168 if (mToken == null) { 1169 throw new IllegalStateException("Can't call before onCreate()"); 1170 } 1171 try { 1172 intent.migrateExtraStreamToClipData(); 1173 intent.prepareToLeaveProcess(mContext); 1174 int res = mSystemService.startVoiceActivity(mToken, intent, 1175 intent.resolveType(mContext.getContentResolver())); 1176 Instrumentation.checkStartActivityResult(res, intent); 1177 } catch (RemoteException e) { 1178 } 1179 } 1180 1181 /** 1182 * Set whether this session will keep the device awake while it is running a voice 1183 * activity. By default, the system holds a wake lock for it while in this state, 1184 * so that it can work even if the screen is off. Setting this to false removes that 1185 * wake lock, allowing the CPU to go to sleep. This is typically used if the 1186 * session decides it has been waiting too long for a response from the user and 1187 * doesn't want to let this continue to drain the battery. 1188 * 1189 * <p>Passing false here will release the wake lock, and you can call later with 1190 * true to re-acquire it. It will also be automatically re-acquired for you each 1191 * time you start a new voice activity task -- that is when you call 1192 * {@link #startVoiceActivity}.</p> 1193 */ 1194 public void setKeepAwake(boolean keepAwake) { 1195 if (mToken == null) { 1196 throw new IllegalStateException("Can't call before onCreate()"); 1197 } 1198 try { 1199 mSystemService.setKeepAwake(mToken, keepAwake); 1200 } catch (RemoteException e) { 1201 } 1202 } 1203 1204 /** 1205 * Request that all system dialogs (and status bar shade etc) be closed, allowing 1206 * access to the session's UI. This will <em>not</em> cause the lock screen to be 1207 * dismissed. 1208 */ 1209 public void closeSystemDialogs() { 1210 if (mToken == null) { 1211 throw new IllegalStateException("Can't call before onCreate()"); 1212 } 1213 try { 1214 mSystemService.closeSystemDialogs(mToken); 1215 } catch (RemoteException e) { 1216 } 1217 } 1218 1219 /** 1220 * Convenience for inflating views. 1221 */ 1222 public LayoutInflater getLayoutInflater() { 1223 return mInflater; 1224 } 1225 1226 /** 1227 * Retrieve the window being used to show the session's UI. 1228 */ 1229 public Dialog getWindow() { 1230 return mWindow; 1231 } 1232 1233 /** 1234 * Finish the session. This completely destroys the session -- the next time it is shown, 1235 * an entirely new one will be created. You do not normally call this function; instead, 1236 * use {@link #hide} and allow the system to destroy your session if it needs its RAM. 1237 */ 1238 public void finish() { 1239 if (mToken == null) { 1240 throw new IllegalStateException("Can't call before onCreate()"); 1241 } 1242 try { 1243 mSystemService.finish(mToken); 1244 } catch (RemoteException e) { 1245 } 1246 } 1247 1248 /** 1249 * Initiatize a new session. At this point you don't know exactly what this 1250 * session will be used for; you will find that out in {@link #onShow}. 1251 */ 1252 public void onCreate() { 1253 doOnCreate(); 1254 } 1255 1256 private void doOnCreate() { 1257 mTheme = mTheme != 0 ? mTheme 1258 : com.android.internal.R.style.Theme_DeviceDefault_VoiceInteractionSession; 1259 mInflater = (LayoutInflater)mContext.getSystemService( 1260 Context.LAYOUT_INFLATER_SERVICE); 1261 mWindow = new SoftInputWindow(mContext, "VoiceInteractionSession", mTheme, 1262 mCallbacks, this, mDispatcherState, 1263 WindowManager.LayoutParams.TYPE_VOICE_INTERACTION, Gravity.BOTTOM, true); 1264 mWindow.getWindow().addFlags( 1265 WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED | 1266 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | 1267 WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR); 1268 initViews(); 1269 mWindow.getWindow().setLayout(MATCH_PARENT, MATCH_PARENT); 1270 mWindow.setToken(mToken); 1271 } 1272 1273 /** 1274 * Called when the session UI is going to be shown. This is called after 1275 * {@link #onCreateContentView} (if the session's content UI needed to be created) and 1276 * immediately prior to the window being shown. This may be called while the window 1277 * is already shown, if a show request has come in while it is shown, to allow you to 1278 * update the UI to match the new show arguments. 1279 * 1280 * @param args The arguments that were supplied to 1281 * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}. 1282 * @param showFlags The show flags originally provided to 1283 * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}. 1284 */ 1285 public void onShow(Bundle args, int showFlags) { 1286 } 1287 1288 /** 1289 * Called immediately after stopping to show the session UI. 1290 */ 1291 public void onHide() { 1292 } 1293 1294 /** 1295 * Last callback to the session as it is being finished. 1296 */ 1297 public void onDestroy() { 1298 } 1299 1300 /** 1301 * Hook in which to create the session's UI. 1302 */ 1303 public View onCreateContentView() { 1304 return null; 1305 } 1306 1307 public void setContentView(View view) { 1308 mContentFrame.removeAllViews(); 1309 mContentFrame.addView(view, new FrameLayout.LayoutParams( 1310 ViewGroup.LayoutParams.MATCH_PARENT, 1311 ViewGroup.LayoutParams.MATCH_PARENT)); 1312 mContentFrame.requestApplyInsets(); 1313 } 1314 1315 void doOnHandleAssist(Bundle data, AssistStructure structure, Throwable failure, 1316 AssistContent content) { 1317 if (failure != null) { 1318 onAssistStructureFailure(failure); 1319 } 1320 onHandleAssist(data, structure, content); 1321 } 1322 1323 /** 1324 * Called when there has been a failure transferring the {@link AssistStructure} to 1325 * the assistant. This may happen, for example, if the data is too large and results 1326 * in an out of memory exception, or the client has provided corrupt data. This will 1327 * be called immediately before {@link #onHandleAssist} and the AssistStructure supplied 1328 * there afterwards will be null. 1329 * 1330 * @param failure The failure exception that was thrown when building the 1331 * {@link AssistStructure}. 1332 */ 1333 public void onAssistStructureFailure(Throwable failure) { 1334 } 1335 1336 /** 1337 * Called to receive data from the application that the user was currently viewing when 1338 * an assist session is started. If the original show request did not specify 1339 * {@link #SHOW_WITH_ASSIST}, this method will not be called. 1340 * 1341 * @param data Arbitrary data supplied by the app through 1342 * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}. 1343 * May be null if assist data has been disabled by the user or device policy. 1344 * @param structure If available, the structure definition of all windows currently 1345 * displayed by the app. May be null if assist data has been disabled by the user 1346 * or device policy; will be an empty stub if the application has disabled assist 1347 * by marking its window as secure. 1348 * @param content Additional content data supplied by the app through 1349 * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}. 1350 * May be null if assist data has been disabled by the user or device policy; will 1351 * not be automatically filled in with data from the app if the app has marked its 1352 * window as secure. 1353 */ 1354 public void onHandleAssist(@Nullable Bundle data, @Nullable AssistStructure structure, 1355 @Nullable AssistContent content) { 1356 } 1357 1358 /** 1359 * Called to receive a screenshot of what the user was currently viewing when an assist 1360 * session is started. May be null if screenshots are disabled by the user, policy, 1361 * or application. If the original show request did not specify 1362 * {@link #SHOW_WITH_SCREENSHOT}, this method will not be called. 1363 */ 1364 public void onHandleScreenshot(@Nullable Bitmap screenshot) { 1365 } 1366 1367 public boolean onKeyDown(int keyCode, KeyEvent event) { 1368 return false; 1369 } 1370 1371 public boolean onKeyLongPress(int keyCode, KeyEvent event) { 1372 return false; 1373 } 1374 1375 public boolean onKeyUp(int keyCode, KeyEvent event) { 1376 return false; 1377 } 1378 1379 public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) { 1380 return false; 1381 } 1382 1383 /** 1384 * Called when the user presses the back button while focus is in the session UI. Note 1385 * that this will only happen if the session UI has requested input focus in its window; 1386 * otherwise, the back key will go to whatever window has focus and do whatever behavior 1387 * it normally has there. The default implementation simply calls {@link #hide}. 1388 */ 1389 public void onBackPressed() { 1390 hide(); 1391 } 1392 1393 /** 1394 * Sessions automatically watch for requests that all system UI be closed (such as when 1395 * the user presses HOME), which will appear here. The default implementation always 1396 * calls {@link #hide}. 1397 */ 1398 public void onCloseSystemDialogs() { 1399 hide(); 1400 } 1401 1402 /** 1403 * Called when the lockscreen was shown. 1404 */ 1405 public void onLockscreenShown() { 1406 hide(); 1407 } 1408 1409 @Override 1410 public void onConfigurationChanged(Configuration newConfig) { 1411 } 1412 1413 @Override 1414 public void onLowMemory() { 1415 } 1416 1417 @Override 1418 public void onTrimMemory(int level) { 1419 } 1420 1421 /** 1422 * Compute the interesting insets into your UI. The default implementation 1423 * sets {@link Insets#contentInsets outInsets.contentInsets.top} to the height 1424 * of the window, meaning it should not adjust content underneath. The default touchable 1425 * insets are {@link Insets#TOUCHABLE_INSETS_FRAME}, meaning it consumes all touch 1426 * events within its window frame. 1427 * 1428 * @param outInsets Fill in with the current UI insets. 1429 */ 1430 public void onComputeInsets(Insets outInsets) { 1431 outInsets.contentInsets.left = 0; 1432 outInsets.contentInsets.bottom = 0; 1433 outInsets.contentInsets.right = 0; 1434 View decor = getWindow().getWindow().getDecorView(); 1435 outInsets.contentInsets.top = decor.getHeight(); 1436 outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_FRAME; 1437 outInsets.touchableRegion.setEmpty(); 1438 } 1439 1440 /** 1441 * Called when a task initiated by {@link #startVoiceActivity(android.content.Intent)} 1442 * has actually started. 1443 * 1444 * @param intent The original {@link Intent} supplied to 1445 * {@link #startVoiceActivity(android.content.Intent)}. 1446 * @param taskId Unique ID of the now running task. 1447 */ 1448 public void onTaskStarted(Intent intent, int taskId) { 1449 } 1450 1451 /** 1452 * Called when the last activity of a task initiated by 1453 * {@link #startVoiceActivity(android.content.Intent)} has finished. The default 1454 * implementation calls {@link #finish()} on the assumption that this represents 1455 * the completion of a voice action. You can override the implementation if you would 1456 * like a different behavior. 1457 * 1458 * @param intent The original {@link Intent} supplied to 1459 * {@link #startVoiceActivity(android.content.Intent)}. 1460 * @param taskId Unique ID of the finished task. 1461 */ 1462 public void onTaskFinished(Intent intent, int taskId) { 1463 hide(); 1464 } 1465 1466 /** 1467 * Request to query for what extended commands the session supports. 1468 * 1469 * @param commands An array of commands that are being queried. 1470 * @return Return an array of booleans indicating which of each entry in the 1471 * command array is supported. A true entry in the array indicates the command 1472 * is supported; false indicates it is not. The default implementation returns 1473 * an array of all false entries. 1474 */ 1475 public boolean[] onGetSupportedCommands(String[] commands) { 1476 return new boolean[commands.length]; 1477 } 1478 1479 /** 1480 * Request to confirm with the user before proceeding with an unrecoverable operation, 1481 * corresponding to a {@link android.app.VoiceInteractor.ConfirmationRequest 1482 * VoiceInteractor.ConfirmationRequest}. 1483 * 1484 * @param request The active request. 1485 */ 1486 public void onRequestConfirmation(ConfirmationRequest request) { 1487 } 1488 1489 /** 1490 * Request for the user to pick one of N options, corresponding to a 1491 * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}. 1492 * 1493 * @param request The active request. 1494 */ 1495 public void onRequestPickOption(PickOptionRequest request) { 1496 } 1497 1498 /** 1499 * Request to complete the voice interaction session because the voice activity successfully 1500 * completed its interaction using voice. Corresponds to 1501 * {@link android.app.VoiceInteractor.CompleteVoiceRequest 1502 * VoiceInteractor.CompleteVoiceRequest}. The default implementation just sends an empty 1503 * confirmation back to allow the activity to exit. 1504 * 1505 * @param request The active request. 1506 */ 1507 public void onRequestCompleteVoice(CompleteVoiceRequest request) { 1508 } 1509 1510 /** 1511 * Request to abort the voice interaction session because the voice activity can not 1512 * complete its interaction using voice. Corresponds to 1513 * {@link android.app.VoiceInteractor.AbortVoiceRequest 1514 * VoiceInteractor.AbortVoiceRequest}. The default implementation just sends an empty 1515 * confirmation back to allow the activity to exit. 1516 * 1517 * @param request The active request. 1518 */ 1519 public void onRequestAbortVoice(AbortVoiceRequest request) { 1520 } 1521 1522 /** 1523 * Process an arbitrary extended command from the caller, 1524 * corresponding to a {@link android.app.VoiceInteractor.CommandRequest 1525 * VoiceInteractor.CommandRequest}. 1526 * 1527 * @param request The active request. 1528 */ 1529 public void onRequestCommand(CommandRequest request) { 1530 } 1531 1532 /** 1533 * Called when the {@link android.app.VoiceInteractor} has asked to cancel a {@link Request} 1534 * that was previously delivered to {@link #onRequestConfirmation}, 1535 * {@link #onRequestPickOption}, {@link #onRequestCompleteVoice}, {@link #onRequestAbortVoice}, 1536 * or {@link #onRequestCommand}. 1537 * 1538 * @param request The request that is being canceled. 1539 */ 1540 public void onCancelRequest(Request request) { 1541 } 1542 1543 /** 1544 * Print the Service's state into the given stream. This gets invoked by 1545 * {@link VoiceInteractionSessionService} when its Service 1546 * {@link android.app.Service#dump} method is called. 1547 * 1548 * @param prefix Text to print at the front of each line. 1549 * @param fd The raw file descriptor that the dump is being sent to. 1550 * @param writer The PrintWriter to which you should dump your state. This will be 1551 * closed for you after you return. 1552 * @param args additional arguments to the dump request. 1553 */ 1554 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 1555 writer.print(prefix); writer.print("mToken="); writer.println(mToken); 1556 writer.print(prefix); writer.print("mTheme=#"); writer.println(Integer.toHexString(mTheme)); 1557 writer.print(prefix); writer.print("mInitialized="); writer.println(mInitialized); 1558 writer.print(prefix); writer.print("mWindowAdded="); writer.print(mWindowAdded); 1559 writer.print(" mWindowVisible="); writer.println(mWindowVisible); 1560 writer.print(prefix); writer.print("mWindowWasVisible="); writer.print(mWindowWasVisible); 1561 writer.print(" mInShowWindow="); writer.println(mInShowWindow); 1562 if (mActiveRequests.size() > 0) { 1563 writer.print(prefix); writer.println("Active requests:"); 1564 String innerPrefix = prefix + " "; 1565 for (int i=0; i<mActiveRequests.size(); i++) { 1566 Request req = mActiveRequests.valueAt(i); 1567 writer.print(prefix); writer.print(" #"); writer.print(i); 1568 writer.print(": "); 1569 writer.println(req); 1570 req.dump(innerPrefix, fd, writer, args); 1571 1572 } 1573 } 1574 } 1575} 1576