diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9930c3685c0..21a60e1385c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2120,6 +2120,8 @@
More options
Hold and drag to rearrange displays.
+
+ Mirror built-in display
Cast
diff --git a/src/com/android/settings/connecteddevice/display/DisplayTopology.kt b/src/com/android/settings/connecteddevice/display/DisplayTopology.kt
index c4f0b29ac00..a5086b108b7 100644
--- a/src/com/android/settings/connecteddevice/display/DisplayTopology.kt
+++ b/src/com/android/settings/connecteddevice/display/DisplayTopology.kt
@@ -157,7 +157,7 @@ class TopologyScale(
}
}
-const val PREFERENCE_KEY = "display_topology_preference"
+const val TOPOLOGY_PREFERENCE_KEY = "display_topology_preference"
/** Padding in pane coordinate pixels on each side of a display block. */
const val BLOCK_PADDING = 2f
@@ -220,7 +220,8 @@ class DisplayTopologyPreference(context : Context)
// Prevent highlight when hovering with mouse.
isSelectable = false
- key = PREFERENCE_KEY
+ key = TOPOLOGY_PREFERENCE_KEY
+ isPersistent = false
injector = Injector(context)
}
diff --git a/src/com/android/settings/connecteddevice/display/ExternalDisplayPreferenceFragment.java b/src/com/android/settings/connecteddevice/display/ExternalDisplayPreferenceFragment.java
index de1363d24cd..1314737839d 100644
--- a/src/com/android/settings/connecteddevice/display/ExternalDisplayPreferenceFragment.java
+++ b/src/com/android/settings/connecteddevice/display/ExternalDisplayPreferenceFragment.java
@@ -102,6 +102,8 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
@Nullable
private Preference mDisplayTopologyPreference;
@Nullable
+ private Preference mMirrorPreference;
+ @Nullable
private PreferenceCategory mDisplaysPreference;
@Nullable
private PreferenceCategory mBuiltinDisplayPreference;
@@ -292,6 +294,7 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
if (mDisplaysPreference == null) {
mDisplaysPreference = new PreferenceCategory(context);
mDisplaysPreference.setPersistent(false);
+ mDisplaysPreference.setOrder(40);
}
return mDisplaysPreference;
}
@@ -301,6 +304,7 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
if (mBuiltinDisplayPreference == null) {
mBuiltinDisplayPreference = new PreferenceCategory(context);
mBuiltinDisplayPreference.setPersistent(false);
+ mBuiltinDisplayPreference.setOrder(30);
}
return mBuiltinDisplayPreference;
}
@@ -308,11 +312,19 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
@NonNull Preference getDisplayTopologyPreference(@NonNull Context context) {
if (mDisplayTopologyPreference == null) {
mDisplayTopologyPreference = new DisplayTopologyPreference(context);
- mDisplayTopologyPreference.setPersistent(false);
+ mDisplayTopologyPreference.setOrder(10);
}
return mDisplayTopologyPreference;
}
+ @NonNull Preference getMirrorPreference(@NonNull Context context) {
+ if (mMirrorPreference == null) {
+ mMirrorPreference = new MirrorPreference(context);
+ mMirrorPreference.setOrder(20);
+ }
+ return mMirrorPreference;
+ }
+
private void restoreState(@Nullable Bundle savedInstanceState) {
if (savedInstanceState == null) {
return;
@@ -399,6 +411,9 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
@NonNull PreferenceScreen screen, @NonNull Context context) {
if (mInjector != null && mInjector.getFlags().displayTopologyPaneInDisplayList()) {
screen.addPreference(getDisplayTopologyPreference(context));
+ if (!displaysToShow.isEmpty()) {
+ screen.addPreference(getMirrorPreference(context));
+ }
// If topology is shown, we also show a preference for the built-in display for
// consistency with the topology.
diff --git a/src/com/android/settings/connecteddevice/display/Mirroring.kt b/src/com/android/settings/connecteddevice/display/Mirroring.kt
new file mode 100644
index 00000000000..ff35294604c
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/display/Mirroring.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2025 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.settings.connecteddevice.display
+
+import android.content.Context
+import android.provider.Settings
+
+import androidx.preference.SwitchPreferenceCompat
+
+import com.android.settings.R
+
+const val MIRROR_PREFERENCE_KEY = "mirror_builtin_display"
+
+/**
+ * A switch preference which is backed by the MIRROR_BUILT_IN_DISPLAY global setting.
+ */
+class MirrorPreference(context: Context): SwitchPreferenceCompat(context) {
+ init {
+ setTitle(R.string.external_display_mirroring_title)
+
+ key = MIRROR_PREFERENCE_KEY
+ isPersistent = false
+ }
+
+ override fun onAttached() {
+ super.onAttached()
+ setChecked(0 != Settings.Global.getInt(
+ context.contentResolver, Settings.Secure.MIRROR_BUILT_IN_DISPLAY, 0))
+ }
+
+ override fun onClick() {
+ super.onClick()
+ Settings.Global.putInt(
+ context.contentResolver, Settings.Secure.MIRROR_BUILT_IN_DISPLAY,
+ if (isChecked()) 1 else 0)
+ }
+}
diff --git a/tests/unit/src/com/android/settings/connecteddevice/display/ExternalDisplayPreferenceFragmentTest.java b/tests/unit/src/com/android/settings/connecteddevice/display/ExternalDisplayPreferenceFragmentTest.java
index 93ba97b27f5..87af6b09708 100644
--- a/tests/unit/src/com/android/settings/connecteddevice/display/ExternalDisplayPreferenceFragmentTest.java
+++ b/tests/unit/src/com/android/settings/connecteddevice/display/ExternalDisplayPreferenceFragmentTest.java
@@ -105,7 +105,7 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa
fragment.onSaveInstanceStateCallback(outState);
assertThat(outState.getBoolean(PREVIOUSLY_SHOWN_LIST_KEY)).isTrue();
- pref = mPreferenceScreen.findPreference(DisplayTopologyKt.PREFERENCE_KEY);
+ pref = mPreferenceScreen.findPreference(DisplayTopologyKt.TOPOLOGY_PREFERENCE_KEY);
assertThat(pref).isNull();
pref = mPreferenceScreen.findPreference(
@@ -122,7 +122,9 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa
doReturn(new Display[] {mDisplays[1]}).when(mMockedInjector).getAllDisplays();
mHandler.flush();
- var pref = mPreferenceScreen.findPreference(DisplayTopologyKt.PREFERENCE_KEY);
+ var pref = mPreferenceScreen.findPreference(DisplayTopologyKt.TOPOLOGY_PREFERENCE_KEY);
+ assertThat(pref).isNotNull();
+ pref = mPreferenceScreen.findPreference(MirroringKt.MIRROR_PREFERENCE_KEY);
assertThat(pref).isNotNull();
PreferenceCategory listPref =
@@ -145,8 +147,10 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa
doReturn(new Display[0]).when(mMockedInjector).getAllDisplays();
mHandler.flush();
- var pref = mPreferenceScreen.findPreference(DisplayTopologyKt.PREFERENCE_KEY);
+ var pref = mPreferenceScreen.findPreference(DisplayTopologyKt.TOPOLOGY_PREFERENCE_KEY);
assertThat(pref).isNotNull();
+ pref = mPreferenceScreen.findPreference(MirroringKt.MIRROR_PREFERENCE_KEY);
+ assertThat(pref).isNull();
PreferenceCategory listPref =
mPreferenceScreen.findPreference(DISPLAYS_LIST_PREFERENCE_KEY);