blob: 738ec80a40c4d2ee49ad779fae68581f0acb81dd [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 Wasserman3fcd7172019-09-05 16:32:23 -040019import static com.android.systemui.assist.AssistModule.ASSIST_HANDLE_THREAD_NAME;
20
Govinda Wasserman5dfe9652019-05-30 13:24:24 -040021import android.content.ComponentName;
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040022import android.content.Context;
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040023import android.os.Handler;
24import android.os.SystemClock;
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040025import android.util.Log;
26
Govinda Wasserman2c113402019-05-24 14:11:24 -040027import androidx.annotation.Nullable;
28
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040029import com.android.internal.annotations.VisibleForTesting;
Govinda Wasserman5dfe9652019-05-30 13:24:24 -040030import com.android.internal.app.AssistUtils;
Govinda Wasserman2c113402019-05-24 14:11:24 -040031import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
Govinda Wasserman5dfe9652019-05-30 13:24:24 -040032import com.android.keyguard.KeyguardUpdateMonitor;
Govinda Wasserman48cdd682019-06-04 12:15:23 -040033import com.android.systemui.DumpController;
34import com.android.systemui.Dumpable;
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040035import com.android.systemui.ScreenDecorations;
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040036import 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.Map;
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040042import java.util.concurrent.TimeUnit;
Govinda Wasserman3fcd7172019-09-05 16:32:23 -040043
44import javax.inject.Inject;
45import javax.inject.Named;
46import javax.inject.Provider;
47import javax.inject.Singleton;
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040048
49/**
50 * A class for managing Assistant handle logic.
51 *
52 * Controls when visual handles for Assistant gesture affordance should be shown or hidden using an
53 * {@link AssistHandleBehavior}.
54 */
Govinda Wasserman3fcd7172019-09-05 16:32:23 -040055@Singleton
Govinda Wasserman48cdd682019-06-04 12:15:23 -040056public final class AssistHandleBehaviorController implements AssistHandleCallbacks, Dumpable {
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040057
58 private static final String TAG = "AssistHandleBehavior";
Govinda Wasserman2c113402019-05-24 14:11:24 -040059
Govinda Wasserman48cdd682019-06-04 12:15:23 -040060 private static final long DEFAULT_SHOWN_FREQUENCY_THRESHOLD_MS = 0;
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040061 private static final long DEFAULT_SHOW_AND_GO_DURATION_MS = TimeUnit.SECONDS.toMillis(3);
Govinda Wasserman1f606b02019-05-29 09:16:02 -040062
63 /**
64 * This is the default behavior that will be used once the system is up. It will be set once the
65 * behavior dependencies are available. This ensures proper behavior lifecycle.
66 */
67 private static final AssistHandleBehavior DEFAULT_BEHAVIOR = AssistHandleBehavior.REMINDER_EXP;
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040068
69 private final Context mContext;
Govinda Wasserman5dfe9652019-05-30 13:24:24 -040070 private final AssistUtils mAssistUtils;
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040071 private final Handler mHandler;
72 private final Runnable mHideHandles = this::hideHandles;
Govinda Wasserman48cdd682019-06-04 12:15:23 -040073 private final Runnable mShowAndGo = this::showAndGoInternal;
Govinda Wasserman3fcd7172019-09-05 16:32:23 -040074 private final Provider<ScreenDecorations> mScreenDecorations;
Govinda Wasserman48cdd682019-06-04 12:15:23 -040075 private final PhenotypeHelper mPhenotypeHelper;
Govinda Wasserman3fcd7172019-09-05 16:32:23 -040076 private final Map<AssistHandleBehavior, BehaviorController> mBehaviorMap;
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040077
78 private boolean mHandlesShowing = false;
79 private long mHandlesLastHiddenAt;
Govinda Wassermane9ff1ff2019-07-31 15:26:33 -040080 private long mShowAndGoEndsAt;
Govinda Wasserman1f606b02019-05-29 09:16:02 -040081 /**
82 * This should always be initialized as {@link AssistHandleBehavior#OFF} to ensure proper
83 * behavior lifecycle.
84 */
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040085 private AssistHandleBehavior mCurrentBehavior = AssistHandleBehavior.OFF;
86 private boolean mInGesturalMode;
87
Govinda Wasserman3fcd7172019-09-05 16:32:23 -040088 @Inject
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040089 AssistHandleBehaviorController(
90 Context context,
Govinda Wasserman5dfe9652019-05-30 13:24:24 -040091 AssistUtils assistUtils,
Govinda Wasserman3fcd7172019-09-05 16:32:23 -040092 @Named(ASSIST_HANDLE_THREAD_NAME) Handler handler,
93 Provider<ScreenDecorations> screenDecorations,
Govinda Wasserman48cdd682019-06-04 12:15:23 -040094 PhenotypeHelper phenotypeHelper,
Govinda Wasserman3fcd7172019-09-05 16:32:23 -040095 Map<AssistHandleBehavior, BehaviorController> behaviorMap,
96 NavigationModeController navigationModeController,
97 DumpController dumpController) {
Govinda Wassermanc7495cd2019-05-20 14:43:28 -040098 mContext = context;
Govinda Wasserman5dfe9652019-05-30 13:24:24 -040099 mAssistUtils = assistUtils;
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400100 mHandler = handler;
Govinda Wasserman3fcd7172019-09-05 16:32:23 -0400101 mScreenDecorations = screenDecorations;
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400102 mPhenotypeHelper = phenotypeHelper;
Govinda Wasserman3fcd7172019-09-05 16:32:23 -0400103 mBehaviorMap = behaviorMap;
Govinda Wasserman5dfe9652019-05-30 13:24:24 -0400104
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400105 mInGesturalMode = QuickStepContract.isGesturalMode(
Govinda Wasserman3fcd7172019-09-05 16:32:23 -0400106 navigationModeController.addListener(this::handleNavigationModeChange));
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400107
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400108 setBehavior(getBehaviorMode());
109 mPhenotypeHelper.addOnPropertiesChangedListener(
Govinda Wasserman2c113402019-05-24 14:11:24 -0400110 mHandler::post,
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400111 (properties) -> {
112 if (properties.getKeyset().contains(
113 SystemUiDeviceConfigFlags.ASSIST_HANDLES_BEHAVIOR_MODE)) {
114 setBehavior(properties.getString(
115 SystemUiDeviceConfigFlags.ASSIST_HANDLES_BEHAVIOR_MODE, null));
Govinda Wasserman2c113402019-05-24 14:11:24 -0400116 }
117 });
Govinda Wasserman3fcd7172019-09-05 16:32:23 -0400118
119 dumpController.addListener(this);
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400120 }
121
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400122 @Override // AssistHandleCallbacks
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400123 public void hide() {
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400124 clearPendingCommands();
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400125 mHandler.post(mHideHandles);
126 }
127
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400128 @Override // AssistHandleCallbacks
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400129 public void showAndGo() {
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400130 clearPendingCommands();
131 mHandler.post(mShowAndGo);
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400132 }
133
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400134 private void showAndGoInternal() {
135 maybeShowHandles(/* ignoreThreshold = */ false);
Govinda Wassermane9ff1ff2019-07-31 15:26:33 -0400136 long showAndGoDuration = getShowAndGoDuration();
137 mShowAndGoEndsAt = SystemClock.elapsedRealtime() + showAndGoDuration;
138 mHandler.postDelayed(mHideHandles, showAndGoDuration);
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400139 }
140
141 @Override // AssistHandleCallbacks
142 public void showAndGoDelayed(long delayMs, boolean hideIfShowing) {
143 clearPendingCommands();
144 if (hideIfShowing) {
145 mHandler.post(mHideHandles);
146 }
147 mHandler.postDelayed(mShowAndGo, delayMs);
148 }
149
150 @Override // AssistHandleCallbacks
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400151 public void showAndStay() {
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400152 clearPendingCommands();
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400153 mHandler.post(() -> maybeShowHandles(/* ignoreThreshold = */ true));
154 }
155
Govinda Wassermane9ff1ff2019-07-31 15:26:33 -0400156 public long getShowAndGoRemainingTimeMs() {
157 return Long.max(mShowAndGoEndsAt - SystemClock.elapsedRealtime(), 0);
158 }
159
Govinda Wassermanb7cd1622019-05-31 11:59:19 -0400160 boolean areHandlesShowing() {
161 return mHandlesShowing;
162 }
163
Govinda Wasserman1f606b02019-05-29 09:16:02 -0400164 void onAssistantGesturePerformed() {
Govinda Wasserman5dfe9652019-05-30 13:24:24 -0400165 mBehaviorMap.get(mCurrentBehavior).onAssistantGesturePerformed();
Govinda Wasserman1f606b02019-05-29 09:16:02 -0400166 }
167
Govinda Wasserman40c043a2019-08-12 12:22:13 -0400168 void onAssistHandlesRequested() {
169 if (mInGesturalMode) {
170 mBehaviorMap.get(mCurrentBehavior).onAssistHandlesRequested();
171 }
172 }
173
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400174 void setBehavior(AssistHandleBehavior behavior) {
175 if (mCurrentBehavior == behavior) {
176 return;
177 }
178
Govinda Wasserman5dfe9652019-05-30 13:24:24 -0400179 if (!mBehaviorMap.containsKey(behavior)) {
180 Log.e(TAG, "Unsupported behavior requested: " + behavior.toString());
181 return;
182 }
183
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400184 if (mInGesturalMode) {
Govinda Wasserman5dfe9652019-05-30 13:24:24 -0400185 mBehaviorMap.get(mCurrentBehavior).onModeDeactivated();
186 mBehaviorMap.get(behavior).onModeActivated(mContext, /* callbacks = */ this);
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400187 }
188
189 mCurrentBehavior = behavior;
190 }
191
Govinda Wasserman2c113402019-05-24 14:11:24 -0400192 private void setBehavior(@Nullable String behavior) {
193 try {
194 setBehavior(AssistHandleBehavior.valueOf(behavior));
195 } catch (IllegalArgumentException | NullPointerException e) {
196 Log.e(TAG, "Invalid behavior: " + behavior, e);
197 }
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400198 }
199
Govinda Wasserman5dfe9652019-05-30 13:24:24 -0400200 private boolean handlesUnblocked(boolean ignoreThreshold) {
201 long timeSinceHidden = SystemClock.elapsedRealtime() - mHandlesLastHiddenAt;
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400202 boolean notThrottled = ignoreThreshold || timeSinceHidden >= getShownFrequencyThreshold();
Govinda Wasserman5dfe9652019-05-30 13:24:24 -0400203 ComponentName assistantComponent =
204 mAssistUtils.getAssistComponentForUser(KeyguardUpdateMonitor.getCurrentUser());
205 return notThrottled && assistantComponent != null;
206 }
207
Govinda Wasserman2c113402019-05-24 14:11:24 -0400208 private long getShownFrequencyThreshold() {
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400209 return mPhenotypeHelper.getLong(
Govinda Wasserman2c113402019-05-24 14:11:24 -0400210 SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS,
211 DEFAULT_SHOWN_FREQUENCY_THRESHOLD_MS);
Govinda Wasserman2c113402019-05-24 14:11:24 -0400212 }
213
214 private long getShowAndGoDuration() {
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400215 return mPhenotypeHelper.getLong(
Govinda Wasserman2c113402019-05-24 14:11:24 -0400216 SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS,
217 DEFAULT_SHOW_AND_GO_DURATION_MS);
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400218 }
219
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400220 private String getBehaviorMode() {
221 return mPhenotypeHelper.getString(
222 SystemUiDeviceConfigFlags.ASSIST_HANDLES_BEHAVIOR_MODE,
223 DEFAULT_BEHAVIOR.toString());
224 }
225
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400226 private void maybeShowHandles(boolean ignoreThreshold) {
227 if (mHandlesShowing) {
228 return;
229 }
230
Govinda Wasserman5dfe9652019-05-30 13:24:24 -0400231 if (handlesUnblocked(ignoreThreshold)) {
Govinda Wasserman3fcd7172019-09-05 16:32:23 -0400232 ScreenDecorations screenDecorations = mScreenDecorations.get();
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400233 if (screenDecorations == null) {
234 Log.w(TAG, "Couldn't show handles, ScreenDecorations unavailable");
235 } else {
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400236 mHandlesShowing = true;
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400237 screenDecorations.setAssistHintVisible(true);
238 }
239 }
240 }
241
242 private void hideHandles() {
243 if (!mHandlesShowing) {
244 return;
245 }
246
Govinda Wasserman3fcd7172019-09-05 16:32:23 -0400247 ScreenDecorations screenDecorations = mScreenDecorations.get();
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400248 if (screenDecorations == null) {
249 Log.w(TAG, "Couldn't hide handles, ScreenDecorations unavailable");
250 } else {
Govinda Wasserman24ca1bb2019-06-11 18:21:38 -0400251 mHandlesShowing = false;
252 mHandlesLastHiddenAt = SystemClock.elapsedRealtime();
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400253 screenDecorations.setAssistHintVisible(false);
254 }
255 }
256
257 private void handleNavigationModeChange(int navigationMode) {
258 boolean inGesturalMode = QuickStepContract.isGesturalMode(navigationMode);
259 if (mInGesturalMode == inGesturalMode) {
260 return;
261 }
262
263 mInGesturalMode = inGesturalMode;
264 if (mInGesturalMode) {
Govinda Wasserman5dfe9652019-05-30 13:24:24 -0400265 mBehaviorMap.get(mCurrentBehavior).onModeActivated(mContext, /* callbacks = */ this);
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400266 } else {
Govinda Wasserman5dfe9652019-05-30 13:24:24 -0400267 mBehaviorMap.get(mCurrentBehavior).onModeDeactivated();
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400268 hide();
269 }
270 }
271
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400272 private void clearPendingCommands() {
273 mHandler.removeCallbacks(mHideHandles);
274 mHandler.removeCallbacks(mShowAndGo);
Govinda Wassermane9ff1ff2019-07-31 15:26:33 -0400275 mShowAndGoEndsAt = 0;
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400276 }
277
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400278 @VisibleForTesting
279 void setInGesturalModeForTest(boolean inGesturalMode) {
280 mInGesturalMode = inGesturalMode;
281 }
282
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400283 @Override // Dumpable
284 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
285 pw.println("Current AssistHandleBehaviorController State:");
286
287 pw.println(" mHandlesShowing=" + mHandlesShowing);
288 pw.println(" mHandlesLastHiddenAt=" + mHandlesLastHiddenAt);
289 pw.println(" mInGesturalMode=" + mInGesturalMode);
290
291 pw.println(" Phenotype Flags:");
292 pw.println(" "
293 + SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS
294 + "="
295 + getShowAndGoDuration());
296 pw.println(" "
297 + SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS
298 + "="
299 + getShownFrequencyThreshold());
300 pw.println(" "
301 + SystemUiDeviceConfigFlags.ASSIST_HANDLES_BEHAVIOR_MODE
302 + "="
303 + getBehaviorMode());
304
305 pw.println(" mCurrentBehavior=" + mCurrentBehavior.toString());
306 mBehaviorMap.get(mCurrentBehavior).dump(pw, " ");
307 }
308
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400309 interface BehaviorController {
310 void onModeActivated(Context context, AssistHandleCallbacks callbacks);
Govinda Wasserman1f606b02019-05-29 09:16:02 -0400311 default void onModeDeactivated() {}
312 default void onAssistantGesturePerformed() {}
Govinda Wasserman40c043a2019-08-12 12:22:13 -0400313 default void onAssistHandlesRequested() {}
Govinda Wasserman48cdd682019-06-04 12:15:23 -0400314 default void dump(PrintWriter pw, String prefix) {}
Govinda Wassermanc7495cd2019-05-20 14:43:28 -0400315 }
316}