Topology pane in extended displays list

Show an empty topology pane in the extended displays list. Even show
when no external display is connected.

This will be tweaked in a follow-up CL to include the built-in display
in the pane and the displays list.

This fixes an issue where the displays list is not shown when *no*
external displays are connected, which was not the intent of the flag.
The previous flag CL ag/30358161 only respected the flag when 1 external
display was connected.

Based on mocks at go/al-mm-figma

Bug: b/352650922
Test: reboot with zero displays attached and verify display list and pane are shown
Test: on al-13 device, make the activity fullscreen and verify the margins on left and right of pane are larger
Flag: com.android.settings.flags.display_topology_pane_in_display_list
Change-Id: If39fefe943a26c817fa6f636f21eb8aaa080adde
This commit is contained in:
Matthew DeVore
2024-10-16 19:42:48 +00:00
parent cc267e6a30
commit 6f28e1a3c7
10 changed files with 180 additions and 11 deletions

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android">
<shape android:shape="rectangle">
<corners android:radius="?android:attr/dialogCornerRadius" />
<solid android:color="@color/display_topology_background_color" />
</shape>
</inset>

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:importantForAccessibility="no"
android:layout_height="160dp"
android:layout_width="match_parent"
android:paddingHorizontal="@dimen/display_topology_pane_margin"
android:orientation="horizontal">
<ImageView
android:importantForAccessibility="no"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:src="@drawable/display_topology_background"/>
<FrameLayout
android:id="@+id/display_topology_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/topology_hint"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="top|center_horizontal"
android:paddingTop="10dp"
android:text="@string/external_display_topology_hint"/>
</FrameLayout>

View File

@@ -30,4 +30,7 @@
<!-- Keyboard -->
<dimen name="keyboard_picker_margin">106dp</dimen>
<!-- Connected displays -->
<dimen name="display_topology_pane_margin">80dp</dimen>
</resources>

View File

@@ -79,5 +79,8 @@
<!-- Pointer fill color setting outline color-->
<color name="pointer_fill_outline_color">#FFFFFF</color>
<!-- Connected displays -->
<color name="display_topology_background_color">@color/settingslib_color_charcoal</color>
</resources>

View File

@@ -221,4 +221,7 @@
<!-- Pointer fill color setting outline color-->
<color name="pointer_fill_outline_color">#000000</color>
<!-- Connected displays -->
<color name="display_topology_background_color">@color/settingslib_color_grey100</color>
</resources>

View File

@@ -539,4 +539,7 @@
<dimen name="zen_mode_circular_icon_margin_between">4dp</dimen>
<dimen name="zen_mode_circular_icon_margin_vertical">8dp</dimen>
<dimen name="zen_mode_circular_icon_text_size">18dp</dimen>
<!-- Connected displays -->
<dimen name="display_topology_pane_margin">24dp</dimen>
</resources>

View File

@@ -2027,6 +2027,8 @@
<string name="external_display_not_found_footer_title">Your device must be connected to an external display to mirror your screen</string>
<!-- External Display settings. More resolution options -->
<string name="external_display_more_options_title">More options</string>
<!-- External Display settings. -->
<string name="external_display_topology_hint">Hold and drag to rearrange displays.</string>
<!-- Wifi Display settings. The title of the screen. [CHAR LIMIT=40] -->
<string name="wifi_display_settings_title">Cast</string>

View File

@@ -0,0 +1,40 @@
/*
* 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.settings.connecteddevice.display
import com.android.settings.R
import android.content.Context
import androidx.preference.Preference
const val PREFERENCE_KEY = "display_topology_preference"
/**
* DisplayTopologyPreference allows the user to change the display topology
* when there is one or more extended display attached.
*/
class DisplayTopologyPreference(context : Context) : Preference(context) {
init {
layoutResource = R.layout.display_topology_preference
// Prevent highlight when hovering with mouse.
isSelectable = false
key = PREFERENCE_KEY
}
}

View File

