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