Merge "Improves the REMINDER_EXP mode for Assist Handle behavior" into qt-dev am: 4335d958e4

am: 2c594ee9b6

Change-Id: I82d2fe14385d936292fb91aefc9ea61d8ee77057
This commit is contained in:
Govinda Wasserman
2019-06-06 15:12:11 -07:00
committed by android-build-merger
7 changed files with 488 additions and 51 deletions

View File

@@ -126,6 +126,26 @@ public final class SystemUiDeviceConfigFlags {
public static final String ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS = public static final String ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS =
"assist_handles_show_and_go_duration_ms"; "assist_handles_show_and_go_duration_ms";
/**
* (long) How long, in milliseconds, to wait before showing the Assist Handles temporarily when
* performing a short delayed show.
*/
public static final String ASSIST_HANDLES_SHOW_AND_GO_DELAYED_SHORT_DELAY_MS =
"assist_handles_show_and_go_delayed_short_delay_ms";
/**
* (long) How long, in milliseconds, to wait before showing the Assist Handles temporarily when
* performing a long delayed show.
*/
public static final String ASSIST_HANDLES_SHOW_AND_GO_DELAYED_LONG_DELAY_MS =
"assist_handles_show_and_go_delayed_long_delay_ms";
/**
* (long) How long, in milliseconds, to wait before resetting delayed show delay times.
*/
public static final String ASSIST_HANDLES_SHOW_AND_GO_DELAY_RESET_TIMEOUT_MS =
"assist_handles_show_and_go_delay_reset_timeout_ms";
/** /**
* (long) How long, in milliseconds, to wait before displaying Assist Handles temporarily after * (long) How long, in milliseconds, to wait before displaying Assist Handles temporarily after
* hiding them. * hiding them.
@@ -152,5 +172,23 @@ public final class SystemUiDeviceConfigFlags {
*/ */
public static final String ASSIST_HANDLES_LEARN_COUNT = "assist_handles_learn_count"; public static final String ASSIST_HANDLES_LEARN_COUNT = "assist_handles_learn_count";
/**
* (bool) Whether to suppress handles on lockscreen."
*/
public static final String ASSIST_HANDLES_SUPPRESS_ON_LOCKSCREEN =
"assist_handles_suppress_on_lockscreen";
/**
* (bool) Whether to suppress handles on launcher."
*/
public static final String ASSIST_HANDLES_SUPPRESS_ON_LAUNCHER =
"assist_handles_suppress_on_launcher";
/**
* (bool) Whether to suppress handles on apps."
*/
public static final String ASSIST_HANDLES_SUPPRESS_ON_APPS =
"assist_handles_suppress_on_apps";
private SystemUiDeviceConfigFlags() { } private SystemUiDeviceConfigFlags() { }
} }

View File

