Merge "Added External Display settings page" into main
This commit is contained in:
committed by
Android (Google) Code Review
commit
b3926bdf35
@@ -17,6 +17,8 @@ package com.android.settings.connecteddevice;
|
||||
|
||||
import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE;
|
||||
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
|
||||
import static com.android.settings.flags.Flags.FLAG_RESOLUTION_AND_ENABLE_CONNECTED_DISPLAY_SETTING;
|
||||
import static com.android.settings.flags.Flags.FLAG_ROTATION_CONNECTED_DISPLAY_SETTING;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
@@ -30,6 +32,7 @@ import static org.mockito.Mockito.when;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.platform.test.annotations.EnableFlags;
|
||||
import android.platform.test.flag.junit.SetFlagsRule;
|
||||
@@ -40,13 +43,16 @@ import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceGroup;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.settings.bluetooth.ConnectedBluetoothDeviceUpdater;
|
||||
import com.android.settings.bluetooth.Utils;
|
||||
import com.android.settings.connecteddevice.display.ExternalDisplayUpdater;
|
||||
import com.android.settings.connecteddevice.dock.DockUpdater;
|
||||
import com.android.settings.connecteddevice.stylus.StylusDeviceUpdater;
|
||||
import com.android.settings.connecteddevice.usb.ConnectedUsbDeviceUpdater;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.flags.FakeFeatureFlagsImpl;
|
||||
import com.android.settings.flags.Flags;
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
|
||||
@@ -65,7 +71,6 @@ import org.mockito.Answers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.Shadows;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadows.ShadowApplicationPackageManager;
|
||||
@@ -84,6 +89,8 @@ public class ConnectedDeviceGroupControllerTest {
|
||||
@Mock
|
||||
private DashboardFragment mDashboardFragment;
|
||||
@Mock
|
||||
private ExternalDisplayUpdater mExternalDisplayUpdater;
|
||||
@Mock
|
||||
private ConnectedBluetoothDeviceUpdater mConnectedBluetoothDeviceUpdater;
|
||||
@Mock
|
||||
private ConnectedUsbDeviceUpdater mConnectedUsbDeviceUpdater;
|
||||
@@ -105,6 +112,9 @@ public class ConnectedDeviceGroupControllerTest {
|
||||
private CachedBluetoothDevice mCachedDevice;
|
||||
@Mock
|
||||
private BluetoothDevice mDevice;
|
||||
@Mock
|
||||
private Resources mResources;
|
||||
private final FakeFeatureFlagsImpl mFakeFeatureFlags = new FakeFeatureFlagsImpl();
|
||||
|
||||
private ShadowApplicationPackageManager mPackageManager;
|
||||
private PreferenceGroup mPreferenceGroup;
|
||||
@@ -118,8 +128,10 @@ public class ConnectedDeviceGroupControllerTest {
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mFakeFeatureFlags.setFlag(FLAG_ROTATION_CONNECTED_DISPLAY_SETTING, true);
|
||||
mFakeFeatureFlags.setFlag(FLAG_RESOLUTION_AND_ENABLE_CONNECTED_DISPLAY_SETTING, true);
|
||||
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
mPreference = new Preference(mContext);
|
||||
mPreference.setKey(PREFERENCE_KEY_1);
|
||||
mPackageManager = (ShadowApplicationPackageManager) Shadows.shadowOf(
|
||||
@@ -129,15 +141,19 @@ public class ConnectedDeviceGroupControllerTest {
|
||||
doReturn(mContext).when(mDashboardFragment).getContext();
|
||||
mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, true);
|
||||
when(mContext.getSystemService(InputManager.class)).thenReturn(mInputManager);
|
||||
when(mContext.getResources()).thenReturn(mResources);
|
||||
when(mInputManager.getInputDeviceIds()).thenReturn(new int[]{});
|
||||
|
||||
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
|
||||
mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
|
||||
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
|
||||
|
||||
mConnectedDeviceGroupController = new ConnectedDeviceGroupController(mContext);
|
||||
mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater,
|
||||
mConnectedUsbDeviceUpdater, mConnectedDockUpdater, mStylusDeviceUpdater);
|
||||
mConnectedDeviceGroupController = spy(new ConnectedDeviceGroupController(mContext));
|
||||
when(mConnectedDeviceGroupController.getFeatureFlags()).thenReturn(mFakeFeatureFlags);
|
||||
|
||||
mConnectedDeviceGroupController.init(mExternalDisplayUpdater,
|
||||
mConnectedBluetoothDeviceUpdater, mConnectedUsbDeviceUpdater, mConnectedDockUpdater,
|
||||
mStylusDeviceUpdater);
|
||||
mConnectedDeviceGroupController.mPreferenceGroup = mPreferenceGroup;
|
||||
|
||||
when(mCachedDevice.getName()).thenReturn(DEVICE_NAME);
|
||||
@@ -147,6 +163,7 @@ public class ConnectedDeviceGroupControllerTest {
|
||||
|
||||
FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_SHOW_STYLUS_PREFERENCES,
|
||||
true);
|
||||
when(mPreferenceScreen.getContext()).thenReturn(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -193,6 +210,7 @@ public class ConnectedDeviceGroupControllerTest {
|
||||
// register the callback in onStart()
|
||||
mConnectedDeviceGroupController.onStart();
|
||||
|
||||
verify(mExternalDisplayUpdater).registerCallback();
|
||||
verify(mConnectedBluetoothDeviceUpdater).registerCallback();
|
||||
verify(mConnectedUsbDeviceUpdater).registerCallback();
|
||||
verify(mConnectedDockUpdater).registerCallback();
|
||||
@@ -204,6 +222,7 @@ public class ConnectedDeviceGroupControllerTest {
|
||||
public void onStop_shouldUnregisterUpdaters() {
|
||||
// unregister the callback in onStop()
|
||||
mConnectedDeviceGroupController.onStop();
|
||||
verify(mExternalDisplayUpdater).unregisterCallback();
|
||||
verify(mConnectedBluetoothDeviceUpdater).unregisterCallback();
|
||||
verify(mConnectedUsbDeviceUpdater).unregisterCallback();
|
||||
verify(mConnectedDockUpdater).unregisterCallback();
|
||||
@@ -212,22 +231,36 @@ public class ConnectedDeviceGroupControllerTest {
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_noBluetoothUsbDockFeature_returnUnSupported() {
|
||||
mFakeFeatureFlags.setFlag(FLAG_ROTATION_CONNECTED_DISPLAY_SETTING, false);
|
||||
mFakeFeatureFlags.setFlag(FLAG_RESOLUTION_AND_ENABLE_CONNECTED_DISPLAY_SETTING, false);
|
||||
mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, false);
|
||||
mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_ACCESSORY, false);
|
||||
mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_HOST, false);
|
||||
mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater,
|
||||
mConnectedDeviceGroupController.init(null, mConnectedBluetoothDeviceUpdater,
|
||||
mConnectedUsbDeviceUpdater, null, null);
|
||||
|
||||
assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo(
|
||||
UNSUPPORTED_ON_DEVICE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_connectedDisplay_returnSupported() {
|
||||
mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, false);
|
||||
mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_ACCESSORY, false);
|
||||
mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_HOST, false);
|
||||
mConnectedDeviceGroupController.init(null, mConnectedBluetoothDeviceUpdater,
|
||||
mConnectedUsbDeviceUpdater, null, null);
|
||||
|
||||
assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo(
|
||||
AVAILABLE_UNSEARCHABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_BluetoothFeature_returnSupported() {
|
||||
mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, true);
|
||||
mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_ACCESSORY, false);
|
||||
mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_HOST, false);
|
||||
mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater,
|
||||
mConnectedDeviceGroupController.init(null, mConnectedBluetoothDeviceUpdater,
|
||||
mConnectedUsbDeviceUpdater, null, null);
|
||||
|
||||
assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo(
|
||||
@@ -239,7 +272,7 @@ public class ConnectedDeviceGroupControllerTest {
|
||||
mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, false);
|
||||
mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_ACCESSORY, false);
|
||||
mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_HOST, true);
|
||||
mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater,
|
||||
mConnectedDeviceGroupController.init(null, mConnectedBluetoothDeviceUpdater,
|
||||
mConnectedUsbDeviceUpdater, null, null);
|
||||
|
||||
assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo(
|
||||
@@ -251,7 +284,7 @@ public class ConnectedDeviceGroupControllerTest {
|
||||
mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, false);
|
||||
mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_ACCESSORY, false);
|
||||
mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_HOST, false);
|
||||
mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater,
|
||||
mConnectedDeviceGroupController.init(null, mConnectedBluetoothDeviceUpdater,
|
||||
mConnectedUsbDeviceUpdater, mConnectedDockUpdater, null);
|
||||
|
||||
assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo(
|
||||
@@ -261,6 +294,8 @@ public class ConnectedDeviceGroupControllerTest {
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_noUsiStylusFeature_returnUnSupported() {
|
||||
mFakeFeatureFlags.setFlag(FLAG_ROTATION_CONNECTED_DISPLAY_SETTING, false);
|
||||
mFakeFeatureFlags.setFlag(FLAG_RESOLUTION_AND_ENABLE_CONNECTED_DISPLAY_SETTING, false);
|
||||
mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, false);
|
||||
mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_ACCESSORY, false);
|
||||
mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_HOST, false);
|
||||
@@ -268,7 +303,7 @@ public class ConnectedDeviceGroupControllerTest {
|
||||
when(mInputManager.getInputDevice(0)).thenReturn(new InputDevice.Builder().setSources(
|
||||
InputDevice.SOURCE_DPAD).setExternal(false).build());
|
||||
|
||||
mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater,
|
||||
mConnectedDeviceGroupController.init(null, mConnectedBluetoothDeviceUpdater,
|
||||
mConnectedUsbDeviceUpdater, null, mStylusDeviceUpdater);
|
||||
|
||||
assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo(
|
||||
@@ -284,7 +319,7 @@ public class ConnectedDeviceGroupControllerTest {
|
||||
when(mInputManager.getInputDevice(0)).thenReturn(new InputDevice.Builder().setSources(
|
||||
InputDevice.SOURCE_STYLUS).setExternal(false).build());
|
||||
|
||||
mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater,
|
||||
mConnectedDeviceGroupController.init(null, mConnectedBluetoothDeviceUpdater,
|
||||
mConnectedUsbDeviceUpdater, mConnectedDockUpdater, mStylusDeviceUpdater);
|
||||
|
||||
assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo(
|
||||
|
@@ -33,6 +33,7 @@ android_test {
|
||||
"kotlinx_coroutines_test",
|
||||
"Settings-testutils2",
|
||||
"MediaDrmSettingsFlagsLib",
|
||||
"servicestests-utils",
|
||||
// Don't add SettingsLib libraries here - you can use them directly as they are in the
|
||||
// instrumented Settings app.
|
||||
],
|
||||
|
@@ -0,0 +1,409 @@
|
||||
/*
|
||||
* 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 static android.view.Display.INVALID_DISPLAY;
|
||||
|
||||
import static com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.PREVIOUSLY_SHOWN_LIST_KEY;
|
||||
import static com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.DISPLAYS_LIST_PREFERENCE_KEY;
|
||||
import static com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.EXTERNAL_DISPLAY_CHANGE_RESOLUTION_FOOTER_RESOURCE;
|
||||
import static com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.EXTERNAL_DISPLAY_NOT_FOUND_FOOTER_RESOURCE;
|
||||
import static com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.EXTERNAL_DISPLAY_RESOLUTION_PREFERENCE_KEY;
|
||||
import static com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.EXTERNAL_DISPLAY_RESOLUTION_TITLE_RESOURCE;
|
||||
import static com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.EXTERNAL_DISPLAY_ROTATION_KEY;
|
||||
import static com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.EXTERNAL_DISPLAY_ROTATION_TITLE_RESOURCE;
|
||||
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.settingslib.widget.FooterPreference.KEY_FOOTER;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.Display;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.test.annotation.UiThreadTest;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.DisplayPreference;
|
||||
import com.android.settingslib.widget.FooterPreference;
|
||||
import com.android.settingslib.widget.MainSwitchPreference;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
|
||||
/** Unit tests for {@link ExternalDisplayPreferenceFragment}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBase {
|
||||
@Nullable
|
||||
private ExternalDisplayPreferenceFragment mFragment;
|
||||
private int mPreferenceIdFromResource;
|
||||
private int mDisplayIdArg = INVALID_DISPLAY;
|
||||
private int mResolutionSelectorDisplayId = INVALID_DISPLAY;
|
||||
@Mock
|
||||
private MetricsLogger mMockedMetricsLogger;
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testCreateAndStart() {
|
||||
initFragment();
|
||||
assertThat(mPreferenceIdFromResource).isEqualTo(EXTERNAL_DISPLAY_SETTINGS_RESOURCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testShowDisplayList() {
|
||||
var fragment = initFragment();
|
||||
var outState = new Bundle();
|
||||
fragment.onSaveInstanceStateCallback(outState);
|
||||
assertThat(outState.getBoolean(PREVIOUSLY_SHOWN_LIST_KEY)).isFalse();
|
||||
assertThat(mHandler.getPendingMessages().size()).isEqualTo(1);
|
||||
PreferenceCategory pref = mPreferenceScreen.findPreference(DISPLAYS_LIST_PREFERENCE_KEY);
|
||||
assertThat(pref).isNull();
|
||||
verify(mMockedInjector, never()).getAllDisplays();
|
||||
mHandler.flush();
|
||||
assertThat(mHandler.getPendingMessages().size()).isEqualTo(0);
|
||||
verify(mMockedInjector).getAllDisplays();
|
||||
pref = mPreferenceScreen.findPreference(DISPLAYS_LIST_PREFERENCE_KEY);
|
||||
assertThat(pref).isNotNull();
|
||||
assertThat(pref.getPreferenceCount()).isEqualTo(2);
|
||||
fragment.onSaveInstanceStateCallback(outState);
|
||||
assertThat(outState.getBoolean(PREVIOUSLY_SHOWN_LIST_KEY)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testLaunchDisplaySettingFromList() {
|
||||
initFragment();
|
||||
mHandler.flush();
|
||||
PreferenceCategory pref = mPreferenceScreen.findPreference(DISPLAYS_LIST_PREFERENCE_KEY);
|
||||
assertThat(pref).isNotNull();
|
||||
DisplayPreference display1Pref = (DisplayPreference) pref.getPreference(0);
|
||||
DisplayPreference display2Pref = (DisplayPreference) pref.getPreference(1);
|
||||
assertThat(display1Pref.getKey()).isEqualTo("display_id_" + 1);
|
||||
assertThat("" + display1Pref.getTitle()).isEqualTo("HDMI");
|
||||
assertThat("" + display1Pref.getSummary()).isEqualTo("1920 x 1080");
|
||||
display1Pref.onPreferenceClick(display1Pref);
|
||||
assertThat(mDisplayIdArg).isEqualTo(1);
|
||||
verify(mMockedMetricsLogger).writePreferenceClickMetric(display1Pref);
|
||||
assertThat(display2Pref.getKey()).isEqualTo("display_id_" + 2);
|
||||
assertThat("" + display2Pref.getTitle()).isEqualTo("Overlay #1");
|
||||
assertThat("" + display2Pref.getSummary()).isEqualTo("1240 x 780");
|
||||
display2Pref.onPreferenceClick(display2Pref);
|
||||
assertThat(mDisplayIdArg).isEqualTo(2);
|
||||
verify(mMockedMetricsLogger).writePreferenceClickMetric(display2Pref);
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testShowDisplayListForOnlyOneDisplay_PreviouslyShownList() {
|
||||
var fragment = initFragment();
|
||||
// Previously shown list of displays
|
||||
fragment.onActivityCreatedCallback(createBundleForPreviouslyShownList());
|
||||
// Only one display available
|
||||
doReturn(new Display[] {mDisplays[1]}).when(mMockedInjector).getAllDisplays();
|
||||
mHandler.flush();
|
||||
PreferenceCategory pref = mPreferenceScreen.findPreference(DISPLAYS_LIST_PREFERENCE_KEY);
|
||||
assertThat(pref).isNotNull();
|
||||
assertThat(pref.getPreferenceCount()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testShowEnabledDisplay_OnlyOneDisplayAvailable() {
|
||||
doReturn(true).when(mMockedInjector).isDisplayEnabled(any());
|
||||
// Only one display available
|
||||
doReturn(new Display[] {mDisplays[1]}).when(mMockedInjector).getAllDisplays();
|
||||
// Init
|
||||
initFragment();
|
||||
mHandler.flush();
|
||||
PreferenceCategory list = mPreferenceScreen.findPreference(DISPLAYS_LIST_PREFERENCE_KEY);
|
||||
assertThat(list).isNull();
|
||||
var pref = mPreferenceScreen.findPreference(EXTERNAL_DISPLAY_RESOLUTION_PREFERENCE_KEY);
|
||||
assertThat(pref).isNotNull();
|
||||
pref = mPreferenceScreen.findPreference(EXTERNAL_DISPLAY_ROTATION_KEY);
|
||||
assertThat(pref).isNotNull();
|
||||
var footerPref = (FooterPreference) mPreferenceScreen.findPreference(KEY_FOOTER);
|
||||
assertThat(footerPref).isNotNull();
|
||||
verify(footerPref).setTitle(EXTERNAL_DISPLAY_CHANGE_RESOLUTION_FOOTER_RESOURCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testShowOneEnabledDisplay_FewAvailable() {
|
||||
mDisplayIdArg = 1;
|
||||
doReturn(true).when(mMockedInjector).isDisplayEnabled(any());
|
||||
initFragment();
|
||||
verify(mMockedInjector, never()).getDisplay(anyInt());
|
||||
mHandler.flush();
|
||||
verify(mMockedInjector).getDisplay(mDisplayIdArg);
|
||||
var pref = mPreferenceScreen.findPreference(EXTERNAL_DISPLAY_RESOLUTION_PREFERENCE_KEY);
|
||||
assertThat(pref).isNotNull();
|
||||
pref = mPreferenceScreen.findPreference(EXTERNAL_DISPLAY_ROTATION_KEY);
|
||||
assertThat(pref).isNotNull();
|
||||
var footerPref = (FooterPreference) mPreferenceScreen.findPreference(KEY_FOOTER);
|
||||
assertThat(footerPref).isNotNull();
|
||||
verify(footerPref).setTitle(EXTERNAL_DISPLAY_CHANGE_RESOLUTION_FOOTER_RESOURCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testShowDisabledDisplay() {
|
||||
mDisplayIdArg = 1;
|
||||
initFragment();
|
||||
verify(mMockedInjector, never()).getDisplay(anyInt());
|
||||
mHandler.flush();
|
||||
verify(mMockedInjector).getDisplay(mDisplayIdArg);
|
||||
var mainPref = (MainSwitchPreference) mPreferenceScreen.findPreference(
|
||||
EXTERNAL_DISPLAY_USE_PREFERENCE_KEY);
|
||||
assertThat(mainPref).isNotNull();
|
||||
assertThat("" + mainPref.getTitle()).isEqualTo(
|
||||
getText(EXTERNAL_DISPLAY_USE_TITLE_RESOURCE));
|
||||
assertThat(mainPref.isChecked()).isFalse();
|
||||
assertThat(mainPref.isEnabled()).isTrue();
|
||||
assertThat(mainPref.getOnPreferenceChangeListener()).isNotNull();
|
||||
var pref = mPreferenceScreen.findPreference(EXTERNAL_DISPLAY_RESOLUTION_PREFERENCE_KEY);
|
||||
assertThat(pref).isNull();
|
||||
pref = mPreferenceScreen.findPreference(EXTERNAL_DISPLAY_ROTATION_KEY);
|
||||
assertThat(pref).isNull();
|
||||
var footerPref = (FooterPreference) mPreferenceScreen.findPreference(KEY_FOOTER);
|
||||
assertThat(footerPref).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testNoDisplays() {
|
||||
doReturn(new Display[0]).when(mMockedInjector).getAllDisplays();
|
||||
initFragment();
|
||||
mHandler.flush();
|
||||
var mainPref = (MainSwitchPreference) mPreferenceScreen.findPreference(
|
||||
EXTERNAL_DISPLAY_USE_PREFERENCE_KEY);
|
||||
assertThat(mainPref).isNotNull();
|
||||
assertThat("" + mainPref.getTitle()).isEqualTo(
|
||||
getText(EXTERNAL_DISPLAY_USE_TITLE_RESOURCE));
|
||||
assertThat(mainPref.isChecked()).isFalse();
|
||||
assertThat(mainPref.isEnabled()).isFalse();
|
||||
assertThat(mainPref.getOnPreferenceChangeListener()).isNull();
|
||||
var footerPref = (FooterPreference) mPreferenceScreen.findPreference(KEY_FOOTER);
|
||||
assertThat(footerPref).isNotNull();
|
||||
verify(footerPref).setTitle(EXTERNAL_DISPLAY_NOT_FOUND_FOOTER_RESOURCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testDisplayRotationPreference() {
|
||||
mDisplayIdArg = 1;
|
||||
doReturn(true).when(mMockedInjector).isDisplayEnabled(any());
|
||||
var fragment = initFragment();
|
||||
mHandler.flush();
|
||||
var pref = fragment.getRotationPreference(mContext);
|
||||
assertThat(pref.getKey()).isEqualTo(EXTERNAL_DISPLAY_ROTATION_KEY);
|
||||
assertThat("" + pref.getTitle()).isEqualTo(
|
||||
getText(EXTERNAL_DISPLAY_ROTATION_TITLE_RESOURCE));
|
||||
assertThat(pref.getEntries().length).isEqualTo(4);
|
||||
assertThat(pref.getEntryValues().length).isEqualTo(4);
|
||||
assertThat(pref.getEntryValues()[0].toString()).isEqualTo("0");
|
||||
assertThat(pref.getEntryValues()[1].toString()).isEqualTo("1");
|
||||
assertThat(pref.getEntryValues()[2].toString()).isEqualTo("2");
|
||||
assertThat(pref.getEntryValues()[3].toString()).isEqualTo("3");
|
||||
assertThat(pref.getEntries()[0].length()).isGreaterThan(0);
|
||||
assertThat(pref.getEntries()[1].length()).isGreaterThan(0);
|
||||
assertThat("" + pref.getSummary()).isEqualTo(pref.getEntries()[0].toString());
|
||||
assertThat(pref.getValue()).isEqualTo("0");
|
||||
assertThat(pref.getOnPreferenceChangeListener()).isNotNull();
|
||||
assertThat(pref.isEnabled()).isTrue();
|
||||
var rotation = 1;
|
||||
doReturn(true).when(mMockedInjector).freezeDisplayRotation(mDisplayIdArg, rotation);
|
||||
assertThat(pref.getOnPreferenceChangeListener().onPreferenceChange(pref, rotation + ""))
|
||||
.isTrue();
|
||||
verify(mMockedInjector).freezeDisplayRotation(mDisplayIdArg, rotation);
|
||||
assertThat(pref.getValue()).isEqualTo(rotation + "");
|
||||
verify(mMockedMetricsLogger).writePreferenceClickMetric(pref);
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testDisplayResolutionPreference() {
|
||||
mDisplayIdArg = 1;
|
||||
doReturn(true).when(mMockedInjector).isDisplayEnabled(any());
|
||||
var fragment = initFragment();
|
||||
mHandler.flush();
|
||||
var pref = fragment.getResolutionPreference(mContext);
|
||||
assertThat(pref.getKey()).isEqualTo(EXTERNAL_DISPLAY_RESOLUTION_PREFERENCE_KEY);
|
||||
assertThat("" + pref.getTitle()).isEqualTo(
|
||||
getText(EXTERNAL_DISPLAY_RESOLUTION_TITLE_RESOURCE));
|
||||
assertThat("" + pref.getSummary()).isEqualTo("1920 x 1080");
|
||||
assertThat(pref.isEnabled()).isTrue();
|
||||
assertThat(pref.getOnPreferenceClickListener()).isNotNull();
|
||||
assertThat(pref.getOnPreferenceClickListener().onPreferenceClick(pref)).isTrue();
|
||||
assertThat(mResolutionSelectorDisplayId).isEqualTo(mDisplayIdArg);
|
||||
verify(mMockedMetricsLogger).writePreferenceClickMetric(pref);
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testUseDisplayPreference_EnabledDisplay() {
|
||||
mDisplayIdArg = 1;
|
||||
doReturn(true).when(mMockedInjector).isDisplayEnabled(any());
|
||||
doReturn(true).when(mMockedInjector).enableConnectedDisplay(mDisplayIdArg);
|
||||
doReturn(true).when(mMockedInjector).disableConnectedDisplay(mDisplayIdArg);
|
||||
var fragment = initFragment();
|
||||
mHandler.flush();
|
||||
var pref = fragment.getUseDisplayPreference(mContext);
|
||||
assertThat(pref.getKey()).isEqualTo(EXTERNAL_DISPLAY_USE_PREFERENCE_KEY);
|
||||
assertThat("" + pref.getTitle()).isEqualTo(getText(EXTERNAL_DISPLAY_USE_TITLE_RESOURCE));
|
||||
assertThat(pref.isEnabled()).isTrue();
|
||||
assertThat(pref.isChecked()).isTrue();
|
||||
assertThat(pref.getOnPreferenceChangeListener()).isNotNull();
|
||||
assertThat(pref.getOnPreferenceChangeListener().onPreferenceChange(pref, false)).isTrue();
|
||||
verify(mMockedInjector).disableConnectedDisplay(mDisplayIdArg);
|
||||
assertThat(pref.isChecked()).isFalse();
|
||||
assertThat(pref.getOnPreferenceChangeListener().onPreferenceChange(pref, true)).isTrue();
|
||||
verify(mMockedInjector).enableConnectedDisplay(mDisplayIdArg);
|
||||
assertThat(pref.isChecked()).isTrue();
|
||||
verify(mMockedMetricsLogger, times(2)).writePreferenceClickMetric(pref);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private ExternalDisplayPreferenceFragment initFragment() {
|
||||
if (mFragment != null) {
|
||||
return mFragment;
|
||||
}
|
||||
mFragment = new TestableExternalDisplayPreferenceFragment();
|
||||
mFragment.onCreateCallback(null);
|
||||
mFragment.onActivityCreatedCallback(null);
|
||||
mFragment.onStartCallback();
|
||||
return mFragment;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Bundle createBundleForPreviouslyShownList() {
|
||||
var state = new Bundle();
|
||||
state.putBoolean(PREVIOUSLY_SHOWN_LIST_KEY, true);
|
||||
return state;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private String getText(int id) {
|
||||
return mContext.getResources().getText(id).toString();
|
||||
}
|
||||
|
||||
private class TestableExternalDisplayPreferenceFragment extends
|
||||
ExternalDisplayPreferenceFragment {
|
||||
private final View mMockedRootView;
|
||||
private final TextView mEmptyView;
|
||||
private final Activity mMockedActivity;
|
||||
private final FooterPreference mMockedFooterPreference;
|
||||
private final MetricsLogger mLogger;
|
||||
|
||||
TestableExternalDisplayPreferenceFragment() {
|
||||
super(mMockedInjector);
|
||||
mMockedActivity = mock(Activity.class);
|
||||
mMockedRootView = mock(View.class);
|
||||
mMockedFooterPreference = mock(FooterPreference.class);
|
||||
doReturn(KEY_FOOTER).when(mMockedFooterPreference).getKey();
|
||||
mEmptyView = new TextView(mContext);
|
||||
doReturn(mEmptyView).when(mMockedRootView).findViewById(android.R.id.empty);
|
||||
mLogger = mMockedMetricsLogger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreferenceScreen getPreferenceScreen() {
|
||||
return mPreferenceScreen;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Activity getCurrentActivity() {
|
||||
return mMockedActivity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView() {
|
||||
return mMockedRootView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEmptyView(View view) {
|
||||
assertThat(view).isEqualTo(mEmptyView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getEmptyView() {
|
||||
return mEmptyView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPreferencesFromResource(int resource) {
|
||||
mPreferenceIdFromResource = resource;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
FooterPreference getFooterPreference(@NonNull Context context) {
|
||||
return mMockedFooterPreference;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDisplayIdArg() {
|
||||
return mDisplayIdArg;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void launchResolutionSelector(@NonNull Context context, int displayId) {
|
||||
mResolutionSelectorDisplayId = displayId;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void launchDisplaySettings(final int displayId) {
|
||||
mDisplayIdArg = displayId;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writePreferenceClickMetric(Preference preference) {
|
||||
mLogger.writePreferenceClickMetric(preference);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface allowing to mock and spy on log events.
|
||||
*/
|
||||
public interface MetricsLogger {
|
||||
|
||||
/**
|
||||
* On preference click metric
|
||||
*/
|
||||
void writePreferenceClickMetric(Preference preference);
|
||||
}
|
||||
}
|
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* 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 static com.android.settings.connecteddevice.display.ExternalDisplaySettingsConfiguration.VIRTUAL_DISPLAY_PACKAGE_NAME_SYSTEM_PROPERTY;
|
||||
import static com.android.settings.flags.Flags.FLAG_ROTATION_CONNECTED_DISPLAY_SETTING;
|
||||
import static com.android.settings.flags.Flags.FLAG_RESOLUTION_AND_ENABLE_CONNECTED_DISPLAY_SETTING;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.hardware.display.DisplayManagerGlobal;
|
||||
import android.hardware.display.IDisplayManager;
|
||||
import android.os.RemoteException;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayAdjustments;
|
||||
import android.view.DisplayInfo;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.server.testutils.TestHandler;
|
||||
import com.android.settings.connecteddevice.display.ExternalDisplaySettingsConfiguration.DisplayListener;
|
||||
import com.android.settings.flags.FakeFeatureFlagsImpl;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
public class ExternalDisplayTestBase {
|
||||
@Mock
|
||||
ExternalDisplaySettingsConfiguration.Injector mMockedInjector;
|
||||
@Mock
|
||||
IDisplayManager mMockedIDisplayManager;
|
||||
Resources mResources;
|
||||
DisplayManagerGlobal mDisplayManagerGlobal;
|
||||
FakeFeatureFlagsImpl mFlags = new FakeFeatureFlagsImpl();
|
||||
Context mContext;
|
||||
DisplayListener mListener;
|
||||
TestHandler mHandler = new TestHandler(null);
|
||||
PreferenceManager mPreferenceManager;
|
||||
PreferenceScreen mPreferenceScreen;
|
||||
Display[] mDisplays;
|
||||
|
||||
/**
|
||||
* Setup.
|
||||
*/
|
||||
@Before
|
||||
public void setUp() throws RemoteException {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
mResources = spy(mContext.getResources());
|
||||
doReturn(mResources).when(mContext).getResources();
|
||||
mPreferenceManager = new PreferenceManager(mContext);
|
||||
mPreferenceScreen = mPreferenceManager.createPreferenceScreen(mContext);
|
||||
doReturn(0).when(mMockedIDisplayManager).getPreferredWideGamutColorSpaceId();
|
||||
mDisplayManagerGlobal = new DisplayManagerGlobal(mMockedIDisplayManager);
|
||||
mFlags.setFlag(FLAG_ROTATION_CONNECTED_DISPLAY_SETTING, true);
|
||||
mFlags.setFlag(FLAG_RESOLUTION_AND_ENABLE_CONNECTED_DISPLAY_SETTING, true);
|
||||
mDisplays = new Display[] {
|
||||
createDefaultDisplay(), createExternalDisplay(), createOverlayDisplay()};
|
||||
doReturn(mDisplays).when(mMockedInjector).getAllDisplays();
|
||||
doReturn(mDisplays).when(mMockedInjector).getEnabledDisplays();
|
||||
for (var display : mDisplays) {
|
||||
doReturn(display).when(mMockedInjector).getDisplay(display.getDisplayId());
|
||||
}
|
||||
doReturn(mFlags).when(mMockedInjector).getFlags();
|
||||
doReturn(mHandler).when(mMockedInjector).getHandler();
|
||||
doReturn("").when(mMockedInjector).getSystemProperty(
|
||||
VIRTUAL_DISPLAY_PACKAGE_NAME_SYSTEM_PROPERTY);
|
||||
doAnswer((arg) -> {
|
||||
mListener = arg.getArgument(0);
|
||||
return null;
|
||||
}).when(mMockedInjector).registerDisplayListener(any());
|
||||
doReturn(0).when(mMockedInjector).getDisplayUserRotation(anyInt());
|
||||
doReturn(mContext).when(mMockedInjector).getContext();
|
||||
}
|
||||
|
||||
Display createDefaultDisplay() throws RemoteException {
|
||||
int displayId = 0;
|
||||
var displayInfo = new DisplayInfo();
|
||||
doReturn(displayInfo).when(mMockedIDisplayManager).getDisplayInfo(displayId);
|
||||
displayInfo.displayId = displayId;
|
||||
displayInfo.name = "Built-in";
|
||||
displayInfo.type = Display.TYPE_INTERNAL;
|
||||
displayInfo.supportedModes = new Display.Mode[]{
|
||||
new Display.Mode(0, 2048, 1024, 60, 60, new float[0],
|
||||
new int[0])};
|
||||
displayInfo.appsSupportedModes = displayInfo.supportedModes;
|
||||
return createDisplay(displayInfo);
|
||||
}
|
||||
|
||||
Display createExternalDisplay() throws RemoteException {
|
||||
int displayId = 1;
|
||||
var displayInfo = new DisplayInfo();
|
||||
doReturn(displayInfo).when(mMockedIDisplayManager).getDisplayInfo(displayId);
|
||||
displayInfo.displayId = displayId;
|
||||
displayInfo.name = "HDMI";
|
||||
displayInfo.type = Display.TYPE_EXTERNAL;
|
||||
displayInfo.supportedModes = new Display.Mode[]{
|
||||
new Display.Mode(0, 1920, 1080, 60, 60, new float[0], new int[0]),
|
||||
new Display.Mode(1, 800, 600, 60, 60, new float[0], new int[0]),
|
||||
new Display.Mode(2, 320, 240, 70, 70, new float[0], new int[0]),
|
||||
new Display.Mode(3, 640, 480, 60, 60, new float[0], new int[0]),
|
||||
new Display.Mode(4, 640, 480, 50, 60, new float[0], new int[0]),
|
||||
new Display.Mode(5, 2048, 1024, 60, 60, new float[0], new int[0]),
|
||||
new Display.Mode(6, 720, 480, 60, 60, new float[0], new int[0])};
|
||||
displayInfo.appsSupportedModes = displayInfo.supportedModes;
|
||||
return createDisplay(displayInfo);
|
||||
}
|
||||
|
||||
Display createOverlayDisplay() throws RemoteException {
|
||||
int displayId = 2;
|
||||
var displayInfo = new DisplayInfo();
|
||||
doReturn(displayInfo).when(mMockedIDisplayManager).getDisplayInfo(displayId);
|
||||
displayInfo.displayId = displayId;
|
||||
displayInfo.name = "Overlay #1";
|
||||
displayInfo.type = Display.TYPE_OVERLAY;
|
||||
displayInfo.supportedModes = new Display.Mode[]{
|
||||
new Display.Mode(0, 1240, 780, 60, 60, new float[0],
|
||||
new int[0])};
|
||||
displayInfo.appsSupportedModes = displayInfo.supportedModes;
|
||||
return createDisplay(displayInfo);
|
||||
}
|
||||
|
||||
Display createDisplay(DisplayInfo displayInfo) {
|
||||
return new Display(mDisplayManagerGlobal, displayInfo.displayId, displayInfo,
|
||||
(DisplayAdjustments) null);
|
||||
}
|
||||
}
|
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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 static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.RemoteException;
|
||||
import android.view.Display;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.connecteddevice.DevicePreferenceCallback;
|
||||
import com.android.settingslib.RestrictedLockUtils;
|
||||
import com.android.settingslib.RestrictedPreference;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
|
||||
/** Unit tests for {@link ExternalDisplayUpdater}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExternalDisplayUpdaterTest extends ExternalDisplayTestBase {
|
||||
|
||||
private ExternalDisplayUpdater mUpdater;
|
||||
@Mock
|
||||
private DevicePreferenceCallback mMockedCallback;
|
||||
@Mock
|
||||
private Drawable mMockedDrawable;
|
||||
private RestrictedPreference mPreferenceAdded;
|
||||
private RestrictedPreference mPreferenceRemoved;
|
||||
|
||||
@Before
|
||||
public void setUp() throws RemoteException {
|
||||
super.setUp();
|
||||
mUpdater = new TestableExternalDisplayUpdater(mMockedCallback, /*metricsCategory=*/ 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreferenceAdded() {
|
||||
doAnswer((v) -> {
|
||||
mPreferenceAdded = v.getArgument(0);
|
||||
return null;
|
||||
}).when(mMockedCallback).onDeviceAdded(any());
|
||||
mUpdater.initPreference(mContext, mMockedInjector);
|
||||
mUpdater.registerCallback();
|
||||
mHandler.flush();
|
||||
assertThat(mPreferenceAdded).isNotNull();
|
||||
var summary = mPreferenceAdded.getSummary();
|
||||
assertThat(summary).isNotNull();
|
||||
assertThat(summary.length()).isGreaterThan(0);
|
||||
var title = mPreferenceAdded.getTitle();
|
||||
assertThat(title).isNotNull();
|
||||
assertThat(title.length()).isGreaterThan(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreferenceRemoved() {
|
||||
doAnswer((v) -> {
|
||||
mPreferenceAdded = v.getArgument(0);
|
||||
return null;
|
||||
}).when(mMockedCallback).onDeviceAdded(any());
|
||||
doAnswer((v) -> {
|
||||
mPreferenceRemoved = v.getArgument(0);
|
||||
return null;
|
||||
}).when(mMockedCallback).onDeviceRemoved(any());
|
||||
mUpdater.initPreference(mContext, mMockedInjector);
|
||||
mUpdater.registerCallback();
|
||||
mHandler.flush();
|
||||
assertThat(mPreferenceAdded).isNotNull();
|
||||
assertThat(mPreferenceRemoved).isNull();
|
||||
// Remove display
|
||||
doReturn(new Display[0]).when(mMockedInjector).getAllDisplays();
|
||||
doReturn(new Display[0]).when(mMockedInjector).getEnabledDisplays();
|
||||
mListener.onDisplayRemoved(1);
|
||||
mHandler.flush();
|
||||
assertThat(mPreferenceRemoved).isEqualTo(mPreferenceAdded);
|
||||
}
|
||||
|
||||
class TestableExternalDisplayUpdater extends ExternalDisplayUpdater {
|
||||
TestableExternalDisplayUpdater(
|
||||
DevicePreferenceCallback callback,
|
||||
int metricsCategory) {
|
||||
super(callback, metricsCategory);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected RestrictedLockUtils.EnforcedAdmin checkIfUsbDataSignalingIsDisabled(
|
||||
Context context) {
|
||||
// if null is returned - usb signalling is enabled
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected Drawable getDrawable(Context context) {
|
||||
return mMockedDrawable;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* 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 static android.view.Display.INVALID_DISPLAY;
|
||||
|
||||
import static com.android.settings.connecteddevice.display.ResolutionPreferenceFragment.EXTERNAL_DISPLAY_RESOLUTION_SETTINGS_RESOURCE;
|
||||
import static com.android.settings.connecteddevice.display.ResolutionPreferenceFragment.MORE_OPTIONS_KEY;
|
||||
import static com.android.settings.connecteddevice.display.ResolutionPreferenceFragment.TOP_OPTIONS_KEY;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.test.annotation.UiThreadTest;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settingslib.widget.SelectorWithWidgetPreference;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
|
||||
/** Unit tests for {@link ResolutionPreferenceFragment}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ResolutionPreferenceFragmentTest extends ExternalDisplayTestBase {
|
||||
@Nullable
|
||||
private ResolutionPreferenceFragment mFragment;
|
||||
private int mPreferenceIdFromResource;
|
||||
private int mDisplayIdArg = INVALID_DISPLAY;
|
||||
@Mock
|
||||
private MetricsLogger mMockedMetricsLogger;
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testCreateAndStart() {
|
||||
initFragment();
|
||||
mHandler.flush();
|
||||
assertThat(mPreferenceIdFromResource).isEqualTo(
|
||||
EXTERNAL_DISPLAY_RESOLUTION_SETTINGS_RESOURCE);
|
||||
var pref = mPreferenceScreen.findPreference(TOP_OPTIONS_KEY);
|
||||
assertThat(pref).isNull();
|
||||
pref = mPreferenceScreen.findPreference(MORE_OPTIONS_KEY);
|
||||
assertThat(pref).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testCreateAndStartDefaultDisplayNotAllowed() {
|
||||
mDisplayIdArg = 0;
|
||||
initFragment();
|
||||
mHandler.flush();
|
||||
var pref = mPreferenceScreen.findPreference(TOP_OPTIONS_KEY);
|
||||
assertThat(pref).isNull();
|
||||
pref = mPreferenceScreen.findPreference(MORE_OPTIONS_KEY);
|
||||
assertThat(pref).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testModePreferences() {
|
||||
mDisplayIdArg = 1;
|
||||
initFragment();
|
||||
mHandler.flush();
|
||||
PreferenceCategory topPref = mPreferenceScreen.findPreference(TOP_OPTIONS_KEY);
|
||||
assertThat(topPref).isNotNull();
|
||||
PreferenceCategory morePref = mPreferenceScreen.findPreference(MORE_OPTIONS_KEY);
|
||||
assertThat(morePref).isNotNull();
|
||||
assertThat(topPref.getPreferenceCount()).isEqualTo(3);
|
||||
assertThat(morePref.getPreferenceCount()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testModeChange() {
|
||||
mDisplayIdArg = 1;
|
||||
initFragment();
|
||||
mHandler.flush();
|
||||
PreferenceCategory topPref = mPreferenceScreen.findPreference(TOP_OPTIONS_KEY);
|
||||
assertThat(topPref).isNotNull();
|
||||
var modePref = (SelectorWithWidgetPreference) topPref.getPreference(1);
|
||||
modePref.onClick();
|
||||
var mode = mDisplays[mDisplayIdArg].getSupportedModes()[1];
|
||||
verify(mMockedInjector).setUserPreferredDisplayMode(mDisplayIdArg, mode);
|
||||
}
|
||||
|
||||
private void initFragment() {
|
||||
if (mFragment != null) {
|
||||
return;
|
||||
}
|
||||
mFragment = new TestableResolutionPreferenceFragment();
|
||||
mFragment.onCreateCallback(null);
|
||||
mFragment.onActivityCreatedCallback(null);
|
||||
mFragment.onStartCallback();
|
||||
}
|
||||
|
||||
private class TestableResolutionPreferenceFragment extends ResolutionPreferenceFragment {
|
||||
private final View mMockedRootView;
|
||||
private final TextView mEmptyView;
|
||||
private final Resources mMockedResources;
|
||||
private final MetricsLogger mLogger;
|
||||
TestableResolutionPreferenceFragment() {
|
||||
super(mMockedInjector);
|
||||
mMockedResources = mock(Resources.class);
|
||||
doReturn(61).when(mMockedResources).getInteger(
|
||||
com.android.internal.R.integer.config_externalDisplayPeakRefreshRate);
|
||||
doReturn(1920).when(mMockedResources).getInteger(
|
||||
com.android.internal.R.integer.config_externalDisplayPeakWidth);
|
||||
doReturn(1080).when(mMockedResources).getInteger(
|
||||
com.android.internal.R.integer.config_externalDisplayPeakHeight);
|
||||
doReturn(true).when(mMockedResources).getBoolean(
|
||||
com.android.internal.R.bool.config_refreshRateSynchronizationEnabled);
|
||||
mMockedRootView = mock(View.class);
|
||||
mEmptyView = new TextView(mContext);
|
||||
doReturn(mEmptyView).when(mMockedRootView).findViewById(android.R.id.empty);
|
||||
mLogger = mMockedMetricsLogger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreferenceScreen getPreferenceScreen() {
|
||||
return mPreferenceScreen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView() {
|
||||
return mMockedRootView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEmptyView(View view) {
|
||||
assertThat(view).isEqualTo(mEmptyView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getEmptyView() {
|
||||
return mEmptyView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPreferencesFromResource(int resource) {
|
||||
mPreferenceIdFromResource = resource;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDisplayIdArg() {
|
||||
return mDisplayIdArg;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writePreferenceClickMetric(Preference preference) {
|
||||
mLogger.writePreferenceClickMetric(preference);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
protected Resources getResources(@NonNull Context context) {
|
||||
return mMockedResources;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface allowing to mock and spy on log events.
|
||||
*/
|
||||
public interface MetricsLogger {
|
||||
/**
|
||||
* On preference click metric
|
||||
*/
|
||||
void writePreferenceClickMetric(Preference preference);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user