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