@@ -20,7 +20,6 @@ import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.os.Handler; import android.os.Handler;
import android.os.SystemClock; import android.os.SystemClock;
import android.provider.DeviceConfig;
import android.util.Log; import android.util.Log;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@@ -30,11 +29,15 @@ import com.android.internal.app.AssistUtils;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dependency; import com.android.systemui.Dependency;
import com.android.systemui.DumpController;
import com.android.systemui.Dumpable;
import com.android.systemui.ScreenDecorations; import com.android.systemui.ScreenDecorations;
import com.android.systemui.SysUiServiceProvider; import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.phone.NavigationModeController; import com.android.systemui.statusbar.phone.NavigationModeController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -46,11 +49,11 @@ import java.util.function.Supplier;
* Controls when visual handles for Assistant gesture affordance should be shown or hidden using an * Controls when visual handles for Assistant gesture affordance should be shown or hidden using an
* {@link AssistHandleBehavior}. * {@link AssistHandleBehavior}.
*/ */
public final class AssistHandleBehaviorController implements AssistHandleCallbacks { public final class AssistHandleBehaviorController implements AssistHandleCallbacks, Dumpable {
private static final String TAG = "AssistHandleBehavior"; private static final String TAG = "AssistHandleBehavior";
private static final long DEFAULT_SHOWN_FREQUENCY_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(10); private static final long DEFAULT_SHOWN_FREQUENCY_THRESHOLD_MS = 0;
private static final long DEFAULT_SHOW_AND_GO_DURATION_MS = TimeUnit.SECONDS.toMillis(3); private static final long DEFAULT_SHOW_AND_GO_DURATION_MS = TimeUnit.SECONDS.toMillis(3);
/** /**
@@ -63,7 +66,9 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
private final AssistUtils mAssistUtils; private final AssistUtils mAssistUtils;
private final Handler mHandler; private final Handler mHandler;
private final Runnable mHideHandles = this::hideHandles; private final Runnable mHideHandles = this::hideHandles;
private final Runnable mShowAndGo = this::showAndGoInternal;
private final Supplier<ScreenDecorations> mScreenDecorationsSupplier; private final Supplier<ScreenDecorations> mScreenDecorationsSupplier;
private final PhenotypeHelper mPhenotypeHelper;
private final Map<AssistHandleBehavior, BehaviorController> mBehaviorMap = private final Map<AssistHandleBehavior, BehaviorController> mBehaviorMap =
new EnumMap<>(AssistHandleBehavior.class); new EnumMap<>(AssistHandleBehavior.class);
@@ -80,7 +85,9 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
this( this(
context, context,
assistUtils, assistUtils,
handler, () -> SysUiServiceProvider.getComponent(context, ScreenDecorations.class), handler,
() -> SysUiServiceProvider.getComponent(context, ScreenDecorations.class),
new PhenotypeHelper(),
/* testBehavior = */ null); /* testBehavior = */ null);
} }
@@ -90,15 +97,18 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
AssistUtils assistUtils, AssistUtils assistUtils,
Handler handler, Handler handler,
Supplier<ScreenDecorations> screenDecorationsSupplier, Supplier<ScreenDecorations> screenDecorationsSupplier,
PhenotypeHelper phenotypeHelper,
@Nullable BehaviorController testBehavior) { @Nullable BehaviorController testBehavior) {
mContext = context; mContext = context;
mAssistUtils = assistUtils; mAssistUtils = assistUtils;
mHandler = handler; mHandler = handler;
mScreenDecorationsSupplier = screenDecorationsSupplier; mScreenDecorationsSupplier = screenDecorationsSupplier;
mPhenotypeHelper = phenotypeHelper;
mBehaviorMap.put(AssistHandleBehavior.OFF, new AssistHandleOffBehavior()); mBehaviorMap.put(AssistHandleBehavior.OFF, new AssistHandleOffBehavior());
mBehaviorMap.put(AssistHandleBehavior.LIKE_HOME, new AssistHandleLikeHomeBehavior()); mBehaviorMap.put(AssistHandleBehavior.LIKE_HOME, new AssistHandleLikeHomeBehavior());
mBehaviorMap.put(AssistHandleBehavior.REMINDER_EXP, new AssistHandleReminderExpBehavior()); mBehaviorMap.put(
AssistHandleBehavior.REMINDER_EXP,
new AssistHandleReminderExpBehavior(handler, phenotypeHelper));
if (testBehavior != null) { if (testBehavior != null) {
mBehaviorMap.put(AssistHandleBehavior.TEST, testBehavior); mBehaviorMap.put(AssistHandleBehavior.TEST, testBehavior);
} }
@@ -107,38 +117,48 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
Dependency.get(NavigationModeController.class) Dependency.get(NavigationModeController.class)
.addListener(this::handleNavigationModeChange)); .addListener(this::handleNavigationModeChange));
setBehavior(DeviceConfig.getString( setBehavior(getBehaviorMode());
DeviceConfig.NAMESPACE_SYSTEMUI, mPhenotypeHelper.addOnPropertiesChangedListener(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_BEHAVIOR_MODE,
DEFAULT_BEHAVIOR.toString()));
DeviceConfig.addOnPropertyChangedListener(
DeviceConfig.NAMESPACE_SYSTEMUI,
mHandler::post, mHandler::post,
(namespace, name, value) -> { (properties) -> {
if (SystemUiDeviceConfigFlags.ASSIST_HANDLES_BEHAVIOR_MODE.equals(name)) { if (properties.getKeyset().contains(
setBehavior(value); SystemUiDeviceConfigFlags.ASSIST_HANDLES_BEHAVIOR_MODE)) {
setBehavior(properties.getString(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_BEHAVIOR_MODE, null));
} }
}); });
Dependency.get(DumpController.class).addListener(this);
} }
@Override @Override // AssistHandleCallbacks
public void hide() { public void hide() {
mHandler.removeCallbacks(mHideHandles); clearPendingCommands();
mHandler.post(mHideHandles); mHandler.post(mHideHandles);
} }
@Override @Override // AssistHandleCallbacks
public void showAndGo() { public void showAndGo() {
mHandler.removeCallbacks(mHideHandles); clearPendingCommands();
mHandler.post(() -> { mHandler.post(mShowAndGo);
maybeShowHandles(/* ignoreThreshold = */ false);
mHandler.postDelayed(mHideHandles, getShowAndGoDuration());
});
} }
@Override private void showAndGoInternal() {
maybeShowHandles(/* ignoreThreshold = */ false);
mHandler.postDelayed(mHideHandles, getShowAndGoDuration());
}
@Override // AssistHandleCallbacks
public void showAndGoDelayed(long delayMs, boolean hideIfShowing) {
clearPendingCommands();
if (hideIfShowing) {
mHandler.post(mHideHandles);
}
mHandler.postDelayed(mShowAndGo, delayMs);
}
@Override // AssistHandleCallbacks
public void showAndStay() { public void showAndStay() {
mHandler.removeCallbacks(mHideHandles); clearPendingCommands();
mHandler.post(() -> maybeShowHandles(/* ignoreThreshold = */ true)); mHandler.post(() -> maybeShowHandles(/* ignoreThreshold = */ true));
} }
@@ -178,37 +198,41 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
private boolean handlesUnblocked(boolean ignoreThreshold) { private boolean handlesUnblocked(boolean ignoreThreshold) {
long timeSinceHidden = SystemClock.elapsedRealtime() - mHandlesLastHiddenAt; long timeSinceHidden = SystemClock.elapsedRealtime() - mHandlesLastHiddenAt;
boolean notThrottled = ignoreThreshold || timeSinceHidden > getShownFrequencyThreshold(); boolean notThrottled = ignoreThreshold || timeSinceHidden >= getShownFrequencyThreshold();
ComponentName assistantComponent = ComponentName assistantComponent =
mAssistUtils.getAssistComponentForUser(KeyguardUpdateMonitor.getCurrentUser()); mAssistUtils.getAssistComponentForUser(KeyguardUpdateMonitor.getCurrentUser());
return notThrottled && assistantComponent != null; return notThrottled && assistantComponent != null;
} }
private long getShownFrequencyThreshold() { private long getShownFrequencyThreshold() {
return DeviceConfig.getLong( return mPhenotypeHelper.getLong(
DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS, SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS,
DEFAULT_SHOWN_FREQUENCY_THRESHOLD_MS); DEFAULT_SHOWN_FREQUENCY_THRESHOLD_MS);
} }
private long getShowAndGoDuration() { private long getShowAndGoDuration() {
return DeviceConfig.getLong( return mPhenotypeHelper.getLong(
DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS, SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS,
DEFAULT_SHOW_AND_GO_DURATION_MS); DEFAULT_SHOW_AND_GO_DURATION_MS);
} }
private String getBehaviorMode() {
return mPhenotypeHelper.getString(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_BEHAVIOR_MODE,
DEFAULT_BEHAVIOR.toString());
}
private void maybeShowHandles(boolean ignoreThreshold) { private void maybeShowHandles(boolean ignoreThreshold) {
if (mHandlesShowing) { if (mHandlesShowing) {
return; return;
} }
if (handlesUnblocked(ignoreThreshold)) { if (handlesUnblocked(ignoreThreshold)) {
mHandlesShowing = true;
ScreenDecorations screenDecorations = mScreenDecorationsSupplier.get(); ScreenDecorations screenDecorations = mScreenDecorationsSupplier.get();
if (screenDecorations == null) { if (screenDecorations == null) {
Log.w(TAG, "Couldn't show handles, ScreenDecorations unavailable"); Log.w(TAG, "Couldn't show handles, ScreenDecorations unavailable");
} else { } else {
mHandlesShowing = true;
screenDecorations.setAssistHintVisible(true); screenDecorations.setAssistHintVisible(true);
} }
} }
@@ -244,14 +268,46 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
} }
} }
private void clearPendingCommands() {
mHandler.removeCallbacks(mHideHandles);
mHandler.removeCallbacks(mShowAndGo);
}
@VisibleForTesting @VisibleForTesting
void setInGesturalModeForTest(boolean inGesturalMode) { void setInGesturalModeForTest(boolean inGesturalMode) {
mInGesturalMode = inGesturalMode; mInGesturalMode = inGesturalMode;
} }
@Override // Dumpable
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("Current AssistHandleBehaviorController State:");
pw.println(" mHandlesShowing=" + mHandlesShowing);
pw.println(" mHandlesLastHiddenAt=" + mHandlesLastHiddenAt);
pw.println(" mInGesturalMode=" + mInGesturalMode);
pw.println(" Phenotype Flags:");
pw.println(" "
+ SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS
+ "="
+ getShowAndGoDuration());
pw.println(" "
+ SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS
+ "="
+ getShownFrequencyThreshold());
pw.println(" "
+ SystemUiDeviceConfigFlags.ASSIST_HANDLES_BEHAVIOR_MODE
+ "="
+ getBehaviorMode());
pw.println(" mCurrentBehavior=" + mCurrentBehavior.toString());
mBehaviorMap.get(mCurrentBehavior).dump(pw, " ");
}
interface BehaviorController { interface BehaviorController {
void onModeActivated(Context context, AssistHandleCallbacks callbacks); void onModeActivated(Context context, AssistHandleCallbacks callbacks);
default void onModeDeactivated() {} default void onModeDeactivated() {}
default void onAssistantGesturePerformed() {} default void onAssistantGesturePerformed() {}
default void dump(PrintWriter pw, String prefix) {}
} }
} }

