Loading packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/EditWidgetsActivityControllerTest.kt 0 → 100644 +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() } } } packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt +96 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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) { Loading Loading @@ -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) Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/EditWidgetsActivityControllerTest.kt 0 → 100644 +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() } } }
packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt +96 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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) { Loading Loading @@ -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) Loading