Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 68a5d72f authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Controls Visibility" into sc-dev am: 07a74f10

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13562153

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: Id0872ce848c29ce81d3f37c024db09823839532b
parents 022ac5d4 07a74f10
Loading
Loading
Loading
Loading
+73 −3
Original line number Diff line number Diff line
@@ -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
@@ -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()
    }
@@ -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
    }
}
+19 −15
Original line number Diff line number Diff line
@@ -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;
@@ -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
@@ -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;
@@ -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) {
@@ -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();
@@ -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,
@@ -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() {
+3 −2
Original line number Diff line number Diff line
@@ -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
@@ -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 {
+31 −33
Original line number Diff line number Diff line
@@ -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;
@@ -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);
@@ -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);
    }

@@ -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() {
@@ -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;
@@ -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;
    }
@@ -769,6 +774,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL

        updateCameraVisibility();
        updateLeftAffordanceIcon();
        updateControlsVisibility();

        if (dozing) {
            mOverlayContainer.setVisibility(INVISIBLE);
@@ -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
+98 −13
Original line number Diff line number Diff line
@@ -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
@@ -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
@@ -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())
@@ -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