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