diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index 28ac8cc8b8bd1..1b0a458b3fbcd 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -133,5 +133,17 @@ public final class SystemUiDeviceConfigFlags { public static final String ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS = "assist_handles_shown_frequency_threshold_ms"; + /** + * (long) How long, in milliseconds, for teaching behaviors to wait before considering the user + * taught. + */ + public static final String ASSIST_HANDLES_LEARN_TIME_MS = "assist_handles_learn_time_ms"; + + /** + * (int) How many times for teaching behaviors to see the user perform an action to consider it + * taught. + */ + public static final String ASSIST_HANDLES_LEARN_COUNT = "assist_handles_learn_count"; + private SystemUiDeviceConfigFlags() { } } diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java index ff508ade72f81..1aa47cc6d800a 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java @@ -16,11 +16,7 @@ package com.android.systemui.assist; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Build; import android.os.Handler; import android.os.SystemClock; import android.os.SystemProperties; @@ -37,7 +33,6 @@ import com.android.systemui.SysUiServiceProvider; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.phone.NavigationModeController; -import java.util.Locale; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; @@ -51,18 +46,17 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac private static final String TAG = "AssistHandleBehavior"; - private static final boolean IS_DEBUG_DEVICE = - Build.TYPE.toLowerCase(Locale.ROOT).contains("debug") - || Build.TYPE.toLowerCase(Locale.ROOT).equals("eng"); - private static final String SHOWN_FREQUENCY_THRESHOLD_KEY = "ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS"; private static final long DEFAULT_SHOWN_FREQUENCY_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(10); private static final String SHOW_AND_GO_DURATION_KEY = "ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS"; private static final long DEFAULT_SHOW_AND_GO_DURATION_MS = TimeUnit.SECONDS.toMillis(3); - private static final String BEHAVIOR_KEY = "behavior"; - private static final String SET_BEHAVIOR_ACTION = - "com.android.systemui.SET_ASSIST_HANDLE_BEHAVIOR"; + + /** + * This is the default behavior that will be used once the system is up. It will be set once the + * behavior dependencies are available. This ensures proper behavior lifecycle. + */ + private static final AssistHandleBehavior DEFAULT_BEHAVIOR = AssistHandleBehavior.REMINDER_EXP; private final Context mContext; private final Handler mHandler; @@ -71,6 +65,10 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac private boolean mHandlesShowing = false; private long mHandlesLastHiddenAt; + /** + * This should always be initialized as {@link AssistHandleBehavior#OFF} to ensure proper + * behavior lifecycle. + */ private AssistHandleBehavior mCurrentBehavior = AssistHandleBehavior.OFF; private boolean mInGesturalMode; @@ -95,7 +93,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac setBehavior(DeviceConfig.getString( DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.ASSIST_HANDLES_BEHAVIOR_MODE, - mCurrentBehavior.toString())); + DEFAULT_BEHAVIOR.toString())); DeviceConfig.addOnPropertyChangedListener( DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, @@ -104,20 +102,6 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac setBehavior(value); } }); - - if (IS_DEBUG_DEVICE) { - context.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String behaviorString = intent.getExtras().getString(BEHAVIOR_KEY); - try { - setBehavior(AssistHandleBehavior.valueOf(behaviorString)); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Invalid behavior identifier: " + behaviorString); - } - } - }, new IntentFilter(SET_BEHAVIOR_ACTION)); - } } @Override @@ -141,6 +125,10 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac mHandler.post(() -> maybeShowHandles(/* ignoreThreshold = */ true)); } + void onAssistantGesturePerformed() { + mCurrentBehavior.getController().onAssistantGesturePerformed(); + } + void setBehavior(AssistHandleBehavior behavior) { if (mCurrentBehavior == behavior) { return; @@ -233,6 +221,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac interface BehaviorController { void onModeActivated(Context context, AssistHandleCallbacks callbacks); - void onModeDeactivated(); + default void onModeDeactivated() {} + default void onAssistantGesturePerformed() {} } } diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleOffBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleOffBehavior.java index ebf15ae8755ce..f4130aeb1d083 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleOffBehavior.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleOffBehavior.java @@ -27,9 +27,4 @@ final class AssistHandleOffBehavior implements BehaviorController { public void onModeActivated(Context context, AssistHandleCallbacks callbacks) { callbacks.hide(); } - - @Override - public void onModeDeactivated() { - // Do nothing - } } diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java index 93e48863d2b88..4b6a6dcee91d1 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java @@ -19,9 +19,13 @@ package com.android.systemui.assist; import android.app.ActivityManager; import android.content.ComponentName; import android.content.Context; +import android.os.SystemClock; +import android.provider.DeviceConfig; +import android.provider.Settings; import androidx.annotation.Nullable; +import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.systemui.Dependency; import com.android.systemui.assist.AssistHandleBehaviorController.BehaviorController; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -31,6 +35,8 @@ import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.statusbar.StatusBarState; +import java.util.concurrent.TimeUnit; + /** * Assistant handle behavior that hides the handles when the phone is dozing or in immersive mode, * shows the handles when on lockscreen, and shows the handles temporarily when changing tasks or @@ -38,6 +44,11 @@ import com.android.systemui.statusbar.StatusBarState; */ final class AssistHandleReminderExpBehavior implements BehaviorController { + 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 long DEFAULT_LEARNING_TIME_MS = TimeUnit.DAYS.toMillis(3); + private static final int DEFAULT_LEARNING_COUNT = 3; + private final StatusBarStateController.StateListener mStatusBarStateListener = new StatusBarStateController.StateListener() { @Override @@ -84,6 +95,15 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { private int mRunningTaskId; private boolean mIsNavBarHidden; + /** Whether user has learned the gesture. */ + private boolean mIsLearned; + private long mLastLearningTimestamp; + /** Uptime while in this behavior. */ + private long mLearningTimeElapsed; + /** Number of successful Assistant invocations while in this behavior. */ + private int mLearningCount; + + @Nullable private Context mContext; @Nullable private AssistHandleCallbacks mAssistHandleCallbacks; AssistHandleReminderExpBehavior() { @@ -94,6 +114,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { @Override public void onModeActivated(Context context, AssistHandleCallbacks callbacks) { + mContext = context; mAssistHandleCallbacks = callbacks; mOnLockscreen = onLockscreen(mStatusBarStateController.getState()); mIsDozing = mStatusBarStateController.isDozing(); @@ -102,17 +123,41 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { mRunningTaskId = runningTaskInfo == null ? 0 : runningTaskInfo.taskId; mActivityManagerWrapper.registerTaskStackListener(mTaskStackChangeListener); mOverviewProxyService.addCallback(mOverviewProxyListener); - callbackForCurrentState(); + + mLearningTimeElapsed = Settings.Secure.getLong( + context.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, /* default = */ 0); + mLearningCount = Settings.Secure.getInt( + context.getContentResolver(), LEARNING_EVENT_COUNT_KEY, /* default = */ 0); + mLastLearningTimestamp = SystemClock.uptimeMillis(); + + callbackForCurrentState(/* justUnlocked = */ false); } @Override public void onModeDeactivated() { mAssistHandleCallbacks = null; + if (mContext != null) { + Settings.Secure.putLong( + mContext.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, mLearningTimeElapsed); + Settings.Secure.putInt( + mContext.getContentResolver(), LEARNING_EVENT_COUNT_KEY, mLearningCount); + mContext = null; + } mStatusBarStateController.removeCallback(mStatusBarStateListener); mActivityManagerWrapper.unregisterTaskStackListener(mTaskStackChangeListener); mOverviewProxyService.removeCallback(mOverviewProxyListener); } + @Override + public void onAssistantGesturePerformed() { + if (mContext == null) { + return; + } + + Settings.Secure.putLong( + mContext.getContentResolver(), LEARNING_EVENT_COUNT_KEY, ++mLearningCount); + } + private static boolean isNavBarHidden(int sysuiStateFlags) { return (sysuiStateFlags & QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN) != 0; } @@ -124,7 +169,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { } mOnLockscreen = onLockscreen; - callbackForCurrentState(); + callbackForCurrentState(!onLockscreen); } private void handleDozingChanged(boolean isDozing) { @@ -133,7 +178,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { } mIsDozing = isDozing; - callbackForCurrentState(); + callbackForCurrentState(/* justUnlocked = */ false); } private void handleTaskStackTopChanged(int taskId) { @@ -142,7 +187,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { } mRunningTaskId = taskId; - callbackForCurrentState(); + callbackForCurrentState(/* justUnlocked = */ false); } private void handleSystemUiStateChanged(int sysuiStateFlags) { @@ -152,11 +197,11 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { } mIsNavBarHidden = isNavBarHidden; - callbackForCurrentState(); + callbackForCurrentState(/* justUnlocked = */ false); } private void handleOverviewShown() { - callbackForCurrentState(); + callbackForCurrentState(/* justUnlocked = */ false); } private boolean onLockscreen(int statusBarState) { @@ -164,7 +209,29 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { || statusBarState == StatusBarState.SHADE_LOCKED; } - private void callbackForCurrentState() { + private void callbackForCurrentState(boolean justUnlocked) { + updateLearningStatus(); + + if (mIsLearned) { + callbackForLearnedState(justUnlocked); + } else { + callbackForUnlearnedState(); + } + } + + private void callbackForLearnedState(boolean justUnlocked) { + if (mAssistHandleCallbacks == null) { + return; + } + + if (mIsDozing || mIsNavBarHidden || mOnLockscreen) { + mAssistHandleCallbacks.hide(); + } else if (justUnlocked) { + mAssistHandleCallbacks.showAndGo(); + } + } + + private void callbackForUnlearnedState() { if (mAssistHandleCallbacks == null) { return; } @@ -177,4 +244,33 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { mAssistHandleCallbacks.showAndGo(); } } + + private void updateLearningStatus() { + if (mContext == null) { + return; + } + + long currentTimestamp = SystemClock.uptimeMillis(); + mLearningTimeElapsed += currentTimestamp - mLastLearningTimestamp; + mLastLearningTimestamp = currentTimestamp; + Settings.Secure.putLong( + mContext.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, mLearningTimeElapsed); + + mIsLearned = + mLearningCount >= getLearningCount() || mLearningTimeElapsed >= getLearningTimeMs(); + } + + private long getLearningTimeMs() { + return DeviceConfig.getLong( + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.ASSIST_HANDLES_LEARN_TIME_MS, + DEFAULT_LEARNING_TIME_MS); + } + + private int getLearningCount() { + return DeviceConfig.getInt( + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.ASSIST_HANDLES_LEARN_COUNT, + DEFAULT_LEARNING_COUNT); + } } diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java index bca96623a4b6c..8cf836eddc288 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java @@ -246,13 +246,17 @@ public class AssistManager implements ConfigurationChangedReceiver { if (args == null) { args = new Bundle(); } + int invocationType = args.getInt(INVOCATION_TYPE_KEY, 0); + if (invocationType == INVOCATION_TYPE_GESTURE) { + mHandleController.onAssistantGesturePerformed(); + } args.putInt(INVOCATION_PHONE_STATE_KEY, mPhoneStateMonitor.getPhoneState()); args.putLong(INVOCATION_TIME_MS_KEY, SystemClock.uptimeMillis()); // Logs assistant start with invocation type. MetricsLogger.action( new LogMaker(MetricsEvent.ASSISTANT) .setType(MetricsEvent.TYPE_OPEN).setSubtype( - args.getInt(INVOCATION_TYPE_KEY))); + invocationType)); startAssistInternal(args, assistComponent, isService); }