View File

@@ -29,6 +29,13 @@ public interface AssistHandleCallbacks {
*/ */
void showAndGo(); void showAndGo();
/**
* Same as show and go, but will not do anything until a delay has elapsed.
*
* Will be cancelled if another command is given during the delay.
*/
void showAndGoDelayed(long delayMs, boolean hideIfShowing);
/** Show the Assistant handles. */ /** Show the Assistant handles. */
void showAndStay(); void showAndStay();
} }

View File

@@ -26,6 +26,8 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.QuickStepContract;
import java.io.PrintWriter;
/** /**
* Assistant Handle behavior that makes Assistant handles show/hide when the home handle is * Assistant Handle behavior that makes Assistant handles show/hide when the home handle is
* shown/hidden, respectively. * shown/hidden, respectively.
@@ -108,4 +110,12 @@ final class AssistHandleLikeHomeBehavior implements BehaviorController {
mAssistHandleCallbacks.showAndStay(); mAssistHandleCallbacks.showAndStay();
} }
} }
@Override
public void dump(PrintWriter pw, String prefix) {
pw.println("Current AssistHandleLikeHomeBehavior State:");
pw.println(prefix + " mIsDozing=" + mIsDozing);
pw.println(prefix + " mIsHomeHandleHiding=" + mIsHomeHandleHiding);
}
} }

View File

@@ -16,11 +16,16 @@
package com.android.systemui.assist; package com.android.systemui.assist;
import static com.android.systemui.shared.system.PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.os.SystemClock; import android.os.SystemClock;
import android.provider.DeviceConfig;
import android.provider.Settings; import android.provider.Settings;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@@ -31,10 +36,14 @@ import com.android.systemui.assist.AssistHandleBehaviorController.BehaviorContro
import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarState;
import java.io.PrintWriter;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
@@ -46,8 +55,18 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
private static final String LEARNING_TIME_ELAPSED_KEY = "reminder_exp_learning_time_elapsed"; private static final String LEARNING_TIME_ELAPSED_KEY = "reminder_exp_learning_time_elapsed";
private static final String LEARNING_EVENT_COUNT_KEY = "reminder_exp_learning_event_count"; private static final String LEARNING_EVENT_COUNT_KEY = "reminder_exp_learning_event_count";
private static final long DEFAULT_LEARNING_TIME_MS = TimeUnit.DAYS.toMillis(3); private static final String LEARNED_HINT_LAST_SHOWN_KEY =
private static final int DEFAULT_LEARNING_COUNT = 3; "reminder_exp_learned_hint_last_shown";
private static final long DEFAULT_LEARNING_TIME_MS = TimeUnit.DAYS.toMillis(200);
private static final int DEFAULT_LEARNING_COUNT = 30000;
private static final long DEFAULT_SHOW_AND_GO_DELAYED_SHORT_DELAY_MS = 150;
private static final long DEFAULT_SHOW_AND_GO_DELAYED_LONG_DELAY_MS =
TimeUnit.SECONDS.toMillis(1);
private static final long DEFAULT_SHOW_AND_GO_DELAY_RESET_TIMEOUT_MS =
TimeUnit.SECONDS.toMillis(3);
private static final boolean DEFAULT_SUPPRESS_ON_LOCKSCREEN = false;
private static final boolean DEFAULT_SUPPRESS_ON_LAUNCHER = false;
private static final boolean DEFAULT_SUPPRESS_ON_APPS = false;
private final StatusBarStateController.StateListener mStatusBarStateListener = private final StatusBarStateController.StateListener mStatusBarStateListener =
new StatusBarStateController.StateListener() { new StatusBarStateController.StateListener() {
@@ -64,13 +83,13 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
private final TaskStackChangeListener mTaskStackChangeListener = private final TaskStackChangeListener mTaskStackChangeListener =
new TaskStackChangeListener() { new TaskStackChangeListener() {
@Override @Override
public void onTaskMovedToFront(int taskId) { public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
handleTaskStackTopChanged(taskId); handleTaskStackTopChanged(taskInfo.taskId, taskInfo.topActivity);
} }
@Override @Override
public void onTaskCreated(int taskId, ComponentName componentName) { public void onTaskCreated(int taskId, ComponentName componentName) {
handleTaskStackTopChanged(taskId); handleTaskStackTopChanged(taskId, componentName);
} }
}; };
private final OverviewProxyService.OverviewProxyListener mOverviewProxyListener = private final OverviewProxyService.OverviewProxyListener mOverviewProxyListener =
@@ -85,7 +104,18 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
handleSystemUiStateChanged(sysuiStateFlags); handleSystemUiStateChanged(sysuiStateFlags);
} }
}; };
private final BroadcastReceiver mDefaultHomeBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mDefaultHome = getCurrentDefaultHome();
}
};
private final IntentFilter mDefaultHomeIntentFilter =
new IntentFilter(ACTION_PREFERRED_ACTIVITY_CHANGED);
private final Runnable mResetConsecutiveTaskSwitches = this::resetConsecutiveTaskSwitches;
private final Handler mHandler;
private final PhenotypeHelper mPhenotypeHelper;
private final StatusBarStateController mStatusBarStateController; private final StatusBarStateController mStatusBarStateController;
private final ActivityManagerWrapper mActivityManagerWrapper; private final ActivityManagerWrapper mActivityManagerWrapper;
private final OverviewProxyService mOverviewProxyService; private final OverviewProxyService mOverviewProxyService;
@@ -94,6 +124,8 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
private boolean mIsDozing; private boolean mIsDozing;
private int mRunningTaskId; private int mRunningTaskId;
private boolean mIsNavBarHidden; private boolean mIsNavBarHidden;
private boolean mIsLauncherShowing;
private int mConsecutiveTaskSwitches;
/** Whether user has learned the gesture. */ /** Whether user has learned the gesture. */
private boolean mIsLearned; private boolean mIsLearned;
@@ -102,11 +134,15 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
private long mLearningTimeElapsed; private long mLearningTimeElapsed;
/** Number of successful Assistant invocations while in this behavior. */ /** Number of successful Assistant invocations while in this behavior. */
private int mLearningCount; private int mLearningCount;
private long mLearnedHintLastShownEpochDay;
@Nullable private Context mContext; @Nullable private Context mContext;
@Nullable private AssistHandleCallbacks mAssistHandleCallbacks; @Nullable private AssistHandleCallbacks mAssistHandleCallbacks;
@Nullable private ComponentName mDefaultHome;
AssistHandleReminderExpBehavior() { AssistHandleReminderExpBehavior(Handler handler, PhenotypeHelper phenotypeHelper) {
mHandler = handler;
mPhenotypeHelper = phenotypeHelper;
mStatusBarStateController = Dependency.get(StatusBarStateController.class); mStatusBarStateController = Dependency.get(StatusBarStateController.class);
mActivityManagerWrapper = ActivityManagerWrapper.getInstance(); mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
mOverviewProxyService = Dependency.get(OverviewProxyService.class); mOverviewProxyService = Dependency.get(OverviewProxyService.class);
@@ -116,6 +152,9 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
public void onModeActivated(Context context, AssistHandleCallbacks callbacks) { public void onModeActivated(Context context, AssistHandleCallbacks callbacks) {
mContext = context; mContext = context;
mAssistHandleCallbacks = callbacks; mAssistHandleCallbacks = callbacks;
mConsecutiveTaskSwitches = 0;
mDefaultHome = getCurrentDefaultHome();
context.registerReceiver(mDefaultHomeBroadcastReceiver, mDefaultHomeIntentFilter);
mOnLockscreen = onLockscreen(mStatusBarStateController.getState()); mOnLockscreen = onLockscreen(mStatusBarStateController.getState());
mIsDozing = mStatusBarStateController.isDozing(); mIsDozing = mStatusBarStateController.isDozing();
mStatusBarStateController.addCallback(mStatusBarStateListener); mStatusBarStateController.addCallback(mStatusBarStateListener);
@@ -128,6 +167,8 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
context.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, /* default = */ 0); context.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, /* default = */ 0);
mLearningCount = Settings.Secure.getInt( mLearningCount = Settings.Secure.getInt(
context.getContentResolver(), LEARNING_EVENT_COUNT_KEY, /* default = */ 0); context.getContentResolver(), LEARNING_EVENT_COUNT_KEY, /* default = */ 0);
mLearnedHintLastShownEpochDay = Settings.Secure.getLong(
context.getContentResolver(), LEARNED_HINT_LAST_SHOWN_KEY, /* default = */ 0);
mLastLearningTimestamp = SystemClock.uptimeMillis(); mLastLearningTimestamp = SystemClock.uptimeMillis();
callbackForCurrentState(/* justUnlocked = */ false); callbackForCurrentState(/* justUnlocked = */ false);
@@ -137,10 +178,10 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
public void onModeDeactivated() { public void onModeDeactivated() {
mAssistHandleCallbacks = null; mAssistHandleCallbacks = null;
if (mContext != null) { if (mContext != null) {
Settings.Secure.putLong( mContext.unregisterReceiver(mDefaultHomeBroadcastReceiver);
mContext.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, mLearningTimeElapsed); Settings.Secure.putLong(mContext.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, 0);
Settings.Secure.putInt( Settings.Secure.putInt(mContext.getContentResolver(), LEARNING_EVENT_COUNT_KEY, 0);
mContext.getContentResolver(), LEARNING_EVENT_COUNT_KEY, mLearningCount); Settings.Secure.putLong(mContext.getContentResolver(), LEARNED_HINT_LAST_SHOWN_KEY, 0);
mContext = null; mContext = null;
} }
mStatusBarStateController.removeCallback(mStatusBarStateListener); mStatusBarStateController.removeCallback(mStatusBarStateListener);
@@ -162,12 +203,18 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
return (sysuiStateFlags & QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN) != 0; return (sysuiStateFlags & QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN) != 0;
} }
@Nullable
private static ComponentName getCurrentDefaultHome() {
return PackageManagerWrapper.getInstance().getHomeActivities(new ArrayList<>());
}
private void handleStatusBarStateChanged(int newState) { private void handleStatusBarStateChanged(int newState) {
boolean onLockscreen = onLockscreen(newState); boolean onLockscreen = onLockscreen(newState);
if (mOnLockscreen == onLockscreen) { if (mOnLockscreen == onLockscreen) {
return; return;
} }
resetConsecutiveTaskSwitches();
mOnLockscreen = onLockscreen; mOnLockscreen = onLockscreen;
callbackForCurrentState(!onLockscreen); callbackForCurrentState(!onLockscreen);
} }
@@ -177,16 +224,24 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
return; return;
} }
resetConsecutiveTaskSwitches();
mIsDozing = isDozing; mIsDozing = isDozing;
callbackForCurrentState(/* justUnlocked = */ false); callbackForCurrentState(/* justUnlocked = */ false);
} }
private void handleTaskStackTopChanged(int taskId) { private void handleTaskStackTopChanged(int taskId, @Nullable ComponentName taskComponentName) {
if (mRunningTaskId == taskId) { if (mRunningTaskId == taskId || taskComponentName == null) {
return; return;
} }
mRunningTaskId = taskId; mRunningTaskId = taskId;
mIsLauncherShowing = taskComponentName.equals(mDefaultHome);
if (mIsLauncherShowing) {
resetConsecutiveTaskSwitches();
} else {
rescheduleConsecutiveTaskSwitchesReset();
mConsecutiveTaskSwitches++;
}
callbackForCurrentState(/* justUnlocked = */ false); callbackForCurrentState(/* justUnlocked = */ false);
} }
@@ -196,11 +251,13 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
return; return;
} }
resetConsecutiveTaskSwitches();
mIsNavBarHidden = isNavBarHidden; mIsNavBarHidden = isNavBarHidden;
callbackForCurrentState(/* justUnlocked = */ false); callbackForCurrentState(/* justUnlocked = */ false);
} }
private void handleOverviewShown() { private void handleOverviewShown() {
resetConsecutiveTaskSwitches();
callbackForCurrentState(/* justUnlocked = */ false); callbackForCurrentState(/* justUnlocked = */ false);
} }
@@ -227,21 +284,47 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
if (mIsDozing || mIsNavBarHidden || mOnLockscreen) { if (mIsDozing || mIsNavBarHidden || mOnLockscreen) {
mAssistHandleCallbacks.hide(); mAssistHandleCallbacks.hide();
} else if (justUnlocked) { } else if (justUnlocked) {
long currentEpochDay = LocalDate.now().toEpochDay();
if (mLearnedHintLastShownEpochDay < currentEpochDay) {
if (mContext != null) {
Settings.Secure.putLong(
mContext.getContentResolver(),
LEARNED_HINT_LAST_SHOWN_KEY,
currentEpochDay);
}
mLearnedHintLastShownEpochDay = currentEpochDay;
mAssistHandleCallbacks.showAndGo(); mAssistHandleCallbacks.showAndGo();
} }
} }
}
private void callbackForUnlearnedState() { private void callbackForUnlearnedState() {
if (mAssistHandleCallbacks == null) { if (mAssistHandleCallbacks == null) {
return; return;
} }
if (mIsDozing || mIsNavBarHidden) { if (mIsDozing || mIsNavBarHidden || isSuppressed()) {
mAssistHandleCallbacks.hide(); mAssistHandleCallbacks.hide();
} else if (mOnLockscreen) { } else if (mOnLockscreen) {
mAssistHandleCallbacks.showAndStay(); mAssistHandleCallbacks.showAndStay();
} else { } else if (mIsLauncherShowing) {
mAssistHandleCallbacks.showAndGo(); mAssistHandleCallbacks.showAndGo();
} else if (mConsecutiveTaskSwitches == 1) {
mAssistHandleCallbacks.showAndGoDelayed(
getShowAndGoDelayedShortDelayMs(), /* hideIfShowing = */ false);
} else {
mAssistHandleCallbacks.showAndGoDelayed(
getShowAndGoDelayedLongDelayMs(), /* hideIfShowing = */ true);
}
}
private boolean isSuppressed() {
if (mOnLockscreen) {
return getSuppressOnLockscreen();
} else if (mIsLauncherShowing) {
return getSuppressOnLauncher();
} else {
return getSuppressOnApps();
} }
} }
@@ -260,17 +343,114 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
mLearningCount >= getLearningCount() || mLearningTimeElapsed >= getLearningTimeMs(); mLearningCount >= getLearningCount() || mLearningTimeElapsed >= getLearningTimeMs();
} }
private void resetConsecutiveTaskSwitches() {
mHandler.removeCallbacks(mResetConsecutiveTaskSwitches);
mConsecutiveTaskSwitches = 0;
}
private void rescheduleConsecutiveTaskSwitchesReset() {
mHandler.removeCallbacks(mResetConsecutiveTaskSwitches);
mHandler.postDelayed(mResetConsecutiveTaskSwitches, getShowAndGoDelayResetTimeoutMs());
}
private long getLearningTimeMs() { private long getLearningTimeMs() {
return DeviceConfig.getLong( return mPhenotypeHelper.getLong(
DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.ASSIST_HANDLES_LEARN_TIME_MS, SystemUiDeviceConfigFlags.ASSIST_HANDLES_LEARN_TIME_MS,
DEFAULT_LEARNING_TIME_MS); DEFAULT_LEARNING_TIME_MS);
} }
private int getLearningCount() { private int getLearningCount() {
return DeviceConfig.getInt( return mPhenotypeHelper.getInt(
DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.ASSIST_HANDLES_LEARN_COUNT, SystemUiDeviceConfigFlags.ASSIST_HANDLES_LEARN_COUNT,
DEFAULT_LEARNING_COUNT); DEFAULT_LEARNING_COUNT);
} }
private long getShowAndGoDelayedShortDelayMs() {
return mPhenotypeHelper.getLong(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DELAYED_SHORT_DELAY_MS,
DEFAULT_SHOW_AND_GO_DELAYED_SHORT_DELAY_MS);
}
private long getShowAndGoDelayedLongDelayMs() {
return mPhenotypeHelper.getLong(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DELAYED_LONG_DELAY_MS,
DEFAULT_SHOW_AND_GO_DELAYED_LONG_DELAY_MS);
}
private long getShowAndGoDelayResetTimeoutMs() {
return mPhenotypeHelper.getLong(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DELAY_RESET_TIMEOUT_MS,
DEFAULT_SHOW_AND_GO_DELAY_RESET_TIMEOUT_MS);
}
private boolean getSuppressOnLockscreen() {
return mPhenotypeHelper.getBoolean(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SUPPRESS_ON_LOCKSCREEN,
DEFAULT_SUPPRESS_ON_LOCKSCREEN);
}
private boolean getSuppressOnLauncher() {
return mPhenotypeHelper.getBoolean(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SUPPRESS_ON_LAUNCHER,
DEFAULT_SUPPRESS_ON_LAUNCHER);
}
private boolean getSuppressOnApps() {
return mPhenotypeHelper.getBoolean(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_SUPPRESS_ON_APPS,
DEFAULT_SUPPRESS_ON_APPS);
}
@Override
public void dump(PrintWriter pw, String prefix) {
pw.println(prefix + "Current AssistHandleReminderExpBehavior State:");
pw.println(prefix + " mOnLockscreen=" + mOnLockscreen);
pw.println(prefix + " mIsDozing=" + mIsDozing);
pw.println(prefix + " mRunningTaskId=" + mRunningTaskId);
pw.println(prefix + " mDefaultHome=" + mDefaultHome);
pw.println(prefix + " mIsNavBarHidden=" + mIsNavBarHidden);
pw.println(prefix + " mIsLauncherShowing=" + mIsLauncherShowing);
pw.println(prefix + " mConsecutiveTaskSwitches=" + mConsecutiveTaskSwitches);
pw.println(prefix + " mIsLearned=" + mIsLearned);
pw.println(prefix + " mLastLearningTimestamp=" + mLastLearningTimestamp);
pw.println(prefix + " mLearningTimeElapsed=" + mLearningTimeElapsed);
pw.println(prefix + " mLearningCount=" + mLearningCount);
pw.println(prefix + " mLearnedHintLastShownEpochDay=" + mLearnedHintLastShownEpochDay);
pw.println(
prefix + " mAssistHandleCallbacks present: " + (mAssistHandleCallbacks != null));
pw.println(prefix + " Phenotype Flags:");
pw.println(prefix + " "
+ SystemUiDeviceConfigFlags.ASSIST_HANDLES_LEARN_TIME_MS
+ "="
+ getLearningTimeMs());
pw.println(prefix + " "
+ SystemUiDeviceConfigFlags.ASSIST_HANDLES_LEARN_COUNT
+ "="
+ getLearningCount());
pw.println(prefix + " "
+ SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DELAYED_SHORT_DELAY_MS
+ "="
+ getShowAndGoDelayedShortDelayMs());
pw.println(prefix + " "
+ SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DELAYED_LONG_DELAY_MS
+ "="
+ getShowAndGoDelayedLongDelayMs());
pw.println(prefix + " "
+ SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DELAY_RESET_TIMEOUT_MS
+ "="
+ getShowAndGoDelayResetTimeoutMs());
pw.println(prefix + " "
+ SystemUiDeviceConfigFlags.ASSIST_HANDLES_SUPPRESS_ON_LOCKSCREEN
+ "="
+ getSuppressOnLockscreen());
pw.println(prefix + " "
+ SystemUiDeviceConfigFlags.ASSIST_HANDLES_SUPPRESS_ON_LAUNCHER
+ "="
+ getSuppressOnLauncher());
pw.println(prefix + " "
+ SystemUiDeviceConfigFlags.ASSIST_HANDLES_SUPPRESS_ON_APPS
+ "="
+ getSuppressOnApps());
}
} }

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.assist;
import android.provider.DeviceConfig;
import androidx.annotation.Nullable;
import java.util.concurrent.Executor;
class PhenotypeHelper {
PhenotypeHelper() {}
long getLong(String name, long defaultValue) {
return DeviceConfig.getLong(DeviceConfig.NAMESPACE_SYSTEMUI, name, defaultValue);
}
int getInt(String name, int defaultValue) {
return DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI, name, defaultValue);
}
@Nullable
String getString(String name, @Nullable String defaultValue) {
return DeviceConfig.getString(DeviceConfig.NAMESPACE_SYSTEMUI, name, defaultValue);
}
boolean getBoolean(String name, boolean defaultValue) {
return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, name, defaultValue);
}
void addOnPropertiesChangedListener(
Executor executor, DeviceConfig.OnPropertiesChangedListener listener) {
DeviceConfig.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_SYSTEMUI, executor, listener);
}
}