@@ -97,6 +97,8 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
@Nullable
private FooterPreference mFooterPreference;
@Nullable
private Preference mDisplayTopologyPreference;
@Nullable
private PreferenceCategory mDisplaysPreference;
@Nullable
private Injector mInjector;
@@ -279,6 +281,14 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
return mDisplaysPreference;
}
@NonNull Preference getDisplayTopologyPreference(@NonNull Context context) {
if (mDisplayTopologyPreference == null) {
mDisplayTopologyPreference = new DisplayTopologyPreference(context);
mDisplayTopologyPreference.setPersistent(false);
}
return mDisplayTopologyPreference;
}
private void restoreState(@Nullable Bundle savedInstanceState) {
if (savedInstanceState == null) {
return;
@@ -296,20 +306,16 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
updateScreenForDisplayId(getDisplayIdArg(), screen, mInjector.getContext());
}
private boolean okayToBypassDisplayListSelection() {
if (mInjector != null && forceShowDisplayList(mInjector.getFlags())) {
return false;
}
return !mPreviouslyShownListOfDisplays;
}
private void updateScreenForDisplayId(final int displayId,
@NonNull final PreferenceScreen screen, @NonNull Context context) {
final boolean forceShowList = displayId == INVALID_DISPLAY
&& mInjector != null && forceShowDisplayList(mInjector.getFlags());
final var displaysToShow = getDisplaysToShow(displayId);
if (displaysToShow.isEmpty() && displayId == INVALID_DISPLAY) {
if (!forceShowList && displaysToShow.isEmpty() && displayId == INVALID_DISPLAY) {
showTextWhenNoDisplaysToShow(screen, context);
} else if (displaysToShow.size() == 1
&& ((displayId == INVALID_DISPLAY && okayToBypassDisplayListSelection())
} else if (!forceShowList && displaysToShow.size() == 1
&& ((displayId == INVALID_DISPLAY && !mPreviouslyShownListOfDisplays)
|| displaysToShow.get(0).getDisplayId() == displayId)) {
showDisplaySettings(displaysToShow.get(0), screen, context);
} else if (displayId == INVALID_DISPLAY) {
@@ -367,6 +373,11 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
private void showDisplaysList(@NonNull List<Display> displaysToShow,
@NonNull PreferenceScreen screen, @NonNull Context context) {
if (mInjector != null
&& mInjector.getFlags().displayTopologyPaneInDisplayList()) {
screen.addPreference(getDisplayTopologyPreference(context));
}
var pref = getDisplaysListPreference(context);
pref.setKey(DISPLAYS_LIST_PREFERENCE_KEY);
pref.removeAll();

View File

@@ -15,7 +15,6 @@
*/
package com.android.settings.connecteddevice.display;
import static android.view.Display.INVALID_DISPLAY;
import static com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.PREVIOUSLY_SHOWN_LIST_KEY;
@@ -29,6 +28,7 @@ import static com.android.settings.connecteddevice.display.ExternalDisplayPrefer
import static com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.EXTERNAL_DISPLAY_SETTINGS_RESOURCE;
import static com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.EXTERNAL_DISPLAY_USE_PREFERENCE_KEY;
import static com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.EXTERNAL_DISPLAY_USE_TITLE_RESOURCE;
import static com.android.settings.flags.Flags.FLAG_DISPLAY_TOPOLOGY_PANE_IN_DISPLAY_LIST;
import static com.android.settingslib.widget.FooterPreference.KEY_FOOTER;
import static com.google.common.truth.Truth.assertThat;
@@ -85,6 +85,8 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa
@Test
@UiThreadTest
public void testShowDisplayList() {
mFlags.setFlag(FLAG_DISPLAY_TOPOLOGY_PANE_IN_DISPLAY_LIST, false);
var fragment = initFragment();
var outState = new Bundle();
fragment.onSaveInstanceStateCallback(outState);
@@ -101,6 +103,46 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa
assertThat(pref.getPreferenceCount()).isEqualTo(2);
fragment.onSaveInstanceStateCallback(outState);
assertThat(outState.getBoolean(PREVIOUSLY_SHOWN_LIST_KEY)).isTrue();
pref = mPreferenceScreen.findPreference(DisplayTopologyKt.PREFERENCE_KEY);
assertThat(pref).isNull();
}
@Test
@UiThreadTest
public void testShowDisplayListWithPane_OneExternalDisplay() {
mFlags.setFlag(FLAG_DISPLAY_TOPOLOGY_PANE_IN_DISPLAY_LIST, true);
initFragment();
doReturn(new Display[] {mDisplays[1]}).when(mMockedInjector).getAllDisplays();
mHandler.flush();
var pref = mPreferenceScreen.findPreference(DisplayTopologyKt.PREFERENCE_KEY);
assertThat(pref).isNotNull();
PreferenceCategory listPref =
mPreferenceScreen.findPreference(DISPLAYS_LIST_PREFERENCE_KEY);
assertThat(listPref).isNotNull();
assertThat(listPref.getPreferenceCount()).isEqualTo(1);
}
@Test
@UiThreadTest
public void testShowDisplayListWithPane_NoExternalDisplays() {
mFlags.setFlag(FLAG_DISPLAY_TOPOLOGY_PANE_IN_DISPLAY_LIST, true);
initFragment();
doReturn(new Display[0]).when(mMockedInjector).getAllDisplays();
mHandler.flush();
var pref = mPreferenceScreen.findPreference(DisplayTopologyKt.PREFERENCE_KEY);
assertThat(pref).isNotNull();
// TODO: add the built-in display to the list, which will cause this preference to not be
// null.
PreferenceCategory listPref =
mPreferenceScreen.findPreference(DISPLAYS_LIST_PREFERENCE_KEY);
assertThat(listPref).isNull();
}
@Test