Loading src/com/android/customization/module/ThemePickerInjector.kt +1 −3 Original line number Diff line number Diff line Loading @@ -257,9 +257,7 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject getKeyguardQuickAffordancePickerInteractor(context), getWallpaperInteractor(context), getCurrentWallpaperInfoFactory(context), ) { intent -> context.startActivity(intent) } ) .also { keyguardQuickAffordancePickerViewModelFactory = it } } Loading src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordancePickerBinder.kt +9 −0 Original line number Diff line number Diff line Loading @@ -126,6 +126,15 @@ object KeyguardQuickAffordancePickerBinder { } } } launch { viewModel.activityStartRequests.collect { intent -> if (intent != null) { view.context.startActivity(intent) viewModel.onActivityStarted() } } } } } } Loading src/com/android/customization/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordancePickerViewModel.kt +25 −5 Original line number Diff line number Diff line Loading @@ -64,7 +64,6 @@ private constructor( private val quickAffordanceInteractor: KeyguardQuickAffordancePickerInteractor, private val wallpaperInteractor: WallpaperInteractor, private val wallpaperInfoFactory: CurrentWallpaperInfoFactory, private val activityStarter: (Intent) -> Unit, ) : ViewModel() { @SuppressLint("StaticFieldLeak") private val applicationContext = context.applicationContext Loading Loading @@ -272,7 +271,7 @@ private constructor( }, onLongClicked = if (affordance.configureIntent != null) { { activityStarter(affordance.configureIntent) } { requestActivityStart(affordance.configureIntent) } } else { null }, Loading Loading @@ -318,11 +317,34 @@ private constructor( */ val dialog: Flow<DialogViewModel?> = _dialog.asStateFlow() private val _activityStartRequests = MutableStateFlow<Intent?>(null) /** * Requests to start an activity with the given [Intent]. * * Important: once the activity is started, the [Intent] should be consumed by calling * [onActivityStarted]. */ val activityStartRequests: StateFlow<Intent?> = _activityStartRequests.asStateFlow() /** Notifies that the dialog has been dismissed in the UI. */ fun onDialogDismissed() { _dialog.value = null } /** * Notifies that an activity request from [activityStartRequests] has been fulfilled (e.g. the * activity was started and the view-model can forget needing to start this activity). */ fun onActivityStarted() { _activityStartRequests.value = null } private fun requestActivityStart( intent: Intent, ) { _activityStartRequests.value = intent } private fun showEnablementDialog( icon: Drawable, name: String, Loading Loading @@ -361,7 +383,7 @@ private constructor( style = ButtonStyle.Primary, onClicked = { actionComponentName.toIntent()?.let { intent -> activityStarter(intent) requestActivityStart(intent) } } ), Loading Loading @@ -462,7 +484,6 @@ private constructor( private val quickAffordanceInteractor: KeyguardQuickAffordancePickerInteractor, private val wallpaperInteractor: WallpaperInteractor, private val wallpaperInfoFactory: CurrentWallpaperInfoFactory, private val activityStarter: (Intent) -> Unit, ) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { @Suppress("UNCHECKED_CAST") Loading @@ -471,7 +492,6 @@ private constructor( quickAffordanceInteractor = quickAffordanceInteractor, wallpaperInteractor = wallpaperInteractor, wallpaperInfoFactory = wallpaperInfoFactory, activityStarter = activityStarter, ) as T } Loading tests/src/com/android/customization/model/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordancePickerViewModelTest.kt +15 −7 Original line number Diff line number Diff line Loading @@ -72,8 +72,6 @@ class KeyguardQuickAffordancePickerViewModelTest { private lateinit var quickAffordanceInteractor: KeyguardQuickAffordancePickerInteractor private lateinit var wallpaperInteractor: WallpaperInteractor private var latestStartedActivityIntent: Intent? = null @Before fun setUp() { InjectorProvider.setInjector(TestInjector()) Loading Loading @@ -115,7 +113,6 @@ class KeyguardQuickAffordancePickerViewModelTest { quickAffordanceInteractor = quickAffordanceInteractor, wallpaperInteractor = wallpaperInteractor, wallpaperInfoFactory = TestCurrentWallpaperInfoFactory(context), activityStarter = { intent -> latestStartedActivityIntent = intent }, ) .create(KeyguardQuickAffordancePickerViewModel::class.java) } Loading Loading @@ -249,6 +246,7 @@ class KeyguardQuickAffordancePickerViewModelTest { val slots = collectLastValue(underTest.slots) val quickAffordances = collectLastValue(underTest.quickAffordances) val dialog = collectLastValue(underTest.dialog) val activityStartRequest = collectLastValue(underTest.activityStartRequests) val enablementInstructions = listOf("instruction1", "instruction2") val enablementActionText = "enablementActionText" Loading Loading @@ -283,10 +281,15 @@ class KeyguardQuickAffordancePickerViewModelTest { .isEqualTo(Text.Loaded(enablementActionText)) // When the button is clicked, we expect an intent of the given enablement action // component name is launched. // component name to be emitted. dialog()?.buttons?.first()?.onClicked?.invoke() assertThat(latestStartedActivityIntent?.`package`).isEqualTo(packageName) assertThat(latestStartedActivityIntent?.action).isEqualTo(action) assertThat(activityStartRequest()?.`package`).isEqualTo(packageName) assertThat(activityStartRequest()?.action).isEqualTo(action) // Once we report that the activity was started, the activity start request should be // nullified. underTest.onActivityStarted() assertThat(activityStartRequest()).isNull() // Once we report that the dialog has been dismissed by the user, we expect there to be // no dialog to be shown: Loading @@ -298,6 +301,7 @@ class KeyguardQuickAffordancePickerViewModelTest { fun `Start settings activity when long-pressing an affordance`() = testScope.runTest { val quickAffordances = collectLastValue(underTest.quickAffordances) val activityStartRequest = collectLastValue(underTest.activityStartRequests) // Lets add a configurable affordance to the picker: val configureIntent = Intent("some.action") Loading @@ -315,7 +319,11 @@ class KeyguardQuickAffordancePickerViewModelTest { // Lets try to long-click the affordance: quickAffordances()?.get(affordanceIndex + 1)?.onLongClicked?.invoke() assertThat(latestStartedActivityIntent).isEqualTo(configureIntent) assertThat(activityStartRequest()).isEqualTo(configureIntent) // Once we report that the activity was started, the activity start request should be // nullified. underTest.onActivityStarted() assertThat(activityStartRequest()).isNull() } @Test Loading Loading
src/com/android/customization/module/ThemePickerInjector.kt +1 −3 Original line number Diff line number Diff line Loading @@ -257,9 +257,7 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject getKeyguardQuickAffordancePickerInteractor(context), getWallpaperInteractor(context), getCurrentWallpaperInfoFactory(context), ) { intent -> context.startActivity(intent) } ) .also { keyguardQuickAffordancePickerViewModelFactory = it } } Loading
src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordancePickerBinder.kt +9 −0 Original line number Diff line number Diff line Loading @@ -126,6 +126,15 @@ object KeyguardQuickAffordancePickerBinder { } } } launch { viewModel.activityStartRequests.collect { intent -> if (intent != null) { view.context.startActivity(intent) viewModel.onActivityStarted() } } } } } } Loading
src/com/android/customization/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordancePickerViewModel.kt +25 −5 Original line number Diff line number Diff line Loading @@ -64,7 +64,6 @@ private constructor( private val quickAffordanceInteractor: KeyguardQuickAffordancePickerInteractor, private val wallpaperInteractor: WallpaperInteractor, private val wallpaperInfoFactory: CurrentWallpaperInfoFactory, private val activityStarter: (Intent) -> Unit, ) : ViewModel() { @SuppressLint("StaticFieldLeak") private val applicationContext = context.applicationContext Loading Loading @@ -272,7 +271,7 @@ private constructor( }, onLongClicked = if (affordance.configureIntent != null) { { activityStarter(affordance.configureIntent) } { requestActivityStart(affordance.configureIntent) } } else { null }, Loading Loading @@ -318,11 +317,34 @@ private constructor( */ val dialog: Flow<DialogViewModel?> = _dialog.asStateFlow() private val _activityStartRequests = MutableStateFlow<Intent?>(null) /** * Requests to start an activity with the given [Intent]. * * Important: once the activity is started, the [Intent] should be consumed by calling * [onActivityStarted]. */ val activityStartRequests: StateFlow<Intent?> = _activityStartRequests.asStateFlow() /** Notifies that the dialog has been dismissed in the UI. */ fun onDialogDismissed() { _dialog.value = null } /** * Notifies that an activity request from [activityStartRequests] has been fulfilled (e.g. the * activity was started and the view-model can forget needing to start this activity). */ fun onActivityStarted() { _activityStartRequests.value = null } private fun requestActivityStart( intent: Intent, ) { _activityStartRequests.value = intent } private fun showEnablementDialog( icon: Drawable, name: String, Loading Loading @@ -361,7 +383,7 @@ private constructor( style = ButtonStyle.Primary, onClicked = { actionComponentName.toIntent()?.let { intent -> activityStarter(intent) requestActivityStart(intent) } } ), Loading Loading @@ -462,7 +484,6 @@ private constructor( private val quickAffordanceInteractor: KeyguardQuickAffordancePickerInteractor, private val wallpaperInteractor: WallpaperInteractor, private val wallpaperInfoFactory: CurrentWallpaperInfoFactory, private val activityStarter: (Intent) -> Unit, ) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { @Suppress("UNCHECKED_CAST") Loading @@ -471,7 +492,6 @@ private constructor( quickAffordanceInteractor = quickAffordanceInteractor, wallpaperInteractor = wallpaperInteractor, wallpaperInfoFactory = wallpaperInfoFactory, activityStarter = activityStarter, ) as T } Loading
tests/src/com/android/customization/model/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordancePickerViewModelTest.kt +15 −7 Original line number Diff line number Diff line Loading @@ -72,8 +72,6 @@ class KeyguardQuickAffordancePickerViewModelTest { private lateinit var quickAffordanceInteractor: KeyguardQuickAffordancePickerInteractor private lateinit var wallpaperInteractor: WallpaperInteractor private var latestStartedActivityIntent: Intent? = null @Before fun setUp() { InjectorProvider.setInjector(TestInjector()) Loading Loading @@ -115,7 +113,6 @@ class KeyguardQuickAffordancePickerViewModelTest { quickAffordanceInteractor = quickAffordanceInteractor, wallpaperInteractor = wallpaperInteractor, wallpaperInfoFactory = TestCurrentWallpaperInfoFactory(context), activityStarter = { intent -> latestStartedActivityIntent = intent }, ) .create(KeyguardQuickAffordancePickerViewModel::class.java) } Loading Loading @@ -249,6 +246,7 @@ class KeyguardQuickAffordancePickerViewModelTest { val slots = collectLastValue(underTest.slots) val quickAffordances = collectLastValue(underTest.quickAffordances) val dialog = collectLastValue(underTest.dialog) val activityStartRequest = collectLastValue(underTest.activityStartRequests) val enablementInstructions = listOf("instruction1", "instruction2") val enablementActionText = "enablementActionText" Loading Loading @@ -283,10 +281,15 @@ class KeyguardQuickAffordancePickerViewModelTest { .isEqualTo(Text.Loaded(enablementActionText)) // When the button is clicked, we expect an intent of the given enablement action // component name is launched. // component name to be emitted. dialog()?.buttons?.first()?.onClicked?.invoke() assertThat(latestStartedActivityIntent?.`package`).isEqualTo(packageName) assertThat(latestStartedActivityIntent?.action).isEqualTo(action) assertThat(activityStartRequest()?.`package`).isEqualTo(packageName) assertThat(activityStartRequest()?.action).isEqualTo(action) // Once we report that the activity was started, the activity start request should be // nullified. underTest.onActivityStarted() assertThat(activityStartRequest()).isNull() // Once we report that the dialog has been dismissed by the user, we expect there to be // no dialog to be shown: Loading @@ -298,6 +301,7 @@ class KeyguardQuickAffordancePickerViewModelTest { fun `Start settings activity when long-pressing an affordance`() = testScope.runTest { val quickAffordances = collectLastValue(underTest.quickAffordances) val activityStartRequest = collectLastValue(underTest.activityStartRequests) // Lets add a configurable affordance to the picker: val configureIntent = Intent("some.action") Loading @@ -315,7 +319,11 @@ class KeyguardQuickAffordancePickerViewModelTest { // Lets try to long-click the affordance: quickAffordances()?.get(affordanceIndex + 1)?.onLongClicked?.invoke() assertThat(latestStartedActivityIntent).isEqualTo(configureIntent) assertThat(activityStartRequest()).isEqualTo(configureIntent) // Once we report that the activity was started, the activity start request should be // nullified. underTest.onActivityStarted() assertThat(activityStartRequest()).isNull() } @Test Loading