From 29351944608e5aa0e3b5bd9f4937cce5a133adc3 Mon Sep 17 00:00:00 2001 From: Paige McAuliffe Date: Thu, 23 May 2024 10:37:27 -0700 Subject: [PATCH] Unregister callbacks outside of callback methods PiperOrigin-RevId: 636599205 --- espresso/device/CHANGELOG.md | 1 + .../device/action/ScreenOrientationAction.kt | 71 ++++++++++--------- .../espresso/device/common/ActivityUtil.kt | 21 +++--- 3 files changed, 50 insertions(+), 43 deletions(-) diff --git a/espresso/device/CHANGELOG.md b/espresso/device/CHANGELOG.md index 6debbbf90..9f6097d76 100644 --- a/espresso/device/CHANGELOG.md +++ b/espresso/device/CHANGELOG.md @@ -7,6 +7,7 @@ **Bug Fixes** * Add support for setting screen orientation with multiple resumed activities +* Fix concurrent modification issue when setting screen orientation and fold modes **New Features** diff --git a/espresso/device/java/androidx/test/espresso/device/action/ScreenOrientationAction.kt b/espresso/device/java/androidx/test/espresso/device/action/ScreenOrientationAction.kt index 79a1d03a6..147e4b9c0 100644 --- a/espresso/device/java/androidx/test/espresso/device/action/ScreenOrientationAction.kt +++ b/espresso/device/java/androidx/test/espresso/device/action/ScreenOrientationAction.kt @@ -91,51 +91,58 @@ internal class ScreenOrientationAction(val screenOrientation: ScreenOrientation) if (screenOrientation == ScreenOrientation.LANDSCAPE) Configuration.ORIENTATION_LANDSCAPE else Configuration.ORIENTATION_PORTRAIT - if (configChangesHandled) { - Log.d(TAG, "The current activity handles configuration changes.") - context.registerComponentCallbacks( - object : ComponentCallbacks { - override fun onConfigurationChanged(newConfig: Configuration) { - if (newConfig.orientation == requestedOrientation) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Application's orientation was set to the requested orientation.") - } - context.unregisterComponentCallbacks(this) - latch.countDown() + val componentCallback = + object : ComponentCallbacks { + override fun onConfigurationChanged(newConfig: Configuration) { + if (newConfig.orientation == requestedOrientation) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "Application's orientation was set to the requested orientation.") } + latch.countDown() } + } - override fun onLowMemory() {} + @Deprecated("Deprecated in API 34") override fun onLowMemory() {} + } + + val activityLifecycleCallback = + object : ActivityLifecycleCallback { + override fun onActivityLifecycleChanged(activity: Activity, stage: Stage) { + if ( + activity.localClassName == currentActivityName && + stage == Stage.RESUMED && + activity.resources.configuration.orientation == requestedOrientation + ) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "Test activity was resumed in the requested orientation.") + } + latch.countDown() + } } - ) + } + + if (configChangesHandled) { + Log.d(TAG, "The current activity handles configuration changes.") + context.registerComponentCallbacks(componentCallback) } else { Log.d( TAG, "The current activity does not handle configuration changes and will be recreated when " + - "its orientation changes." + "its orientation changes.", ) - ActivityLifecycleMonitorRegistry.getInstance() - .addLifecycleCallback( - object : ActivityLifecycleCallback { - override fun onActivityLifecycleChanged(activity: Activity, stage: Stage) { - if ( - activity.localClassName == currentActivityName && - stage == Stage.RESUMED && - activity.resources.configuration.orientation == requestedOrientation - ) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Test activity was resumed in the requested orientation.") - } - ActivityLifecycleMonitorRegistry.getInstance().removeLifecycleCallback(this) - latch.countDown() - } - } - } - ) + ActivityLifecycleMonitorRegistry.getInstance().addLifecycleCallback(activityLifecycleCallback) } + deviceController.setScreenOrientation(screenOrientation.getOrientation()) latch.await(5, TimeUnit.SECONDS) + if (configChangesHandled) { + context.unregisterComponentCallbacks(componentCallback) + } else { + ActivityLifecycleMonitorRegistry.getInstance() + .removeLifecycleCallback(activityLifecycleCallback) + } + // Restore accelerometer rotation setting if it was changed if ( getDeviceApiLevel() >= 21 && startingAccelRotationSetting != getAccelerometerRotationSetting() diff --git a/espresso/device/java/androidx/test/espresso/device/common/ActivityUtil.kt b/espresso/device/java/androidx/test/espresso/device/common/ActivityUtil.kt index 64de83885..efdb34a96 100644 --- a/espresso/device/java/androidx/test/espresso/device/common/ActivityUtil.kt +++ b/espresso/device/java/androidx/test/espresso/device/common/ActivityUtil.kt @@ -69,20 +69,19 @@ fun getResumedActivityOrNull(): Activity? { } else if (activities.isEmpty()) { Log.d(TAG, "No activity found in the RESUMED stage. Waiting up to 2 seconds for one.") val latch = CountDownLatch(1) - ActivityLifecycleMonitorRegistry.getInstance() - .addLifecycleCallback( - object : ActivityLifecycleCallback { - override fun onActivityLifecycleChanged(newActivity: Activity, stage: Stage) { - if (stage == Stage.RESUMED) { - Log.d(TAG, "Found ${newActivity.getLocalClassName()} in the RESUMED stage.") - ActivityLifecycleMonitorRegistry.getInstance().removeLifecycleCallback(this) - latch.countDown() - activity = newActivity - } + val callback = + object : ActivityLifecycleCallback { + override fun onActivityLifecycleChanged(newActivity: Activity, stage: Stage) { + if (stage == Stage.RESUMED) { + Log.d(TAG, "Found ${newActivity.localClassName} in the RESUMED stage.") + latch.countDown() + activity = newActivity } } - ) + } + ActivityLifecycleMonitorRegistry.getInstance().addLifecycleCallback(callback) latch.await(2, TimeUnit.SECONDS) + ActivityLifecycleMonitorRegistry.getInstance().removeLifecycleCallback(callback) } else { activity = activities.elementAtOrNull(0) }