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

Commit 3cce6bed authored by Bryce Lee's avatar Bryce Lee
Browse files

Finish EditWidgetsActivity when stopped.

This changelist finishes the EditWidgetActivity when stopped and not
waiting for result from another activity.

Test: atest EditWidgetsActivityControllerTest
Fixes: 354725145
Flag: com.android.systemui.communal_hub
Change-Id: I78a7ceac97ee053050e481e287e3e35b5a7ca042
parent e27a8ea8
Loading
Loading
Loading
Loading
+107 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.communal.widgets

import android.app.Activity
import android.app.Application.ActivityLifecycleCallbacks
import android.os.Bundle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.clearInvocations
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify

@ExperimentalCoroutinesApi
@SmallTest
@RunWith(AndroidJUnit4::class)
class EditWidgetsActivityControllerTest : SysuiTestCase() {
    @Test
    fun activityLifecycle_stoppedWhenNotWaitingForResult() {
        val activity = mock<Activity>()
        val controller = EditWidgetsActivity.ActivityController(activity)

        val callbackCapture = argumentCaptor<ActivityLifecycleCallbacks>()
        verify(activity).registerActivityLifecycleCallbacks(callbackCapture.capture())

        callbackCapture.lastValue.onActivityStopped(activity)

        verify(activity).finish()
    }

    @Test
    fun activityLifecycle_notStoppedWhenNotWaitingForResult() {
        val activity = mock<Activity>()
        val controller = EditWidgetsActivity.ActivityController(activity)

        val callbackCapture = argumentCaptor<ActivityLifecycleCallbacks>()
        verify(activity).registerActivityLifecycleCallbacks(callbackCapture.capture())

        controller.onWaitingForResult(true)
        callbackCapture.lastValue.onActivityStopped(activity)

        verify(activity, never()).finish()
    }

    @Test
    fun activityLifecycle_stoppedAfterResultReturned() {
        val activity = mock<Activity>()
        val controller = EditWidgetsActivity.ActivityController(activity)

        val callbackCapture = argumentCaptor<ActivityLifecycleCallbacks>()
        verify(activity).registerActivityLifecycleCallbacks(callbackCapture.capture())

        controller.onWaitingForResult(true)
        controller.onWaitingForResult(false)
        callbackCapture.lastValue.onActivityStopped(activity)

        verify(activity).finish()
    }

    @Test
    fun activityLifecycle_statePreservedThroughInstanceSave() {
        val activity = mock<Activity>()
        val bundle = Bundle(1)

        run {
            val controller = EditWidgetsActivity.ActivityController(activity)
            val callbackCapture = argumentCaptor<ActivityLifecycleCallbacks>()
            verify(activity).registerActivityLifecycleCallbacks(callbackCapture.capture())

            controller.onWaitingForResult(true)
            callbackCapture.lastValue.onActivitySaveInstanceState(activity, bundle)
        }

        clearInvocations(activity)

        run {
            val controller = EditWidgetsActivity.ActivityController(activity)
            val callbackCapture = argumentCaptor<ActivityLifecycleCallbacks>()
            verify(activity).registerActivityLifecycleCallbacks(callbackCapture.capture())

            callbackCapture.lastValue.onActivityCreated(activity, bundle)
            callbackCapture.lastValue.onActivityStopped(activity)

            verify(activity, never()).finish()
        }
    }
}
+96 −0
Original line number Diff line number Diff line
@@ -16,7 +16,10 @@

package com.android.systemui.communal.widgets

import android.app.Activity
import android.app.Application.ActivityLifecycleCallbacks
import android.content.Intent
import android.content.IntentSender
import android.os.Bundle
import android.os.RemoteException
import android.util.Log
@@ -66,12 +69,78 @@ constructor(
        const val EXTRA_OPEN_WIDGET_PICKER_ON_START = "open_widget_picker_on_start"
    }

    /**
     * [ActivityController] handles closing the activity in the case it is backgrounded without
     * waiting for an activity result
     */
    class ActivityController(activity: Activity) {
        companion object {
            private const val STATE_EXTRA_IS_WAITING_FOR_RESULT = "extra_is_waiting_for_result"
        }

        private var waitingForResult: Boolean = false

        init {
            activity.registerActivityLifecycleCallbacks(
                object : ActivityLifecycleCallbacks {
                    override fun onActivityCreated(
                        activity: Activity,
                        savedInstanceState: Bundle?
                    ) {
                        waitingForResult =
                            savedInstanceState?.getBoolean(STATE_EXTRA_IS_WAITING_FOR_RESULT)
                                ?: false
                    }

                    override fun onActivityStarted(activity: Activity) {
                        // Nothing to implement.
                    }

                    override fun onActivityResumed(activity: Activity) {
                        // Nothing to implement.
                    }

                    override fun onActivityPaused(activity: Activity) {
                        // Nothing to implement.
                    }

                    override fun onActivityStopped(activity: Activity) {
                        // If we're not backgrounded due to waiting for a resul (either widget
                        // selection
                        // or configuration), finish activity.
                        if (!waitingForResult) {
                            activity.finish()
                        }
                    }

                    override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
                        outState.putBoolean(STATE_EXTRA_IS_WAITING_FOR_RESULT, waitingForResult)
                    }

                    override fun onActivityDestroyed(activity: Activity) {
                        // Nothing to implement.
                    }
                }
            )
        }

        /**
         * Invoked when waiting for an activity result changes, either initiating such wait or
         * finishing due to the return of a result.
         */
        fun onWaitingForResult(waitingForResult: Boolean) {
            this.waitingForResult = waitingForResult
        }
    }

    private val logger = Logger(logBuffer, "EditWidgetsActivity")

    private val widgetConfigurator by lazy { widgetConfiguratorFactory.create(this) }

    private var shouldOpenWidgetPickerOnStart = false

    private val activityController: ActivityController = ActivityController(this)

    private val addWidgetActivityLauncher: ActivityResultLauncher<Intent> =
        registerForActivityResult(StartActivityForResult()) { result ->
            when (result.resultCode) {
@@ -186,7 +255,34 @@ constructor(
        }
    }

    override fun startActivityForResult(intent: Intent, requestCode: Int, options: Bundle?) {
        activityController.onWaitingForResult(true)
        super.startActivityForResult(intent, requestCode, options)
    }

    override fun startIntentSenderForResult(
        intent: IntentSender,
        requestCode: Int,
        fillInIntent: Intent?,
        flagsMask: Int,
        flagsValues: Int,
        extraFlags: Int,
        options: Bundle?
    ) {
        activityController.onWaitingForResult(true)
        super.startIntentSenderForResult(
            intent,
            requestCode,
            fillInIntent,
            flagsMask,
            flagsValues,
            extraFlags,
            options
        )
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        activityController.onWaitingForResult(false)
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == WidgetConfigurationController.REQUEST_CODE) {
            widgetConfigurator.setConfigurationResult(resultCode)