Loading packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt +73 −3 Original line number Diff line number Diff line Loading @@ -16,10 +16,19 @@ package com.android.systemui.controls.dagger import android.content.ContentResolver import android.content.Context import android.database.ContentObserver import android.provider.Settings import com.android.systemui.controls.controller.ControlsController import com.android.systemui.controls.management.ControlsListingController import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.dagger.SysUISingleton import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.settings.SecureSettings import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT import dagger.Lazy import java.util.Optional import javax.inject.Inject Loading @@ -28,15 +37,43 @@ import javax.inject.Inject * Pseudo-component to inject into classes outside `com.android.systemui.controls`. * * If `featureEnabled` is false, all the optionals should be empty. The controllers will only be * instantiated if `featureEnabled` is true. * instantiated if `featureEnabled` is true. Can also be queried for the availability of controls. */ @SysUISingleton class ControlsComponent @Inject constructor( @ControlsFeatureEnabled private val featureEnabled: Boolean, private val context: Context, private val lazyControlsController: Lazy<ControlsController>, private val lazyControlsUiController: Lazy<ControlsUiController>, private val lazyControlsListingController: Lazy<ControlsListingController> private val lazyControlsListingController: Lazy<ControlsListingController>, private val lockPatternUtils: LockPatternUtils, private val keyguardStateController: KeyguardStateController, private val userTracker: UserTracker, private val secureSettings: SecureSettings ) { private val contentResolver: ContentResolver get() = context.contentResolver private var canShowWhileLockedSetting = false val showWhileLockedObserver = object : ContentObserver(null) { override fun onChange(selfChange: Boolean) { updateShowWhileLocked() } } init { if (featureEnabled) { secureSettings.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT), false, /* notifyForDescendants */ showWhileLockedObserver ) updateShowWhileLocked() } } fun getControlsController(): Optional<ControlsController> { return if (featureEnabled) Optional.of(lazyControlsController.get()) else Optional.empty() } Loading @@ -52,4 +89,37 @@ class ControlsComponent @Inject constructor( Optional.empty() } } /** * @return true if controls are feature-enabled and have available services to serve controls */ fun isEnabled() = featureEnabled && lazyControlsController.get().available /** * Returns one of 3 states: * * AVAILABLE - Controls can be made visible * * AVAILABLE_AFTER_UNLOCK - Controls can be made visible only after device unlock * * UNAVAILABLE - Controls are not enabled */ fun getVisibility(): Visibility { if (!isEnabled()) return Visibility.UNAVAILABLE if (lockPatternUtils.getStrongAuthForUser(userTracker.userHandle.identifier) == STRONG_AUTH_REQUIRED_AFTER_BOOT) { return Visibility.AVAILABLE_AFTER_UNLOCK } if (!canShowWhileLockedSetting && !keyguardStateController.isUnlocked()) { return Visibility.AVAILABLE_AFTER_UNLOCK } return Visibility.AVAILABLE } private fun updateShowWhileLocked() { canShowWhileLockedSetting = secureSettings.getInt( Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT, 0) != 0 } enum class Visibility { AVAILABLE, AVAILABLE_AFTER_UNLOCK, UNAVAILABLE } } packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +19 −15 Original line number Diff line number Diff line Loading @@ -25,6 +25,8 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOM import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE; import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING; import android.animation.Animator; Loading Loading @@ -250,6 +252,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private final IWindowManager mIWindowManager; private final Executor mBackgroundExecutor; private List<ControlsServiceInfo> mControlsServiceInfos = new ArrayList<>(); private ControlsComponent mControlsComponent; private Optional<ControlsController> mControlsControllerOptional; private final RingerModeTracker mRingerModeTracker; private int mDialogPressDelay = DIALOG_PRESS_DELAY; // ms Loading Loading @@ -338,6 +341,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mSysuiColorExtractor = colorExtractor; mStatusBarService = statusBarService; mNotificationShadeWindowController = notificationShadeWindowController; mControlsComponent = controlsComponent; mControlsUiControllerOptional = controlsComponent.getControlsUiController(); mIWindowManager = iWindowManager; mBackgroundExecutor = backgroundExecutor; Loading Loading @@ -387,7 +391,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, if (mDialog.mWalletViewController != null) { mDialog.mWalletViewController.onDeviceLockStateChanged(!unlocked); } if (!mDialog.isShowingControls() && shouldShowControls()) { if (!mDialog.isShowingControls() && mControlsComponent.getVisibility() == AVAILABLE) { mDialog.showControls(mControlsUiControllerOptional.get()); } if (unlocked) { Loading @@ -397,14 +402,15 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } }); if (controlsComponent.getControlsListingController().isPresent()) { controlsComponent.getControlsListingController().get() if (mControlsComponent.getControlsListingController().isPresent()) { mControlsComponent.getControlsListingController().get() .addCallback(list -> { mControlsServiceInfos = list; // This callback may occur after the dialog has been shown. If so, add // controls into the already visible space or show the lock msg if needed. if (mDialog != null) { if (!mDialog.isShowingControls() && shouldShowControls()) { if (!mDialog.isShowingControls() && mControlsComponent.getVisibility() == AVAILABLE) { mDialog.showControls(mControlsUiControllerOptional.get()); } else if (shouldShowLockMessage(mDialog)) { mDialog.showLockMessage(); Loading Loading @@ -704,7 +710,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mDepthController.setShowingHomeControls(true); ControlsUiController uiController = null; if (mControlsUiControllerOptional.isPresent() && shouldShowControls()) { if (mControlsComponent.getVisibility() == AVAILABLE) { uiController = mControlsUiControllerOptional.get(); } ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, mOverflowAdapter, Loading Loading @@ -2687,26 +2693,24 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, return isPanelDebugModeEnabled(context); } private boolean shouldShowControls() { boolean showOnLockScreen = mShowLockScreenCardsAndControls && mLockPatternUtils .getStrongAuthForUser(getCurrentUser().id) != STRONG_AUTH_REQUIRED_AFTER_BOOT; return controlsAvailable() && (mKeyguardStateController.isUnlocked() || showOnLockScreen); } private boolean controlsAvailable() { return mDeviceProvisioned && mControlsUiControllerOptional.isPresent() && mControlsUiControllerOptional.get().getAvailable() && mControlsComponent.isEnabled() && !mControlsServiceInfos.isEmpty(); } private boolean shouldShowLockMessage(ActionsDialog dialog) { return mControlsComponent.getVisibility() == AVAILABLE_AFTER_UNLOCK || isWalletAvailableAfterUnlock(dialog); } // Temporary while we move items out of the power menu private boolean isWalletAvailableAfterUnlock(ActionsDialog dialog) { boolean isLockedAfterBoot = mLockPatternUtils.getStrongAuthForUser(getCurrentUser().id) == STRONG_AUTH_REQUIRED_AFTER_BOOT; return !mKeyguardStateController.isUnlocked() && (!mShowLockScreenCardsAndControls || isLockedAfterBoot) && (controlsAvailable() || dialog.isWalletViewAvailable()); && dialog.isWalletViewAvailable(); } private void onPowerMenuLockScreenSettingsChanged() { Loading packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt +3 −2 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import com.android.internal.logging.MetricsLogger import com.android.systemui.R import com.android.systemui.controls.ControlsServiceInfo import com.android.systemui.controls.dagger.ControlsComponent import com.android.systemui.controls.dagger.ControlsComponent.Visibility.UNAVAILABLE import com.android.systemui.controls.management.ControlsListingController import com.android.systemui.controls.ui.ControlsDialog import com.android.systemui.dagger.qualifiers.Background Loading Loading @@ -91,7 +92,7 @@ class DeviceControlsTile @Inject constructor( override fun isAvailable(): Boolean { return featureFlags.isKeyguardLayoutEnabled && controlsLockscreen && controlsComponent.getControlsUiController().isPresent controlsComponent.getVisibility() != UNAVAILABLE } override fun newTileState(): QSTile.State { Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +31 −33 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.phone; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE; import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_BUTTON; import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_UNLOCK; Loading Loading @@ -183,6 +184,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private ControlsComponent mControlsComponent; private int mLockScreenMode; private BroadcastDispatcher mBroadcastDispatcher; private KeyguardUpdateMonitor mKeyguardUpdateMonitor; public KeyguardBottomAreaView(Context context) { this(context, null); Loading Loading @@ -295,7 +297,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); getContext().registerReceiverAsUser(mDevicePolicyReceiver, UserHandle.ALL, filter, null, null); Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mUpdateMonitorCallback); mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback); mKeyguardStateController.addCallback(this); } Loading @@ -307,7 +310,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mRightExtension.destroy(); mLeftExtension.destroy(); getContext().unregisterReceiver(mDevicePolicyReceiver); Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mUpdateMonitorCallback); mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback); } private void initAccessibility() { Loading Loading @@ -410,12 +413,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } private void updateLeftAffordanceIcon() { if (mDozing) { mAltLeftButton.setVisibility(GONE); } else if (mAltLeftButton.getDrawable() != null) { mAltLeftButton.setVisibility(VISIBLE); } if (!mShowLeftAffordance || mDozing) { mLeftAffordanceView.setVisibility(GONE); return; Loading @@ -430,6 +427,14 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mLeftAffordanceView.setContentDescription(state.contentDescription); } private void updateControlsVisibility() { if (mDozing || mControlsComponent.getVisibility() != AVAILABLE) { mAltLeftButton.setVisibility(GONE); } else { mAltLeftButton.setVisibility(VISIBLE); } } public boolean isLeftVoiceAssist() { return mLeftIsVoiceAssist; } Loading Loading @@ -769,6 +774,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL updateCameraVisibility(); updateLeftAffordanceIcon(); updateControlsVisibility(); if (dozing) { mOverlayContainer.setVisibility(INVISIBLE); Loading Loading @@ -889,36 +895,28 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } private void setupControls() { if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) { boolean inNewLayout = mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL; boolean settingEnabled = Settings.Global.getInt(mContext.getContentResolver(), "controls_lockscreen", 0) == 1; if (!inNewLayout || !settingEnabled || !mControlsComponent.isEnabled()) { mAltLeftButton.setVisibility(View.GONE); mAltLeftButton.setOnClickListener(null); return; } if (Settings.Global.getInt(mContext.getContentResolver(), "controls_lockscreen", 0) == 0) { return; } if (mControlsComponent.getControlsListingController().isPresent()) { mControlsComponent.getControlsListingController().get() .addCallback(list -> { if (!list.isEmpty()) { mAltLeftButton.setImageDrawable(list.get(0).loadIcon()); mAltLeftButton.setVisibility(View.VISIBLE); mAltLeftButton.setOnClickListener((v) -> { ControlsUiController ui = mControlsComponent .getControlsUiController().get(); mControlsDialog = new ControlsDialog(mContext, mBroadcastDispatcher) .show(ui); }); } else { mAltLeftButton.setVisibility(View.GONE); mAltLeftButton.setOnClickListener(null); } updateControlsVisibility(); }); } } /** * Optionally add controls when in the new lockscreen mode Loading packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt +98 −13 Original line number Diff line number Diff line Loading @@ -17,11 +17,18 @@ package com.android.systemui.controls.dagger import android.testing.AndroidTestingRunner import android.provider.Settings import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT import com.android.systemui.SysuiTestCase import com.android.systemui.controls.controller.ControlsController import com.android.systemui.controls.management.ControlsListingController import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.settings.SecureSettings import dagger.Lazy import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse Loading @@ -29,7 +36,11 @@ import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Answers import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.anyInt import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest Loading @@ -42,20 +53,29 @@ class ControlsComponentTest : SysuiTestCase() { private lateinit var uiController: ControlsUiController @Mock private lateinit var listingController: ControlsListingController @Mock private lateinit var keyguardStateController: KeyguardStateController @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var userTracker: UserTracker @Mock private lateinit var lockPatternUtils: LockPatternUtils @Mock private lateinit var secureSettings: SecureSettings companion object { fun <T> eq(value: T): T = Mockito.eq(value) ?: value } @Before fun setUp() { MockitoAnnotations.initMocks(this) `when`(userTracker.userHandle.identifier).thenReturn(0) } @Test fun testFeatureEnabled() { val component = ControlsComponent( true, Lazy { controller }, Lazy { uiController }, Lazy { listingController } ) val component = setupComponent(true) assertTrue(component.getControlsController().isPresent) assertEquals(controller, component.getControlsController().get()) Loading @@ -67,15 +87,80 @@ class ControlsComponentTest : SysuiTestCase() { @Test fun testFeatureDisabled() { val component = ControlsComponent( false, Lazy { controller }, Lazy { uiController }, Lazy { listingController } ) val component = setupComponent(false) assertFalse(component.getControlsController().isPresent) assertFalse(component.getControlsUiController().isPresent) assertFalse(component.getControlsListingController().isPresent) } @Test fun testFeatureDisabledVisibility() { val component = setupComponent(false) assertEquals(ControlsComponent.Visibility.UNAVAILABLE, component.getVisibility()) } @Test fun testFeatureEnabledAfterBootVisibility() { `when`(controller.available).thenReturn(true) `when`(lockPatternUtils.getStrongAuthForUser(anyInt())) .thenReturn(STRONG_AUTH_REQUIRED_AFTER_BOOT) val component = setupComponent(true) assertEquals(ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK, component.getVisibility()) } @Test fun testFeatureEnabledAndCannotShowOnLockScreenVisibility() { `when`(controller.available).thenReturn(true) `when`(lockPatternUtils.getStrongAuthForUser(anyInt())) .thenReturn(STRONG_AUTH_NOT_REQUIRED) `when`(keyguardStateController.isUnlocked()).thenReturn(false) `when`(secureSettings.getInt(eq(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT), anyInt())) .thenReturn(0) val component = setupComponent(true) assertEquals(ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK, component.getVisibility()) } @Test fun testFeatureEnabledAndCanShowOnLockScreenVisibility() { `when`(controller.available).thenReturn(true) `when`(lockPatternUtils.getStrongAuthForUser(anyInt())) .thenReturn(STRONG_AUTH_NOT_REQUIRED) `when`(keyguardStateController.isUnlocked()).thenReturn(false) `when`(secureSettings.getInt(eq(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT), anyInt())) .thenReturn(1) val component = setupComponent(true) assertEquals(ControlsComponent.Visibility.AVAILABLE, component.getVisibility()) } @Test fun testFeatureEnabledAndCanShowWhileUnlockedVisibility() { `when`(secureSettings.getInt(eq(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT), anyInt())) .thenReturn(0) `when`(controller.available).thenReturn(true) `when`(lockPatternUtils.getStrongAuthForUser(anyInt())) .thenReturn(STRONG_AUTH_NOT_REQUIRED) `when`(keyguardStateController.isUnlocked()).thenReturn(true) val component = setupComponent(true) assertEquals(ControlsComponent.Visibility.AVAILABLE, component.getVisibility()) } private fun setupComponent(enabled: Boolean): ControlsComponent { return ControlsComponent( enabled, mContext, Lazy { controller }, Lazy { uiController }, Lazy { listingController }, lockPatternUtils, keyguardStateController, userTracker, secureSettings ) } } Loading
packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt +73 −3 Original line number Diff line number Diff line Loading @@ -16,10 +16,19 @@ package com.android.systemui.controls.dagger import android.content.ContentResolver import android.content.Context import android.database.ContentObserver import android.provider.Settings import com.android.systemui.controls.controller.ControlsController import com.android.systemui.controls.management.ControlsListingController import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.dagger.SysUISingleton import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.settings.SecureSettings import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT import dagger.Lazy import java.util.Optional import javax.inject.Inject Loading @@ -28,15 +37,43 @@ import javax.inject.Inject * Pseudo-component to inject into classes outside `com.android.systemui.controls`. * * If `featureEnabled` is false, all the optionals should be empty. The controllers will only be * instantiated if `featureEnabled` is true. * instantiated if `featureEnabled` is true. Can also be queried for the availability of controls. */ @SysUISingleton class ControlsComponent @Inject constructor( @ControlsFeatureEnabled private val featureEnabled: Boolean, private val context: Context, private val lazyControlsController: Lazy<ControlsController>, private val lazyControlsUiController: Lazy<ControlsUiController>, private val lazyControlsListingController: Lazy<ControlsListingController> private val lazyControlsListingController: Lazy<ControlsListingController>, private val lockPatternUtils: LockPatternUtils, private val keyguardStateController: KeyguardStateController, private val userTracker: UserTracker, private val secureSettings: SecureSettings ) { private val contentResolver: ContentResolver get() = context.contentResolver private var canShowWhileLockedSetting = false val showWhileLockedObserver = object : ContentObserver(null) { override fun onChange(selfChange: Boolean) { updateShowWhileLocked() } } init { if (featureEnabled) { secureSettings.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT), false, /* notifyForDescendants */ showWhileLockedObserver ) updateShowWhileLocked() } } fun getControlsController(): Optional<ControlsController> { return if (featureEnabled) Optional.of(lazyControlsController.get()) else Optional.empty() } Loading @@ -52,4 +89,37 @@ class ControlsComponent @Inject constructor( Optional.empty() } } /** * @return true if controls are feature-enabled and have available services to serve controls */ fun isEnabled() = featureEnabled && lazyControlsController.get().available /** * Returns one of 3 states: * * AVAILABLE - Controls can be made visible * * AVAILABLE_AFTER_UNLOCK - Controls can be made visible only after device unlock * * UNAVAILABLE - Controls are not enabled */ fun getVisibility(): Visibility { if (!isEnabled()) return Visibility.UNAVAILABLE if (lockPatternUtils.getStrongAuthForUser(userTracker.userHandle.identifier) == STRONG_AUTH_REQUIRED_AFTER_BOOT) { return Visibility.AVAILABLE_AFTER_UNLOCK } if (!canShowWhileLockedSetting && !keyguardStateController.isUnlocked()) { return Visibility.AVAILABLE_AFTER_UNLOCK } return Visibility.AVAILABLE } private fun updateShowWhileLocked() { canShowWhileLockedSetting = secureSettings.getInt( Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT, 0) != 0 } enum class Visibility { AVAILABLE, AVAILABLE_AFTER_UNLOCK, UNAVAILABLE } }
packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +19 −15 Original line number Diff line number Diff line Loading @@ -25,6 +25,8 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOM import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE; import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING; import android.animation.Animator; Loading Loading @@ -250,6 +252,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private final IWindowManager mIWindowManager; private final Executor mBackgroundExecutor; private List<ControlsServiceInfo> mControlsServiceInfos = new ArrayList<>(); private ControlsComponent mControlsComponent; private Optional<ControlsController> mControlsControllerOptional; private final RingerModeTracker mRingerModeTracker; private int mDialogPressDelay = DIALOG_PRESS_DELAY; // ms Loading Loading @@ -338,6 +341,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mSysuiColorExtractor = colorExtractor; mStatusBarService = statusBarService; mNotificationShadeWindowController = notificationShadeWindowController; mControlsComponent = controlsComponent; mControlsUiControllerOptional = controlsComponent.getControlsUiController(); mIWindowManager = iWindowManager; mBackgroundExecutor = backgroundExecutor; Loading Loading @@ -387,7 +391,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, if (mDialog.mWalletViewController != null) { mDialog.mWalletViewController.onDeviceLockStateChanged(!unlocked); } if (!mDialog.isShowingControls() && shouldShowControls()) { if (!mDialog.isShowingControls() && mControlsComponent.getVisibility() == AVAILABLE) { mDialog.showControls(mControlsUiControllerOptional.get()); } if (unlocked) { Loading @@ -397,14 +402,15 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } }); if (controlsComponent.getControlsListingController().isPresent()) { controlsComponent.getControlsListingController().get() if (mControlsComponent.getControlsListingController().isPresent()) { mControlsComponent.getControlsListingController().get() .addCallback(list -> { mControlsServiceInfos = list; // This callback may occur after the dialog has been shown. If so, add // controls into the already visible space or show the lock msg if needed. if (mDialog != null) { if (!mDialog.isShowingControls() && shouldShowControls()) { if (!mDialog.isShowingControls() && mControlsComponent.getVisibility() == AVAILABLE) { mDialog.showControls(mControlsUiControllerOptional.get()); } else if (shouldShowLockMessage(mDialog)) { mDialog.showLockMessage(); Loading Loading @@ -704,7 +710,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mDepthController.setShowingHomeControls(true); ControlsUiController uiController = null; if (mControlsUiControllerOptional.isPresent() && shouldShowControls()) { if (mControlsComponent.getVisibility() == AVAILABLE) { uiController = mControlsUiControllerOptional.get(); } ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, mOverflowAdapter, Loading Loading @@ -2687,26 +2693,24 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, return isPanelDebugModeEnabled(context); } private boolean shouldShowControls() { boolean showOnLockScreen = mShowLockScreenCardsAndControls && mLockPatternUtils .getStrongAuthForUser(getCurrentUser().id) != STRONG_AUTH_REQUIRED_AFTER_BOOT; return controlsAvailable() && (mKeyguardStateController.isUnlocked() || showOnLockScreen); } private boolean controlsAvailable() { return mDeviceProvisioned && mControlsUiControllerOptional.isPresent() && mControlsUiControllerOptional.get().getAvailable() && mControlsComponent.isEnabled() && !mControlsServiceInfos.isEmpty(); } private boolean shouldShowLockMessage(ActionsDialog dialog) { return mControlsComponent.getVisibility() == AVAILABLE_AFTER_UNLOCK || isWalletAvailableAfterUnlock(dialog); } // Temporary while we move items out of the power menu private boolean isWalletAvailableAfterUnlock(ActionsDialog dialog) { boolean isLockedAfterBoot = mLockPatternUtils.getStrongAuthForUser(getCurrentUser().id) == STRONG_AUTH_REQUIRED_AFTER_BOOT; return !mKeyguardStateController.isUnlocked() && (!mShowLockScreenCardsAndControls || isLockedAfterBoot) && (controlsAvailable() || dialog.isWalletViewAvailable()); && dialog.isWalletViewAvailable(); } private void onPowerMenuLockScreenSettingsChanged() { Loading
packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt +3 −2 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import com.android.internal.logging.MetricsLogger import com.android.systemui.R import com.android.systemui.controls.ControlsServiceInfo import com.android.systemui.controls.dagger.ControlsComponent import com.android.systemui.controls.dagger.ControlsComponent.Visibility.UNAVAILABLE import com.android.systemui.controls.management.ControlsListingController import com.android.systemui.controls.ui.ControlsDialog import com.android.systemui.dagger.qualifiers.Background Loading Loading @@ -91,7 +92,7 @@ class DeviceControlsTile @Inject constructor( override fun isAvailable(): Boolean { return featureFlags.isKeyguardLayoutEnabled && controlsLockscreen && controlsComponent.getControlsUiController().isPresent controlsComponent.getVisibility() != UNAVAILABLE } override fun newTileState(): QSTile.State { Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +31 −33 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.phone; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE; import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_BUTTON; import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_UNLOCK; Loading Loading @@ -183,6 +184,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private ControlsComponent mControlsComponent; private int mLockScreenMode; private BroadcastDispatcher mBroadcastDispatcher; private KeyguardUpdateMonitor mKeyguardUpdateMonitor; public KeyguardBottomAreaView(Context context) { this(context, null); Loading Loading @@ -295,7 +297,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); getContext().registerReceiverAsUser(mDevicePolicyReceiver, UserHandle.ALL, filter, null, null); Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mUpdateMonitorCallback); mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback); mKeyguardStateController.addCallback(this); } Loading @@ -307,7 +310,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mRightExtension.destroy(); mLeftExtension.destroy(); getContext().unregisterReceiver(mDevicePolicyReceiver); Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mUpdateMonitorCallback); mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback); } private void initAccessibility() { Loading Loading @@ -410,12 +413,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } private void updateLeftAffordanceIcon() { if (mDozing) { mAltLeftButton.setVisibility(GONE); } else if (mAltLeftButton.getDrawable() != null) { mAltLeftButton.setVisibility(VISIBLE); } if (!mShowLeftAffordance || mDozing) { mLeftAffordanceView.setVisibility(GONE); return; Loading @@ -430,6 +427,14 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mLeftAffordanceView.setContentDescription(state.contentDescription); } private void updateControlsVisibility() { if (mDozing || mControlsComponent.getVisibility() != AVAILABLE) { mAltLeftButton.setVisibility(GONE); } else { mAltLeftButton.setVisibility(VISIBLE); } } public boolean isLeftVoiceAssist() { return mLeftIsVoiceAssist; } Loading Loading @@ -769,6 +774,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL updateCameraVisibility(); updateLeftAffordanceIcon(); updateControlsVisibility(); if (dozing) { mOverlayContainer.setVisibility(INVISIBLE); Loading Loading @@ -889,36 +895,28 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } private void setupControls() { if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) { boolean inNewLayout = mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL; boolean settingEnabled = Settings.Global.getInt(mContext.getContentResolver(), "controls_lockscreen", 0) == 1; if (!inNewLayout || !settingEnabled || !mControlsComponent.isEnabled()) { mAltLeftButton.setVisibility(View.GONE); mAltLeftButton.setOnClickListener(null); return; } if (Settings.Global.getInt(mContext.getContentResolver(), "controls_lockscreen", 0) == 0) { return; } if (mControlsComponent.getControlsListingController().isPresent()) { mControlsComponent.getControlsListingController().get() .addCallback(list -> { if (!list.isEmpty()) { mAltLeftButton.setImageDrawable(list.get(0).loadIcon()); mAltLeftButton.setVisibility(View.VISIBLE); mAltLeftButton.setOnClickListener((v) -> { ControlsUiController ui = mControlsComponent .getControlsUiController().get(); mControlsDialog = new ControlsDialog(mContext, mBroadcastDispatcher) .show(ui); }); } else { mAltLeftButton.setVisibility(View.GONE); mAltLeftButton.setOnClickListener(null); } updateControlsVisibility(); }); } } /** * Optionally add controls when in the new lockscreen mode Loading
packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt +98 −13 Original line number Diff line number Diff line Loading @@ -17,11 +17,18 @@ package com.android.systemui.controls.dagger import android.testing.AndroidTestingRunner import android.provider.Settings import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT import com.android.systemui.SysuiTestCase import com.android.systemui.controls.controller.ControlsController import com.android.systemui.controls.management.ControlsListingController import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.settings.SecureSettings import dagger.Lazy import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse Loading @@ -29,7 +36,11 @@ import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Answers import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.anyInt import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest Loading @@ -42,20 +53,29 @@ class ControlsComponentTest : SysuiTestCase() { private lateinit var uiController: ControlsUiController @Mock private lateinit var listingController: ControlsListingController @Mock private lateinit var keyguardStateController: KeyguardStateController @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var userTracker: UserTracker @Mock private lateinit var lockPatternUtils: LockPatternUtils @Mock private lateinit var secureSettings: SecureSettings companion object { fun <T> eq(value: T): T = Mockito.eq(value) ?: value } @Before fun setUp() { MockitoAnnotations.initMocks(this) `when`(userTracker.userHandle.identifier).thenReturn(0) } @Test fun testFeatureEnabled() { val component = ControlsComponent( true, Lazy { controller }, Lazy { uiController }, Lazy { listingController } ) val component = setupComponent(true) assertTrue(component.getControlsController().isPresent) assertEquals(controller, component.getControlsController().get()) Loading @@ -67,15 +87,80 @@ class ControlsComponentTest : SysuiTestCase() { @Test fun testFeatureDisabled() { val component = ControlsComponent( false, Lazy { controller }, Lazy { uiController }, Lazy { listingController } ) val component = setupComponent(false) assertFalse(component.getControlsController().isPresent) assertFalse(component.getControlsUiController().isPresent) assertFalse(component.getControlsListingController().isPresent) } @Test fun testFeatureDisabledVisibility() { val component = setupComponent(false) assertEquals(ControlsComponent.Visibility.UNAVAILABLE, component.getVisibility()) } @Test fun testFeatureEnabledAfterBootVisibility() { `when`(controller.available).thenReturn(true) `when`(lockPatternUtils.getStrongAuthForUser(anyInt())) .thenReturn(STRONG_AUTH_REQUIRED_AFTER_BOOT) val component = setupComponent(true) assertEquals(ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK, component.getVisibility()) } @Test fun testFeatureEnabledAndCannotShowOnLockScreenVisibility() { `when`(controller.available).thenReturn(true) `when`(lockPatternUtils.getStrongAuthForUser(anyInt())) .thenReturn(STRONG_AUTH_NOT_REQUIRED) `when`(keyguardStateController.isUnlocked()).thenReturn(false) `when`(secureSettings.getInt(eq(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT), anyInt())) .thenReturn(0) val component = setupComponent(true) assertEquals(ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK, component.getVisibility()) } @Test fun testFeatureEnabledAndCanShowOnLockScreenVisibility() { `when`(controller.available).thenReturn(true) `when`(lockPatternUtils.getStrongAuthForUser(anyInt())) .thenReturn(STRONG_AUTH_NOT_REQUIRED) `when`(keyguardStateController.isUnlocked()).thenReturn(false) `when`(secureSettings.getInt(eq(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT), anyInt())) .thenReturn(1) val component = setupComponent(true) assertEquals(ControlsComponent.Visibility.AVAILABLE, component.getVisibility()) } @Test fun testFeatureEnabledAndCanShowWhileUnlockedVisibility() { `when`(secureSettings.getInt(eq(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT), anyInt())) .thenReturn(0) `when`(controller.available).thenReturn(true) `when`(lockPatternUtils.getStrongAuthForUser(anyInt())) .thenReturn(STRONG_AUTH_NOT_REQUIRED) `when`(keyguardStateController.isUnlocked()).thenReturn(true) val component = setupComponent(true) assertEquals(ControlsComponent.Visibility.AVAILABLE, component.getVisibility()) } private fun setupComponent(enabled: Boolean): ControlsComponent { return ControlsComponent( enabled, mContext, Lazy { controller }, Lazy { uiController }, Lazy { listingController }, lockPatternUtils, keyguardStateController, userTracker, secureSettings ) } }