diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index fe90caf2646c8..0a9a1842dd775 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -913,8 +913,9 @@ android:excludeFromRecents="true" android:launchMode="singleInstance" android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden" - android:visibleToInstantApps="true"> - + android:visibleToInstantApps="true" + android:exported="true" + /> - + android:visibleToInstantApps="true" + android:exported="true" + /> 500 + + + + + + diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index b48296fe54bea..0eea5cccae82d 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -3153,4 +3153,11 @@ Set default notes app in Settings + + + Install app diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfiguration.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfiguration.kt index d7d17006168ea..c92180647ab32 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfiguration.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfiguration.kt @@ -17,6 +17,7 @@ package com.android.systemui.controls.controller interface ControlsTileResourceConfiguration { + fun getPackageName(): String? fun getTileTitleId(): Int fun getTileImageId(): Int -} \ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImpl.kt index c96d3d4a26024..02490605fbb81 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImpl.kt @@ -20,12 +20,14 @@ import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton import javax.inject.Inject -/** - * Default Instance for ControlsTileResourceConfiguration. - */ +/** Default Instance for ControlsTileResourceConfiguration. */ @SysUISingleton -class ControlsTileResourceConfigurationImpl @Inject constructor() - : ControlsTileResourceConfiguration { +class ControlsTileResourceConfigurationImpl @Inject constructor() : + ControlsTileResourceConfiguration { + override fun getPackageName(): String? { + return null + } + override fun getTileTitleId(): Int { return R.string.quick_controls_title } @@ -33,4 +35,4 @@ class ControlsTileResourceConfigurationImpl @Inject constructor() override fun getTileImageId(): Int { return R.drawable.controls_icon } -} \ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt index 7509a8ad0c881..94e5633195246 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt @@ -29,9 +29,9 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.policy.KeyguardStateController import dagger.Lazy -import kotlinx.coroutines.flow.StateFlow import java.util.Optional import javax.inject.Inject +import kotlinx.coroutines.flow.StateFlow /** * Pseudo-component to inject into classes outside `com.android.systemui.controls`. @@ -40,26 +40,26 @@ import javax.inject.Inject * 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, - private val lazyControlsUiController: Lazy, - private val lazyControlsListingController: Lazy, - private val lockPatternUtils: LockPatternUtils, - private val keyguardStateController: KeyguardStateController, - private val userTracker: UserTracker, - controlsSettingsRepository: ControlsSettingsRepository, - optionalControlsTileResourceConfiguration: Optional +class ControlsComponent +@Inject +constructor( + @ControlsFeatureEnabled private val featureEnabled: Boolean, + private val context: Context, + private val lazyControlsController: Lazy, + private val lazyControlsUiController: Lazy, + private val lazyControlsListingController: Lazy, + private val lockPatternUtils: LockPatternUtils, + private val keyguardStateController: KeyguardStateController, + private val userTracker: UserTracker, + controlsSettingsRepository: ControlsSettingsRepository, + optionalControlsTileResourceConfiguration: Optional ) { val canShowWhileLockedSetting: StateFlow = - controlsSettingsRepository.canShowControlsInLockscreen + controlsSettingsRepository.canShowControlsInLockscreen private val controlsTileResourceConfiguration: ControlsTileResourceConfiguration = - optionalControlsTileResourceConfiguration.orElse( - ControlsTileResourceConfigurationImpl() - ) + optionalControlsTileResourceConfiguration.orElse(ControlsTileResourceConfigurationImpl()) fun getControlsController(): Optional { return if (featureEnabled) Optional.of(lazyControlsController.get()) else Optional.empty() @@ -77,9 +77,7 @@ class ControlsComponent @Inject constructor( } } - /** - * @return true if controls are feature-enabled and the user has the setting enabled - */ + /** @return true if controls are feature-enabled and the user has the setting enabled */ fun isEnabled() = featureEnabled /** @@ -90,8 +88,10 @@ class ControlsComponent @Inject constructor( */ fun getVisibility(): Visibility { if (!isEnabled()) return Visibility.UNAVAILABLE - if (lockPatternUtils.getStrongAuthForUser(userTracker.userHandle.identifier) - == STRONG_AUTH_REQUIRED_AFTER_BOOT) { + if ( + lockPatternUtils.getStrongAuthForUser(userTracker.userHandle.identifier) == + STRONG_AUTH_REQUIRED_AFTER_BOOT + ) { return Visibility.AVAILABLE_AFTER_UNLOCK } if (!canShowWhileLockedSetting.value && !keyguardStateController.isUnlocked()) { @@ -102,7 +102,13 @@ class ControlsComponent @Inject constructor( } enum class Visibility { - AVAILABLE, AVAILABLE_AFTER_UNLOCK, UNAVAILABLE + AVAILABLE, + AVAILABLE_AFTER_UNLOCK, + UNAVAILABLE + } + + fun getPackageName(): String? { + return controlsTileResourceConfiguration.getPackageName() } fun getTileTitleId(): Int { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt index 27a5974e6299c..064a44a29d31e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt @@ -20,6 +20,7 @@ package com.android.systemui.keyguard import android.content.ContentProvider import android.content.ContentValues import android.content.Context +import android.content.Intent import android.content.UriMatcher import android.content.pm.PackageManager import android.content.pm.ProviderInfo @@ -286,7 +287,7 @@ class CustomizationProvider : Contract.LockScreenQuickAffordances.AffordanceTable.Columns .ENABLEMENT_ACTION_TEXT, Contract.LockScreenQuickAffordances.AffordanceTable.Columns - .ENABLEMENT_COMPONENT_NAME, + .ENABLEMENT_ACTION_INTENT, Contract.LockScreenQuickAffordances.AffordanceTable.Columns.CONFIGURE_INTENT, ) ) @@ -303,8 +304,8 @@ class CustomizationProvider : .ENABLEMENT_INSTRUCTIONS_DELIMITER ), representation.actionText, - representation.actionComponentName, - representation.configureIntent?.toUri(0), + representation.actionIntent?.toUri(Intent.URI_INTENT_SCHEME), + representation.configureIntent?.toUri(Intent.URI_INTENT_SCHEME), ) ) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt index abb63c4d34ce8..0991b5f149db0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt @@ -17,6 +17,7 @@ package com.android.systemui.keyguard.data.quickaffordance +import android.content.ComponentName import android.content.Context import android.content.Intent import androidx.annotation.DrawableRes @@ -34,6 +35,7 @@ import com.android.systemui.controls.ui.ControlsActivity import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.Companion.appStoreIntent import com.android.systemui.util.kotlin.getOrNull import javax.inject.Inject import kotlinx.coroutines.channels.awaitClose @@ -76,18 +78,31 @@ constructor( component.getControlsListingController().getOrNull()?.getCurrentServices() val hasFavorites = component.getControlsController().getOrNull()?.getFavorites()?.isNotEmpty() == true - if (currentServices.isNullOrEmpty() || !hasFavorites) { - return KeyguardQuickAffordanceConfig.PickerScreenState.Disabled( - instructions = - listOf( - context.getString( - R.string.keyguard_affordance_enablement_dialog_home_instruction_1 - ), - context.getString( - R.string.keyguard_affordance_enablement_dialog_home_instruction_2 - ), - ), - ) + val componentPackageName = component.getPackageName() + when { + currentServices.isNullOrEmpty() && !componentPackageName.isNullOrEmpty() -> { + // No home app installed but we know which app we want to install. + return disabledPickerState( + actionText = context.getString(R.string.install_app), + actionIntent = appStoreIntent(context, componentPackageName), + ) + } + currentServices.isNullOrEmpty() && componentPackageName.isNullOrEmpty() -> { + // No home app installed and we don't know which app we want to install. + return disabledPickerState() + } + !hasFavorites -> { + // Home app installed but no favorites selected. + val activityClass = component.getControlsUiController().get().resolveActivity() + return disabledPickerState( + actionText = context.getString(R.string.controls_open_app), + actionIntent = + Intent().apply { + component = ComponentName(context, activityClass) + putExtra(ControlsUiController.EXTRA_ANIMATE, true) + }, + ) + } } return KeyguardQuickAffordanceConfig.PickerScreenState.Default() @@ -172,6 +187,27 @@ constructor( } } + private fun disabledPickerState( + actionText: String? = null, + actionIntent: Intent? = null, + ): KeyguardQuickAffordanceConfig.PickerScreenState.Disabled { + check(actionIntent == null || actionText != null) + + return KeyguardQuickAffordanceConfig.PickerScreenState.Disabled( + instructions = + listOf( + context.getString( + R.string.keyguard_affordance_enablement_dialog_home_instruction_1 + ), + context.getString( + R.string.keyguard_affordance_enablement_dialog_home_instruction_2 + ), + ), + actionText = actionText, + actionIntent = actionIntent, + ) + } + companion object { private const val TAG = "HomeControlsKeyguardQuickAffordanceConfig" } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt index 28dc5bdcc45f1..42fe9bcf8ec4d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt @@ -18,11 +18,13 @@ package com.android.systemui.keyguard.data.quickaffordance import android.app.AlertDialog +import android.content.Context import android.content.Intent +import android.net.Uri +import com.android.systemui.R import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.Icon import com.android.systemui.keyguard.shared.quickaffordance.ActivationState -import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract import kotlinx.coroutines.flow.Flow /** Defines interface that can act as data source for a single quick affordance model. */ @@ -80,9 +82,9 @@ interface KeyguardQuickAffordanceConfig { /** * The picker shows the item for selecting this affordance as disabled. Clicking on it will - * show the given instructions to the user. If [actionText] and [actionComponentName] are - * provided (optional) a button will be shown to open an activity to help the user complete - * the steps described in the instructions. + * show the given instructions to the user. If [actionText] and [actionIntent] are provided + * (optional) a button will be shown to open an activity to help the user complete the steps + * described in the instructions. */ data class Disabled( /** List of human-readable instructions for setting up the quick affordance. */ @@ -93,24 +95,22 @@ interface KeyguardQuickAffordanceConfig { */ val actionText: String? = null, /** - * Optional component name to be able to build an `Intent` that opens an `Activity` for - * the user to be able to set up the quick affordance and make it enabled. - * - * This is either just an action for the `Intent` or a package name and action, - * separated by - * [Contract.LockScreenQuickAffordances.AffordanceTable.COMPONENT_NAME_SEPARATOR] for - * convenience, you can use the [componentName] function. + * Optional [Intent] that opens an `Activity` for the user to be able to set up the + * quick affordance and make it enabled. */ - val actionComponentName: String? = null, + val actionIntent: Intent? = null, ) : PickerScreenState() { init { check(instructions.isNotEmpty()) { "Instructions must not be empty!" } check( - (actionText.isNullOrEmpty() && actionComponentName.isNullOrEmpty()) || - (!actionText.isNullOrEmpty() && !actionComponentName.isNullOrEmpty()) + (actionText.isNullOrEmpty() && actionIntent == null) || + (!actionText.isNullOrEmpty() && actionIntent != null) ) { - "actionText and actionComponentName must either both be null/empty or both be" + - " non-empty!" + """ + actionText and actionIntent must either both be null/empty or both be + non-null and non-empty! + """ + .trimIndent() } } } @@ -163,17 +163,33 @@ interface KeyguardQuickAffordanceConfig { } companion object { - fun componentName( - packageName: String? = null, - action: String?, - ): String? { - return when { - action.isNullOrEmpty() -> null - !packageName.isNullOrEmpty() -> - "$packageName${Contract.LockScreenQuickAffordances.AffordanceTable - .COMPONENT_NAME_SEPARATOR}$action" - else -> action + + /** + * Returns an [Intent] that can be used to start an activity that opens the app store app to + * a page showing the app with the passed-in [packageName]. + * + * If the feature isn't enabled on this device/variant/configuration, a `null` will be + * returned. + */ + fun appStoreIntent(context: Context, packageName: String?): Intent? { + if (packageName.isNullOrEmpty()) { + return null + } + + val appStorePackageName = context.getString(R.string.config_appStorePackageName) + val linkTemplate = context.getString(R.string.config_appStoreAppLinkTemplate) + if (appStorePackageName.isEmpty() || linkTemplate.isEmpty()) { + return null + } + + check(linkTemplate.contains(APP_PACKAGE_NAME_PLACEHOLDER)) + + return Intent(Intent.ACTION_VIEW).apply { + setPackage(appStorePackageName) + data = Uri.parse(linkTemplate.replace(APP_PACKAGE_NAME_PLACEHOLDER, packageName)) } } + + private const val APP_PACKAGE_NAME_PLACEHOLDER = "\$packageName" } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt index 96c94d7d64b65..bd3b83c00f579 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt @@ -18,6 +18,7 @@ package com.android.systemui.keyguard.data.repository import android.content.Context +import android.content.Intent import android.os.UserHandle import android.util.LayoutDirection import com.android.systemui.Dumpable @@ -176,8 +177,18 @@ constructor( pickerState is KeyguardQuickAffordanceConfig.PickerScreenState.Default, instructions = disabledPickerState?.instructions, actionText = disabledPickerState?.actionText, - actionComponentName = disabledPickerState?.actionComponentName, - configureIntent = defaultPickerState?.configureIntent, + actionIntent = + disabledPickerState?.actionIntent?.apply { + addFlags( + Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK + ) + }, + configureIntent = + defaultPickerState?.configureIntent?.apply { + addFlags( + Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK + ) + }, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePickerRepresentation.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePickerRepresentation.kt index e7e9159402904..3c96eafe867a1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePickerRepresentation.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePickerRepresentation.kt @@ -41,11 +41,8 @@ data class KeyguardQuickAffordancePickerRepresentation( */ val actionText: String? = null, - /** - * If not enabled, an optional component name (package and action) for a button that takes the - * user to a destination where they can re-enable it. - */ - val actionComponentName: String? = null, + /** Optional [Intent] to use to start an activity to re-enable this affordance. */ + val actionIntent: Intent? = null, /** Optional [Intent] to use to start an activity to configure this affordance. */ val configureIntent: Intent? = null, diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt index 0ce20cdbe63d0..8da7cec41dd7f 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt @@ -19,6 +19,7 @@ package com.android.systemui.notetask.quickaffordance import android.app.role.OnRoleHoldersChangedListener import android.app.role.RoleManager import android.content.Context +import android.content.Intent import android.hardware.input.InputSettings import android.os.Build import android.os.UserHandle @@ -42,7 +43,6 @@ import com.android.systemui.notetask.NoteTaskController import com.android.systemui.notetask.NoteTaskEnabledKey import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE import com.android.systemui.notetask.NoteTaskInfoResolver -import com.android.systemui.shared.customization.data.content.CustomizationProviderContract.LockScreenQuickAffordances.AffordanceTable.COMPONENT_NAME_SEPARATOR import com.android.systemui.stylus.StylusManager import dagger.Lazy import java.util.concurrent.Executor @@ -130,8 +130,9 @@ constructor( context.getString( R.string.keyguard_affordance_enablement_dialog_notes_app_action ), - "${context.packageName}$COMPONENT_NAME_SEPARATOR" + - "$ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE", + Intent(ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE).apply { + setPackage(context.packageName) + } ) } else -> PickerScreenState.UnavailableOnDevice diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt index 7fb088eb783f8..bd4e8da6b326a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt @@ -20,6 +20,7 @@ import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith @@ -27,16 +28,20 @@ import org.junit.runner.RunWith @RunWith(AndroidTestingRunner::class) @SmallTest class ControlsTileResourceConfigurationImplTest : SysuiTestCase() { + + @Test + fun getPackageName() { + assertThat(ControlsTileResourceConfigurationImpl().getPackageName()).isNull() + } + @Test fun getTileImageId() { val instance = ControlsTileResourceConfigurationImpl() - assertEquals(instance.getTileImageId(), - R.drawable.controls_icon) + assertEquals(instance.getTileImageId(), R.drawable.controls_icon) } @Test fun getTileTitleId() { val instance = ControlsTileResourceConfigurationImpl() - assertEquals(instance.getTileTitleId(), - R.string.quick_controls_title) + assertEquals(instance.getTileTitleId(), R.string.quick_controls_title) } -} \ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt index 9144b13c7f3e6..0b27bc9c7dbdf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt @@ -22,10 +22,10 @@ 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.settings.FakeControlsSettingsRepository import com.android.systemui.controls.controller.ControlsController import com.android.systemui.controls.controller.ControlsTileResourceConfiguration import com.android.systemui.controls.management.ControlsListingController +import com.android.systemui.controls.settings.FakeControlsSettingsRepository import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.policy.KeyguardStateController @@ -40,32 +40,25 @@ import org.junit.runner.RunWith import org.mockito.Answers import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.any import org.mockito.Mockito.anyInt import org.mockito.Mockito.`when` -import org.mockito.Mockito.any import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidTestingRunner::class) class ControlsComponentTest : SysuiTestCase() { - @Mock - private lateinit var controller: ControlsController - @Mock - 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 controller: ControlsController + @Mock 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 optionalControlsTileResourceConfiguration: - Optional - @Mock - private lateinit var controlsTileResourceConfiguration: ControlsTileResourceConfiguration + Optional + @Mock private lateinit var controlsTileResourceConfiguration: ControlsTileResourceConfiguration private lateinit var controlsSettingsRepository: FakeControlsSettingsRepository @@ -123,8 +116,7 @@ class ControlsComponentTest : SysuiTestCase() { @Test fun testFeatureEnabledAndCannotShowOnLockScreenVisibility() { - `when`(lockPatternUtils.getStrongAuthForUser(anyInt())) - .thenReturn(STRONG_AUTH_NOT_REQUIRED) + `when`(lockPatternUtils.getStrongAuthForUser(anyInt())).thenReturn(STRONG_AUTH_NOT_REQUIRED) `when`(keyguardStateController.isUnlocked()).thenReturn(false) controlsSettingsRepository.setCanShowControlsInLockscreen(false) val component = setupComponent(true) @@ -134,8 +126,7 @@ class ControlsComponentTest : SysuiTestCase() { @Test fun testFeatureEnabledAndCanShowOnLockScreenVisibility() { - `when`(lockPatternUtils.getStrongAuthForUser(anyInt())) - .thenReturn(STRONG_AUTH_NOT_REQUIRED) + `when`(lockPatternUtils.getStrongAuthForUser(anyInt())).thenReturn(STRONG_AUTH_NOT_REQUIRED) `when`(keyguardStateController.isUnlocked()).thenReturn(false) controlsSettingsRepository.setCanShowControlsInLockscreen(true) val component = setupComponent(true) @@ -146,8 +137,7 @@ class ControlsComponentTest : SysuiTestCase() { @Test fun testFeatureEnabledAndCanShowWhileUnlockedVisibility() { controlsSettingsRepository.setCanShowControlsInLockscreen(false) - `when`(lockPatternUtils.getStrongAuthForUser(anyInt())) - .thenReturn(STRONG_AUTH_NOT_REQUIRED) + `when`(lockPatternUtils.getStrongAuthForUser(anyInt())).thenReturn(STRONG_AUTH_NOT_REQUIRED) `when`(keyguardStateController.isUnlocked()).thenReturn(true) val component = setupComponent(true) @@ -158,8 +148,7 @@ class ControlsComponentTest : SysuiTestCase() { fun testGetTileImageId() { val tileImageId = 0 - `when`(controlsTileResourceConfiguration.getTileImageId()) - .thenReturn(tileImageId) + `when`(controlsTileResourceConfiguration.getTileImageId()).thenReturn(tileImageId) val component = setupComponent(true) assertEquals(component.getTileImageId(), tileImageId) } @@ -168,12 +157,19 @@ class ControlsComponentTest : SysuiTestCase() { fun testGetTileTitleId() { val tileTitleId = 0 - `when`(controlsTileResourceConfiguration.getTileTitleId()) - .thenReturn(tileTitleId) + `when`(controlsTileResourceConfiguration.getTileTitleId()).thenReturn(tileTitleId) val component = setupComponent(true) assertEquals(component.getTileTitleId(), tileTitleId) } + @Test + fun getPackageName() { + val packageName = "packageName" + `when`(controlsTileResourceConfiguration.getPackageName()).thenReturn(packageName) + val component = setupComponent(true) + assertEquals(component.getPackageName(), packageName) + } + private fun setupComponent(enabled: Boolean): ControlsComponent { return ControlsComponent( enabled, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt index f8cb40885d21e..1815ea9530e27 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.keyguard.data.quickaffordance +import android.app.Activity import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase @@ -24,6 +25,7 @@ import com.android.systemui.controls.ControlsServiceInfo import com.android.systemui.controls.controller.ControlsController import com.android.systemui.controls.dagger.ControlsComponent import com.android.systemui.controls.management.ControlsListingController +import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat @@ -75,6 +77,7 @@ class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTes @Mock private lateinit var component: ControlsComponent @Mock private lateinit var controlsController: ControlsController @Mock private lateinit var controlsListingController: ControlsListingController + @Mock private lateinit var controlsUiController: ControlsUiController @Mock private lateinit var controlsServiceInfo: ControlsServiceInfo @Captor private lateinit var callbackCaptor: @@ -98,6 +101,8 @@ class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTes whenever(component.getControlsController()).thenReturn(Optional.of(controlsController)) whenever(component.getControlsListingController()) .thenReturn(Optional.of(controlsListingController)) + whenever(controlsUiController.resolveActivity()).thenReturn(FakeActivity::class.java) + whenever(component.getControlsUiController()).thenReturn(Optional.of(controlsUiController)) if (hasPanels) { whenever(controlsServiceInfo.panelActivity).thenReturn(mock()) } @@ -178,4 +183,6 @@ class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTes ) job.cancel() } + + private class FakeActivity : Activity() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfigTest.kt new file mode 100644 index 0000000000000..7941a23fe30d4 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfigTest.kt @@ -0,0 +1,90 @@ +/* + * Copyright 2023 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.keyguard.data.quickaffordance + +import android.content.Intent +import android.net.Uri +import androidx.test.filters.SmallTest +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@SmallTest +@RunWith(JUnit4::class) +class KeyguardQuickAffordanceConfigTest : SysuiTestCase() { + + @Test + fun appStoreIntent() { + overrideResource(R.string.config_appStorePackageName, "app.store.package.name") + overrideResource(R.string.config_appStoreAppLinkTemplate, "prefix?id=\$packageName") + val packageName = "com.app.package.name" + + val intent = KeyguardQuickAffordanceConfig.appStoreIntent(context, packageName) + + assertThat(intent).isNotNull() + assertThat(intent?.`package`).isEqualTo("app.store.package.name") + assertThat(intent?.action).isEqualTo(Intent.ACTION_VIEW) + assertThat(intent?.data).isEqualTo(Uri.parse("prefix?id=$packageName")) + } + + @Test + fun appStoreIntent_packageNameNotConfigured_returnNull() { + overrideResource(R.string.config_appStorePackageName, "") + overrideResource(R.string.config_appStoreAppLinkTemplate, "prefix?id=\$packageName") + val packageName = "com.app.package.name" + + val intent = KeyguardQuickAffordanceConfig.appStoreIntent(context, packageName) + + assertThat(intent).isNull() + } + + @Test(expected = IllegalStateException::class) + fun appStoreIntent_packageNameMisconfigured_throwsIllegalStateException() { + overrideResource(R.string.config_appStorePackageName, "app.store.package.name") + overrideResource( + R.string.config_appStoreAppLinkTemplate, + "prefix?id=\$misconfiguredPackageName" + ) + val packageName = "com.app.package.name" + + KeyguardQuickAffordanceConfig.appStoreIntent(context, packageName) + } + + @Test + fun appStoreIntent_linkTemplateNotConfigured_returnNull() { + overrideResource(R.string.config_appStorePackageName, "app.store.package.name") + overrideResource(R.string.config_appStoreAppLinkTemplate, "") + val packageName = "com.app.package.name" + + val intent = KeyguardQuickAffordanceConfig.appStoreIntent(context, packageName) + + assertThat(intent).isNull() + } + + @Test + fun appStoreIntent_appPackageNameNull_returnNull() { + overrideResource(R.string.config_appStorePackageName, "app.store.package.name") + overrideResource(R.string.config_appStoreAppLinkTemplate, "prefix?id=\$packageName") + + val intent = KeyguardQuickAffordanceConfig.appStoreIntent(context, null) + + assertThat(intent).isNull() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt index 4526580047331..cc64cc270b970 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt @@ -40,7 +40,6 @@ import com.android.systemui.notetask.LaunchNotesRoleSettingsTrampolineActivity.C import com.android.systemui.notetask.NoteTaskController import com.android.systemui.notetask.NoteTaskEntryPoint import com.android.systemui.notetask.NoteTaskInfoResolver -import com.android.systemui.shared.customization.data.content.CustomizationProviderContract.LockScreenQuickAffordances.AffordanceTable.COMPONENT_NAME_SEPARATOR import com.android.systemui.stylus.StylusManager import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any @@ -283,7 +282,7 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun getPickerScreenState_nodefaultNoteAppSet_shouldReturnDisable() = runTest { + fun getPickerScreenState_noDefaultNoteAppSet_shouldReturnDisabled() = runTest { val underTest = createUnderTest(isEnabled = true) whenever( roleManager.getRoleHoldersAsUser( @@ -293,16 +292,16 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { ) .thenReturn(emptyList()) - assertThat(underTest.getPickerScreenState()) - .isEqualTo( - KeyguardQuickAffordanceConfig.PickerScreenState.Disabled( - listOf("Select a default notes app to use the notetaking shortcut"), - actionText = "Select app", - actionComponentName = - "${context.packageName}$COMPONENT_NAME_SEPARATOR" + - "$ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE" - ) - ) + val pickerScreenState = underTest.getPickerScreenState() + assertThat(pickerScreenState is KeyguardQuickAffordanceConfig.PickerScreenState.Disabled) + .isTrue() + val disabled = pickerScreenState as KeyguardQuickAffordanceConfig.PickerScreenState.Disabled + assertThat(disabled.instructions) + .isEqualTo(listOf("Select a default notes app to use the notetaking shortcut")) + assertThat(disabled.actionText).isEqualTo("Select app") + assertThat(disabled.actionIntent?.action) + .isEqualTo(ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE) + assertThat(disabled.actionIntent?.`package`).isEqualTo(context.packageName) } // endregion