summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/EditWidgetsActivityControllerTest.kt107
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt96
2 files changed, 203 insertions, 0 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/EditWidgetsActivityControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/EditWidgetsActivityControllerTest.kt
new file mode 100644
index 000000000000..50fdb31b0414
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/EditWidgetsActivityControllerTest.kt
@@ -0,0 +1,107 @@
+/*
+ * 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()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index 948f4cda7c59..2bcbc9aa74ac 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -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) {
@@ -193,7 +262,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)