blob: 6da3818c5d055b8b32de61bfb76499775b9a8587 [file] [log] [blame]
Govinda Wassermanc7495cd2019-05-20 14:43:28 -04001/*
2 * Copyright (C) 2019 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 com.android.systemui.assist;
18
Govinda Wasserman5dfe9652019-05-30 13:24:24 -040019import android.content.ComponentName;
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040020import android.content.Context;
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040021import android.os.Handler;
22import android.os.SystemClock;
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040023import android.util.Log;
24
Govinda Wasserman2c113402019-05-24 14:11:24 -040025import androidx.annotation.Nullable;
26
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040027import com.android.internal.annotations.VisibleForTesting;
Govinda Wasserman5dfe9652019-05-30 13:24:24 -040028import com.android.internal.app.AssistUtils;
Govinda Wasserman2c113402019-05-24 14:11:24 -040029import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
Govinda Wasserman5dfe9652019-05-30 13:24:24 -040030import com.android.keyguard.KeyguardUpdateMonitor;
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040031import com.android.systemui.Dependency;
Govinda Wasserman48cdd682019-06-04 12:15:23 -040032import com.android.systemui.DumpController;
33import com.android.systemui.Dumpable;
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040034import com.android.systemui.ScreenDecorations;
35import com.android.systemui.SysUiServiceProvider;
36import com.android.systemui.shared.system.QuickStepContract;
37import com.android.systemui.statusbar.phone.NavigationModeController;
38
Govinda Wasserman48cdd682019-06-04 12:15:23 -040039import java.io.FileDescriptor;
40import java.io.PrintWriter;
Govinda Wasserman5dfe9652019-05-30 13:24:24 -040041import java.util.EnumMap;
42import java.util.Map;
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040043import java.util.concurrent.TimeUnit;
44import java.util.function.Supplier;
45
46/**
47 * A class for managing Assistant handle logic.
48 *
49 * Controls when visual handles for Assistant gesture affordance should be shown or hidden using an
50 * {@link AssistHandleBehavior}.
51 */
Govinda Wasserman48cdd682019-06-04 12:15:23 -040052public final class AssistHandleBehaviorController implements AssistHandleCallbacks, Dumpable {
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040053
54 private static final String TAG = "AssistHandleBehavior";
Govinda Wasserman2c113402019-05-24 14:11:24 -040055
Govinda Wasserman48cdd682019-06-04 12:15:23 -040056 private static final long DEFAULT_SHOWN_FREQUENCY_THRESHOLD_MS = 0;
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040057 private static final long DEFAULT_SHOW_AND_GO_DURATION_MS = TimeUnit.SECONDS.toMillis(3);
Govinda Wasserman1f606b02019-05-29 09:16:02 -040058
59 /**
60 * This is the default behavior that will be used once the system is up. It will be set once the
61 * behavior dependencies are available. This ensures proper behavior lifecycle.
62 */
63 private static final AssistHandleBehavior DEFAULT_BEHAVIOR = AssistHandleBehavior.REMINDER_EXP;
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040064
65 private final Context mContext;
Govinda Wasserman5dfe9652019-05-30 13:24:24 -040066 private final AssistUtils mAssistUtils;
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040067 private final Handler mHandler;
68 private final Runnable mHideHandles = this::hideHandles;
Govinda Wasserman48cdd682019-06-04 12:15:23 -040069 private final Runnable mShowAndGo = this::showAndGoInternal;
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040070 private final Supplier<ScreenDecorations> mScreenDecorationsSupplier;
Govinda Wasserman48cdd682019-06-04 12:15:23 -040071 private final PhenotypeHelper mPhenotypeHelper;
Govinda Wasserman5dfe9652019-05-30 13:24:24 -040072 private final Map<AssistHandleBehavior, BehaviorController> mBehaviorMap =
73 new EnumMap<>(AssistHandleBehavior.class);
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040074
75 private boolean mHandlesShowing = false;
76 private long mHandlesLastHiddenAt;
Govinda Wassermane9ff1ff2019-07-31 15:26:33 -040077 private long mShowAndGoEndsAt;
Govinda Wasserman1f606b02019-05-29 09:16:02 -040078 /**
79 * This should always be initialized as {@link AssistHandleBehavior#OFF} to ensure proper
80 * behavior lifecycle.
81 */
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040082 private AssistHandleBehavior mCurrentBehavior = AssistHandleBehavior.OFF;
83 private boolean mInGesturalMode;
84
Govinda Wasserman5dfe9652019-05-30 13:24:24 -040085 AssistHandleBehaviorController(Context context, AssistUtils assistUtils, Handler handler) {
86 this(
87 context,
88 assistUtils,
Govinda Wasserman48cdd682019-06-04 12:15:23 -040089 handler,
90 () -> SysUiServiceProvider.getComponent(context, ScreenDecorations.class),
91 new PhenotypeHelper(),
Govinda Wasserman5dfe9652019-05-30 13:24:24 -040092 /* testBehavior = */ null);
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040093 }
94
95 @VisibleForTesting
96 AssistHandleBehaviorController(
97 Context context,
Govinda Wasserman5dfe9652019-05-30 13:24:24 -040098 AssistUtils assistUtils,
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040099 Handler handler,
Govinda Wasserman5dfe9652019-05-30 13:24:24 -0400100 Supplier<ScreenDecorations> screenDecorationsSupplier,
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400101 PhenotypeHelper phenotypeHelper,
Govinda Wasserman5dfe9652019-05-30 13:24:24 -0400102 @Nullable BehaviorController testBehavior) {
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400103 mContext = context;
Govinda Wasserman5dfe9652019-05-30 13:24:24 -0400104 mAssistUtils = assistUtils;
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400105 mHandler = handler;
106 mScreenDecorationsSupplier = screenDecorationsSupplier;
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400107 mPhenotypeHelper = phenotypeHelper;
Govinda Wasserman5dfe9652019-05-30 13:24:24 -0400108 mBehaviorMap.put(AssistHandleBehavior.OFF, new AssistHandleOffBehavior());
109 mBehaviorMap.put(AssistHandleBehavior.LIKE_HOME, new AssistHandleLikeHomeBehavior());
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400110 mBehaviorMap.put(
111 AssistHandleBehavior.REMINDER_EXP,
112 new AssistHandleReminderExpBehavior(handler, phenotypeHelper));
Govinda Wasserman5dfe9652019-05-30 13:24:24 -0400113 if (testBehavior != null) {
114 mBehaviorMap.put(AssistHandleBehavior.TEST, testBehavior);
115 }
116
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400117 mInGesturalMode = QuickStepContract.isGesturalMode(
118 Dependency.get(NavigationModeController.class)
119 .addListener(this::handleNavigationModeChange));
120
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400121 setBehavior(getBehaviorMode());
122 mPhenotypeHelper.addOnPropertiesChangedListener(
Govinda Wasserman2c113402019-05-24 14:11:24 -0400123 mHandler::post,
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400124 (properties) -> {
125 if (properties.getKeyset().contains(
126 SystemUiDeviceConfigFlags.ASSIST_HANDLES_BEHAVIOR_MODE)) {
127 setBehavior(properties.getString(
128 SystemUiDeviceConfigFlags.ASSIST_HANDLES_BEHAVIOR_MODE, null));
Govinda Wasserman2c113402019-05-24 14:11:24 -0400129 }
130 });
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400131 Dependency.get(DumpController.class).addListener(this);
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400132 }
133
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400134 @Override // AssistHandleCallbacks
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400135 public void hide() {
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400136 clearPendingCommands();
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400137 mHandler.post(mHideHandles);
138 }
139
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400140 @Override // AssistHandleCallbacks
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400141 public void showAndGo() {
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400142 clearPendingCommands();
143 mHandler.post(mShowAndGo);
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400144 }
145
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400146 private void showAndGoInternal() {
147 maybeShowHandles(/* ignoreThreshold = */ false);
Govinda Wassermane9ff1ff2019-07-31 15:26:33 -0400148 long showAndGoDuration = getShowAndGoDuration();
149 mShowAndGoEndsAt = SystemClock.elapsedRealtime() + showAndGoDuration;
150 mHandler.postDelayed(mHideHandles, showAndGoDuration);
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400151 }
152
153 @Override // AssistHandleCallbacks
154 public void showAndGoDelayed(long delayMs, boolean hideIfShowing) {
155 clearPendingCommands();
156 if (hideIfShowing) {
157 mHandler.post(mHideHandles);
158 }
159 mHandler.postDelayed(mShowAndGo, delayMs);
160 }
161
162 @Override // AssistHandleCallbacks
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400163 public void showAndStay() {
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400164 clearPendingCommands();
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400165 mHandler.post(() -> maybeShowHandles(/* ignoreThreshold = */ true));
166 }
167
Govinda Wassermane9ff1ff2019-07-31 15:26:33 -0400168 public long getShowAndGoRemainingTimeMs() {
169 return Long.max(mShowAndGoEndsAt - SystemClock.elapsedRealtime(), 0);
170 }
171
Govinda Wassermanb7cd1622019-05-31 11:59:19 -0400172 boolean areHandlesShowing() {
173 return mHandlesShowing;
174 }
175
Govinda Wasserman1f606b02019-05-29 09:16:02 -0400176 void onAssistantGesturePerformed() {
Govinda Wasserman5dfe9652019-05-30 13:24:24 -0400177 mBehaviorMap.get(mCurrentBehavior).onAssistantGesturePerformed();
Govinda Wasserman1f606b02019-05-29 09:16:02 -0400178 }
179
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400180 void setBehavior(AssistHandleBehavior behavior) {
181 if (mCurrentBehavior == behavior) {
182 return;
183 }
184
Govinda Wasserman5dfe9652019-05-30 13:24:24 -0400185 if (!mBehaviorMap.containsKey(behavior)) {
186 Log.e(TAG, "Unsupported behavior requested: " + behavior.toString());
187 return;
188 }
189
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400190 if (mInGesturalMode) {
Govinda Wasserman5dfe9652019-05-30 13:24:24 -0400191 mBehaviorMap.get(mCurrentBehavior).onModeDeactivated();
192 mBehaviorMap.get(behavior).onModeActivated(mContext, /* callbacks = */ this);
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400193 }
194
195 mCurrentBehavior = behavior;
196 }
197
Govinda Wasserman2c113402019-05-24 14:11:24 -0400198 private void setBehavior(@Nullable String behavior) {
199 try {
200 setBehavior(AssistHandleBehavior.valueOf(behavior));
201 } catch (IllegalArgumentException | NullPointerException e) {
202 Log.e(TAG, "Invalid behavior: " + behavior, e);
203 }
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400204 }
205
Govinda Wasserman5dfe9652019-05-30 13:24:24 -0400206 private boolean handlesUnblocked(boolean ignoreThreshold) {
207 long timeSinceHidden = SystemClock.elapsedRealtime() - mHandlesLastHiddenAt;
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400208 boolean notThrottled = ignoreThreshold || timeSinceHidden >= getShownFrequencyThreshold();
Govinda Wasserman5dfe9652019-05-30 13:24:24 -0400209 ComponentName assistantComponent =
210 mAssistUtils.getAssistComponentForUser(KeyguardUpdateMonitor.getCurrentUser());
211 return notThrottled && assistantComponent != null;
212 }
213
Govinda Wasserman2c113402019-05-24 14:11:24 -0400214 private long getShownFrequencyThreshold() {
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400215 return mPhenotypeHelper.getLong(
Govinda Wasserman2c113402019-05-24 14:11:24 -0400216 SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS,
217 DEFAULT_SHOWN_FREQUENCY_THRESHOLD_MS);
Govinda Wasserman2c113402019-05-24 14:11:24 -0400218 }
219
220 private long getShowAndGoDuration() {
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400221 return mPhenotypeHelper.getLong(
Govinda Wasserman2c113402019-05-24 14:11:24 -0400222 SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS,
223 DEFAULT_SHOW_AND_GO_DURATION_MS);
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400224 }
225
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400226 private String getBehaviorMode() {
227 return mPhenotypeHelper.getString(
228 SystemUiDeviceConfigFlags.ASSIST_HANDLES_BEHAVIOR_MODE,
229 DEFAULT_BEHAVIOR.toString());
230 }
231
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400232 private void maybeShowHandles(boolean ignoreThreshold) {
233 if (mHandlesShowing) {
234 return;
235 }
236
Govinda Wasserman5dfe9652019-05-30 13:24:24 -0400237 if (handlesUnblocked(ignoreThreshold)) {
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400238 ScreenDecorations screenDecorations = mScreenDecorationsSupplier.get();
239 if (screenDecorations == null) {
240 Log.w(TAG, "Couldn't show handles, ScreenDecorations unavailable");
241 } else {
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400242 mHandlesShowing = true;
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400243 screenDecorations.setAssistHintVisible(true);
244 }
245 }
246 }
247
248 private void hideHandles() {
249 if (!mHandlesShowing) {
250 return;
251 }
252
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400253 ScreenDecorations screenDecorations = mScreenDecorationsSupplier.get();
254 if (screenDecorations == null) {
255 Log.w(TAG, "Couldn't hide handles, ScreenDecorations unavailable");
256 } else {
Govinda Wasserman24ca1bb2019-06-11 18:21:38 -0400257 mHandlesShowing = false;
258 mHandlesLastHiddenAt = SystemClock.elapsedRealtime();
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400259 screenDecorations.setAssistHintVisible(false);
260 }
261 }
262
263 private void handleNavigationModeChange(int navigationMode) {
264 boolean inGesturalMode = QuickStepContract.isGesturalMode(navigationMode);
265 if (mInGesturalMode == inGesturalMode) {
266 return;
267 }
268
269 mInGesturalMode = inGesturalMode;
270 if (mInGesturalMode) {
Govinda Wasserman5dfe9652019-05-30 13:24:24 -0400271 mBehaviorMap.get(mCurrentBehavior).onModeActivated(mContext, /* callbacks = */ this);
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400272 } else {
Govinda Wasserman5dfe9652019-05-30 13:24:24 -0400273 mBehaviorMap.get(mCurrentBehavior).onModeDeactivated();
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400274 hide();
275 }
276 }
277
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400278 private void clearPendingCommands() {
279 mHandler.removeCallbacks(mHideHandles);
280 mHandler.removeCallbacks(mShowAndGo);
Govinda Wassermane9ff1ff2019-07-31 15:26:33 -0400281 mShowAndGoEndsAt = 0;
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400282 }
283
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400284 @VisibleForTesting
285 void setInGesturalModeForTest(boolean inGesturalMode) {
286 mInGesturalMode = inGesturalMode;
287 }
288
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400289 @Override // Dumpable
290 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
291 pw.println("Current AssistHandleBehaviorController State:");
292
293 pw.println(" mHandlesShowing=" + mHandlesShowing);
294 pw.println(" mHandlesLastHiddenAt=" + mHandlesLastHiddenAt);
295 pw.println(" mInGesturalMode=" + mInGesturalMode);
296
297 pw.println(" Phenotype Flags:");
298 pw.println(" "
299 + SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS
300 + "="
301 + getShowAndGoDuration());
302 pw.println(" "
303 + SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS
304 + "="
305 + getShownFrequencyThreshold());
306 pw.println(" "
307 + SystemUiDeviceConfigFlags.ASSIST_HANDLES_BEHAVIOR_MODE
308 + "="
309 + getBehaviorMode());
310
311 pw.println(" mCurrentBehavior=" + mCurrentBehavior.toString());
312 mBehaviorMap.get(mCurrentBehavior).dump(pw, " ");
313 }
314
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400315 interface BehaviorController {
316 void onModeActivated(Context context, AssistHandleCallbacks callbacks);
Govinda Wasserman1f606b02019-05-29 09:16:02 -0400317 default void onModeDeactivated() {}
318 default void onAssistantGesturePerformed() {}
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400319 default void dump(PrintWriter pw, String prefix) {}
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400320 }
321}