View File

@@ -20,6 +20,7 @@ import static org.mockito.AdditionalAnswers.answerVoid;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.reset; import static org.mockito.Mockito.reset;
@@ -35,11 +36,13 @@ import android.testing.TestableLooper.RunWithLooper;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import com.android.internal.app.AssistUtils; import com.android.internal.app.AssistUtils;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.systemui.ScreenDecorations; import com.android.systemui.ScreenDecorations;
import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.recents.OverviewProxyService;
import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -59,6 +62,7 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
@Mock private ScreenDecorations mMockScreenDecorations; @Mock private ScreenDecorations mMockScreenDecorations;
@Mock private AssistUtils mMockAssistUtils; @Mock private AssistUtils mMockAssistUtils;
@Mock private Handler mMockHandler; @Mock private Handler mMockHandler;
@Mock private PhenotypeHelper mMockPhenotypeHelper;
@Mock private AssistHandleBehaviorController.BehaviorController mMockBehaviorController; @Mock private AssistHandleBehaviorController.BehaviorController mMockBehaviorController;
@Before @Before
@@ -69,14 +73,21 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
doAnswer(answerVoid(Runnable::run)).when(mMockHandler).post(any(Runnable.class)); doAnswer(answerVoid(Runnable::run)).when(mMockHandler).post(any(Runnable.class));
doAnswer(answerVoid(Runnable::run)).when(mMockHandler) doAnswer(answerVoid(Runnable::run)).when(mMockHandler)
.postDelayed(any(Runnable.class), anyLong()); .postDelayed(any(Runnable.class), anyLong());
mAssistHandleBehaviorController = mAssistHandleBehaviorController =
new AssistHandleBehaviorController( new AssistHandleBehaviorController(
mContext, mContext,
mMockAssistUtils, mMockAssistUtils,
mMockHandler, () -> mMockScreenDecorations, mMockHandler, () -> mMockScreenDecorations,
mMockPhenotypeHelper,
mMockBehaviorController); mMockBehaviorController);
} }
@After
public void teardown() {
mAssistHandleBehaviorController.setBehavior(AssistHandleBehavior.OFF);
}
@Test @Test
public void hide_hidesHandlesWhenShowing() { public void hide_hidesHandlesWhenShowing() {
// Arrange // Arrange
@@ -185,6 +196,9 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
public void showAndGo_doesNothingIfRecentlyHidden() { public void showAndGo_doesNothingIfRecentlyHidden() {
// Arrange // Arrange
when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME); when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME);
when(mMockPhenotypeHelper.getLong(
eq(SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS),
anyLong())).thenReturn(10000L);
mAssistHandleBehaviorController.showAndGo(); mAssistHandleBehaviorController.showAndGo();
reset(mMockScreenDecorations); reset(mMockScreenDecorations);
@@ -209,6 +223,87 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
verifyNoMoreInteractions(mMockScreenDecorations); verifyNoMoreInteractions(mMockScreenDecorations);
} }
@Test
public void showAndGoDelayed_showsThenHidesHandlesWhenHiding() {
// Arrange
when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME);
mAssistHandleBehaviorController.hide();
reset(mMockScreenDecorations);
// Act
mAssistHandleBehaviorController.showAndGoDelayed(1000, false);
// Assert
InOrder inOrder = inOrder(mMockScreenDecorations);
inOrder.verify(mMockScreenDecorations).setAssistHintVisible(true);
inOrder.verify(mMockScreenDecorations).setAssistHintVisible(false);
inOrder.verifyNoMoreInteractions();
}
@Test
public void showAndGoDelayed_hidesHandlesAfterTimeoutWhenShowing() {
// Arrange
when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME);
mAssistHandleBehaviorController.showAndStay();
reset(mMockScreenDecorations);
// Act
mAssistHandleBehaviorController.showAndGoDelayed(1000, false);
// Assert
verify(mMockScreenDecorations).setAssistHintVisible(false);
verifyNoMoreInteractions(mMockScreenDecorations);
}
@Test
public void showAndGoDelayed_hidesInitiallyThenShowsThenHidesAfterTimeoutWhenHideRequested() {
// Arrange
when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME);
mAssistHandleBehaviorController.showAndStay();
reset(mMockScreenDecorations);
// Act
mAssistHandleBehaviorController.showAndGoDelayed(1000, true);
// Assert
InOrder inOrder = inOrder(mMockScreenDecorations);
inOrder.verify(mMockScreenDecorations).setAssistHintVisible(false);
inOrder.verify(mMockScreenDecorations).setAssistHintVisible(true);
inOrder.verify(mMockScreenDecorations).setAssistHintVisible(false);
inOrder.verifyNoMoreInteractions();
}
@Test
public void showAndGoDelayed_doesNothingIfRecentlyHidden() {
// Arrange
when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME);
when(mMockPhenotypeHelper.getLong(
eq(SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS),
anyLong())).thenReturn(10000L);
mAssistHandleBehaviorController.showAndGo();
reset(mMockScreenDecorations);
// Act
mAssistHandleBehaviorController.showAndGoDelayed(1000, false);
// Assert
verifyNoMoreInteractions(mMockScreenDecorations);
}
@Test
public void showAndGoDelayed_doesNothingWhenThereIsNoAssistant() {
// Arrange
when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(null);
mAssistHandleBehaviorController.hide();
reset(mMockScreenDecorations);
// Act
mAssistHandleBehaviorController.showAndGoDelayed(1000, false);
// Assert
verifyNoMoreInteractions(mMockScreenDecorations);
}
@Test @Test
public void setBehavior_activatesTheBehaviorWhenInGesturalMode() { public void setBehavior_activatesTheBehaviorWhenInGesturalMode() {
// Arrange // Arrange