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

Commit b7c4aec8 authored by Ale Nijamkin's avatar Ale Nijamkin Committed by Android (Google) Code Review
Browse files

Merge "Fixes activity leak in Wallpaper Picker." into udc-dev

parents e0a0f51e ab0cdaf2
Loading
Loading
Loading
Loading
+1 −3
Original line number Diff line number Diff line
@@ -257,9 +257,7 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject
                    getKeyguardQuickAffordancePickerInteractor(context),
                    getWallpaperInteractor(context),
                    getCurrentWallpaperInfoFactory(context),
                ) { intent ->
                    context.startActivity(intent)
                }
                )
                .also { keyguardQuickAffordancePickerViewModelFactory = it }
    }

+9 −0
Original line number Diff line number Diff line
@@ -126,6 +126,15 @@ object KeyguardQuickAffordancePickerBinder {
                            }
                    }
                }

                launch {
                    viewModel.activityStartRequests.collect { intent ->
                        if (intent != null) {
                            view.context.startActivity(intent)
                            viewModel.onActivityStarted()
                        }
                    }
                }
            }
        }
    }
+25 −5
Original line number Diff line number Diff line
@@ -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
@@ -272,7 +271,7 @@ private constructor(
                            },
                        onLongClicked =
                            if (affordance.configureIntent != null) {
                                { activityStarter(affordance.configureIntent) }
                                { requestActivityStart(affordance.configureIntent) }
                            } else {
                                null
                            },
@@ -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,
@@ -361,7 +383,7 @@ private constructor(
                            style = ButtonStyle.Primary,
                            onClicked = {
                                actionComponentName.toIntent()?.let { intent ->
                                    activityStarter(intent)
                                    requestActivityStart(intent)
                                }
                            }
                        ),
@@ -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")
@@ -471,7 +492,6 @@ private constructor(
                quickAffordanceInteractor = quickAffordanceInteractor,
                wallpaperInteractor = wallpaperInteractor,
                wallpaperInfoFactory = wallpaperInfoFactory,
                activityStarter = activityStarter,
            )
                as T
        }
+15 −7
Original line number Diff line number Diff line
@@ -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())
@@ -115,7 +113,6 @@ class KeyguardQuickAffordancePickerViewModelTest {
                    quickAffordanceInteractor = quickAffordanceInteractor,
                    wallpaperInteractor = wallpaperInteractor,
                    wallpaperInfoFactory = TestCurrentWallpaperInfoFactory(context),
                    activityStarter = { intent -> latestStartedActivityIntent = intent },
                )
                .create(KeyguardQuickAffordancePickerViewModel::class.java)
    }
@@ -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"
@@ -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:
@@ -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")
@@ -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