diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 2bc23f1c1..4169500d9 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -7,6 +7,7 @@ **Bug Fixes** * Downgrade to kotlin 1.9 +* Support launching activities in fullscreen mode with ActivityScenario on XR devices **New Features** diff --git a/core/java/androidx/test/core/app/ActivityScenario.java b/core/java/androidx/test/core/app/ActivityScenario.java index e82c603b1..af86f0686 100644 --- a/core/java/androidx/test/core/app/ActivityScenario.java +++ b/core/java/androidx/test/core/app/ActivityScenario.java @@ -25,6 +25,8 @@ import android.app.Instrumentation.ActivityResult; import android.content.ComponentName; import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.Property; import android.os.Build.VERSION; import android.os.Bundle; import android.os.Looper; @@ -200,7 +202,7 @@ private ActivityScenario(Class activityClass) { */ public static ActivityScenario launch(Class activityClass) { ActivityScenario scenario = new ActivityScenario<>(checkNotNull(activityClass)); - scenario.launchInternal(/*activityOptions=*/ null, /*launchActivityForResult=*/ false); + scenario.checkXrAndLaunchInternal(/* activityOptions= */ null); return scenario; } @@ -213,7 +215,7 @@ public static ActivityScenario launch(Class activityC public static ActivityScenario launch( Class activityClass, @Nullable Bundle activityOptions) { ActivityScenario scenario = new ActivityScenario<>(checkNotNull(activityClass)); - scenario.launchInternal(activityOptions, /*launchActivityForResult=*/ false); + scenario.checkXrAndLaunchInternal(activityOptions); return scenario; } @@ -234,7 +236,7 @@ public static ActivityScenario launch( */ public static ActivityScenario launch(Intent startActivityIntent) { ActivityScenario scenario = new ActivityScenario<>(checkNotNull(startActivityIntent)); - scenario.launchInternal(/*activityOptions=*/ null, /*launchActivityForResult=*/ false); + scenario.checkXrAndLaunchInternal(/* activityOptions= */ null); return scenario; } @@ -249,7 +251,7 @@ public static ActivityScenario launch(Intent startActivi public static ActivityScenario launch( Intent startActivityIntent, @Nullable Bundle activityOptions) { ActivityScenario scenario = new ActivityScenario<>(checkNotNull(startActivityIntent)); - scenario.launchInternal(activityOptions, /*launchActivityForResult=*/ false); + scenario.checkXrAndLaunchInternal(activityOptions); return scenario; } @@ -383,6 +385,39 @@ private void launchInternal(@Nullable Bundle activityOptions, boolean launchActi } } + /** + * An internal helper method for handling launching the activity for the given scenario instance + * on XR devices. + * + * @param activityOptions activity options bundle to be passed when launching this activity + */ + private void checkXrAndLaunchInternal(@Nullable Bundle activityOptions) { + // Activities cannot be launched in full screen mode from the application context on XR devices. + // Check if the current device is an XR device and fall back to launching activity for result + // if the android.window.PROPERTY_XR_ACTIVITY_START_MODE property is set to full screen mode + // so that the bootstrap activity can act as a temporary focused activity which the requested + // activity is launched from. + PackageManager packageManager = getInstrumentation().getTargetContext().getPackageManager(); + if (packageManager.hasSystemFeature("android.software.xr.immersive")) { + String packageName = getInstrumentation().getTargetContext().getPackageName(); + try { + Property startMode = + packageManager.getProperty( + "android.window.PROPERTY_XR_ACTIVITY_START_MODE", packageName); + if (startMode.getString().equals("XR_ACTIVITY_START_MODE_FULL_SPACE_MANAGED") + || startMode.getString().equals("XR_ACTIVITY_START_MODE_FULL_SPACE_UNMANAGED")) { + launchInternal(activityOptions, true); + } else { + launchInternal(activityOptions, false); + } + } catch (PackageManager.NameNotFoundException e) { + launchInternal(activityOptions, false); + } + } else { + launchInternal(activityOptions, false); + } + } + /** * Finishes the managed activity and cleans up device's state. This method blocks execution until * the activity becomes {@link State#DESTROYED}.