blob: 8e6f77b2fd0c727c06d120a7301d0fc50b7c258b [file] [log] [blame]
Dianne Hackborn91097de2014-04-04 18:02:06 -07001/**
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.service.voice;
18
Winson Chungec1ef092017-10-25 16:22:34 -070019import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
20
Sunny Goyald40c3452019-03-20 12:46:55 -070021import android.annotation.CallbackExecutor;
22import android.annotation.IntRange;
23import android.annotation.NonNull;
James Cook6cf39752015-06-04 14:18:43 -070024import android.annotation.Nullable;
Amith Yamasani0af6fa72016-01-17 15:36:19 -080025import android.app.Activity;
Dianne Hackbornc03c9162014-05-02 10:45:59 -070026import android.app.Dialog;
Sunny Goyald40c3452019-03-20 12:46:55 -070027import android.app.DirectAction;
Dianne Hackbornc03c9162014-05-02 10:45:59 -070028import android.app.Instrumentation;
Dianne Hackborn3d07c942015-03-13 18:02:54 -070029import android.app.VoiceInteractor;
Dianne Hackborn69c6adc2015-06-02 10:52:59 -070030import android.app.assist.AssistContent;
31import android.app.assist.AssistStructure;
Dianne Hackborn1e383822015-04-10 14:02:33 -070032import android.content.ComponentCallbacks2;
Dianne Hackborn91097de2014-04-04 18:02:06 -070033import android.content.Context;
Dianne Hackbornc03c9162014-05-02 10:45:59 -070034import android.content.Intent;
Sunny Goyald40c3452019-03-20 12:46:55 -070035import android.content.pm.ParceledListSlice;
Dianne Hackborn1e383822015-04-10 14:02:33 -070036import android.content.res.Configuration;
Dianne Hackbornc03c9162014-05-02 10:45:59 -070037import android.content.res.TypedArray;
Dianne Hackborn27eac1d2015-03-16 17:15:53 -070038import android.graphics.Bitmap;
Dianne Hackborne30e02f2014-05-27 18:24:45 -070039import android.graphics.Rect;
Dianne Hackbornc03c9162014-05-02 10:45:59 -070040import android.graphics.Region;
41import android.inputmethodservice.SoftInputWindow;
Dianne Hackborn91097de2014-04-04 18:02:06 -070042import android.os.Binder;
43import android.os.Bundle;
Sunny Goyald40c3452019-03-20 12:46:55 -070044import android.os.CancellationSignal;
Dianne Hackborn91097de2014-04-04 18:02:06 -070045import android.os.Handler;
46import android.os.IBinder;
Sunny Goyald40c3452019-03-20 12:46:55 -070047import android.os.ICancellationSignal;
Dianne Hackborn91097de2014-04-04 18:02:06 -070048import android.os.Message;
Sunny Goyald40c3452019-03-20 12:46:55 -070049import android.os.RemoteCallback;
Dianne Hackborn91097de2014-04-04 18:02:06 -070050import android.os.RemoteException;
Dianne Hackborn57dd7372015-07-27 18:11:14 -070051import android.os.UserHandle;
Dianne Hackborn91097de2014-04-04 18:02:06 -070052import android.util.ArrayMap;
Dianne Hackborn57dd7372015-07-27 18:11:14 -070053import android.util.DebugUtils;
Dianne Hackborn91097de2014-04-04 18:02:06 -070054import android.util.Log;
Dianne Hackborne30e02f2014-05-27 18:24:45 -070055import android.view.Gravity;
Dianne Hackbornc03c9162014-05-02 10:45:59 -070056import android.view.KeyEvent;
57import android.view.LayoutInflater;
58import android.view.View;
Dianne Hackbornc03c9162014-05-02 10:45:59 -070059import android.view.ViewTreeObserver;
60import android.view.WindowManager;
61import android.widget.FrameLayout;
Amith Yamasani0af6fa72016-01-17 15:36:19 -080062
Sunny Goyald40c3452019-03-20 12:46:55 -070063import com.android.internal.annotations.Immutable;
Dianne Hackbornc03c9162014-05-02 10:45:59 -070064import com.android.internal.app.IVoiceInteractionManagerService;
Jorim Jaggi225d3b52015-04-01 11:18:57 -070065import com.android.internal.app.IVoiceInteractionSessionShowCallback;
Dianne Hackborn91097de2014-04-04 18:02:06 -070066import com.android.internal.app.IVoiceInteractor;
67import com.android.internal.app.IVoiceInteractorCallback;
68import com.android.internal.app.IVoiceInteractorRequest;
69import com.android.internal.os.HandlerCaller;
70import com.android.internal.os.SomeArgs;
Sunny Goyald40c3452019-03-20 12:46:55 -070071import com.android.internal.util.Preconditions;
72import com.android.internal.util.function.pooled.PooledLambda;
Dianne Hackborn91097de2014-04-04 18:02:06 -070073
Dianne Hackborn57dd7372015-07-27 18:11:14 -070074import java.io.FileDescriptor;
75import java.io.PrintWriter;
Dianne Hackborna2c076d2014-05-30 16:42:57 -070076import java.lang.ref.WeakReference;
Sunny Goyald40c3452019-03-20 12:46:55 -070077import java.util.Collections;
78import java.util.List;
Svet Ganov3b6be082019-04-28 10:21:01 -070079import java.util.Map;
Sunny Goyald40c3452019-03-20 12:46:55 -070080import java.util.concurrent.Executor;
81import java.util.function.Consumer;
Dianne Hackborna2c076d2014-05-30 16:42:57 -070082
Dianne Hackborna2c076d2014-05-30 16:42:57 -070083/**
Dianne Hackborn4e106ce2015-01-14 15:15:34 -080084 * An active voice interaction session, providing a facility for the implementation
Dianne Hackbornffeecb12015-02-25 11:08:11 -080085 * to interact with the user in the voice interaction layer. The user interface is
86 * initially shown by default, and can be created be overriding {@link #onCreateContentView()}
87 * in which the UI can be built.
Dianne Hackborn4e106ce2015-01-14 15:15:34 -080088 *
89 * <p>A voice interaction session can be self-contained, ultimately calling {@link #finish}
90 * when done. It can also initiate voice interactions with applications by calling
91 * {@link #startVoiceActivity}</p>.
Dianne Hackborna2c076d2014-05-30 16:42:57 -070092 */
Dianne Hackborn2ee5c362015-05-29 17:58:53 -070093public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCallbacks2 {
Dianne Hackborn91097de2014-04-04 18:02:06 -070094 static final String TAG = "VoiceInteractionSession";
Amith Yamasanie8222e52016-04-08 15:28:47 -070095 static final boolean DEBUG = false;
Dianne Hackborn91097de2014-04-04 18:02:06 -070096
Dianne Hackborn2ee5c362015-05-29 17:58:53 -070097 /**
98 * Flag received in {@link #onShow}: originator requested that the session be started with
99 * assist data from the currently focused activity.
100 */
101 public static final int SHOW_WITH_ASSIST = 1<<0;
102
103 /**
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700104 * Flag received in {@link #onShow}: originator requested that the session be started with
105 * a screen shot of the currently focused activity.
106 */
107 public static final int SHOW_WITH_SCREENSHOT = 1<<1;
108
109 /**
110 * Flag for use with {@link #onShow}: indicates that the session has been started from the
111 * system assist gesture.
112 */
113 public static final int SHOW_SOURCE_ASSIST_GESTURE = 1<<2;
114
Dianne Hackborn17f69352015-07-17 18:04:14 -0700115 /**
116 * Flag for use with {@link #onShow}: indicates that the application itself has invoked
117 * the assistant.
118 */
119 public static final int SHOW_SOURCE_APPLICATION = 1<<3;
120
Amith Yamasani0af6fa72016-01-17 15:36:19 -0800121 /**
122 * Flag for use with {@link #onShow}: indicates that an Activity has invoked the voice
123 * interaction service for a local interaction using
124 * {@link Activity#startLocalVoiceInteraction(Bundle)}.
125 */
126 public static final int SHOW_SOURCE_ACTIVITY = 1<<4;
127
Nicholas Sauer78b89172018-06-21 08:23:58 -0700128 /**
129 * Flag for use with {@link #onShow}: indicates that the voice interaction service was invoked
130 * from a physical button.
131 */
132 public static final int SHOW_SOURCE_PUSH_TO_TALK = 1 << 5;
133
jiayuzhouf9f565c2018-08-17 13:17:46 -0700134 /**
135 * Flag for use with {@link #onShow}: indicates that the voice interaction service was invoked
136 * from a notification.
137 */
138 public static final int SHOW_SOURCE_NOTIFICATION = 1 << 6;
139
jiayuzhou2c80b592019-01-15 16:01:30 -0800140 /**
141 * Flag for use with {@link #onShow}: indicates that the voice interaction service was invoked
jiayuzhou21cbc632019-04-15 16:04:31 -0700142 * from an Android automotive system UI.
jiayuzhou2c80b592019-01-15 16:01:30 -0800143 */
144 public static final int SHOW_SOURCE_AUTOMOTIVE_SYSTEM_UI = 1 << 7;
145
Dianne Hackbornc03c9162014-05-02 10:45:59 -0700146 final Context mContext;
147 final HandlerCaller mHandlerCaller;
148
149 final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState();
150
151 IVoiceInteractionManagerService mSystemService;
152 IBinder mToken;
153
154 int mTheme = 0;
155 LayoutInflater mInflater;
156 TypedArray mThemeAttrs;
157 View mRootView;
158 FrameLayout mContentFrame;
159 SoftInputWindow mWindow;
160
Dianne Hackborn59da80582017-02-06 17:42:41 -0800161 boolean mUiEnabled = true;
Dianne Hackbornc03c9162014-05-02 10:45:59 -0700162 boolean mInitialized;
163 boolean mWindowAdded;
164 boolean mWindowVisible;
165 boolean mWindowWasVisible;
166 boolean mInShowWindow;
167
168 final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<IBinder, Request>();
169
170 final Insets mTmpInsets = new Insets();
Dianne Hackbornc03c9162014-05-02 10:45:59 -0700171
Dianne Hackborna2c076d2014-05-30 16:42:57 -0700172 final WeakReference<VoiceInteractionSession> mWeakRef
173 = new WeakReference<VoiceInteractionSession>(this);
174
Svet Ganov3b6be082019-04-28 10:21:01 -0700175 // Registry of remote callbacks pending a reply with reply handles.
176 final Map<SafeResultListener, Consumer<Bundle>> mRemoteCallbacks = new ArrayMap<>();
177
Sunny Goyald40c3452019-03-20 12:46:55 -0700178 ICancellationSignal mKillCallback;
179
Dianne Hackborn91097de2014-04-04 18:02:06 -0700180 final IVoiceInteractor mInteractor = new IVoiceInteractor.Stub() {
181 @Override
182 public IVoiceInteractorRequest startConfirmation(String callingPackage,
James Cook6cf39752015-06-04 14:18:43 -0700183 IVoiceInteractorCallback callback, VoiceInteractor.Prompt prompt, Bundle extras) {
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700184 ConfirmationRequest request = new ConfirmationRequest(callingPackage,
185 Binder.getCallingUid(), callback, VoiceInteractionSession.this,
186 prompt, extras);
187 addRequest(request);
188 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_CONFIRMATION,
189 request));
Dianne Hackborn91097de2014-04-04 18:02:06 -0700190 return request.mInterface;
191 }
192
193 @Override
Dianne Hackborn3d07c942015-03-13 18:02:54 -0700194 public IVoiceInteractorRequest startPickOption(String callingPackage,
James Cook6cf39752015-06-04 14:18:43 -0700195 IVoiceInteractorCallback callback, VoiceInteractor.Prompt prompt,
Dianne Hackborn3d07c942015-03-13 18:02:54 -0700196 VoiceInteractor.PickOptionRequest.Option[] options, Bundle extras) {
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700197 PickOptionRequest request = new PickOptionRequest(callingPackage,
198 Binder.getCallingUid(), callback, VoiceInteractionSession.this,
199 prompt, options, extras);
200 addRequest(request);
201 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_PICK_OPTION,
202 request));
Dianne Hackborn3d07c942015-03-13 18:02:54 -0700203 return request.mInterface;
204 }
205
206 @Override
Barnaby Jamesd3fdb8b2014-07-07 10:51:06 -0700207 public IVoiceInteractorRequest startCompleteVoice(String callingPackage,
James Cook6cf39752015-06-04 14:18:43 -0700208 IVoiceInteractorCallback callback, VoiceInteractor.Prompt message, Bundle extras) {
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700209 CompleteVoiceRequest request = new CompleteVoiceRequest(callingPackage,
210 Binder.getCallingUid(), callback, VoiceInteractionSession.this,
211 message, extras);
212 addRequest(request);
213 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_COMPLETE_VOICE,
214 request));
Barnaby Jamesd3fdb8b2014-07-07 10:51:06 -0700215 return request.mInterface;
216 }
217
218 @Override
Dianne Hackborna2c076d2014-05-30 16:42:57 -0700219 public IVoiceInteractorRequest startAbortVoice(String callingPackage,
James Cook6cf39752015-06-04 14:18:43 -0700220 IVoiceInteractorCallback callback, VoiceInteractor.Prompt message, Bundle extras) {
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700221 AbortVoiceRequest request = new AbortVoiceRequest(callingPackage,
222 Binder.getCallingUid(), callback, VoiceInteractionSession.this,
223 message, extras);
224 addRequest(request);
225 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_ABORT_VOICE,
226 request));
Dianne Hackborna2c076d2014-05-30 16:42:57 -0700227 return request.mInterface;
228 }
229
230 @Override
Dianne Hackborn91097de2014-04-04 18:02:06 -0700231 public IVoiceInteractorRequest startCommand(String callingPackage,
232 IVoiceInteractorCallback callback, String command, Bundle extras) {
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700233 CommandRequest request = new CommandRequest(callingPackage,
234 Binder.getCallingUid(), callback, VoiceInteractionSession.this,
235 command, extras);
Dianne Hackborn593334a2015-06-30 14:38:17 -0700236 addRequest(request);
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700237 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_COMMAND,
238 request));
Dianne Hackborn91097de2014-04-04 18:02:06 -0700239 return request.mInterface;
240 }
241
242 @Override
243 public boolean[] supportsCommands(String callingPackage, String[] commands) {
244 Message msg = mHandlerCaller.obtainMessageIOO(MSG_SUPPORTS_COMMANDS,
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700245 0, commands, null);
Dianne Hackborn91097de2014-04-04 18:02:06 -0700246 SomeArgs args = mHandlerCaller.sendMessageAndWait(msg);
247 if (args != null) {
248 boolean[] res = (boolean[])args.arg1;
249 args.recycle();
250 return res;
251 }
252 return new boolean[commands.length];
253 }
Sunny Goyald40c3452019-03-20 12:46:55 -0700254
255 @Override
256 public void notifyDirectActionsChanged(int taskId, IBinder assistToken) {
257 mHandlerCaller.getHandler().sendMessage(PooledLambda.obtainMessage(
258 VoiceInteractionSession::onDirectActionsInvalidated,
259 VoiceInteractionSession.this, new ActivityId(taskId, assistToken))
260 );
261 }
262
263 @Override
264 public void setKillCallback(ICancellationSignal callback) {
265 mKillCallback = callback;
266 }
Dianne Hackborn91097de2014-04-04 18:02:06 -0700267 };
268
269 final IVoiceInteractionSession mSession = new IVoiceInteractionSession.Stub() {
Dianne Hackbornc03c9162014-05-02 10:45:59 -0700270 @Override
Jorim Jaggi225d3b52015-04-01 11:18:57 -0700271 public void show(Bundle sessionArgs, int flags,
272 IVoiceInteractionSessionShowCallback showCallback) {
273 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOO(MSG_SHOW,
274 flags, sessionArgs, showCallback));
Dianne Hackbornffeecb12015-02-25 11:08:11 -0800275 }
276
277 @Override
278 public void hide() {
Winson Chungfc3ec4c2017-06-01 15:35:48 -0700279 // Remove any pending messages to show the session
280 mHandlerCaller.removeMessages(MSG_SHOW);
Dianne Hackbornffeecb12015-02-25 11:08:11 -0800281 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_HIDE));
282 }
283
284 @Override
Sunny Goyald40c3452019-03-20 12:46:55 -0700285 public void handleAssist(final int taskId, final IBinder assistToken, final Bundle data,
286 final AssistStructure structure, final AssistContent content, final int index,
287 final int count) {
Dianne Hackborn5688b032015-04-02 18:25:35 -0700288 // We want to pre-warm the AssistStructure before handing it off to the main
Dianne Hackborn782d4982015-07-08 17:36:37 -0700289 // thread. We also want to do this on a separate thread, so that if the app
290 // is for some reason slow (due to slow filling in of async children in the
291 // structure), we don't block other incoming IPCs (such as the screenshot) to
292 // us (since we are a oneway interface, they get serialized). (Okay?)
293 Thread retriever = new Thread("AssistStructure retriever") {
294 @Override
295 public void run() {
296 Throwable failure = null;
297 if (structure != null) {
298 try {
299 structure.ensureData();
300 } catch (Throwable e) {
301 Log.w(TAG, "Failure retrieving AssistStructure", e);
302 failure = e;
303 }
304 }
Sunny Goyald40c3452019-03-20 12:46:55 -0700305
306 SomeArgs args = SomeArgs.obtain();
307 args.argi1 = taskId;
308 args.arg1 = data;
309 args.arg2 = (failure == null) ? structure : null;
310 args.arg3 = failure;
311 args.arg4 = content;
312 args.arg5 = assistToken;
313 args.argi5 = index;
314 args.argi6 = count;
315
316 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(
317 MSG_HANDLE_ASSIST, args));
Dianne Hackborn782d4982015-07-08 17:36:37 -0700318 }
319 };
320 retriever.start();
Dianne Hackbornae6688b2015-02-11 17:02:41 -0800321 }
322
323 @Override
Dianne Hackborn27eac1d2015-03-16 17:15:53 -0700324 public void handleScreenshot(Bitmap screenshot) {
325 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_HANDLE_SCREENSHOT,
326 screenshot));
327 }
328
329 @Override
Dianne Hackbornc03c9162014-05-02 10:45:59 -0700330 public void taskStarted(Intent intent, int taskId) {
331 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_TASK_STARTED,
332 taskId, intent));
333 }
334
335 @Override
336 public void taskFinished(Intent intent, int taskId) {
337 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_TASK_FINISHED,
338 taskId, intent));
339 }
340
341 @Override
342 public void closeSystemDialogs() {
343 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_CLOSE_SYSTEM_DIALOGS));
344 }
345
346 @Override
Jorim Jaggi19695d92015-07-20 15:51:40 -0700347 public void onLockscreenShown() {
348 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_ON_LOCKSCREEN_SHOWN));
349 }
350
351 @Override
Dianne Hackbornc03c9162014-05-02 10:45:59 -0700352 public void destroy() {
353 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_DESTROY));
354 }
Dianne Hackborn91097de2014-04-04 18:02:06 -0700355 };
356
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700357 /**
358 * Base class representing a request from a voice-driver app to perform a particular
359 * voice operation with the user. See related subclasses for the types of requests
360 * that are possible.
361 */
Dianne Hackborn593334a2015-06-30 14:38:17 -0700362 public static class Request {
Dianne Hackborn91097de2014-04-04 18:02:06 -0700363 final IVoiceInteractorRequest mInterface = new IVoiceInteractorRequest.Stub() {
364 @Override
365 public void cancel() throws RemoteException {
Dianne Hackborna2c076d2014-05-30 16:42:57 -0700366 VoiceInteractionSession session = mSession.get();
367 if (session != null) {
368 session.mHandlerCaller.sendMessage(
369 session.mHandlerCaller.obtainMessageO(MSG_CANCEL, Request.this));
370 }
Dianne Hackborn91097de2014-04-04 18:02:06 -0700371 }
372 };
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700373 final String mCallingPackage;
374 final int mCallingUid;
Dianne Hackborn91097de2014-04-04 18:02:06 -0700375 final IVoiceInteractorCallback mCallback;
Dianne Hackborna2c076d2014-05-30 16:42:57 -0700376 final WeakReference<VoiceInteractionSession> mSession;
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700377 final Bundle mExtras;
Dianne Hackborna2c076d2014-05-30 16:42:57 -0700378
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700379 Request(String packageName, int uid, IVoiceInteractorCallback callback,
380 VoiceInteractionSession session, Bundle extras) {
381 mCallingPackage = packageName;
382 mCallingUid = uid;
Dianne Hackborn91097de2014-04-04 18:02:06 -0700383 mCallback = callback;
Dianne Hackborna2c076d2014-05-30 16:42:57 -0700384 mSession = session.mWeakRef;
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700385 mExtras = extras;
386 }
387
388 /**
389 * Return the uid of the application that initiated the request.
390 */
391 public int getCallingUid() {
392 return mCallingUid;
393 }
394
395 /**
396 * Return the package name of the application that initiated the request.
397 */
398 public String getCallingPackage() {
399 return mCallingPackage;
400 }
401
402 /**
403 * Return any additional extra information that was supplied as part of the request.
404 */
405 public Bundle getExtras() {
406 return mExtras;
Dianne Hackborna2c076d2014-05-30 16:42:57 -0700407 }
408
Dianne Hackborn1b4447f2015-07-20 14:49:58 -0700409 /**
410 * Check whether this request is currently active. A request becomes inactive after
411 * calling {@link #cancel} or a final result method that completes the request. After
412 * this point, further interactions with the request will result in
413 * {@link java.lang.IllegalStateException} errors; you should not catch these errors,
414 * but can use this method if you need to determine the state of the request. Returns
415 * true if the request is still active.
416 */
417 public boolean isActive() {
418 VoiceInteractionSession session = mSession.get();
419 if (session == null) {
420 return false;
421 }
422 return session.isRequestActive(mInterface.asBinder());
423 }
424
Dianne Hackborna2c076d2014-05-30 16:42:57 -0700425 void finishRequest() {
426 VoiceInteractionSession session = mSession.get();
427 if (session == null) {
428 throw new IllegalStateException("VoiceInteractionSession has been destroyed");
429 }
430 Request req = session.removeRequest(mInterface.asBinder());
431 if (req == null) {
432 throw new IllegalStateException("Request not active: " + this);
433 } else if (req != this) {
434 throw new IllegalStateException("Current active request " + req
435 + " not same as calling request " + this);
436 }
Dianne Hackborn91097de2014-04-04 18:02:06 -0700437 }
438
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700439 /**
Dianne Hackborn593334a2015-06-30 14:38:17 -0700440 * Ask the app to cancel this current request.
Dianne Hackborn1b4447f2015-07-20 14:49:58 -0700441 * This also finishes the request (it is no longer active).
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700442 */
443 public void cancel() {
Dianne Hackborn91097de2014-04-04 18:02:06 -0700444 try {
445 if (DEBUG) Log.d(TAG, "sendCancelResult: req=" + mInterface);
Dianne Hackborna2c076d2014-05-30 16:42:57 -0700446 finishRequest();
Dianne Hackborn91097de2014-04-04 18:02:06 -0700447 mCallback.deliverCancel(mInterface);
448 } catch (RemoteException e) {
449 }
450 }
Dianne Hackborn57dd7372015-07-27 18:11:14 -0700451
452 @Override
453 public String toString() {
454 StringBuilder sb = new StringBuilder(128);
455 DebugUtils.buildShortClassTag(this, sb);
456 sb.append(" ");
457 sb.append(mInterface.asBinder());
458 sb.append(" pkg=");
459 sb.append(mCallingPackage);
460 sb.append(" uid=");
461 UserHandle.formatUid(sb, mCallingUid);
462 sb.append('}');
463 return sb.toString();
464 }
465
466 void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
467 writer.print(prefix); writer.print("mInterface=");
468 writer.println(mInterface.asBinder());
469 writer.print(prefix); writer.print("mCallingPackage="); writer.print(mCallingPackage);
470 writer.print(" mCallingUid="); UserHandle.formatUid(writer, mCallingUid);
471 writer.println();
472 writer.print(prefix); writer.print("mCallback=");
473 writer.println(mCallback.asBinder());
474 if (mExtras != null) {
475 writer.print(prefix); writer.print("mExtras=");
476 writer.println(mExtras);
477 }
478 }
Dianne Hackborn91097de2014-04-04 18:02:06 -0700479 }
480
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700481 /**
482 * A request for confirmation from the user of an operation, as per
483 * {@link android.app.VoiceInteractor.ConfirmationRequest
484 * VoiceInteractor.ConfirmationRequest}.
485 */
486 public static final class ConfirmationRequest extends Request {
James Cook6cf39752015-06-04 14:18:43 -0700487 final VoiceInteractor.Prompt mPrompt;
Dianne Hackborn91097de2014-04-04 18:02:06 -0700488
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700489 ConfirmationRequest(String packageName, int uid, IVoiceInteractorCallback callback,
James Cook6cf39752015-06-04 14:18:43 -0700490 VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras) {
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700491 super(packageName, uid, callback, session, extras);
492 mPrompt = prompt;
493 }
494
495 /**
496 * Return the prompt informing the user of what will happen, as per
497 * {@link android.app.VoiceInteractor.ConfirmationRequest
498 * VoiceInteractor.ConfirmationRequest}.
499 */
James Cook6cf39752015-06-04 14:18:43 -0700500 @Nullable
501 public VoiceInteractor.Prompt getVoicePrompt() {
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700502 return mPrompt;
503 }
504
505 /**
James Cook6cf39752015-06-04 14:18:43 -0700506 * Return the prompt informing the user of what will happen, as per
507 * {@link android.app.VoiceInteractor.ConfirmationRequest
508 * VoiceInteractor.ConfirmationRequest}.
509 * @deprecated Prefer {@link #getVoicePrompt()} which allows multiple voice prompts.
510 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -0700511 @Deprecated
James Cook6cf39752015-06-04 14:18:43 -0700512 @Nullable
513 public CharSequence getPrompt() {
514 return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null);
515 }
516
517 /**
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700518 * Report that the voice interactor has confirmed the operation with the user, resulting
519 * in a call to
520 * {@link android.app.VoiceInteractor.ConfirmationRequest#onConfirmationResult
521 * VoiceInteractor.ConfirmationRequest.onConfirmationResult}.
Dianne Hackborn1b4447f2015-07-20 14:49:58 -0700522 * This finishes the request (it is no longer active).
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700523 */
524 public void sendConfirmationResult(boolean confirmed, Bundle result) {
Dianne Hackborn593334a2015-06-30 14:38:17 -0700525 try {
526 if (DEBUG) Log.d(TAG, "sendConfirmationResult: req=" + mInterface
527 + " confirmed=" + confirmed + " result=" + result);
528 finishRequest();
529 mCallback.deliverConfirmationResult(mInterface, confirmed, result);
530 } catch (RemoteException e) {
531 }
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700532 }
Dianne Hackborn57dd7372015-07-27 18:11:14 -0700533
534 void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
535 super.dump(prefix, fd, writer, args);
536 writer.print(prefix); writer.print("mPrompt=");
537 writer.println(mPrompt);
538 }
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700539 }
540
541 /**
542 * A request for the user to pick from a set of option, as per
543 * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
544 */
545 public static final class PickOptionRequest extends Request {
James Cook6cf39752015-06-04 14:18:43 -0700546 final VoiceInteractor.Prompt mPrompt;
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700547 final VoiceInteractor.PickOptionRequest.Option[] mOptions;
548
549 PickOptionRequest(String packageName, int uid, IVoiceInteractorCallback callback,
James Cook6cf39752015-06-04 14:18:43 -0700550 VoiceInteractionSession session, VoiceInteractor.Prompt prompt,
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700551 VoiceInteractor.PickOptionRequest.Option[] options, Bundle extras) {
552 super(packageName, uid, callback, session, extras);
553 mPrompt = prompt;
554 mOptions = options;
555 }
556
557 /**
558 * Return the prompt informing the user of what they are picking, as per
559 * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
560 */
James Cook6cf39752015-06-04 14:18:43 -0700561 @Nullable
562 public VoiceInteractor.Prompt getVoicePrompt() {
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700563 return mPrompt;
564 }
565
566 /**
James Cook6cf39752015-06-04 14:18:43 -0700567 * Return the prompt informing the user of what they are picking, as per
568 * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
569 * @deprecated Prefer {@link #getVoicePrompt()} which allows multiple voice prompts.
570 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -0700571 @Deprecated
James Cook6cf39752015-06-04 14:18:43 -0700572 @Nullable
573 public CharSequence getPrompt() {
574 return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null);
575 }
576
577 /**
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700578 * Return the set of options the user is picking from, as per
579 * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
580 */
581 public VoiceInteractor.PickOptionRequest.Option[] getOptions() {
582 return mOptions;
583 }
584
Dianne Hackborn593334a2015-06-30 14:38:17 -0700585 void sendPickOptionResult(boolean finished,
586 VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) {
587 try {
588 if (DEBUG) Log.d(TAG, "sendPickOptionResult: req=" + mInterface
589 + " finished=" + finished + " selections=" + selections
590 + " result=" + result);
591 if (finished) {
592 finishRequest();
593 }
594 mCallback.deliverPickOptionResult(mInterface, finished, selections, result);
595 } catch (RemoteException e) {
596 }
597 }
598
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700599 /**
600 * Report an intermediate option selection from the request, without completing it (the
601 * request is still active and the app is waiting for the final option selection),
602 * resulting in a call to
603 * {@link android.app.VoiceInteractor.PickOptionRequest#onPickOptionResult
604 * VoiceInteractor.PickOptionRequest.onPickOptionResult} with false for finished.
605 */
606 public void sendIntermediatePickOptionResult(
607 VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) {
608 sendPickOptionResult(false, selections, result);
609 }
610
611 /**
612 * Report the final option selection for the request, completing the request
613 * and resulting in a call to
614 * {@link android.app.VoiceInteractor.PickOptionRequest#onPickOptionResult
615 * VoiceInteractor.PickOptionRequest.onPickOptionResult} with false for finished.
Dianne Hackborn1b4447f2015-07-20 14:49:58 -0700616 * This finishes the request (it is no longer active).
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700617 */
618 public void sendPickOptionResult(
619 VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) {
620 sendPickOptionResult(true, selections, result);
621 }
Dianne Hackborn57dd7372015-07-27 18:11:14 -0700622
623 void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
624 super.dump(prefix, fd, writer, args);
625 writer.print(prefix); writer.print("mPrompt=");
626 writer.println(mPrompt);
627 if (mOptions != null) {
628 writer.print(prefix); writer.println("Options:");
629 for (int i=0; i<mOptions.length; i++) {
630 VoiceInteractor.PickOptionRequest.Option op = mOptions[i];
631 writer.print(prefix); writer.print(" #"); writer.print(i); writer.println(":");
632 writer.print(prefix); writer.print(" mLabel=");
633 writer.println(op.getLabel());
634 writer.print(prefix); writer.print(" mIndex=");
635 writer.println(op.getIndex());
636 if (op.countSynonyms() > 0) {
637 writer.print(prefix); writer.println(" Synonyms:");
638 for (int j=0; j<op.countSynonyms(); j++) {
639 writer.print(prefix); writer.print(" #"); writer.print(j);
640 writer.print(": "); writer.println(op.getSynonymAt(j));
641 }
642 }
643 if (op.getExtras() != null) {
644 writer.print(prefix); writer.print(" mExtras=");
645 writer.println(op.getExtras());
646 }
647 }
648 }
649 }
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700650 }
651
652 /**
653 * A request to simply inform the user that the voice operation has completed, as per
654 * {@link android.app.VoiceInteractor.CompleteVoiceRequest
655 * VoiceInteractor.CompleteVoiceRequest}.
656 */
657 public static final class CompleteVoiceRequest extends Request {
James Cook6cf39752015-06-04 14:18:43 -0700658 final VoiceInteractor.Prompt mPrompt;
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700659
660 CompleteVoiceRequest(String packageName, int uid, IVoiceInteractorCallback callback,
James Cook6cf39752015-06-04 14:18:43 -0700661 VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras) {
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700662 super(packageName, uid, callback, session, extras);
James Cook6cf39752015-06-04 14:18:43 -0700663 mPrompt = prompt;
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700664 }
665
666 /**
667 * Return the message informing the user of the completion, as per
668 * {@link android.app.VoiceInteractor.CompleteVoiceRequest
669 * VoiceInteractor.CompleteVoiceRequest}.
670 */
James Cook6cf39752015-06-04 14:18:43 -0700671 @Nullable
672 public VoiceInteractor.Prompt getVoicePrompt() {
673 return mPrompt;
674 }
675
676 /**
677 * Return the message informing the user of the completion, as per
678 * {@link android.app.VoiceInteractor.CompleteVoiceRequest
679 * VoiceInteractor.CompleteVoiceRequest}.
680 * @deprecated Prefer {@link #getVoicePrompt()} which allows a separate visual message.
681 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -0700682 @Deprecated
James Cook6cf39752015-06-04 14:18:43 -0700683 @Nullable
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700684 public CharSequence getMessage() {
James Cook6cf39752015-06-04 14:18:43 -0700685 return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null);
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700686 }
687
688 /**
689 * Report that the voice interactor has finished completing the voice operation, resulting
690 * in a call to
691 * {@link android.app.VoiceInteractor.CompleteVoiceRequest#onCompleteResult
692 * VoiceInteractor.CompleteVoiceRequest.onCompleteResult}.
Dianne Hackborn1b4447f2015-07-20 14:49:58 -0700693 * This finishes the request (it is no longer active).
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700694 */
695 public void sendCompleteResult(Bundle result) {
Dianne Hackborn593334a2015-06-30 14:38:17 -0700696 try {
697 if (DEBUG) Log.d(TAG, "sendCompleteVoiceResult: req=" + mInterface
698 + " result=" + result);
699 finishRequest();
700 mCallback.deliverCompleteVoiceResult(mInterface, result);
701 } catch (RemoteException e) {
702 }
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700703 }
Dianne Hackborn57dd7372015-07-27 18:11:14 -0700704
705 void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
706 super.dump(prefix, fd, writer, args);
707 writer.print(prefix); writer.print("mPrompt=");
708 writer.println(mPrompt);
709 }
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700710 }
711
712 /**
713 * A request to report that the current user interaction can not be completed with voice, as per
714 * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
715 */
716 public static final class AbortVoiceRequest extends Request {
James Cook6cf39752015-06-04 14:18:43 -0700717 final VoiceInteractor.Prompt mPrompt;
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700718
719 AbortVoiceRequest(String packageName, int uid, IVoiceInteractorCallback callback,
James Cook6cf39752015-06-04 14:18:43 -0700720 VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras) {
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700721 super(packageName, uid, callback, session, extras);
James Cook6cf39752015-06-04 14:18:43 -0700722 mPrompt = prompt;
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700723 }
724
725 /**
726 * Return the message informing the user of the problem, as per
727 * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
728 */
James Cook6cf39752015-06-04 14:18:43 -0700729 @Nullable
730 public VoiceInteractor.Prompt getVoicePrompt() {
731 return mPrompt;
732 }
733
734 /**
735 * Return the message informing the user of the problem, as per
736 * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
737 * @deprecated Prefer {@link #getVoicePrompt()} which allows a separate visual message.
738 */
Aurimas Liutikas514c5ef2016-05-24 15:22:55 -0700739 @Deprecated
James Cook6cf39752015-06-04 14:18:43 -0700740 @Nullable
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700741 public CharSequence getMessage() {
James Cook6cf39752015-06-04 14:18:43 -0700742 return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null);
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700743 }
744
745 /**
746 * Report that the voice interactor has finished aborting the voice operation, resulting
747 * in a call to
748 * {@link android.app.VoiceInteractor.AbortVoiceRequest#onAbortResult
Dianne Hackborn1b4447f2015-07-20 14:49:58 -0700749 * VoiceInteractor.AbortVoiceRequest.onAbortResult}. This finishes the request (it
750 * is no longer active).
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700751 */
752 public void sendAbortResult(Bundle result) {
Dianne Hackborn593334a2015-06-30 14:38:17 -0700753 try {
754 if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface
755 + " result=" + result);
756 finishRequest();
757 mCallback.deliverAbortVoiceResult(mInterface, result);
758 } catch (RemoteException e) {
759 }
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700760 }
Dianne Hackborn57dd7372015-07-27 18:11:14 -0700761
762 void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
763 super.dump(prefix, fd, writer, args);
764 writer.print(prefix); writer.print("mPrompt=");
765 writer.println(mPrompt);
766 }
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700767 }
768
769 /**
770 * A generic vendor-specific request, as per
771 * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}.
772 */
773 public static final class CommandRequest extends Request {
774 final String mCommand;
775
776 CommandRequest(String packageName, int uid, IVoiceInteractorCallback callback,
777 VoiceInteractionSession session, String command, Bundle extras) {
778 super(packageName, uid, callback, session, extras);
779 mCommand = command;
780 }
781
782 /**
783 * Return the command that is being executed, as per
784 * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}.
785 */
786 public String getCommand() {
787 return mCommand;
788 }
789
Dianne Hackborn593334a2015-06-30 14:38:17 -0700790 void sendCommandResult(boolean finished, Bundle result) {
791 try {
792 if (DEBUG) Log.d(TAG, "sendCommandResult: req=" + mInterface
793 + " result=" + result);
794 if (finished) {
795 finishRequest();
796 }
797 mCallback.deliverCommandResult(mInterface, finished, result);
798 } catch (RemoteException e) {
799 }
800 }
801
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700802 /**
803 * Report an intermediate result of the request, without completing it (the request
804 * is still active and the app is waiting for the final result), resulting in a call to
805 * {@link android.app.VoiceInteractor.CommandRequest#onCommandResult
806 * VoiceInteractor.CommandRequest.onCommandResult} with false for isCompleted.
807 */
808 public void sendIntermediateResult(Bundle result) {
809 sendCommandResult(false, result);
810 }
811
812 /**
813 * Report the final result of the request, completing the request and resulting in a call to
814 * {@link android.app.VoiceInteractor.CommandRequest#onCommandResult
815 * VoiceInteractor.CommandRequest.onCommandResult} with true for isCompleted.
Dianne Hackborn1b4447f2015-07-20 14:49:58 -0700816 * This finishes the request (it is no longer active).
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700817 */
818 public void sendResult(Bundle result) {
819 sendCommandResult(true, result);
Dianne Hackborn91097de2014-04-04 18:02:06 -0700820 }
Dianne Hackborn57dd7372015-07-27 18:11:14 -0700821
822 void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
823 super.dump(prefix, fd, writer, args);
824 writer.print(prefix); writer.print("mCommand=");
825 writer.println(mCommand);
826 }
Dianne Hackborn91097de2014-04-04 18:02:06 -0700827 }
828
829 static final int MSG_START_CONFIRMATION = 1;
Dianne Hackborn3d07c942015-03-13 18:02:54 -0700830 static final int MSG_START_PICK_OPTION = 2;
831 static final int MSG_START_COMPLETE_VOICE = 3;
832 static final int MSG_START_ABORT_VOICE = 4;
833 static final int MSG_START_COMMAND = 5;
834 static final int MSG_SUPPORTS_COMMANDS = 6;
835 static final int MSG_CANCEL = 7;
Dianne Hackborn91097de2014-04-04 18:02:06 -0700836
Dianne Hackbornc03c9162014-05-02 10:45:59 -0700837 static final int MSG_TASK_STARTED = 100;
838 static final int MSG_TASK_FINISHED = 101;
839 static final int MSG_CLOSE_SYSTEM_DIALOGS = 102;
840 static final int MSG_DESTROY = 103;
Dianne Hackbornae6688b2015-02-11 17:02:41 -0800841 static final int MSG_HANDLE_ASSIST = 104;
Dianne Hackborn27eac1d2015-03-16 17:15:53 -0700842 static final int MSG_HANDLE_SCREENSHOT = 105;
843 static final int MSG_SHOW = 106;
844 static final int MSG_HIDE = 107;
Jorim Jaggi19695d92015-07-20 15:51:40 -0700845 static final int MSG_ON_LOCKSCREEN_SHOWN = 108;
Dianne Hackbornc03c9162014-05-02 10:45:59 -0700846
847 class MyCallbacks implements HandlerCaller.Callback, SoftInputWindow.Callback {
Dianne Hackborn91097de2014-04-04 18:02:06 -0700848 @Override
849 public void executeMessage(Message msg) {
Dianne Hackbornd0a15902015-07-15 11:18:09 -0700850 SomeArgs args = null;
Dianne Hackborn91097de2014-04-04 18:02:06 -0700851 switch (msg.what) {
852 case MSG_START_CONFIRMATION:
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700853 if (DEBUG) Log.d(TAG, "onConfirm: req=" + msg.obj);
854 onRequestConfirmation((ConfirmationRequest) msg.obj);
Dianne Hackborn91097de2014-04-04 18:02:06 -0700855 break;
Dianne Hackborn3d07c942015-03-13 18:02:54 -0700856 case MSG_START_PICK_OPTION:
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700857 if (DEBUG) Log.d(TAG, "onPickOption: req=" + msg.obj);
858 onRequestPickOption((PickOptionRequest) msg.obj);
Dianne Hackborn3d07c942015-03-13 18:02:54 -0700859 break;
Barnaby Jamesd3fdb8b2014-07-07 10:51:06 -0700860 case MSG_START_COMPLETE_VOICE:
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700861 if (DEBUG) Log.d(TAG, "onCompleteVoice: req=" + msg.obj);
862 onRequestCompleteVoice((CompleteVoiceRequest) msg.obj);
Barnaby Jamesd3fdb8b2014-07-07 10:51:06 -0700863 break;
Dianne Hackborna2c076d2014-05-30 16:42:57 -0700864 case MSG_START_ABORT_VOICE:
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700865 if (DEBUG) Log.d(TAG, "onAbortVoice: req=" + msg.obj);
866 onRequestAbortVoice((AbortVoiceRequest) msg.obj);
Dianne Hackborna2c076d2014-05-30 16:42:57 -0700867 break;
Dianne Hackborn91097de2014-04-04 18:02:06 -0700868 case MSG_START_COMMAND:
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700869 if (DEBUG) Log.d(TAG, "onCommand: req=" + msg.obj);
870 onRequestCommand((CommandRequest) msg.obj);
Dianne Hackborn91097de2014-04-04 18:02:06 -0700871 break;
872 case MSG_SUPPORTS_COMMANDS:
Dianne Hackbornc03c9162014-05-02 10:45:59 -0700873 args = (SomeArgs)msg.obj;
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700874 if (DEBUG) Log.d(TAG, "onGetSupportedCommands: cmds=" + args.arg1);
875 args.arg1 = onGetSupportedCommands((String[]) args.arg1);
Dianne Hackbornd0a15902015-07-15 11:18:09 -0700876 args.complete();
877 args = null;
Dianne Hackborn91097de2014-04-04 18:02:06 -0700878 break;
879 case MSG_CANCEL:
Dianne Hackbornffeecb12015-02-25 11:08:11 -0800880 if (DEBUG) Log.d(TAG, "onCancel: req=" + ((Request)msg.obj));
Dianne Hackborn2ee5c362015-05-29 17:58:53 -0700881 onCancelRequest((Request) msg.obj);
Dianne Hackborn91097de2014-04-04 18:02:06 -0700882 break;
Dianne Hackbornc03c9162014-05-02 10:45:59 -0700883 case MSG_TASK_STARTED:
884 if (DEBUG) Log.d(TAG, "onTaskStarted: intent=" + msg.obj
885 + " taskId=" + msg.arg1);
886 onTaskStarted((Intent) msg.obj, msg.arg1);
887 break;
888 case MSG_TASK_FINISHED:
889 if (DEBUG) Log.d(TAG, "onTaskFinished: intent=" + msg.obj
890 + " taskId=" + msg.arg1);
891 onTaskFinished((Intent) msg.obj, msg.arg1);
892 break;
893 case MSG_CLOSE_SYSTEM_DIALOGS:
894 if (DEBUG) Log.d(TAG, "onCloseSystemDialogs");
895 onCloseSystemDialogs();
896 break;
897 case MSG_DESTROY:
898 if (DEBUG) Log.d(TAG, "doDestroy");
899 doDestroy();
900 break;
Dianne Hackbornae6688b2015-02-11 17:02:41 -0800901 case MSG_HANDLE_ASSIST:
Dianne Hackborn09d57fe2015-05-27 18:05:52 -0700902 args = (SomeArgs)msg.obj;
Sunny Goyald40c3452019-03-20 12:46:55 -0700903 if (DEBUG) Log.d(TAG, "onHandleAssist: taskId=" + args.argi1
904 + "assistToken=" + args.arg5 + " data=" + args.arg1
Amith Yamasanie8222e52016-04-08 15:28:47 -0700905 + " structure=" + args.arg2 + " content=" + args.arg3
906 + " activityIndex=" + args.argi5 + " activityCount=" + args.argi6);
Sunny Goyald40c3452019-03-20 12:46:55 -0700907 doOnHandleAssist(args.argi1, (IBinder) args.arg5, (Bundle) args.arg1,
908 (AssistStructure) args.arg2, (Throwable) args.arg3,
909 (AssistContent) args.arg4, args.argi5, args.argi6);
Dianne Hackbornae6688b2015-02-11 17:02:41 -0800910 break;
Dianne Hackborn27eac1d2015-03-16 17:15:53 -0700911 case MSG_HANDLE_SCREENSHOT:
912 if (DEBUG) Log.d(TAG, "onHandleScreenshot: " + msg.obj);
913 onHandleScreenshot((Bitmap) msg.obj);
914 break;
Dianne Hackbornffeecb12015-02-25 11:08:11 -0800915 case MSG_SHOW:
Jorim Jaggi225d3b52015-04-01 11:18:57 -0700916 args = (SomeArgs)msg.obj;
917 if (DEBUG) Log.d(TAG, "doShow: args=" + args.arg1
918 + " flags=" + msg.arg1
919 + " showCallback=" + args.arg2);
920 doShow((Bundle) args.arg1, msg.arg1,
921 (IVoiceInteractionSessionShowCallback) args.arg2);
Dianne Hackbornffeecb12015-02-25 11:08:11 -0800922 break;
923 case MSG_HIDE:
924 if (DEBUG) Log.d(TAG, "doHide");
925 doHide();
926 break;
Jorim Jaggi19695d92015-07-20 15:51:40 -0700927 case MSG_ON_LOCKSCREEN_SHOWN:
928 if (DEBUG) Log.d(TAG, "onLockscreenShown");
929 onLockscreenShown();
930 break;
Dianne Hackborn91097de2014-04-04 18:02:06 -0700931 }
Dianne Hackbornd0a15902015-07-15 11:18:09 -0700932 if (args != null) {
933 args.recycle();
934 }
Dianne Hackborn91097de2014-04-04 18:02:06 -0700935 }
Dianne Hackborn91097de2014-04-04 18:02:06 -0700936
Dianne Hackbornc03c9162014-05-02 10:45:59 -0700937 @Override
938 public void onBackPressed() {
939 VoiceInteractionSession.this.onBackPressed();
940 }
941 }
942
943 final MyCallbacks mCallbacks = new MyCallbacks();
944
945 /**
946 * Information about where interesting parts of the input method UI appear.
947 */
948 public static final class Insets {
949 /**
Dianne Hackborne30e02f2014-05-27 18:24:45 -0700950 * This is the part of the UI that is the main content. It is
Dianne Hackbornc03c9162014-05-02 10:45:59 -0700951 * used to determine the basic space needed, to resize/pan the
952 * application behind. It is assumed that this inset does not
953 * change very much, since any change will cause a full resize/pan
954 * of the application behind. This value is relative to the top edge
955 * of the input method window.
956 */
Dianne Hackborne30e02f2014-05-27 18:24:45 -0700957 public final Rect contentInsets = new Rect();
Dianne Hackbornc03c9162014-05-02 10:45:59 -0700958
959 /**
960 * This is the region of the UI that is touchable. It is used when
961 * {@link #touchableInsets} is set to {@link #TOUCHABLE_INSETS_REGION}.
962 * The region should be specified relative to the origin of the window frame.
963 */
964 public final Region touchableRegion = new Region();
965
966 /**
967 * Option for {@link #touchableInsets}: the entire window frame
968 * can be touched.
969 */
970 public static final int TOUCHABLE_INSETS_FRAME
971 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
972
973 /**
974 * Option for {@link #touchableInsets}: the area inside of
975 * the content insets can be touched.
976 */
977 public static final int TOUCHABLE_INSETS_CONTENT
978 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
979
980 /**
981 * Option for {@link #touchableInsets}: the region specified by
982 * {@link #touchableRegion} can be touched.
983 */
984 public static final int TOUCHABLE_INSETS_REGION
985 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
986
987 /**
988 * Determine which area of the window is touchable by the user. May
989 * be one of: {@link #TOUCHABLE_INSETS_FRAME},
990 * {@link #TOUCHABLE_INSETS_CONTENT}, or {@link #TOUCHABLE_INSETS_REGION}.
991 */
992 public int touchableInsets;
993 }
994
995 final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
996 new ViewTreeObserver.OnComputeInternalInsetsListener() {
997 public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
998 onComputeInsets(mTmpInsets);
Dianne Hackborne30e02f2014-05-27 18:24:45 -0700999 info.contentInsets.set(mTmpInsets.contentInsets);
1000 info.visibleInsets.set(mTmpInsets.contentInsets);
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001001 info.touchableRegion.set(mTmpInsets.touchableRegion);
1002 info.setTouchableInsets(mTmpInsets.touchableInsets);
1003 }
1004 };
Dianne Hackborn91097de2014-04-04 18:02:06 -07001005
1006 public VoiceInteractionSession(Context context) {
1007 this(context, new Handler());
1008 }
1009
1010 public VoiceInteractionSession(Context context, Handler handler) {
1011 mContext = context;
1012 mHandlerCaller = new HandlerCaller(context, handler.getLooper(),
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001013 mCallbacks, true);
Dianne Hackborn91097de2014-04-04 18:02:06 -07001014 }
1015
Dianne Hackbornd59a5d52015-04-04 14:52:14 -07001016 public Context getContext() {
1017 return mContext;
1018 }
1019
Dianne Hackborn2ee5c362015-05-29 17:58:53 -07001020 void addRequest(Request req) {
Dianne Hackborn1b4447f2015-07-20 14:49:58 -07001021 synchronized (this) {
1022 mActiveRequests.put(req.mInterface.asBinder(), req);
1023 }
1024 }
1025
1026 boolean isRequestActive(IBinder reqInterface) {
1027 synchronized (this) {
1028 return mActiveRequests.containsKey(reqInterface);
1029 }
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001030 }
1031
1032 Request removeRequest(IBinder reqInterface) {
1033 synchronized (this) {
Andreas Gampe8ef92bd2015-03-17 21:22:49 -07001034 return mActiveRequests.remove(reqInterface);
Dianne Hackborn91097de2014-04-04 18:02:06 -07001035 }
1036 }
1037
Dianne Hackborn593334a2015-06-30 14:38:17 -07001038 void doCreate(IVoiceInteractionManagerService service, IBinder token) {
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001039 mSystemService = service;
1040 mToken = token;
Dianne Hackborn593334a2015-06-30 14:38:17 -07001041 onCreate();
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001042 }
1043
Jorim Jaggi225d3b52015-04-01 11:18:57 -07001044 void doShow(Bundle args, int flags, final IVoiceInteractionSessionShowCallback showCallback) {
Dianne Hackbornffeecb12015-02-25 11:08:11 -08001045 if (DEBUG) Log.v(TAG, "Showing window: mWindowAdded=" + mWindowAdded
1046 + " mWindowVisible=" + mWindowVisible);
1047
1048 if (mInShowWindow) {
1049 Log.w(TAG, "Re-entrance in to showWindow");
1050 return;
1051 }
1052
1053 try {
1054 mInShowWindow = true;
Dianne Hackborn59da80582017-02-06 17:42:41 -08001055 onPrepareShow(args, flags);
Dianne Hackbornffeecb12015-02-25 11:08:11 -08001056 if (!mWindowVisible) {
Dianne Hackborn59da80582017-02-06 17:42:41 -08001057 ensureWindowAdded();
Dianne Hackbornffeecb12015-02-25 11:08:11 -08001058 }
1059 onShow(args, flags);
1060 if (!mWindowVisible) {
1061 mWindowVisible = true;
Dianne Hackborn59da80582017-02-06 17:42:41 -08001062 if (mUiEnabled) {
1063 mWindow.show();
1064 }
Dianne Hackbornffeecb12015-02-25 11:08:11 -08001065 }
Jorim Jaggi225d3b52015-04-01 11:18:57 -07001066 if (showCallback != null) {
Dianne Hackborn59da80582017-02-06 17:42:41 -08001067 if (mUiEnabled) {
1068 mRootView.invalidate();
1069 mRootView.getViewTreeObserver().addOnPreDrawListener(
1070 new ViewTreeObserver.OnPreDrawListener() {
1071 @Override
1072 public boolean onPreDraw() {
1073 mRootView.getViewTreeObserver().removeOnPreDrawListener(this);
1074 try {
1075 showCallback.onShown();
1076 } catch (RemoteException e) {
1077 Log.w(TAG, "Error calling onShown", e);
1078 }
1079 return true;
Jorim Jaggi225d3b52015-04-01 11:18:57 -07001080 }
Dianne Hackborn59da80582017-02-06 17:42:41 -08001081 });
1082 } else {
1083 try {
1084 showCallback.onShown();
1085 } catch (RemoteException e) {
1086 Log.w(TAG, "Error calling onShown", e);
1087 }
1088 }
Jorim Jaggi225d3b52015-04-01 11:18:57 -07001089 }
Dianne Hackbornffeecb12015-02-25 11:08:11 -08001090 } finally {
1091 mWindowWasVisible = true;
1092 mInShowWindow = false;
1093 }
1094 }
1095
1096 void doHide() {
1097 if (mWindowVisible) {
Dianne Hackborn59da80582017-02-06 17:42:41 -08001098 ensureWindowHidden();
Dianne Hackbornffeecb12015-02-25 11:08:11 -08001099 mWindowVisible = false;
1100 onHide();
1101 }
1102 }
1103
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001104 void doDestroy() {
Dianne Hackborn9a35d782014-08-07 12:34:37 -07001105 onDestroy();
Sunny Goyald40c3452019-03-20 12:46:55 -07001106 if (mKillCallback != null) {
1107 try {
1108 mKillCallback.cancel();
1109 } catch (RemoteException e) {
1110 /* ignore */
1111 }
Svet Ganov3b6be082019-04-28 10:21:01 -07001112 mKillCallback = null;
Sunny Goyald40c3452019-03-20 12:46:55 -07001113 }
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001114 if (mInitialized) {
1115 mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
1116 mInsetsComputer);
1117 if (mWindowAdded) {
1118 mWindow.dismiss();
1119 mWindowAdded = false;
1120 }
1121 mInitialized = false;
1122 }
1123 }
1124
Dianne Hackborn59da80582017-02-06 17:42:41 -08001125 void ensureWindowCreated() {
1126 if (mInitialized) {
1127 return;
1128 }
1129
1130 if (!mUiEnabled) {
1131 throw new IllegalStateException("setUiEnabled is false");
1132 }
1133
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001134 mInitialized = true;
Dianne Hackborn59da80582017-02-06 17:42:41 -08001135 mInflater = (LayoutInflater)mContext.getSystemService(
1136 Context.LAYOUT_INFLATER_SERVICE);
1137 mWindow = new SoftInputWindow(mContext, "VoiceInteractionSession", mTheme,
1138 mCallbacks, this, mDispatcherState,
1139 WindowManager.LayoutParams.TYPE_VOICE_INTERACTION, Gravity.BOTTOM, true);
Tiger Huang52724442020-01-20 21:38:42 +08001140 mWindow.getWindow().getAttributes().setFitInsetsTypes(0 /* types */);
Dianne Hackborn59da80582017-02-06 17:42:41 -08001141 mWindow.getWindow().addFlags(
1142 WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED |
1143 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
1144 WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001145
1146 mThemeAttrs = mContext.obtainStyledAttributes(android.R.styleable.VoiceInteractionSession);
1147 mRootView = mInflater.inflate(
1148 com.android.internal.R.layout.voice_interaction_session, null);
1149 mRootView.setSystemUiVisibility(
Jorim Jaggi225d3b52015-04-01 11:18:57 -07001150 View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
Dianne Hackborn59da80582017-02-06 17:42:41 -08001151 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001152 mWindow.setContentView(mRootView);
1153 mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
1154
1155 mContentFrame = (FrameLayout)mRootView.findViewById(android.R.id.content);
Dianne Hackborn59da80582017-02-06 17:42:41 -08001156
1157 mWindow.getWindow().setLayout(MATCH_PARENT, MATCH_PARENT);
1158 mWindow.setToken(mToken);
1159 }
1160
1161 void ensureWindowAdded() {
1162 if (mUiEnabled && !mWindowAdded) {
1163 mWindowAdded = true;
1164 ensureWindowCreated();
1165 View v = onCreateContentView();
1166 if (v != null) {
1167 setContentView(v);
1168 }
1169 }
1170 }
1171
1172 void ensureWindowHidden() {
1173 if (mWindow != null) {
1174 mWindow.hide();
1175 }
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001176 }
1177
Dianne Hackborn69c6adc2015-06-02 10:52:59 -07001178 /**
Dianne Hackborn1de11862015-07-15 14:20:51 -07001179 * Equivalent to {@link VoiceInteractionService#setDisabledShowContext
1180 * VoiceInteractionService.setDisabledShowContext(int)}.
1181 */
1182 public void setDisabledShowContext(int flags) {
1183 try {
1184 mSystemService.setDisabledShowContext(flags);
1185 } catch (RemoteException e) {
1186 }
1187 }
1188
1189 /**
1190 * Equivalent to {@link VoiceInteractionService#getDisabledShowContext
1191 * VoiceInteractionService.getDisabledShowContext}.
1192 */
1193 public int getDisabledShowContext() {
1194 try {
1195 return mSystemService.getDisabledShowContext();
1196 } catch (RemoteException e) {
1197 return 0;
1198 }
1199 }
1200
1201 /**
Dianne Hackborn17f69352015-07-17 18:04:14 -07001202 * Return which show context flags have been disabled by the user through the system
1203 * settings UI, so the session will never get this data. Returned flags are any combination of
1204 * {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST} and
1205 * {@link VoiceInteractionSession#SHOW_WITH_SCREENSHOT
1206 * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}. Note that this only tells you about
1207 * global user settings, not about restrictions that may be applied contextual based on
1208 * the current application the user is in or other transient states.
1209 */
1210 public int getUserDisabledShowContext() {
1211 try {
1212 return mSystemService.getUserDisabledShowContext();
1213 } catch (RemoteException e) {
1214 return 0;
1215 }
1216 }
1217
1218 /**
Dianne Hackborn69c6adc2015-06-02 10:52:59 -07001219 * Show the UI for this session. This asks the system to go through the process of showing
1220 * your UI, which will eventually culminate in {@link #onShow}. This is similar to calling
1221 * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
1222 * @param args Arbitrary arguments that will be propagated {@link #onShow}.
1223 * @param flags Indicates additional optional behavior that should be performed. May
Dianne Hackborn1de11862015-07-15 14:20:51 -07001224 * be any combination of
1225 * {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST} and
1226 * {@link VoiceInteractionSession#SHOW_WITH_SCREENSHOT
1227 * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}
Dianne Hackborn69c6adc2015-06-02 10:52:59 -07001228 * to request that the system generate and deliver assist data on the current foreground
1229 * app as part of showing the session UI.
1230 */
1231 public void show(Bundle args, int flags) {
1232 if (mToken == null) {
1233 throw new IllegalStateException("Can't call before onCreate()");
1234 }
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001235 try {
Dianne Hackborn69c6adc2015-06-02 10:52:59 -07001236 mSystemService.showSessionFromSession(mToken, args, flags);
Dianne Hackbornffeecb12015-02-25 11:08:11 -08001237 } catch (RemoteException e) {
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001238 }
1239 }
1240
Dianne Hackborn69c6adc2015-06-02 10:52:59 -07001241 /**
1242 * Hide the session's UI, if currently shown. Call this when you are done with your
1243 * user interaction.
1244 */
Dianne Hackbornffeecb12015-02-25 11:08:11 -08001245 public void hide() {
Dianne Hackborn69c6adc2015-06-02 10:52:59 -07001246 if (mToken == null) {
1247 throw new IllegalStateException("Can't call before onCreate()");
1248 }
Dianne Hackbornffeecb12015-02-25 11:08:11 -08001249 try {
1250 mSystemService.hideSessionFromSession(mToken);
1251 } catch (RemoteException e) {
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001252 }
1253 }
1254
1255 /**
Dianne Hackborn59da80582017-02-06 17:42:41 -08001256 * Control whether the UI layer for this session is enabled. It is enabled by default.
1257 * If set to false, you will not be able to provide a UI through {@link #onCreateContentView()}.
1258 */
1259 public void setUiEnabled(boolean enabled) {
1260 if (mUiEnabled != enabled) {
1261 mUiEnabled = enabled;
1262 if (mWindowVisible) {
1263 if (enabled) {
1264 ensureWindowAdded();
1265 mWindow.show();
1266 } else {
1267 ensureWindowHidden();
1268 }
1269 }
1270 }
1271 }
1272
1273 /**
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001274 * You can call this to customize the theme used by your IME's window.
1275 * This must be set before {@link #onCreate}, so you
1276 * will typically call it in your constructor with the resource ID
1277 * of your custom theme.
1278 */
1279 public void setTheme(int theme) {
1280 if (mWindow != null) {
1281 throw new IllegalStateException("Must be called before onCreate()");
1282 }
1283 mTheme = theme;
1284 }
1285
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001286 /**
1287 * Ask that a new activity be started for voice interaction. This will create a
1288 * new dedicated task in the activity manager for this voice interaction session;
1289 * this means that {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK}
1290 * will be set for you to make it a new task.
1291 *
1292 * <p>The newly started activity will be displayed to the user in a special way, as
1293 * a layer under the voice interaction UI.</p>
1294 *
1295 * <p>As the voice activity runs, it can retrieve a {@link android.app.VoiceInteractor}
1296 * through which it can perform voice interactions through your session. These requests
1297 * for voice interactions will appear as callbacks on {@link #onGetSupportedCommands},
Dianne Hackborn2ee5c362015-05-29 17:58:53 -07001298 * {@link #onRequestConfirmation}, {@link #onRequestPickOption},
1299 * {@link #onRequestCompleteVoice}, {@link #onRequestAbortVoice},
1300 * or {@link #onRequestCommand}
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001301 *
1302 * <p>You will receive a call to {@link #onTaskStarted} when the task starts up
1303 * and {@link #onTaskFinished} when the last activity has finished.
1304 *
1305 * @param intent The Intent to start this voice interaction. The given Intent will
1306 * always have {@link Intent#CATEGORY_VOICE Intent.CATEGORY_VOICE} added to it, since
1307 * this is part of a voice interaction.
1308 */
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001309 public void startVoiceActivity(Intent intent) {
1310 if (mToken == null) {
1311 throw new IllegalStateException("Can't call before onCreate()");
1312 }
1313 try {
Dianne Hackborne30e02f2014-05-27 18:24:45 -07001314 intent.migrateExtraStreamToClipData();
Jeff Sharkey344744b2016-01-28 19:03:30 -07001315 intent.prepareToLeaveProcess(mContext);
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001316 int res = mSystemService.startVoiceActivity(mToken, intent,
Philip P. Moltmann9c5226f2020-01-10 08:53:43 -08001317 intent.resolveType(mContext.getContentResolver()), mContext.getFeatureId());
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001318 Instrumentation.checkStartActivityResult(res, intent);
1319 } catch (RemoteException e) {
1320 }
1321 }
1322
Winson Chung83471632016-12-13 11:02:12 -08001323 /**
1324 * <p>Ask that a new assistant activity be started. This will create a new task in the
1325 * in activity manager: this means that
1326 * {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK}
1327 * will be set for you to make it a new task.</p>
1328 *
1329 * <p>The newly started activity will be displayed on top of other activities in the system
1330 * in a new layer that is not affected by multi-window mode. Tasks started from this activity
1331 * will go into the normal activity layer and not this new layer.</p>
1332 *
1333 * <p>By default, the system will create a window for the UI for this session. If you are using
1334 * an assistant activity instead, then you can disable the window creation by calling
1335 * {@link #setUiEnabled} in {@link #onPrepareShow(Bundle, int)}.</p>
1336 */
1337 public void startAssistantActivity(Intent intent) {
1338 if (mToken == null) {
1339 throw new IllegalStateException("Can't call before onCreate()");
1340 }
1341 try {
1342 intent.migrateExtraStreamToClipData();
1343 intent.prepareToLeaveProcess(mContext);
1344 int res = mSystemService.startAssistantActivity(mToken, intent,
Philip P. Moltmann9c5226f2020-01-10 08:53:43 -08001345 intent.resolveType(mContext.getContentResolver()), mContext.getFeatureId());
Winson Chung83471632016-12-13 11:02:12 -08001346 Instrumentation.checkStartActivityResult(res, intent);
1347 } catch (RemoteException e) {
1348 }
1349 }
1350
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001351 /**
Sunny Goyald40c3452019-03-20 12:46:55 -07001352 * Requests a list of supported actions from an app.
1353 *
1354 * @param activityId Ths activity id of the app to get the actions from.
1355 * @param resultExecutor The handler to receive the callback
Svet Ganov3b6be082019-04-28 10:21:01 -07001356 * @param cancellationSignal A signal to cancel the operation in progress,
1357 * or {@code null} if none.
Sunny Goyald40c3452019-03-20 12:46:55 -07001358 * @param callback The callback to receive the response
1359 */
1360 public final void requestDirectActions(@NonNull ActivityId activityId,
Svet Ganov3b6be082019-04-28 10:21:01 -07001361 @Nullable CancellationSignal cancellationSignal,
Sunny Goyald40c3452019-03-20 12:46:55 -07001362 @NonNull @CallbackExecutor Executor resultExecutor,
1363 @NonNull Consumer<List<DirectAction>> callback) {
Svet Ganov3b6be082019-04-28 10:21:01 -07001364 Preconditions.checkNotNull(activityId);
1365 Preconditions.checkNotNull(resultExecutor);
1366 Preconditions.checkNotNull(callback);
Sunny Goyald40c3452019-03-20 12:46:55 -07001367 if (mToken == null) {
1368 throw new IllegalStateException("Can't call before onCreate()");
1369 }
Svet Ganov3b6be082019-04-28 10:21:01 -07001370
1371 if (cancellationSignal != null) {
1372 cancellationSignal.throwIfCanceled();
1373 }
1374
1375 final RemoteCallback cancellationCallback = (cancellationSignal != null)
1376 ? new RemoteCallback(b -> {
1377 if (b != null) {
1378 final IBinder cancellation = b.getBinder(
1379 VoiceInteractor.KEY_CANCELLATION_SIGNAL);
1380 if (cancellation != null) {
1381 cancellationSignal.setRemote(ICancellationSignal.Stub.asInterface(
1382 cancellation));
1383 }
1384 }
1385 })
1386 : null;
1387
Sunny Goyald40c3452019-03-20 12:46:55 -07001388 try {
1389 mSystemService.requestDirectActions(mToken, activityId.getTaskId(),
Svet Ganov3b6be082019-04-28 10:21:01 -07001390 activityId.getAssistToken(), cancellationCallback,
1391 new RemoteCallback(createSafeResultListener((result) -> {
1392 List<DirectAction> list;
1393 if (result == null) {
1394 list = Collections.emptyList();
1395 } else {
1396 final ParceledListSlice<DirectAction> pls = result.getParcelable(
1397 DirectAction.KEY_ACTIONS_LIST);
1398 if (pls != null) {
1399 final List<DirectAction> receivedList = pls.getList();
1400 list = (receivedList != null) ? receivedList : Collections.emptyList();
1401 } else {
1402 list = Collections.emptyList();
1403 }
1404 }
1405 resultExecutor.execute(() -> callback.accept(list));
1406 })));
Sunny Goyald40c3452019-03-20 12:46:55 -07001407 } catch (RemoteException e) {
1408 e.rethrowFromSystemServer();
1409 }
1410 }
1411
1412 /**
1413 * Called when the direct actions are invalidated.
1414 */
1415 public void onDirectActionsInvalidated(@NonNull ActivityId activityId) {
1416
1417 }
1418
1419 /**
1420 * Asks that an action be performed by the app. This will send a request to the app which
1421 * provided this action.
1422 *
1423 * <p> An action could take time to execute and the result is provided asynchronously
1424 * via a callback. If the action is taking longer and you want to cancel its execution
1425 * you can pass in a cancellation signal through which to notify the app to abort the
1426 * action.
1427 *
1428 * @param action The action to be performed.
1429 * @param extras Any optional extras sent to the app as part of the request
1430 * @param cancellationSignal A signal to cancel the operation in progress,
1431 * or {@code null} if none.
1432 * @param resultExecutor The handler to receive the callback.
1433 * @param resultListener The callback to receive the response.
1434 *
Svet Ganov3b6be082019-04-28 10:21:01 -07001435 * @see #requestDirectActions(ActivityId, CancellationSignal, Executor, Consumer)
1436 * @see Activity#onGetDirectActions(CancellationSignal, Consumer)
Sunny Goyald40c3452019-03-20 12:46:55 -07001437 */
1438 public final void performDirectAction(@NonNull DirectAction action, @Nullable Bundle extras,
1439 @Nullable CancellationSignal cancellationSignal,
1440 @NonNull @CallbackExecutor Executor resultExecutor,
1441 @NonNull Consumer<Bundle> resultListener) {
1442 if (mToken == null) {
1443 throw new IllegalStateException("Can't call before onCreate()");
1444 }
1445 Preconditions.checkNotNull(resultExecutor);
1446 Preconditions.checkNotNull(resultListener);
1447
1448 if (cancellationSignal != null) {
1449 cancellationSignal.throwIfCanceled();
1450 }
1451
Svet Ganov3b6be082019-04-28 10:21:01 -07001452 final RemoteCallback cancellationCallback = (cancellationSignal != null)
1453 ? new RemoteCallback(createSafeResultListener(b -> {
1454 if (b != null) {
1455 final IBinder cancellation = b.getBinder(
1456 VoiceInteractor.KEY_CANCELLATION_SIGNAL);
1457 if (cancellation != null) {
1458 cancellationSignal.setRemote(ICancellationSignal.Stub.asInterface(
1459 cancellation));
1460 }
Sunny Goyald40c3452019-03-20 12:46:55 -07001461 }
Svet Ganov3b6be082019-04-28 10:21:01 -07001462 }))
1463 : null;
1464
1465 final RemoteCallback resultCallback = new RemoteCallback(createSafeResultListener(b -> {
1466 if (b != null) {
1467 resultExecutor.execute(() -> resultListener.accept(b));
Sunny Goyald40c3452019-03-20 12:46:55 -07001468 } else {
1469 resultExecutor.execute(() -> resultListener.accept(Bundle.EMPTY));
1470 }
Svet Ganov3b6be082019-04-28 10:21:01 -07001471 }));
Sunny Goyald40c3452019-03-20 12:46:55 -07001472
1473 try {
1474 mSystemService.performDirectAction(mToken, action.getId(), extras,
Svet Ganov3b6be082019-04-28 10:21:01 -07001475 action.getTaskId(), action.getActivityId(), cancellationCallback,
1476 resultCallback);
Sunny Goyald40c3452019-03-20 12:46:55 -07001477 } catch (RemoteException e) {
1478 e.rethrowFromSystemServer();
1479 }
1480 }
1481
1482 /**
Dianne Hackborn3d07c942015-03-13 18:02:54 -07001483 * Set whether this session will keep the device awake while it is running a voice
1484 * activity. By default, the system holds a wake lock for it while in this state,
1485 * so that it can work even if the screen is off. Setting this to false removes that
1486 * wake lock, allowing the CPU to go to sleep. This is typically used if the
1487 * session decides it has been waiting too long for a response from the user and
1488 * doesn't want to let this continue to drain the battery.
1489 *
1490 * <p>Passing false here will release the wake lock, and you can call later with
1491 * true to re-acquire it. It will also be automatically re-acquired for you each
1492 * time you start a new voice activity task -- that is when you call
1493 * {@link #startVoiceActivity}.</p>
1494 */
1495 public void setKeepAwake(boolean keepAwake) {
Dianne Hackborn69c6adc2015-06-02 10:52:59 -07001496 if (mToken == null) {
1497 throw new IllegalStateException("Can't call before onCreate()");
1498 }
Dianne Hackborn3d07c942015-03-13 18:02:54 -07001499 try {
1500 mSystemService.setKeepAwake(mToken, keepAwake);
1501 } catch (RemoteException e) {
1502 }
1503 }
1504
1505 /**
Dianne Hackborn4e88bcd2015-07-01 13:41:03 -07001506 * Request that all system dialogs (and status bar shade etc) be closed, allowing
1507 * access to the session's UI. This will <em>not</em> cause the lock screen to be
1508 * dismissed.
1509 */
1510 public void closeSystemDialogs() {
1511 if (mToken == null) {
1512 throw new IllegalStateException("Can't call before onCreate()");
1513 }
1514 try {
1515 mSystemService.closeSystemDialogs(mToken);
1516 } catch (RemoteException e) {
1517 }
1518 }
1519
1520 /**
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001521 * Convenience for inflating views.
1522 */
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001523 public LayoutInflater getLayoutInflater() {
Dianne Hackborn59da80582017-02-06 17:42:41 -08001524 ensureWindowCreated();
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001525 return mInflater;
1526 }
1527
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001528 /**
1529 * Retrieve the window being used to show the session's UI.
1530 */
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001531 public Dialog getWindow() {
Dianne Hackborn59da80582017-02-06 17:42:41 -08001532 ensureWindowCreated();
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001533 return mWindow;
1534 }
1535
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001536 /**
Dianne Hackborn69c6adc2015-06-02 10:52:59 -07001537 * Finish the session. This completely destroys the session -- the next time it is shown,
1538 * an entirely new one will be created. You do not normally call this function; instead,
1539 * use {@link #hide} and allow the system to destroy your session if it needs its RAM.
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001540 */
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001541 public void finish() {
1542 if (mToken == null) {
1543 throw new IllegalStateException("Can't call before onCreate()");
1544 }
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001545 try {
1546 mSystemService.finish(mToken);
1547 } catch (RemoteException e) {
1548 }
1549 }
1550
Dianne Hackborn2ee5c362015-05-29 17:58:53 -07001551 /**
1552 * Initiatize a new session. At this point you don't know exactly what this
1553 * session will be used for; you will find that out in {@link #onShow}.
1554 */
1555 public void onCreate() {
1556 doOnCreate();
1557 }
1558
Dianne Hackborn2ee5c362015-05-29 17:58:53 -07001559 private void doOnCreate() {
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001560 mTheme = mTheme != 0 ? mTheme
1561 : com.android.internal.R.style.Theme_DeviceDefault_VoiceInteractionSession;
Dianne Hackborn59da80582017-02-06 17:42:41 -08001562 }
1563
1564 /**
1565 * Called prior to {@link #onShow} before any UI setup has occurred. Not generally useful.
1566 *
1567 * @param args The arguments that were supplied to
1568 * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
1569 * @param showFlags The show flags originally provided to
1570 * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
1571 */
1572 public void onPrepareShow(Bundle args, int showFlags) {
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001573 }
1574
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001575 /**
Dianne Hackbornffeecb12015-02-25 11:08:11 -08001576 * Called when the session UI is going to be shown. This is called after
1577 * {@link #onCreateContentView} (if the session's content UI needed to be created) and
1578 * immediately prior to the window being shown. This may be called while the window
1579 * is already shown, if a show request has come in while it is shown, to allow you to
1580 * update the UI to match the new show arguments.
Dianne Hackbornae6688b2015-02-11 17:02:41 -08001581 *
1582 * @param args The arguments that were supplied to
Dianne Hackbornffeecb12015-02-25 11:08:11 -08001583 * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
1584 * @param showFlags The show flags originally provided to
1585 * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
Dianne Hackbornae6688b2015-02-11 17:02:41 -08001586 */
Dianne Hackbornffeecb12015-02-25 11:08:11 -08001587 public void onShow(Bundle args, int showFlags) {
1588 }
1589
1590 /**
1591 * Called immediately after stopping to show the session UI.
1592 */
1593 public void onHide() {
Dianne Hackbornae6688b2015-02-11 17:02:41 -08001594 }
1595
1596 /**
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001597 * Last callback to the session as it is being finished.
1598 */
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001599 public void onDestroy() {
1600 }
1601
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001602 /**
1603 * Hook in which to create the session's UI.
1604 */
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001605 public View onCreateContentView() {
1606 return null;
1607 }
1608
1609 public void setContentView(View view) {
Dianne Hackborn59da80582017-02-06 17:42:41 -08001610 ensureWindowCreated();
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001611 mContentFrame.removeAllViews();
Winson Chungec1ef092017-10-25 16:22:34 -07001612 mContentFrame.addView(view, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
Adam Powell41607d52015-06-17 13:37:06 -07001613 mContentFrame.requestApplyInsets();
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001614 }
1615
Sunny Goyald40c3452019-03-20 12:46:55 -07001616 void doOnHandleAssist(int taskId, IBinder assistToken, Bundle data, AssistStructure structure,
1617 Throwable failure, AssistContent content, int index, int count) {
Dianne Hackborn782d4982015-07-08 17:36:37 -07001618 if (failure != null) {
1619 onAssistStructureFailure(failure);
1620 }
Sunny Goyald40c3452019-03-20 12:46:55 -07001621 AssistState assistState = new AssistState(new ActivityId(taskId, assistToken),
1622 data, structure, content, index, count);
1623 onHandleAssist(assistState);
Amith Yamasanie8222e52016-04-08 15:28:47 -07001624 }
1625
Dianne Hackborn782d4982015-07-08 17:36:37 -07001626 /**
1627 * Called when there has been a failure transferring the {@link AssistStructure} to
1628 * the assistant. This may happen, for example, if the data is too large and results
1629 * in an out of memory exception, or the client has provided corrupt data. This will
1630 * be called immediately before {@link #onHandleAssist} and the AssistStructure supplied
1631 * there afterwards will be null.
1632 *
1633 * @param failure The failure exception that was thrown when building the
1634 * {@link AssistStructure}.
1635 */
1636 public void onAssistStructureFailure(Throwable failure) {
1637 }
1638
1639 /**
1640 * Called to receive data from the application that the user was currently viewing when
Dianne Hackborn17f69352015-07-17 18:04:14 -07001641 * an assist session is started. If the original show request did not specify
1642 * {@link #SHOW_WITH_ASSIST}, this method will not be called.
Dianne Hackborn782d4982015-07-08 17:36:37 -07001643 *
1644 * @param data Arbitrary data supplied by the app through
1645 * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}.
Dianne Hackborn17f69352015-07-17 18:04:14 -07001646 * May be null if assist data has been disabled by the user or device policy.
Dianne Hackborn782d4982015-07-08 17:36:37 -07001647 * @param structure If available, the structure definition of all windows currently
Dianne Hackborn17f69352015-07-17 18:04:14 -07001648 * displayed by the app. May be null if assist data has been disabled by the user
1649 * or device policy; will be an empty stub if the application has disabled assist
1650 * by marking its window as secure.
Dianne Hackborn782d4982015-07-08 17:36:37 -07001651 * @param content Additional content data supplied by the app through
1652 * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}.
Dianne Hackborn17f69352015-07-17 18:04:14 -07001653 * May be null if assist data has been disabled by the user or device policy; will
1654 * not be automatically filled in with data from the app if the app has marked its
1655 * window as secure.
Sunny Goyald40c3452019-03-20 12:46:55 -07001656 *
1657 * @deprecated use {@link #onHandleAssist(AssistState)}
Dianne Hackborn782d4982015-07-08 17:36:37 -07001658 */
Sunny Goyald40c3452019-03-20 12:46:55 -07001659 @Deprecated
Dianne Hackborn17f69352015-07-17 18:04:14 -07001660 public void onHandleAssist(@Nullable Bundle data, @Nullable AssistStructure structure,
1661 @Nullable AssistContent content) {
Dianne Hackborn09d57fe2015-05-27 18:05:52 -07001662 }
1663
Dianne Hackborn782d4982015-07-08 17:36:37 -07001664 /**
Sunny Goyald40c3452019-03-20 12:46:55 -07001665 * Called to receive data from the application that the user was currently viewing when
1666 * an assist session is started. If the original show request did not specify
1667 * {@link #SHOW_WITH_ASSIST}, this method will not be called.
1668 *
1669 * <p>This method is called for all activities along with an index and count that indicates
1670 * which activity the data is for. {@code index} will be between 0 and {@code count}-1 and
1671 * this method is called once for each activity in no particular order. The {@code count}
1672 * indicates how many activities to expect assist data for, including the top focused one.
1673 * The focused activity can be determined by calling {@link AssistState#isFocused()}.
1674 *
1675 * <p>To be responsive to assist requests, process assist data as soon as it is received,
1676 * without waiting for all queued activities to return assist data.
1677 *
1678 * @param state The state object capturing the state of an activity.
1679 */
1680 public void onHandleAssist(@NonNull AssistState state) {
1681 if (state.getIndex() == 0) {
1682 onHandleAssist(state.getAssistData(), state.getAssistStructure(),
1683 state.getAssistContent());
1684 } else {
1685 onHandleAssistSecondary(state.getAssistData(), state.getAssistStructure(),
1686 state.getAssistContent(), state.getIndex(), state.getCount());
1687 }
1688 }
1689
1690 /**
Amith Yamasanie8222e52016-04-08 15:28:47 -07001691 * Called to receive data from other applications that the user was or is interacting with,
1692 * that are currently on the screen in a multi-window display environment, not including the
1693 * currently focused activity. This could be
1694 * a free-form window, a picture-in-picture window, or another window in a split-screen display.
1695 * <p>
1696 * This method is very similar to
1697 * {@link #onHandleAssist} except that it is called
1698 * for additional non-focused activities along with an index and count that indicates
1699 * which additional activity the data is for. {@code index} will be between 1 and
1700 * {@code count}-1 and this method is called once for each additional window, in no particular
1701 * order. The {@code count} indicates how many windows to expect assist data for, including the
1702 * top focused activity, which continues to be returned via {@link #onHandleAssist}.
1703 * <p>
1704 * To be responsive to assist requests, process assist data as soon as it is received,
1705 * without waiting for all queued activities to return assist data.
1706 *
1707 * @param data Arbitrary data supplied by the app through
1708 * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}.
1709 * May be null if assist data has been disabled by the user or device policy.
1710 * @param structure If available, the structure definition of all windows currently
1711 * displayed by the app. May be null if assist data has been disabled by the user
1712 * or device policy; will be an empty stub if the application has disabled assist
1713 * by marking its window as secure.
1714 * @param content Additional content data supplied by the app through
1715 * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}.
1716 * May be null if assist data has been disabled by the user or device policy; will
1717 * not be automatically filled in with data from the app if the app has marked its
1718 * window as secure.
1719 * @param index the index of the additional activity that this data
1720 * is for.
1721 * @param count the total number of additional activities for which the assist data is being
1722 * returned, including the focused activity that is returned via
1723 * {@link #onHandleAssist}.
Sunny Goyald40c3452019-03-20 12:46:55 -07001724 *
1725 * @deprecated use {@link #onHandleAssist(AssistState)}
Amith Yamasanie8222e52016-04-08 15:28:47 -07001726 */
Sunny Goyald40c3452019-03-20 12:46:55 -07001727 @Deprecated
Amith Yamasanie8222e52016-04-08 15:28:47 -07001728 public void onHandleAssistSecondary(@Nullable Bundle data, @Nullable AssistStructure structure,
1729 @Nullable AssistContent content, int index, int count) {
1730 }
1731
1732 /**
Dianne Hackborn782d4982015-07-08 17:36:37 -07001733 * Called to receive a screenshot of what the user was currently viewing when an assist
Dianne Hackborn17f69352015-07-17 18:04:14 -07001734 * session is started. May be null if screenshots are disabled by the user, policy,
1735 * or application. If the original show request did not specify
1736 * {@link #SHOW_WITH_SCREENSHOT}, this method will not be called.
Dianne Hackborn782d4982015-07-08 17:36:37 -07001737 */
Dianne Hackborn17f69352015-07-17 18:04:14 -07001738 public void onHandleScreenshot(@Nullable Bitmap screenshot) {
Dianne Hackborn27eac1d2015-03-16 17:15:53 -07001739 }
1740
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001741 public boolean onKeyDown(int keyCode, KeyEvent event) {
1742 return false;
1743 }
1744
1745 public boolean onKeyLongPress(int keyCode, KeyEvent event) {
1746 return false;
1747 }
1748
1749 public boolean onKeyUp(int keyCode, KeyEvent event) {
1750 return false;
1751 }
1752
1753 public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
1754 return false;
1755 }
1756
Dianne Hackbornd59a5d52015-04-04 14:52:14 -07001757 /**
1758 * Called when the user presses the back button while focus is in the session UI. Note
1759 * that this will only happen if the session UI has requested input focus in its window;
1760 * otherwise, the back key will go to whatever window has focus and do whatever behavior
Dianne Hackborn69c6adc2015-06-02 10:52:59 -07001761 * it normally has there. The default implementation simply calls {@link #hide}.
Dianne Hackbornd59a5d52015-04-04 14:52:14 -07001762 */
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001763 public void onBackPressed() {
Dianne Hackbornffeecb12015-02-25 11:08:11 -08001764 hide();
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001765 }
1766
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001767 /**
1768 * Sessions automatically watch for requests that all system UI be closed (such as when
1769 * the user presses HOME), which will appear here. The default implementation always
Dianne Hackborn69c6adc2015-06-02 10:52:59 -07001770 * calls {@link #hide}.
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001771 */
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001772 public void onCloseSystemDialogs() {
Dianne Hackbornffeecb12015-02-25 11:08:11 -08001773 hide();
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001774 }
1775
Jorim Jaggi19695d92015-07-20 15:51:40 -07001776 /**
1777 * Called when the lockscreen was shown.
1778 */
1779 public void onLockscreenShown() {
1780 hide();
1781 }
1782
Dianne Hackborn1e383822015-04-10 14:02:33 -07001783 @Override
1784 public void onConfigurationChanged(Configuration newConfig) {
1785 }
1786
1787 @Override
1788 public void onLowMemory() {
1789 }
1790
1791 @Override
1792 public void onTrimMemory(int level) {
1793 }
1794
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001795 /**
1796 * Compute the interesting insets into your UI. The default implementation
Dianne Hackbornae6688b2015-02-11 17:02:41 -08001797 * sets {@link Insets#contentInsets outInsets.contentInsets.top} to the height
1798 * of the window, meaning it should not adjust content underneath. The default touchable
1799 * insets are {@link Insets#TOUCHABLE_INSETS_FRAME}, meaning it consumes all touch
1800 * events within its window frame.
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001801 *
1802 * @param outInsets Fill in with the current UI insets.
1803 */
1804 public void onComputeInsets(Insets outInsets) {
Dianne Hackborne30e02f2014-05-27 18:24:45 -07001805 outInsets.contentInsets.left = 0;
Dianne Hackborne30e02f2014-05-27 18:24:45 -07001806 outInsets.contentInsets.bottom = 0;
Dianne Hackbornae6688b2015-02-11 17:02:41 -08001807 outInsets.contentInsets.right = 0;
1808 View decor = getWindow().getWindow().getDecorView();
1809 outInsets.contentInsets.top = decor.getHeight();
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001810 outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_FRAME;
1811 outInsets.touchableRegion.setEmpty();
1812 }
1813
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001814 /**
1815 * Called when a task initiated by {@link #startVoiceActivity(android.content.Intent)}
1816 * has actually started.
1817 *
1818 * @param intent The original {@link Intent} supplied to
1819 * {@link #startVoiceActivity(android.content.Intent)}.
1820 * @param taskId Unique ID of the now running task.
1821 */
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001822 public void onTaskStarted(Intent intent, int taskId) {
1823 }
1824
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001825 /**
1826 * Called when the last activity of a task initiated by
1827 * {@link #startVoiceActivity(android.content.Intent)} has finished. The default
1828 * implementation calls {@link #finish()} on the assumption that this represents
1829 * the completion of a voice action. You can override the implementation if you would
1830 * like a different behavior.
1831 *
1832 * @param intent The original {@link Intent} supplied to
1833 * {@link #startVoiceActivity(android.content.Intent)}.
1834 * @param taskId Unique ID of the finished task.
1835 */
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001836 public void onTaskFinished(Intent intent, int taskId) {
Dianne Hackbornffeecb12015-02-25 11:08:11 -08001837 hide();
Dianne Hackbornc03c9162014-05-02 10:45:59 -07001838 }
1839
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001840 /**
1841 * Request to query for what extended commands the session supports.
1842 *
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001843 * @param commands An array of commands that are being queried.
1844 * @return Return an array of booleans indicating which of each entry in the
1845 * command array is supported. A true entry in the array indicates the command
1846 * is supported; false indicates it is not. The default implementation returns
1847 * an array of all false entries.
1848 */
Dianne Hackborn2ee5c362015-05-29 17:58:53 -07001849 public boolean[] onGetSupportedCommands(String[] commands) {
Dianne Hackborn593334a2015-06-30 14:38:17 -07001850 return new boolean[commands.length];
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001851 }
1852
1853 /**
1854 * Request to confirm with the user before proceeding with an unrecoverable operation,
1855 * corresponding to a {@link android.app.VoiceInteractor.ConfirmationRequest
1856 * VoiceInteractor.ConfirmationRequest}.
1857 *
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001858 * @param request The active request.
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001859 */
Dianne Hackborn2ee5c362015-05-29 17:58:53 -07001860 public void onRequestConfirmation(ConfirmationRequest request) {
Dianne Hackborn2ee5c362015-05-29 17:58:53 -07001861 }
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001862
1863 /**
Dianne Hackborn3d07c942015-03-13 18:02:54 -07001864 * Request for the user to pick one of N options, corresponding to a
1865 * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
1866 *
Dianne Hackborn3d07c942015-03-13 18:02:54 -07001867 * @param request The active request.
Dianne Hackborn3d07c942015-03-13 18:02:54 -07001868 */
Dianne Hackborn2ee5c362015-05-29 17:58:53 -07001869 public void onRequestPickOption(PickOptionRequest request) {
Dianne Hackborn2ee5c362015-05-29 17:58:53 -07001870 }
Dianne Hackborn3d07c942015-03-13 18:02:54 -07001871
1872 /**
Barnaby Jamesd3fdb8b2014-07-07 10:51:06 -07001873 * Request to complete the voice interaction session because the voice activity successfully
1874 * completed its interaction using voice. Corresponds to
1875 * {@link android.app.VoiceInteractor.CompleteVoiceRequest
1876 * VoiceInteractor.CompleteVoiceRequest}. The default implementation just sends an empty
1877 * confirmation back to allow the activity to exit.
1878 *
Barnaby Jamesd3fdb8b2014-07-07 10:51:06 -07001879 * @param request The active request.
Barnaby Jamesd3fdb8b2014-07-07 10:51:06 -07001880 */
Dianne Hackborn2ee5c362015-05-29 17:58:53 -07001881 public void onRequestCompleteVoice(CompleteVoiceRequest request) {
Barnaby Jamesd3fdb8b2014-07-07 10:51:06 -07001882 }
1883
1884 /**
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001885 * Request to abort the voice interaction session because the voice activity can not
1886 * complete its interaction using voice. Corresponds to
1887 * {@link android.app.VoiceInteractor.AbortVoiceRequest
1888 * VoiceInteractor.AbortVoiceRequest}. The default implementation just sends an empty
1889 * confirmation back to allow the activity to exit.
1890 *
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001891 * @param request The active request.
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001892 */
Dianne Hackborn2ee5c362015-05-29 17:58:53 -07001893 public void onRequestAbortVoice(AbortVoiceRequest request) {
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001894 }
1895
1896 /**
1897 * Process an arbitrary extended command from the caller,
1898 * corresponding to a {@link android.app.VoiceInteractor.CommandRequest
1899 * VoiceInteractor.CommandRequest}.
1900 *
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001901 * @param request The active request.
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001902 */
Dianne Hackborn2ee5c362015-05-29 17:58:53 -07001903 public void onRequestCommand(CommandRequest request) {
Dianne Hackborn2ee5c362015-05-29 17:58:53 -07001904 }
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001905
1906 /**
Dianne Hackborn593334a2015-06-30 14:38:17 -07001907 * Called when the {@link android.app.VoiceInteractor} has asked to cancel a {@link Request}
Dianne Hackborn2ee5c362015-05-29 17:58:53 -07001908 * that was previously delivered to {@link #onRequestConfirmation},
1909 * {@link #onRequestPickOption}, {@link #onRequestCompleteVoice}, {@link #onRequestAbortVoice},
1910 * or {@link #onRequestCommand}.
Dianne Hackborna2c076d2014-05-30 16:42:57 -07001911 *
1912 * @param request The request that is being canceled.
1913 */
Dianne Hackborn2ee5c362015-05-29 17:58:53 -07001914 public void onCancelRequest(Request request) {
Dianne Hackborn2ee5c362015-05-29 17:58:53 -07001915 }
Dianne Hackborn57dd7372015-07-27 18:11:14 -07001916
1917 /**
1918 * Print the Service's state into the given stream. This gets invoked by
1919 * {@link VoiceInteractionSessionService} when its Service
1920 * {@link android.app.Service#dump} method is called.
1921 *
1922 * @param prefix Text to print at the front of each line.
1923 * @param fd The raw file descriptor that the dump is being sent to.
1924 * @param writer The PrintWriter to which you should dump your state. This will be
1925 * closed for you after you return.
1926 * @param args additional arguments to the dump request.
1927 */
1928 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
1929 writer.print(prefix); writer.print("mToken="); writer.println(mToken);
1930 writer.print(prefix); writer.print("mTheme=#"); writer.println(Integer.toHexString(mTheme));
Dianne Hackborn59da80582017-02-06 17:42:41 -08001931 writer.print(prefix); writer.print("mUiEnabled="); writer.println(mUiEnabled);
1932 writer.print(" mInitialized="); writer.println(mInitialized);
Dianne Hackborn57dd7372015-07-27 18:11:14 -07001933 writer.print(prefix); writer.print("mWindowAdded="); writer.print(mWindowAdded);
1934 writer.print(" mWindowVisible="); writer.println(mWindowVisible);
1935 writer.print(prefix); writer.print("mWindowWasVisible="); writer.print(mWindowWasVisible);
1936 writer.print(" mInShowWindow="); writer.println(mInShowWindow);
1937 if (mActiveRequests.size() > 0) {
1938 writer.print(prefix); writer.println("Active requests:");
1939 String innerPrefix = prefix + " ";
1940 for (int i=0; i<mActiveRequests.size(); i++) {
1941 Request req = mActiveRequests.valueAt(i);
1942 writer.print(prefix); writer.print(" #"); writer.print(i);
1943 writer.print(": ");
1944 writer.println(req);
1945 req.dump(innerPrefix, fd, writer, args);
1946
1947 }
1948 }
1949 }
Sunny Goyald40c3452019-03-20 12:46:55 -07001950
Svet Ganov3b6be082019-04-28 10:21:01 -07001951 private SafeResultListener createSafeResultListener(
1952 @NonNull Consumer<Bundle> consumer) {
1953 synchronized (this) {
1954 final SafeResultListener listener = new SafeResultListener(consumer, this);
1955 mRemoteCallbacks.put(listener, consumer);
1956 return listener;
Sunny Goyald40c3452019-03-20 12:46:55 -07001957 }
Svet Ganov3b6be082019-04-28 10:21:01 -07001958 }
Sunny Goyald40c3452019-03-20 12:46:55 -07001959
Svet Ganov3b6be082019-04-28 10:21:01 -07001960 private Consumer<Bundle> removeSafeResultListener(@NonNull SafeResultListener listener) {
1961 synchronized (this) {
1962 return mRemoteCallbacks.remove(listener);
Sunny Goyald40c3452019-03-20 12:46:55 -07001963 }
1964 }
1965
1966 /**
1967 * Represents assist state captured when this session was started.
1968 * It contains the various assist data objects and a reference to
1969 * the source activity.
1970 */
1971 @Immutable
1972 public static final class AssistState {
1973 private final @NonNull ActivityId mActivityId;
1974 private final int mIndex;
1975 private final int mCount;
1976 private final @Nullable Bundle mData;
1977 private final @Nullable AssistStructure mStructure;
1978 private final @Nullable AssistContent mContent;
1979
1980 AssistState(@NonNull ActivityId activityId, @Nullable Bundle data,
1981 @Nullable AssistStructure structure, @Nullable AssistContent content,
1982 int index, int count) {
1983 mActivityId = activityId;
1984 mIndex = index;
1985 mCount = count;
1986 mData = data;
1987 mStructure = structure;
1988 mContent = content;
1989 }
1990
1991 /**
1992 * @return whether the source activity is focused.
1993 */
1994 public boolean isFocused() {
1995 return mIndex == 0;
1996 }
1997
1998 /**
Svet Ganov07e5fb22019-04-30 11:07:09 -07001999 * @return the index of the activity that this state is for or -1
2000 * if there was no assist data captured.
Sunny Goyald40c3452019-03-20 12:46:55 -07002001 */
2002 public @IntRange(from = -1) int getIndex() {
2003 return mIndex;
2004 }
2005
2006 /**s
2007 * @return the total number of activities for which the assist data is
2008 * being returned.
2009 */
2010 public @IntRange(from = 0) int getCount() {
2011 return mCount;
2012 }
2013
2014 /**
2015 * @return the id of the source activity
2016 */
2017 public @NonNull ActivityId getActivityId() {
2018 return mActivityId;
2019 }
2020
2021 /**
2022 * @return Arbitrary data supplied by the app through
2023 * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}.
2024 * May be null if assist data has been disabled by the user or device policy.
2025 */
2026 public @Nullable Bundle getAssistData() {
2027 return mData;
2028 }
2029
2030 /**
2031 * @return If available, the structure definition of all windows currently
2032 * displayed by the app. May be null if assist data has been disabled by the user
2033 * or device policy; will be an empty stub if the application has disabled assist
2034 * by marking its window as secure.
2035 */
2036 public @Nullable AssistStructure getAssistStructure() {
2037 return mStructure;
2038 }
2039
2040 /**
2041 * @return Additional content data supplied by the app through
2042 * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}.
2043 * May be null if assist data has been disabled by the user or device policy; will
2044 * not be automatically filled in with data from the app if the app has marked its
2045 * window as secure.
2046 */
2047 public @Nullable AssistContent getAssistContent() {
2048 return mContent;
2049 }
2050 }
2051
2052 /**
Svet Ganov07e5fb22019-04-30 11:07:09 -07002053 * Represents the id of an assist source activity. You can use
2054 * {@link #equals(Object)} to compare instances of this class.
Sunny Goyald40c3452019-03-20 12:46:55 -07002055 */
2056 public static class ActivityId {
2057 private final int mTaskId;
2058 private final IBinder mAssistToken;
2059
2060 ActivityId(int taskId, IBinder assistToken) {
2061 mTaskId = taskId;
2062 mAssistToken = assistToken;
2063 }
2064
2065 int getTaskId() {
2066 return mTaskId;
2067 }
2068
2069 IBinder getAssistToken() {
2070 return mAssistToken;
2071 }
2072
2073 @Override
2074 public boolean equals(Object o) {
2075 if (this == o) {
2076 return true;
2077 }
2078 if (o == null || getClass() != o.getClass()) {
2079 return false;
2080 }
2081
2082 ActivityId that = (ActivityId) o;
2083
2084 if (mTaskId != that.mTaskId) {
2085 return false;
2086 }
2087 return mAssistToken != null
2088 ? mAssistToken.equals(that.mAssistToken)
2089 : that.mAssistToken == null;
2090 }
2091
2092 @Override
2093 public int hashCode() {
2094 int result = mTaskId;
2095 result = 31 * result + (mAssistToken != null ? mAssistToken.hashCode() : 0);
2096 return result;
2097 }
2098 }
Svet Ganov3b6be082019-04-28 10:21:01 -07002099
2100 private static class SafeResultListener implements RemoteCallback.OnResultListener {
2101 private final @NonNull WeakReference<VoiceInteractionSession> mWeakSession;
2102
2103 SafeResultListener(@NonNull Consumer<Bundle> action,
2104 @NonNull VoiceInteractionSession session) {
2105 mWeakSession = new WeakReference<>(session);
2106 }
2107
2108 @Override
2109 public void onResult(Bundle result) {
2110 final VoiceInteractionSession session = mWeakSession.get();
2111 if (session != null) {
2112 final Consumer<Bundle> consumer = session.removeSafeResultListener(this);
2113 if (consumer != null) {
2114 consumer.accept(result);
2115 }
2116 }
2117 }
2118 }
Dianne Hackborn91097de2014-04-04 18:02:06 -07002119}