From 61c92d6f5b821779e4715c67976cb019be982f39 Mon Sep 17 00:00:00 2001 From: Govinda Wasserman Date: Thu, 5 Sep 2019 16:32:23 -0400 Subject: [PATCH] Trigger doze detection using WakefulnessLifecycle Previously, doze detection used StatusBarStateController, which only provides a signal at the end of the doze transition. This resulted in the handles only animating away once the screen was already off. Using the WakefulnessLifecycle allows for a callback when the transition begins instead. Test: Tested locally BUG:137742178 Change-Id: I289ef5b5081fa96b1c8e82c4373c7654e8df80cb --- .../com/android/systemui/SystemUIModule.java | 3 +- .../AssistHandleBehaviorController.java | 58 ++-- .../assist/AssistHandleLikeHomeBehavior.java | 56 ++-- .../assist/AssistHandleOffBehavior.java | 8 + .../AssistHandleReminderExpBehavior.java | 121 +++++---- .../systemui/assist/AssistManager.java | 14 +- .../android/systemui/assist/AssistModule.java | 88 ++++++ .../systemui/assist/PhenotypeHelper.java | 10 + .../AssistHandleBehaviorControllerTest.java | 37 ++- .../AssistHandleLikeHomeBehaviorTest.java | 250 ++++++++++++++++++ .../assist/AssistHandleOffBehaviorTest.java | 101 +++++++ 11 files changed, 629 insertions(+), 117 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/assist/AssistModule.java create mode 100644 packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleLikeHomeBehaviorTest.java create mode 100644 packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleOffBehaviorTest.java diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/SystemUIModule.java index 33950144ac19b..ff4eb83bd7963 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIModule.java @@ -20,6 +20,7 @@ import android.annotation.Nullable; import android.content.Context; import android.content.pm.PackageManager; +import com.android.systemui.assist.AssistModule; import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.KeyguardLiftController; @@ -34,7 +35,7 @@ import dagger.Provides; * A dagger module for injecting components of System UI that are not overridden by the System UI * implementation. */ -@Module +@Module(includes = {AssistModule.class}) public abstract class SystemUIModule { @Singleton diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java index 38ca70865a580..9bdfa03408aa8 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java @@ -16,6 +16,8 @@ package com.android.systemui.assist; +import static com.android.systemui.assist.AssistModule.ASSIST_HANDLE_THREAD_NAME; + import android.content.ComponentName; import android.content.Context; import android.os.Handler; @@ -28,20 +30,21 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.AssistUtils; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.Dependency; import com.android.systemui.DumpController; import com.android.systemui.Dumpable; import com.android.systemui.ScreenDecorations; -import com.android.systemui.SysUiServiceProvider; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.phone.NavigationModeController; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.EnumMap; import java.util.Map; import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; /** * A class for managing Assistant handle logic. @@ -49,6 +52,7 @@ import java.util.function.Supplier; * Controls when visual handles for Assistant gesture affordance should be shown or hidden using an * {@link AssistHandleBehavior}. */ +@Singleton public final class AssistHandleBehaviorController implements AssistHandleCallbacks, Dumpable { private static final String TAG = "AssistHandleBehavior"; @@ -67,10 +71,9 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac private final Handler mHandler; private final Runnable mHideHandles = this::hideHandles; private final Runnable mShowAndGo = this::showAndGoInternal; - private final Supplier mScreenDecorationsSupplier; + private final Provider mScreenDecorations; private final PhenotypeHelper mPhenotypeHelper; - private final Map mBehaviorMap = - new EnumMap<>(AssistHandleBehavior.class); + private final Map mBehaviorMap; private boolean mHandlesShowing = false; private long mHandlesLastHiddenAt; @@ -82,41 +85,25 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac private AssistHandleBehavior mCurrentBehavior = AssistHandleBehavior.OFF; private boolean mInGesturalMode; - AssistHandleBehaviorController(Context context, AssistUtils assistUtils, Handler handler) { - this( - context, - assistUtils, - handler, - () -> SysUiServiceProvider.getComponent(context, ScreenDecorations.class), - new PhenotypeHelper(), - /* testBehavior = */ null); - } - - @VisibleForTesting + @Inject AssistHandleBehaviorController( Context context, AssistUtils assistUtils, - Handler handler, - Supplier screenDecorationsSupplier, + @Named(ASSIST_HANDLE_THREAD_NAME) Handler handler, + Provider screenDecorations, PhenotypeHelper phenotypeHelper, - @Nullable BehaviorController testBehavior) { + Map behaviorMap, + NavigationModeController navigationModeController, + DumpController dumpController) { mContext = context; mAssistUtils = assistUtils; mHandler = handler; - mScreenDecorationsSupplier = screenDecorationsSupplier; + mScreenDecorations = screenDecorations; mPhenotypeHelper = phenotypeHelper; - mBehaviorMap.put(AssistHandleBehavior.OFF, new AssistHandleOffBehavior()); - mBehaviorMap.put(AssistHandleBehavior.LIKE_HOME, new AssistHandleLikeHomeBehavior()); - mBehaviorMap.put( - AssistHandleBehavior.REMINDER_EXP, - new AssistHandleReminderExpBehavior(handler, phenotypeHelper)); - if (testBehavior != null) { - mBehaviorMap.put(AssistHandleBehavior.TEST, testBehavior); - } + mBehaviorMap = behaviorMap; mInGesturalMode = QuickStepContract.isGesturalMode( - Dependency.get(NavigationModeController.class) - .addListener(this::handleNavigationModeChange)); + navigationModeController.addListener(this::handleNavigationModeChange)); setBehavior(getBehaviorMode()); mPhenotypeHelper.addOnPropertiesChangedListener( @@ -128,7 +115,8 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac SystemUiDeviceConfigFlags.ASSIST_HANDLES_BEHAVIOR_MODE, null)); } }); - Dependency.get(DumpController.class).registerDumpable(TAG, this); + + dumpController.registerDumpable(TAG, this); } @Override // AssistHandleCallbacks @@ -241,7 +229,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac } if (handlesUnblocked(ignoreThreshold)) { - ScreenDecorations screenDecorations = mScreenDecorationsSupplier.get(); + ScreenDecorations screenDecorations = mScreenDecorations.get(); if (screenDecorations == null) { Log.w(TAG, "Couldn't show handles, ScreenDecorations unavailable"); } else { @@ -256,7 +244,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac return; } - ScreenDecorations screenDecorations = mScreenDecorationsSupplier.get(); + ScreenDecorations screenDecorations = mScreenDecorations.get(); if (screenDecorations == null) { Log.w(TAG, "Couldn't hide handles, ScreenDecorations unavailable"); } else { diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java index 763315d2c4e2d..a0d8b4c58f1ad 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java @@ -20,56 +20,82 @@ import android.content.Context; import androidx.annotation.Nullable; -import com.android.systemui.Dependency; import com.android.systemui.assist.AssistHandleBehaviorController.BehaviorController; +import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.model.SysUiState; -import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.system.QuickStepContract; import java.io.PrintWriter; +import javax.inject.Inject; +import javax.inject.Singleton; + +import dagger.Lazy; + /** * Assistant Handle behavior that makes Assistant handles show/hide when the home handle is * shown/hidden, respectively. */ +@Singleton final class AssistHandleLikeHomeBehavior implements BehaviorController { - private final StatusBarStateController.StateListener mStatusBarStateListener = - new StatusBarStateController.StateListener() { + private final WakefulnessLifecycle.Observer mWakefulnessLifecycleObserver = + new WakefulnessLifecycle.Observer() { @Override - public void onDozingChanged(boolean isDozing) { - handleDozingChanged(isDozing); + public void onStartedWakingUp() { + handleDozingChanged(/* isDozing = */ true); + } + + @Override + public void onFinishedWakingUp() { + handleDozingChanged(/* isDozing = */ false); + } + + @Override + public void onStartedGoingToSleep() { + handleDozingChanged(/* isDozing = */ true); + } + + @Override + public void onFinishedGoingToSleep() { + handleDozingChanged(/* isDozing = */ true); } }; private final SysUiState.SysUiStateCallback mSysUiStateCallback = this::handleSystemUiStateChange; - private final StatusBarStateController mStatusBarStateController; - private final SysUiState mSysUiFlagContainer; + + private final Lazy mWakefulnessLifecycle; + private final Lazy mSysUiFlagContainer; private boolean mIsDozing; private boolean mIsHomeHandleHiding; @Nullable private AssistHandleCallbacks mAssistHandleCallbacks; - AssistHandleLikeHomeBehavior() { - mStatusBarStateController = Dependency.get(StatusBarStateController.class); - mSysUiFlagContainer = Dependency.get(SysUiState.class); + @Inject + AssistHandleLikeHomeBehavior( + Lazy wakefulnessLifecycle, + Lazy sysUiFlagContainer) { + mWakefulnessLifecycle = wakefulnessLifecycle; + mSysUiFlagContainer = sysUiFlagContainer; } @Override public void onModeActivated(Context context, AssistHandleCallbacks callbacks) { mAssistHandleCallbacks = callbacks; - mIsDozing = mStatusBarStateController.isDozing(); - mStatusBarStateController.addCallback(mStatusBarStateListener); - mSysUiFlagContainer.addCallback(mSysUiStateCallback); + mIsDozing = mWakefulnessLifecycle.get().getWakefulness() + != WakefulnessLifecycle.WAKEFULNESS_AWAKE; + mWakefulnessLifecycle.get().addObserver(mWakefulnessLifecycleObserver); + mSysUiFlagContainer.get().addCallback(mSysUiStateCallback); callbackForCurrentState(); } @Override public void onModeDeactivated() { mAssistHandleCallbacks = null; - mSysUiFlagContainer.removeCallback(mSysUiStateCallback); + mWakefulnessLifecycle.get().removeObserver(mWakefulnessLifecycleObserver); + mSysUiFlagContainer.get().removeCallback(mSysUiStateCallback); } private static boolean isHomeHandleHiding(int sysuiStateFlags) { diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleOffBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleOffBehavior.java index f4130aeb1d083..df913f9ce9cdc 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleOffBehavior.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleOffBehavior.java @@ -20,9 +20,17 @@ import android.content.Context; import com.android.systemui.assist.AssistHandleBehaviorController.BehaviorController; +import javax.inject.Inject; +import javax.inject.Singleton; + /** Assistant handle behavior that hides the Assistant handles. */ +@Singleton final class AssistHandleOffBehavior implements BehaviorController { + @Inject + AssistHandleOffBehavior() { + } + @Override public void onModeActivated(Context context, AssistHandleCallbacks callbacks) { callbacks.hide(); diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java index 5dd3cb1d8bdcc..d371dbead42b0 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java @@ -16,7 +16,8 @@ package com.android.systemui.assist; -import static com.android.systemui.DejankUtils.whitelistIpcs; +import static com.android.systemui.assist.AssistModule.ASSIST_HANDLE_THREAD_NAME; +import static com.android.systemui.assist.AssistModule.UPTIME_NAME; import android.app.ActivityManager; import android.content.BroadcastReceiver; @@ -26,14 +27,14 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ResolveInfo; import android.os.Handler; -import android.os.SystemClock; import android.provider.Settings; import androidx.annotation.Nullable; +import androidx.slice.Clock; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; -import com.android.systemui.Dependency; import com.android.systemui.assist.AssistHandleBehaviorController.BehaviorController; +import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.recents.OverviewProxyService; @@ -49,11 +50,18 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import dagger.Lazy; + /** * 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 * entering overview. */ +@Singleton final class AssistHandleReminderExpBehavior implements BehaviorController { private static final String LEARNING_TIME_ELAPSED_KEY = "reminder_exp_learning_time_elapsed"; @@ -86,11 +94,6 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { public void onStateChanged(int newState) { handleStatusBarStateChanged(newState); } - - @Override - public void onDozingChanged(boolean isDozing) { - handleDozingChanged(isDozing); - } }; private final TaskStackChangeListener mTaskStackChangeListener = new TaskStackChangeListener() { @@ -111,9 +114,20 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { handleOverviewShown(); } }; - private final SysUiState.SysUiStateCallback mSysUiStateCallback = this::handleSystemUiStateChanged; + private final WakefulnessLifecycle.Observer mWakefulnessLifecycleObserver = + new WakefulnessLifecycle.Observer() { + @Override + public void onFinishedWakingUp() { + handleDozingChanged(false); + } + + @Override + public void onStartedGoingToSleep() { + handleDozingChanged(true); + } + }; private final BroadcastReceiver mDefaultHomeBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -123,12 +137,15 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { private final IntentFilter mDefaultHomeIntentFilter; private final Runnable mResetConsecutiveTaskSwitches = this::resetConsecutiveTaskSwitches; + private final Clock mClock; private final Handler mHandler; private final PhenotypeHelper mPhenotypeHelper; - private final StatusBarStateController mStatusBarStateController; - private final ActivityManagerWrapper mActivityManagerWrapper; - private final OverviewProxyService mOverviewProxyService; - private final SysUiState mSysUiFlagContainer; + private final Lazy mStatusBarStateController; + private final Lazy mActivityManagerWrapper; + private final Lazy mOverviewProxyService; + private final Lazy mSysUiFlagContainer; + private final Lazy mWakefulnessLifecycle; + private final Lazy mPackageManagerWrapper; private boolean mOnLockscreen; private boolean mIsDozing; @@ -150,13 +167,26 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { @Nullable private AssistHandleCallbacks mAssistHandleCallbacks; @Nullable private ComponentName mDefaultHome; - AssistHandleReminderExpBehavior(Handler handler, PhenotypeHelper phenotypeHelper) { + @Inject + AssistHandleReminderExpBehavior( + @Named(UPTIME_NAME) Clock clock, + @Named(ASSIST_HANDLE_THREAD_NAME) Handler handler, + PhenotypeHelper phenotypeHelper, + Lazy statusBarStateController, + Lazy activityManagerWrapper, + Lazy overviewProxyService, + Lazy sysUiFlagContainer, + Lazy wakefulnessLifecycle, + Lazy packageManagerWrapper) { + mClock = clock; mHandler = handler; mPhenotypeHelper = phenotypeHelper; - mStatusBarStateController = Dependency.get(StatusBarStateController.class); - mActivityManagerWrapper = ActivityManagerWrapper.getInstance(); - mOverviewProxyService = Dependency.get(OverviewProxyService.class); - mSysUiFlagContainer = Dependency.get(SysUiState.class); + mStatusBarStateController = statusBarStateController; + mActivityManagerWrapper = activityManagerWrapper; + mOverviewProxyService = overviewProxyService; + mSysUiFlagContainer = sysUiFlagContainer; + mWakefulnessLifecycle = wakefulnessLifecycle; + mPackageManagerWrapper = packageManagerWrapper; mDefaultHomeIntentFilter = new IntentFilter(); for (String action : DEFAULT_HOME_CHANGE_ACTIONS) { mDefaultHomeIntentFilter.addAction(action); @@ -170,14 +200,17 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { mConsecutiveTaskSwitches = 0; mDefaultHome = getCurrentDefaultHome(); context.registerReceiver(mDefaultHomeBroadcastReceiver, mDefaultHomeIntentFilter); - mOnLockscreen = onLockscreen(mStatusBarStateController.getState()); - mIsDozing = mStatusBarStateController.isDozing(); - mStatusBarStateController.addCallback(mStatusBarStateListener); - ActivityManager.RunningTaskInfo runningTaskInfo = mActivityManagerWrapper.getRunningTask(); + mOnLockscreen = onLockscreen(mStatusBarStateController.get().getState()); + mStatusBarStateController.get().addCallback(mStatusBarStateListener); + ActivityManager.RunningTaskInfo runningTaskInfo = + mActivityManagerWrapper.get().getRunningTask(); mRunningTaskId = runningTaskInfo == null ? 0 : runningTaskInfo.taskId; - mActivityManagerWrapper.registerTaskStackListener(mTaskStackChangeListener); - mOverviewProxyService.addCallback(mOverviewProxyListener); - mSysUiFlagContainer.addCallback(mSysUiStateCallback); + mActivityManagerWrapper.get().registerTaskStackListener(mTaskStackChangeListener); + mOverviewProxyService.get().addCallback(mOverviewProxyListener); + mSysUiFlagContainer.get().addCallback(mSysUiStateCallback); + mIsDozing = mWakefulnessLifecycle.get().getWakefulness() + != WakefulnessLifecycle.WAKEFULNESS_AWAKE; + mWakefulnessLifecycle.get().addObserver(mWakefulnessLifecycleObserver); mLearningTimeElapsed = Settings.Secure.getLong( context.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, /* default = */ 0); @@ -185,7 +218,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { 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 = mClock.currentTimeMillis(); callbackForCurrentState(/* justUnlocked = */ false); } @@ -200,10 +233,11 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { Settings.Secure.putLong(mContext.getContentResolver(), LEARNED_HINT_LAST_SHOWN_KEY, 0); mContext = null; } - mStatusBarStateController.removeCallback(mStatusBarStateListener); - mActivityManagerWrapper.unregisterTaskStackListener(mTaskStackChangeListener); - mOverviewProxyService.removeCallback(mOverviewProxyListener); - mSysUiFlagContainer.removeCallback(mSysUiStateCallback); + mStatusBarStateController.get().removeCallback(mStatusBarStateListener); + mActivityManagerWrapper.get().unregisterTaskStackListener(mTaskStackChangeListener); + mOverviewProxyService.get().removeCallback(mOverviewProxyListener); + mSysUiFlagContainer.get().removeCallback(mSysUiStateCallback); + mWakefulnessLifecycle.get().removeObserver(mWakefulnessLifecycleObserver); } @Override @@ -223,15 +257,10 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { } } - private static boolean isNavBarHidden(int sysuiStateFlags) { - return (sysuiStateFlags & QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN) != 0; - } - @Nullable - private static ComponentName getCurrentDefaultHome() { + private ComponentName getCurrentDefaultHome() { List homeActivities = new ArrayList<>(); - ComponentName defaultHome = - PackageManagerWrapper.getInstance().getHomeActivities(homeActivities); + ComponentName defaultHome = mPackageManagerWrapper.get().getHomeActivities(homeActivities); if (defaultHome != null) { return defaultHome; } @@ -287,7 +316,8 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { } private void handleSystemUiStateChanged(int sysuiStateFlags) { - boolean isNavBarHidden = isNavBarHidden(sysuiStateFlags); + boolean isNavBarHidden = + (sysuiStateFlags & QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN) != 0; if (mIsNavBarHidden == isNavBarHidden) { return; } @@ -374,24 +404,15 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { return; } - long currentTimestamp = SystemClock.uptimeMillis(); + long currentTimestamp = mClock.currentTimeMillis(); mLearningTimeElapsed += currentTimestamp - mLastLearningTimestamp; mLastLearningTimestamp = currentTimestamp; - // TODO(b/140034473) - whitelistIpcs(() -> Settings.Secure.putLong( - mContext.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, mLearningTimeElapsed)); mIsLearned = mLearningCount >= getLearningCount() || mLearningTimeElapsed >= getLearningTimeMs(); - mHandler.post(this::recordLearnTimeElapsed); - } - - private void recordLearnTimeElapsed() { - if (mContext != null) { - Settings.Secure.putLong( - mContext.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, mLearningTimeElapsed); - } + mHandler.post(() -> Settings.Secure.putLong( + mContext.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, mLearningTimeElapsed)); } private void resetConsecutiveTaskSwitches() { diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java index eb60b3db7a112..1f950f60fd6a3 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java @@ -21,7 +21,6 @@ import android.os.AsyncTask; import android.os.Binder; import android.os.Bundle; import android.os.Handler; -import android.os.HandlerThread; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; @@ -157,17 +156,18 @@ public class AssistManager implements ConfigurationChangedReceiver { }; @Inject - public AssistManager(DeviceProvisionedController controller, Context context) { + public AssistManager( + DeviceProvisionedController controller, + Context context, + AssistUtils assistUtils, + AssistHandleBehaviorController handleController) { mContext = context; mDeviceProvisionedController = controller; mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); - mAssistUtils = new AssistUtils(context); + mAssistUtils = assistUtils; mAssistDisclosure = new AssistDisclosure(context, new Handler()); mPhoneStateMonitor = new PhoneStateMonitor(context); - final HandlerThread assistHandleThread = new HandlerThread("AssistHandleThread"); - assistHandleThread.start(); - mHandleController = new AssistHandleBehaviorController( - context, mAssistUtils, assistHandleThread.getThreadHandler()); + mHandleController = handleController; registerVoiceInteractionSessionListener(); mInterestingConfigChanges = new InterestingConfigChanges(ActivityInfo.CONFIG_ORIENTATION diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java new file mode 100644 index 0000000000000..2a82d215e44a8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java @@ -0,0 +1,88 @@ +/* + * 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.content.Context; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.SystemClock; + +import androidx.slice.Clock; + +import com.android.internal.app.AssistUtils; +import com.android.systemui.ScreenDecorations; +import com.android.systemui.SysUiServiceProvider; + +import java.util.EnumMap; +import java.util.Map; + +import javax.inject.Named; +import javax.inject.Singleton; + +import dagger.Module; +import dagger.Provides; + +/** Module for dagger injections related to the Assistant. */ +@Module +public abstract class AssistModule { + + static final String ASSIST_HANDLE_THREAD_NAME = "assist_handle_thread"; + static final String UPTIME_NAME = "uptime"; + + @Provides + @Singleton + @Named(ASSIST_HANDLE_THREAD_NAME) + static Handler provideBackgroundHandler() { + final HandlerThread backgroundHandlerThread = + new HandlerThread("AssistHandleThread"); + backgroundHandlerThread.start(); + return backgroundHandlerThread.getThreadHandler(); + } + + @Provides + @Singleton + static Map + provideAssistHandleBehaviorControllerMap( + AssistHandleOffBehavior offBehavior, + AssistHandleLikeHomeBehavior likeHomeBehavior, + AssistHandleReminderExpBehavior reminderExpBehavior) { + Map map = + new EnumMap<>(AssistHandleBehavior.class); + map.put(AssistHandleBehavior.OFF, offBehavior); + map.put(AssistHandleBehavior.LIKE_HOME, likeHomeBehavior); + map.put(AssistHandleBehavior.REMINDER_EXP, reminderExpBehavior); + return map; + } + + @Provides + static ScreenDecorations provideScreenDecorations(Context context) { + return SysUiServiceProvider.getComponent(context, ScreenDecorations.class); + } + + @Provides + @Singleton + static AssistUtils provideAssistUtils(Context context) { + return new AssistUtils(context); + } + + @Provides + @Singleton + @Named(UPTIME_NAME) + static Clock provideSystemClock() { + return SystemClock::uptimeMillis; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/assist/PhenotypeHelper.java b/packages/SystemUI/src/com/android/systemui/assist/PhenotypeHelper.java index c21a717dc8cf4..cbb56e8e979dd 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/PhenotypeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/assist/PhenotypeHelper.java @@ -24,8 +24,18 @@ import androidx.annotation.Nullable; import java.util.concurrent.Executor; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Wrapper class for retrieving phenotype flag values. + * + * Can be mocked in tests for ease of testing the effects of particular values. + */ +@Singleton public class PhenotypeHelper { + @Inject public PhenotypeHelper() {} public long getLong(String name, long defaultValue) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java index 6db5ef92c12cc..9c920f52d56a5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java @@ -42,6 +42,7 @@ import com.android.systemui.ScreenDecorations; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.recents.OverviewProxyService; +import com.android.systemui.statusbar.phone.NavigationModeController; import org.junit.After; import org.junit.Before; @@ -51,6 +52,9 @@ import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.EnumMap; +import java.util.Map; + @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper @@ -64,7 +68,12 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase { @Mock private AssistUtils mMockAssistUtils; @Mock private Handler mMockHandler; @Mock private PhenotypeHelper mMockPhenotypeHelper; - @Mock private AssistHandleBehaviorController.BehaviorController mMockBehaviorController; + @Mock private AssistHandleOffBehavior mMockOffBehavior; + @Mock private AssistHandleLikeHomeBehavior mMockLikeHomeBehavior; + @Mock private AssistHandleReminderExpBehavior mMockReminderExpBehavior; + @Mock private AssistHandleBehaviorController.BehaviorController mMockTestBehavior; + @Mock private NavigationModeController mMockNavigationModeController; + @Mock private DumpController mMockDumpController; @Before public void setup() { @@ -76,13 +85,23 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase { doAnswer(answerVoid(Runnable::run)).when(mMockHandler) .postDelayed(any(Runnable.class), anyLong()); + Map behaviorMap = + new EnumMap<>(AssistHandleBehavior.class); + behaviorMap.put(AssistHandleBehavior.OFF, mMockOffBehavior); + behaviorMap.put(AssistHandleBehavior.LIKE_HOME, mMockLikeHomeBehavior); + behaviorMap.put(AssistHandleBehavior.REMINDER_EXP, mMockReminderExpBehavior); + behaviorMap.put(AssistHandleBehavior.TEST, mMockTestBehavior); + mAssistHandleBehaviorController = new AssistHandleBehaviorController( mContext, mMockAssistUtils, - mMockHandler, () -> mMockScreenDecorations, + mMockHandler, + () -> mMockScreenDecorations, mMockPhenotypeHelper, - mMockBehaviorController); + behaviorMap, + mMockNavigationModeController, + mMockDumpController); } @After @@ -316,8 +335,8 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase { mAssistHandleBehaviorController.setBehavior(AssistHandleBehavior.TEST); // Assert - verify(mMockBehaviorController).onModeActivated(mContext, mAssistHandleBehaviorController); - verifyNoMoreInteractions(mMockBehaviorController); + verify(mMockTestBehavior).onModeActivated(mContext, mAssistHandleBehaviorController); + verifyNoMoreInteractions(mMockTestBehavior); } @Test @@ -326,14 +345,14 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase { when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME); mAssistHandleBehaviorController.setBehavior(AssistHandleBehavior.TEST); mAssistHandleBehaviorController.setInGesturalModeForTest(true); - reset(mMockBehaviorController); + reset(mMockTestBehavior); // Act mAssistHandleBehaviorController.setBehavior(AssistHandleBehavior.OFF); // Assert - verify(mMockBehaviorController).onModeDeactivated(); - verifyNoMoreInteractions(mMockBehaviorController); + verify(mMockTestBehavior).onModeDeactivated(); + verifyNoMoreInteractions(mMockTestBehavior); } @Test @@ -346,6 +365,6 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase { mAssistHandleBehaviorController.setBehavior(AssistHandleBehavior.TEST); // Assert - verifyNoMoreInteractions(mMockBehaviorController); + verifyNoMoreInteractions(mMockTestBehavior); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleLikeHomeBehaviorTest.java b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleLikeHomeBehaviorTest.java new file mode 100644 index 0000000000000..c53289c62d946 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleLikeHomeBehaviorTest.java @@ -0,0 +1,250 @@ +/* + * 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 static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.model.SysUiState; +import com.android.systemui.shared.system.QuickStepContract; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class AssistHandleLikeHomeBehaviorTest extends SysuiTestCase { + + private AssistHandleLikeHomeBehavior mAssistHandleLikeHomeBehavior; + + @Mock private WakefulnessLifecycle mMockWakefulnessLifecycle; + @Mock private SysUiState mMockSysUiState; + @Mock private AssistHandleCallbacks mMockAssistHandleCallbacks; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mAssistHandleLikeHomeBehavior = new AssistHandleLikeHomeBehavior( + () -> mMockWakefulnessLifecycle, () -> mMockSysUiState); + } + + @Test + public void onModeActivated_beginsObserving() { + // Arrange + + // Act + mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks); + + // Assert + verify(mMockWakefulnessLifecycle).getWakefulness(); + verify(mMockWakefulnessLifecycle).addObserver(any(WakefulnessLifecycle.Observer.class)); + verify(mMockSysUiState).addCallback(any(SysUiState.SysUiStateCallback.class)); + verifyNoMoreInteractions(mMockWakefulnessLifecycle, mMockSysUiState); + } + + @Test + public void onModeActivated_showsHandlesWhenAwake() { + // Arrange + when(mMockWakefulnessLifecycle.getWakefulness()) + .thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE); + + // Act + mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks); + + // Assert + verify(mMockAssistHandleCallbacks).showAndStay(); + verifyNoMoreInteractions(mMockAssistHandleCallbacks); + } + + @Test + public void onModeActivated_hidesHandlesWhenNotAwake() { + // Arrange + when(mMockWakefulnessLifecycle.getWakefulness()) + .thenReturn(WakefulnessLifecycle.WAKEFULNESS_ASLEEP); + + // Act + mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks); + + // Assert + verify(mMockAssistHandleCallbacks).hide(); + verifyNoMoreInteractions(mMockAssistHandleCallbacks); + } + + @Test + public void onModeDeactivated_stopsObserving() { + // Arrange + mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks); + ArgumentCaptor observer = + ArgumentCaptor.forClass(WakefulnessLifecycle.Observer.class); + ArgumentCaptor sysUiStateCallback = + ArgumentCaptor.forClass(SysUiState.SysUiStateCallback.class); + verify(mMockWakefulnessLifecycle).addObserver(observer.capture()); + verify(mMockSysUiState).addCallback(sysUiStateCallback.capture()); + reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks); + + // Act + mAssistHandleLikeHomeBehavior.onModeDeactivated(); + + // Assert + verify(mMockWakefulnessLifecycle).removeObserver(eq(observer.getValue())); + verify(mMockSysUiState).removeCallback(eq(sysUiStateCallback.getValue())); + verifyNoMoreInteractions( + mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks); + } + + @Test + public void onAssistantGesturePerformed_doesNothing() { + // Arrange + mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks); + reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks); + + // Act + mAssistHandleLikeHomeBehavior.onAssistantGesturePerformed(); + + // Assert + verifyNoMoreInteractions( + mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks); + } + + @Test + public void onAssistHandlesRequested_doesNothing() { + // Arrange + mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks); + reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks); + + // Act + mAssistHandleLikeHomeBehavior.onAssistHandlesRequested(); + + // Assert + verifyNoMoreInteractions( + mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks); + } + + @Test + public void onWake_handlesShow() { + // Arrange + when(mMockWakefulnessLifecycle.getWakefulness()) + .thenReturn(WakefulnessLifecycle.WAKEFULNESS_ASLEEP); + ArgumentCaptor observer = + ArgumentCaptor.forClass(WakefulnessLifecycle.Observer.class); + mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks); + verify(mMockWakefulnessLifecycle).addObserver(observer.capture()); + reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks); + + // Act + observer.getValue().onStartedWakingUp(); + + // Assert + verifyNoMoreInteractions( + mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks); + + // Act + observer.getValue().onFinishedWakingUp(); + + // Assert + verify(mMockAssistHandleCallbacks).showAndStay(); + verifyNoMoreInteractions( + mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks); + } + + @Test + public void onSleep_handlesHide() { + // Arrange + when(mMockWakefulnessLifecycle.getWakefulness()) + .thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE); + ArgumentCaptor observer = + ArgumentCaptor.forClass(WakefulnessLifecycle.Observer.class); + mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks); + verify(mMockWakefulnessLifecycle).addObserver(observer.capture()); + reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks); + + // Act + observer.getValue().onStartedGoingToSleep(); + + // Assert + verify(mMockAssistHandleCallbacks).hide(); + verifyNoMoreInteractions( + mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks); + + // Act + observer.getValue().onFinishedGoingToSleep(); + + // Assert + verifyNoMoreInteractions( + mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks); + } + + @Test + public void onHomeHandleHide_handlesHide() { + // Arrange + when(mMockWakefulnessLifecycle.getWakefulness()) + .thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE); + ArgumentCaptor sysUiStateCallback = + ArgumentCaptor.forClass(SysUiState.SysUiStateCallback.class); + mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks); + verify(mMockSysUiState).addCallback(sysUiStateCallback.capture()); + reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks); + + // Act + sysUiStateCallback.getValue().onSystemUiStateChanged( + QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN); + + // Assert + verify(mMockAssistHandleCallbacks).hide(); + verifyNoMoreInteractions( + mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks); + } + + @Test + public void onHomeHandleUnhide_handlesShow() { + // Arrange + when(mMockWakefulnessLifecycle.getWakefulness()) + .thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE); + ArgumentCaptor sysUiStateCallback = + ArgumentCaptor.forClass(SysUiState.SysUiStateCallback.class); + mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks); + verify(mMockSysUiState).addCallback(sysUiStateCallback.capture()); + sysUiStateCallback.getValue().onSystemUiStateChanged( + QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN); + reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks); + + // Act + sysUiStateCallback.getValue().onSystemUiStateChanged( + ~QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN); + + // Assert + verify(mMockAssistHandleCallbacks).showAndStay(); + verifyNoMoreInteractions( + mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleOffBehaviorTest.java b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleOffBehaviorTest.java new file mode 100644 index 0000000000000..15d4d5b4792f3 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleOffBehaviorTest.java @@ -0,0 +1,101 @@ +/* + * 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 static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class AssistHandleOffBehaviorTest extends SysuiTestCase { + + private AssistHandleOffBehavior mAssistHandleOffBehavior; + + @Mock private AssistHandleCallbacks mMockAssistHandleCallbacks; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mAssistHandleOffBehavior = new AssistHandleOffBehavior(); + } + + @Test + public void onModeActivated_hidesHandles() { + // Arrange + + // Act + mAssistHandleOffBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks); + + // Assert + verify(mMockAssistHandleCallbacks).hide(); + verifyNoMoreInteractions(mMockAssistHandleCallbacks); + } + + @Test + public void onModeDeactivated_doesNothing() { + // Arrange + mAssistHandleOffBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks); + reset(mMockAssistHandleCallbacks); + + // Act + mAssistHandleOffBehavior.onModeDeactivated(); + + // Assert + verifyNoMoreInteractions(mMockAssistHandleCallbacks); + } + + @Test + public void onAssistantGesturePerformed_doesNothing() { + // Arrange + mAssistHandleOffBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks); + reset(mMockAssistHandleCallbacks); + + // Act + mAssistHandleOffBehavior.onAssistantGesturePerformed(); + + // Assert + verifyNoMoreInteractions(mMockAssistHandleCallbacks); + } + + @Test + public void onAssistHandlesRequested_doesNothing() { + // Arrange + mAssistHandleOffBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks); + reset(mMockAssistHandleCallbacks); + + // Act + mAssistHandleOffBehavior.onAssistHandlesRequested(); + + // Assert + verifyNoMoreInteractions(mMockAssistHandleCallbacks); + } +}