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

Commit fac8e2cf authored by Ale Nijamkin's avatar Ale Nijamkin Committed by Automerger Merge Worker
Browse files

Merge "Respects device policy to disble quick affordances" into tm-qpr-dev am: 06acd509

parents cc48157e 06acd509
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -52,6 +52,11 @@ A picker experience may:
* Unselect an already-selected quick affordance from a slot
* Unselect all already-selected quick affordances from a slot

## Device Policy
Returning `DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL` or
`DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL` from
`DevicePolicyManager#getKeyguardDisabledFeatures` will disable the keyguard quick affordance feature on the device.

## Testing
* Add a unit test for your implementation of `KeyguardQuickAffordanceConfig`
* Manually verify that your implementation works in multi-user environments from both the main user and a secondary user
+6 −6
Original line number Diff line number Diff line
@@ -131,7 +131,7 @@ class CustomizationProvider :
            throw UnsupportedOperationException()
        }

        return insertSelection(values)
        return runBlocking { insertSelection(values) }
    }

    override fun query(
@@ -171,7 +171,7 @@ class CustomizationProvider :
            throw UnsupportedOperationException()
        }

        return deleteSelection(uri, selectionArgs)
        return runBlocking { deleteSelection(uri, selectionArgs) }
    }

    override fun call(method: String, arg: String?, extras: Bundle?): Bundle? {
@@ -189,7 +189,7 @@ class CustomizationProvider :
        }
    }

    private fun insertSelection(values: ContentValues?): Uri? {
    private suspend fun insertSelection(values: ContentValues?): Uri? {
        if (values == null) {
            throw IllegalArgumentException("Cannot insert selection, no values passed in!")
        }
@@ -311,7 +311,7 @@ class CustomizationProvider :
            }
    }

    private fun querySlots(): Cursor {
    private suspend fun querySlots(): Cursor {
        return MatrixCursor(
                arrayOf(
                    Contract.LockScreenQuickAffordances.SlotTable.Columns.ID,
@@ -330,7 +330,7 @@ class CustomizationProvider :
            }
    }

    private fun queryFlags(): Cursor {
    private suspend fun queryFlags(): Cursor {
        return MatrixCursor(
                arrayOf(
                    Contract.FlagsTable.Columns.NAME,
@@ -353,7 +353,7 @@ class CustomizationProvider :
            }
    }

    private fun deleteSelection(
    private suspend fun deleteSelection(
        uri: Uri,
        selectionArgs: Array<out String>?,
    ): Int {
+54 −8
Original line number Diff line number Diff line
@@ -18,12 +18,14 @@
package com.android.systemui.keyguard.domain.interactor

import android.app.AlertDialog
import android.app.admin.DevicePolicyManager
import android.content.Intent
import android.util.Log
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
@@ -41,13 +43,17 @@ import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.KeyguardStateController
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.withContext

@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class KeyguardQuickAffordanceInteractor
@Inject
@@ -61,6 +67,8 @@ constructor(
    private val featureFlags: FeatureFlags,
    private val repository: Lazy<KeyguardQuickAffordanceRepository>,
    private val launchAnimator: DialogLaunchAnimator,
    private val devicePolicyManager: DevicePolicyManager,
    @Background private val backgroundDispatcher: CoroutineDispatcher,
) {
    private val isUsingRepository: Boolean
        get() = featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES)
@@ -74,9 +82,13 @@ constructor(
        get() = featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES)

    /** Returns an observable for the quick affordance at the given position. */
    fun quickAffordance(
    suspend fun quickAffordance(
        position: KeyguardQuickAffordancePosition
    ): Flow<KeyguardQuickAffordanceModel> {
        if (isFeatureDisabledByDevicePolicy()) {
            return flowOf(KeyguardQuickAffordanceModel.Hidden)
        }

        return combine(
            quickAffordanceAlwaysVisible(position),
            keyguardInteractor.isDozing,
@@ -148,13 +160,20 @@ constructor(
     *
     * @return `true` if the affordance was selected successfully; `false` otherwise.
     */
    fun select(slotId: String, affordanceId: String): Boolean {
    suspend fun select(slotId: String, affordanceId: String): Boolean {
        check(isUsingRepository)
        if (isFeatureDisabledByDevicePolicy()) {
            return false
        }

        val slots = repository.get().getSlotPickerRepresentations()
        val slot = slots.find { it.id == slotId } ?: return false
        val selections =
            repository.get().getCurrentSelections().getOrDefault(slotId, emptyList()).toMutableList()
            repository
                .get()
                .getCurrentSelections()
                .getOrDefault(slotId, emptyList())
                .toMutableList()
        val alreadySelected = selections.remove(affordanceId)
        if (!alreadySelected) {
            while (selections.size > 0 && selections.size >= slot.maxSelectedAffordances) {
@@ -183,8 +202,11 @@ constructor(
     * @return `true` if the affordance was successfully removed; `false` otherwise (for example, if
     * the affordance was not on the slot to begin with).
     */
    fun unselect(slotId: String, affordanceId: String?): Boolean {
    suspend fun unselect(slotId: String, affordanceId: String?): Boolean {
        check(isUsingRepository)
        if (isFeatureDisabledByDevicePolicy()) {
            return false
        }

        val slots = repository.get().getSlotPickerRepresentations()
        if (slots.find { it.id == slotId } == null) {
@@ -203,7 +225,11 @@ constructor(
        }

        val selections =
            repository.get().getCurrentSelections().getOrDefault(slotId, emptyList()).toMutableList()
            repository
                .get()
                .getCurrentSelections()
                .getOrDefault(slotId, emptyList())
                .toMutableList()
        return if (selections.remove(affordanceId)) {
            repository
                .get()
@@ -219,6 +245,10 @@ constructor(

    /** Returns affordance IDs indexed by slot ID, for all known slots. */
    suspend fun getSelections(): Map<String, List<KeyguardQuickAffordancePickerRepresentation>> {
        if (isFeatureDisabledByDevicePolicy()) {
            return emptyMap()
        }

        val slots = repository.get().getSlotPickerRepresentations()
        val selections = repository.get().getCurrentSelections()
        val affordanceById =
@@ -343,13 +373,17 @@ constructor(
        return repository.get().getAffordancePickerRepresentations()
    }

    fun getSlotPickerRepresentations(): List<KeyguardSlotPickerRepresentation> {
    suspend fun getSlotPickerRepresentations(): List<KeyguardSlotPickerRepresentation> {
        check(isUsingRepository)

        if (isFeatureDisabledByDevicePolicy()) {
            return emptyList()
        }

        return repository.get().getSlotPickerRepresentations()
    }

    fun getPickerFlags(): List<KeyguardPickerFlag> {
    suspend fun getPickerFlags(): List<KeyguardPickerFlag> {
        return listOf(
            KeyguardPickerFlag(
                name = Contract.FlagsTable.FLAG_NAME_REVAMPED_WALLPAPER_UI,
@@ -357,7 +391,9 @@ constructor(
            ),
            KeyguardPickerFlag(
                name = Contract.FlagsTable.FLAG_NAME_CUSTOM_LOCK_SCREEN_QUICK_AFFORDANCES_ENABLED,
                value = featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES),
                value =
                    !isFeatureDisabledByDevicePolicy() &&
                        featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES),
            ),
            KeyguardPickerFlag(
                name = Contract.FlagsTable.FLAG_NAME_CUSTOM_CLOCKS_ENABLED,
@@ -374,6 +410,16 @@ constructor(
        )
    }

    private suspend fun isFeatureDisabledByDevicePolicy(): Boolean {
        val flags =
            withContext(backgroundDispatcher) {
                devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId)
            }
        val flagsToCheck = DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL
        // TODO(b/268218507): "or" with DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL
        return flagsToCheck and flags != 0
    }

    companion object {
        private const val TAG = "KeyguardQuickAffordanceInteractor"
        private const val DELIMITER = "::"
+4 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@

package com.android.systemui.keyguard

import android.app.admin.DevicePolicyManager
import android.content.ContentValues
import android.content.pm.PackageManager
import android.content.pm.ProviderInfo
@@ -90,6 +91,7 @@ class CustomizationProviderTest : SysuiTestCase() {
    @Mock private lateinit var previewSurfacePackage: SurfaceControlViewHost.SurfacePackage
    @Mock private lateinit var launchAnimator: DialogLaunchAnimator
    @Mock private lateinit var commandQueue: CommandQueue
    @Mock private lateinit var devicePolicyManager: DevicePolicyManager

    private lateinit var underTest: CustomizationProvider
    private lateinit var testScope: TestScope
@@ -183,6 +185,8 @@ class CustomizationProviderTest : SysuiTestCase() {
                featureFlags = featureFlags,
                repository = { quickAffordanceRepository },
                launchAnimator = launchAnimator,
                devicePolicyManager = devicePolicyManager,
                backgroundDispatcher = testDispatcher,
            )
        underTest.previewManager =
            KeyguardRemotePreviewManager(
+55 −43
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@

package com.android.systemui.keyguard.domain.interactor

import android.app.admin.DevicePolicyManager
import android.content.Intent
import android.os.UserHandle
import androidx.test.filters.SmallTest
@@ -54,7 +55,10 @@ import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.FakeSettings
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.test.runBlockingTest
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -70,6 +74,7 @@ import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.MockitoAnnotations

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(Parameterized::class)
class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
@@ -219,8 +224,10 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
    @Mock private lateinit var expandable: Expandable
    @Mock private lateinit var launchAnimator: DialogLaunchAnimator
    @Mock private lateinit var commandQueue: CommandQueue
    @Mock private lateinit var devicePolicyManager: DevicePolicyManager

    private lateinit var underTest: KeyguardQuickAffordanceInteractor
    private lateinit var testScope: TestScope

    @JvmField @Parameter(0) var needStrongAuthAfterBoot: Boolean = false
    @JvmField @Parameter(1) var canShowWhileLocked: Boolean = false
@@ -292,6 +299,8 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
                set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
                set(Flags.FACE_AUTH_REFACTOR, true)
            }
        val testDispatcher = StandardTestDispatcher()
        testScope = TestScope(testDispatcher)
        underTest =
            KeyguardQuickAffordanceInteractor(
                keyguardInteractor =
@@ -322,11 +331,14 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
                featureFlags = featureFlags,
                repository = { quickAffordanceRepository },
                launchAnimator = launchAnimator,
                devicePolicyManager = devicePolicyManager,
                backgroundDispatcher = testDispatcher,
            )
    }

    @Test
    fun onQuickAffordanceTriggered() = runBlockingTest {
    fun onQuickAffordanceTriggered() =
        testScope.runTest {
            setUpMocks(
                needStrongAuthAfterBoot = needStrongAuthAfterBoot,
                keyguardIsUnlocked = keyguardIsUnlocked,
Loading