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}.