diff --git a/res/values/strings.xml b/res/values/strings.xml index ac99f2d5160..6d865c21096 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -14360,6 +14360,16 @@ Data usage charges may apply. No filter is perfect, but this should help hide sexually explicit sites Allow all sites + + Google Search + + SafeSearch filtering ON + + Helps filter out explicit images, text, and links from search results on this device + + SafeSearch filtering OFF + + Account settings may still filter or blur explicit results Enter supervision PIN diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java index 58d278704b2..d7f53ca4cb5 100644 --- a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java +++ b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java @@ -419,32 +419,7 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll mContext, SettingsEnums.ACTION_BLUETOOTH_PROFILE_LE_AUDIO_OFF, isCurrentDeviceInOrByPassAllowList()); - - LocalBluetoothProfile asha = mProfileManager.getHearingAidProfile(); - LocalBluetoothProfile broadcastAssistant = - mProfileManager.getLeAudioBroadcastAssistantProfile(); - - for (CachedBluetoothDevice leAudioDevice : mProfileDeviceMap.get(profile.toString())) { - Log.d(TAG, - "device:" + leAudioDevice.getDevice().getAnonymizedAddress() - + " disable LE profile"); - profile.setEnabled(leAudioDevice.getDevice(), false); - if (asha != null) { - asha.setEnabled(leAudioDevice.getDevice(), true); - } - if (broadcastAssistant != null) { - Log.d(TAG, - "device:" + leAudioDevice.getDevice().getAnonymizedAddress() - + " disable LE broadcast assistant profile"); - broadcastAssistant.setEnabled(leAudioDevice.getDevice(), false); - } - } - - if (!SystemProperties.getBoolean(ENABLE_DUAL_MODE_AUDIO, false)) { - Log.i(TAG, "Enabling classic audio profiles because dual mode is disabled"); - enableProfileAfterUserDisablesLeAudio(mProfileManager.getA2dpProfile()); - enableProfileAfterUserDisablesLeAudio(mProfileManager.getHeadsetProfile()); - } + Utils.setLeAudioEnabled(mManager, List.copyOf(mCachedDeviceGroup), false); } /** @@ -462,75 +437,7 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll mContext, SettingsEnums.ACTION_BLUETOOTH_PROFILE_LE_AUDIO_ON, isCurrentDeviceInOrByPassAllowList()); - - if (!SystemProperties.getBoolean(ENABLE_DUAL_MODE_AUDIO, false)) { - Log.i(TAG, "Disabling classic audio profiles because dual mode is disabled"); - disableProfileBeforeUserEnablesLeAudio(mProfileManager.getA2dpProfile()); - disableProfileBeforeUserEnablesLeAudio(mProfileManager.getHeadsetProfile()); - } - LocalBluetoothProfile asha = mProfileManager.getHearingAidProfile(); - LocalBluetoothProfile broadcastAssistant = - mProfileManager.getLeAudioBroadcastAssistantProfile(); - - for (CachedBluetoothDevice leAudioDevice : mProfileDeviceMap.get(profile.toString())) { - Log.d(TAG, - "device:" + leAudioDevice.getDevice().getAnonymizedAddress() - + " enable LE profile"); - profile.setEnabled(leAudioDevice.getDevice(), true); - if (asha != null) { - asha.setEnabled(leAudioDevice.getDevice(), false); - } - if (broadcastAssistant != null) { - Log.d(TAG, - "device:" + leAudioDevice.getDevice().getAnonymizedAddress() - + " enable LE broadcast assistant profile"); - broadcastAssistant.setEnabled(leAudioDevice.getDevice(), true); - } - } - } - - private void disableProfileBeforeUserEnablesLeAudio(LocalBluetoothProfile profile) { - if (profile != null && mProfileDeviceMap.get(profile.toString()) != null) { - Log.d(TAG, "Disable " + profile.toString() + " before user enables LE"); - for (CachedBluetoothDevice profileDevice : mProfileDeviceMap.get(profile.toString())) { - if (profile.isEnabled(profileDevice.getDevice())) { - Log.d(TAG, "The " + profileDevice.getDevice().getAnonymizedAddress() + ":" - + profile.toString() + " set disable"); - profile.setEnabled(profileDevice.getDevice(), false); - } else { - Log.d(TAG, "The " + profileDevice.getDevice().getAnonymizedAddress() + ":" - + profile.toString() + " profile is disabled. Do nothing."); - } - } - } else { - if (profile == null) { - Log.w(TAG, "profile is null"); - } else { - Log.w(TAG, profile.toString() + " is not in " + mProfileDeviceMap); - } - } - } - - private void enableProfileAfterUserDisablesLeAudio(LocalBluetoothProfile profile) { - if (profile != null && mProfileDeviceMap.get(profile.toString()) != null) { - Log.d(TAG, "enable " + profile.toString() + "after user disables LE"); - for (CachedBluetoothDevice profileDevice : mProfileDeviceMap.get(profile.toString())) { - if (!profile.isEnabled(profileDevice.getDevice())) { - Log.d(TAG, "The " + profileDevice.getDevice().getAnonymizedAddress() + ":" - + profile.toString() + " set enable"); - profile.setEnabled(profileDevice.getDevice(), true); - } else { - Log.d(TAG, "The " + profileDevice.getDevice().getAnonymizedAddress() + ":" - + profile.toString() + " profile is enabled. Do nothing."); - } - } - } else { - if (profile == null) { - Log.w(TAG, "profile is null"); - } else { - Log.w(TAG, profile.toString() + " is not in " + mProfileDeviceMap); - } - } + Utils.setLeAudioEnabled(mManager, List.copyOf(mCachedDeviceGroup), true); } /** diff --git a/src/com/android/settings/bluetooth/BluetoothKeyMissingReceiver.java b/src/com/android/settings/bluetooth/BluetoothKeyMissingReceiver.java index e7e0b4a100e..cfe9c056d39 100644 --- a/src/com/android/settings/bluetooth/BluetoothKeyMissingReceiver.java +++ b/src/com/android/settings/bluetooth/BluetoothKeyMissingReceiver.java @@ -55,9 +55,18 @@ public final class BluetoothKeyMissingReceiver extends BroadcastReceiver { } BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + if (device == null) { + return; + } PowerManager powerManager = context.getSystemService(PowerManager.class); if (TextUtils.equals(action, BluetoothDevice.ACTION_KEY_MISSING)) { Log.d(TAG, "Receive ACTION_KEY_MISSING"); + if (device.getBondState() == BluetoothDevice.BOND_NONE) { + Log.d( + TAG, + "Device " + device.getAnonymizedAddress() + " is already unbonded, skip."); + return; + } Integer keyMissingCount = BluetoothUtils.getKeyMissingCount(device); if (keyMissingCount != null && keyMissingCount != 1) { Log.d(TAG, "Key missing count is " + keyMissingCount + ", skip."); diff --git a/src/com/android/settings/bluetooth/Utils.java b/src/com/android/settings/bluetooth/Utils.java index 9f4bb132921..76c3ed0c73b 100644 --- a/src/com/android/settings/bluetooth/Utils.java +++ b/src/com/android/settings/bluetooth/Utils.java @@ -28,6 +28,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; import android.util.Log; @@ -45,15 +46,20 @@ import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.BluetoothUtils.ErrorListener; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; +import com.android.settingslib.bluetooth.HearingAidProfile; +import com.android.settingslib.bluetooth.LeAudioProfile; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothManager.BluetoothManagerCallback; +import com.android.settingslib.bluetooth.LocalBluetoothProfile; +import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import com.android.settingslib.utils.ThreadUtils; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableList; +import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; @@ -70,6 +76,7 @@ import java.util.stream.Collectors; public final class Utils { private static final String TAG = "BluetoothUtils"; + private static final String ENABLE_DUAL_MODE_AUDIO = "persist.bluetooth.enable_dual_mode_audio"; static final boolean V = BluetoothUtils.V; // verbose logging static final boolean D = BluetoothUtils.D; // regular logging @@ -360,4 +367,119 @@ public final class Utils { dialog.show(); return dialog; } + + /** Enables/disables LE Audio profile for the device. */ + public static void setLeAudioEnabled( + @NonNull LocalBluetoothManager manager, + @NonNull CachedBluetoothDevice cachedDevice, + boolean enable) { + List devices = + List.copyOf(findAllCachedBluetoothDevicesByGroupId(manager, cachedDevice)); + setLeAudioEnabled(manager, devices, enable); + } + + /** Enables/disables LE Audio profile for the devices in the same csip group. */ + public static void setLeAudioEnabled( + @NonNull LocalBluetoothManager manager, + @NonNull List devicesWithSameGroupId, + boolean enable) { + LocalBluetoothProfileManager profileManager = manager.getProfileManager(); + LeAudioProfile leAudioProfile = profileManager.getLeAudioProfile(); + List leAudioDevices = + getDevicesWithProfile(devicesWithSameGroupId, leAudioProfile); + if (leAudioDevices.isEmpty()) { + Log.i(TAG, "Fail to setLeAudioEnabled, no LE Audio profile found."); + } + boolean dualModeEnabled = SystemProperties.getBoolean(ENABLE_DUAL_MODE_AUDIO, false); + + if (enable && !dualModeEnabled) { + Log.i(TAG, "Disabling classic audio profiles because dual mode is disabled"); + setProfileEnabledWhenChangingLeAudio( + devicesWithSameGroupId, profileManager.getA2dpProfile(), false); + setProfileEnabledWhenChangingLeAudio( + devicesWithSameGroupId, profileManager.getHeadsetProfile(), false); + } + + HearingAidProfile asha = profileManager.getHearingAidProfile(); + LocalBluetoothLeBroadcastAssistant broadcastAssistant = + profileManager.getLeAudioBroadcastAssistantProfile(); + + for (CachedBluetoothDevice leAudioDevice : leAudioDevices) { + Log.d( + TAG, + "device:" + + leAudioDevice.getDevice().getAnonymizedAddress() + + " set LE profile enabled: " + + enable); + leAudioProfile.setEnabled(leAudioDevice.getDevice(), enable); + if (asha != null) { + asha.setEnabled(leAudioDevice.getDevice(), !enable); + } + if (broadcastAssistant != null) { + Log.d( + TAG, + "device:" + + leAudioDevice.getDevice().getAnonymizedAddress() + + " enable LE broadcast assistant profile: " + + enable); + broadcastAssistant.setEnabled(leAudioDevice.getDevice(), enable); + } + } + + if (!enable && !dualModeEnabled) { + Log.i(TAG, "Enabling classic audio profiles because dual mode is disabled"); + setProfileEnabledWhenChangingLeAudio( + devicesWithSameGroupId, profileManager.getA2dpProfile(), true); + setProfileEnabledWhenChangingLeAudio( + devicesWithSameGroupId, profileManager.getHeadsetProfile(), true); + } + } + + private static List getDevicesWithProfile( + List devices, LocalBluetoothProfile profile) { + List devicesWithProfile = new ArrayList<>(); + for (CachedBluetoothDevice device : devices) { + for (LocalBluetoothProfile currentProfile : device.getProfiles()) { + if (currentProfile.toString().equals(profile.toString())) { + devicesWithProfile.add(device); + } + } + } + return devicesWithProfile; + } + + private static void setProfileEnabledWhenChangingLeAudio( + List devices, + @Nullable LocalBluetoothProfile profile, + boolean enable) { + if (profile == null) { + Log.i(TAG, "profile is null"); + return; + } + List deviceWithProfile = getDevicesWithProfile(devices, profile); + Log.d(TAG, "Set " + profile + " enabled:" + enable + " when switching LE Audio"); + for (CachedBluetoothDevice profileDevice : deviceWithProfile) { + if (profile.isEnabled(profileDevice.getDevice()) != enable) { + Log.d( + TAG, + "The " + + profileDevice.getDevice().getAnonymizedAddress() + + ":" + + profile + + " set to " + + enable); + profile.setEnabled(profileDevice.getDevice(), enable); + } else { + Log.d( + TAG, + "The " + + profileDevice.getDevice().getAnonymizedAddress() + + ":" + + profile + + " profile is already " + + enable + + ". Do nothing."); + } + } + } } diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java index 92ebc57bf52..f1fcd694b0a 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java @@ -192,7 +192,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController Log.d(TAG, "Skip handleOnBroadcastReady, not in starting process"); return; } - handleOnBroadcastReady(); + handleOnBroadcastReady(metadata); } @Override @@ -273,7 +273,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController + mSinksInAdding); if (mSinksToWaitFor.contains(sink)) { mSinksToWaitFor.remove(sink); - if (mSinksToWaitFor.isEmpty()) { + if (mSinksToWaitFor.isEmpty() && mBroadcast != null) { // To avoid users advance to share then pair flow before the // primary/active sinks successfully join the audio sharing, // popup dialog till adding source complete for mSinksToWaitFor. @@ -284,7 +284,8 @@ public class AudioSharingSwitchBarController extends BasePreferenceController /* userTriggered= */ false, /* deviceCountInSharing= */ 1, /* candidateDeviceCount= */ 0); - showAudioSharingDialog(eventData); + showJoinAudioSharingDialog(eventData, + mBroadcast.getLatestBluetoothLeBroadcastMetadata()); } } } @@ -501,9 +502,10 @@ public class AudioSharingSwitchBarController extends BasePreferenceController mBtManager == null ? null : mBtManager.getCachedDeviceManager(); CachedBluetoothDevice cachedDevice = deviceManager == null ? null : deviceManager.findDevice(device); - if (cachedDevice != null) { + if (cachedDevice != null && mBroadcast != null) { Log.d(TAG, "handleAutoAddSourceAfterPair, device = " + device.getAnonymizedAddress()); - addSourceToTargetSinks(ImmutableList.of(device), cachedDevice.getName()); + addSourceToTargetSinks(ImmutableList.of(device), cachedDevice.getName(), + mBroadcast.getLatestBluetoothLeBroadcastMetadata()); } } @@ -642,7 +644,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController return mAssistant != null && mAssistant.getAllConnectedDevices().isEmpty(); } - private void handleOnBroadcastReady() { + private void handleOnBroadcastReady(@NonNull BluetoothLeBroadcastMetadata metadata) { List targetActiveSinks = mTargetActiveItem == null ? ImmutableList.of() : mGroupedConnectedDevices.getOrDefault( mTargetActiveItem.getGroupId(), ImmutableList.of()); @@ -656,7 +658,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController // Auto add primary/active sinks w/o user interactions. if (!targetActiveSinks.isEmpty() && mTargetActiveItem != null) { Log.d(TAG, "handleOnBroadcastReady: automatically add source to active sinks."); - addSourceToTargetSinks(targetActiveSinks, mTargetActiveItem.getName()); + addSourceToTargetSinks(targetActiveSinks, mTargetActiveItem.getName(), metadata); // To avoid users advance to share then pair flow before the primary/active sinks // successfully join the audio sharing, save the primary/active sinks in mSinksToWaitFor // and popup dialog till adding source complete for these sinks. @@ -677,7 +679,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController AudioSharingDeviceItem target = mDeviceItemsForSharing.get(0); List targetSinks = mGroupedConnectedDevices.getOrDefault( target.getGroupId(), ImmutableList.of()); - addSourceToTargetSinks(targetSinks, target.getName()); + addSourceToTargetSinks(targetSinks, target.getName(), metadata); cleanUpStatesForStartSharing(); // TODO: Add metric for auto add by intent return; @@ -698,20 +700,21 @@ public class AudioSharingSwitchBarController extends BasePreferenceController // successfully join the audio sharing, popup dialog till adding source complete for // mSinksToWaitFor. if (mSinksToWaitFor.isEmpty() && !mStoppingSharing.get()) { - showAudioSharingDialog(eventData); + showJoinAudioSharingDialog(eventData, metadata); } } - private void showAudioSharingDialog(Pair[] eventData) { + private void showJoinAudioSharingDialog(Pair[] eventData, + @Nullable BluetoothLeBroadcastMetadata metadata) { if (!BluetoothUtils.isBroadcasting(mBtManager)) { - Log.d(TAG, "Skip showAudioSharingDialog, broadcast is stopped"); + Log.d(TAG, "Skip showJoinAudioSharingDialog, broadcast is stopped"); return; } AudioSharingDialogFragment.DialogEventListener listener = new AudioSharingDialogFragment.DialogEventListener() { @Override public void onPositiveClick() { - // Could go to other pages, dismiss the progress dialog. + // Could go to other pages (pair new device), dismiss the progress dialog. dismissProgressDialogIfNeeded(); cleanUpStatesForStartSharing(); } @@ -720,19 +723,17 @@ public class AudioSharingSwitchBarController extends BasePreferenceController public void onItemClick(@NonNull AudioSharingDeviceItem item) { List targetSinks = mGroupedConnectedDevices.getOrDefault( item.getGroupId(), ImmutableList.of()); - addSourceToTargetSinks(targetSinks, item.getName()); + addSourceToTargetSinks(targetSinks, item.getName(), metadata); cleanUpStatesForStartSharing(); } @Override public void onCancelClick() { - // Could go to other pages, dismiss the progress dialog. + // Could go to other pages (show qr code), dismiss the progress dialog. dismissProgressDialogIfNeeded(); cleanUpStatesForStartSharing(); } }; - BluetoothLeBroadcastMetadata metadata = mBroadcast == null ? null - : mBroadcast.getLatestBluetoothLeBroadcastMetadata(); AudioSharingUtils.postOnMainThread( mContext, () -> AudioSharingDialogFragment.show( @@ -828,13 +829,27 @@ public class AudioSharingSwitchBarController extends BasePreferenceController }); } - private void addSourceToTargetSinks(List targetActiveSinks, - @NonNull String sinkName) { - mSinksInAdding.addAll(targetActiveSinks); + private void addSourceToTargetSinks(List targetGroupedSinks, + @NonNull String targetSinkName, @Nullable BluetoothLeBroadcastMetadata metadata) { + if (targetGroupedSinks.isEmpty()) { + Log.d(TAG, "Skip addSourceToTargetSinks, no sinks."); + return; + } + if (metadata == null) { + Log.d(TAG, "Skip addSourceToTargetSinks, metadata is null"); + return; + } + if (mAssistant == null) { + Log.d(TAG, "skip addSourceToTargetDevices, assistant profile is null."); + return; + } + mSinksInAdding.addAll(targetGroupedSinks); String progressMessage = mContext.getString( - R.string.audio_sharing_progress_dialog_add_source_content, sinkName); + R.string.audio_sharing_progress_dialog_add_source_content, targetSinkName); showProgressDialog(progressMessage); - AudioSharingUtils.addSourceToTargetSinks(targetActiveSinks, mBtManager); + for (BluetoothDevice sink : targetGroupedSinks) { + mAssistant.addSource(sink, metadata, /* isGroupOp= */ false); + } } private void showProgressDialog(@NonNull String progressMessage) { diff --git a/src/com/android/settings/contract/SettingsContract.kt b/src/com/android/settings/contract/SettingsContract.kt index 3a354194eb6..20ff0c0cbcc 100644 --- a/src/com/android/settings/contract/SettingsContract.kt +++ b/src/com/android/settings/contract/SettingsContract.kt @@ -69,6 +69,12 @@ const val KEY_SCREEN_ATTENTION = "screen_attention" /** Contract key for the "Use adaptive connectivity" setting. */ const val KEY_ADAPTIVE_CONNECTIVITY = "adaptive_connectivity" +/** Contract key for the "Auto-switch Wi-Fi to Cellular" setting. */ +const val KEY_ADAPTIVE_WIFI_SCORER = "adaptive_wifi_scorer" + +/** Contract key for the " Auto-switch mobile network for battery life" setting. */ +const val KEY_ADAPTIVE_MOBILE_NETWORK = "adaptive_mobile_network" + /** Contract key for the "WiFi hotspot" setting. */ const val KEY_WIFI_HOTSPOT = "enable_wifi_ap" diff --git a/src/com/android/settings/deviceinfo/PhoneNumberPreferenceController.java b/src/com/android/settings/deviceinfo/PhoneNumberPreferenceController.java index b49d62d444f..bb39d88245c 100644 --- a/src/com/android/settings/deviceinfo/PhoneNumberPreferenceController.java +++ b/src/com/android/settings/deviceinfo/PhoneNumberPreferenceController.java @@ -65,9 +65,10 @@ public class PhoneNumberPreferenceController extends BasePreferenceController { @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); - if (!SubscriptionUtil.isSimHardwareVisible(mContext)) { + if (!isAvailable()) { return; } + final Preference preference = screen.findPreference(getPreferenceKey()); final PreferenceCategory category = screen.findPreference(KEY_PREFERENCE_CATEGORY); mPreferenceList.add(preference); diff --git a/src/com/android/settings/display/DeviceStateAutoRotateSettingController.java b/src/com/android/settings/display/DeviceStateAutoRotateSettingController.java index d3950ee1396..5c2dec21810 100644 --- a/src/com/android/settings/display/DeviceStateAutoRotateSettingController.java +++ b/src/com/android/settings/display/DeviceStateAutoRotateSettingController.java @@ -35,7 +35,6 @@ import com.android.settings.core.TogglePreferenceController; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager; -import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager; import com.android.settingslib.search.SearchIndexableRaw; import java.util.List; @@ -46,7 +45,7 @@ public class DeviceStateAutoRotateSettingController extends TogglePreferenceCont private TwoStatePreference mPreference; - private final DeviceStateRotationLockSettingsManager mAutoRotateSettingsManager; + private final DeviceStateAutoRotateSettingManager mAutoRotateSettingsManager; private final int mOrder; private final DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener mDeviceStateAutoRotateSettingListener = () -> updateState(mPreference); @@ -62,7 +61,8 @@ public class DeviceStateAutoRotateSettingController extends TogglePreferenceCont mMetricsFeatureProvider = metricsFeatureProvider; mDeviceState = deviceState; mDeviceStateDescription = deviceStateDescription; - mAutoRotateSettingsManager = DeviceStateRotationLockSettingsManager.getInstance(context); + mAutoRotateSettingsManager = + DeviceStateAutoRotateSettingManagerProvider.getSingletonInstance(context); mOrder = order; } diff --git a/src/com/android/settings/display/DeviceStateAutoRotateSettingManagerProvider.kt b/src/com/android/settings/display/DeviceStateAutoRotateSettingManagerProvider.kt new file mode 100644 index 00000000000..906ffe4b566 --- /dev/null +++ b/src/com/android/settings/display/DeviceStateAutoRotateSettingManagerProvider.kt @@ -0,0 +1,62 @@ +/* + * 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.display + +import android.content.Context +import android.hardware.devicestate.DeviceStateManager +import android.os.Build +import android.os.Handler +import android.os.Looper +import com.android.internal.annotations.VisibleForTesting +import com.android.settingslib.devicestate.AndroidSecureSettings +import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager +import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManagerProvider.createInstance +import com.android.settingslib.devicestate.PosturesHelper +import com.android.settingslib.utils.ThreadUtils +import com.android.window.flags.Flags + +/** + * Provides appropriate instance of [DeviceStateAutoRotateSettingManager], based on the value of + * [Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR]. + */ +object DeviceStateAutoRotateSettingManagerProvider { + private var nullableSingletonSettingManager: DeviceStateAutoRotateSettingManager? = null + + /** + * Provides a singleton instance of [DeviceStateAutoRotateSettingManager], based on the + * value of[Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR]. It is supposed to + * be used by apps that don't support dagger to provide and manager instance. + */ + @JvmStatic + fun getSingletonInstance(context: Context) = + nullableSingletonSettingManager ?: createInstance( + context, + ThreadUtils.getBackgroundExecutor(), + AndroidSecureSettings(context.contentResolver), + Handler(Looper.getMainLooper()), + PosturesHelper(context, context.getSystemService(DeviceStateManager::class.java)) + ).also { + nullableSingletonSettingManager = it + } + + /** Resets the singleton instance of [DeviceStateAutoRotateSettingManager]. */ + @JvmStatic + @VisibleForTesting + fun resetInstance() { + nullableSingletonSettingManager = null + } +} diff --git a/src/com/android/settings/display/DeviceStateAutoRotationHelper.java b/src/com/android/settings/display/DeviceStateAutoRotationHelper.java index 3bf9def2316..7b285248e1c 100644 --- a/src/com/android/settings/display/DeviceStateAutoRotationHelper.java +++ b/src/com/android/settings/display/DeviceStateAutoRotationHelper.java @@ -16,6 +16,8 @@ package com.android.settings.display; +import static com.android.settingslib.devicestate.DeviceStateAutoRotateSettingUtils.isDeviceStateRotationLockEnabled; + import android.content.Context; import android.util.Log; @@ -25,7 +27,6 @@ import com.android.internal.view.RotationPolicy; import com.android.settings.R; import com.android.settings.core.BasePreferenceController; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager; import com.android.settingslib.devicestate.SettableDeviceState; import com.android.settingslib.search.SearchIndexableRaw; @@ -51,8 +52,8 @@ public class DeviceStateAutoRotationHelper { static ImmutableList createPreferenceControllers( Context context) { - List settableDeviceStates = DeviceStateRotationLockSettingsManager - .getInstance(context).getSettableDeviceStates(); + List settableDeviceStates = DeviceStateAutoRotateSettingManagerProvider + .getSingletonInstance(context).getSettableDeviceStates(); int numDeviceStates = settableDeviceStates.size(); if (numDeviceStates == 0) { return ImmutableList.of(); @@ -99,7 +100,7 @@ public class DeviceStateAutoRotationHelper { /** Returns whether the device state based auto-rotation settings are enabled. */ public static boolean isDeviceStateRotationEnabled(Context context) { return RotationPolicy.isRotationLockToggleVisible(context) - && DeviceStateRotationLockSettingsManager.isDeviceStateRotationLockEnabled(context); + && isDeviceStateRotationLockEnabled(context); } /** @@ -108,6 +109,6 @@ public class DeviceStateAutoRotationHelper { */ public static boolean isDeviceStateRotationEnabledForA11y(Context context) { return RotationPolicy.isRotationSupported(context) - && DeviceStateRotationLockSettingsManager.isDeviceStateRotationLockEnabled(context); + && isDeviceStateRotationLockEnabled(context); } } diff --git a/src/com/android/settings/display/SmartAutoRotateController.java b/src/com/android/settings/display/SmartAutoRotateController.java index c99b2f853ca..4bc28ff41e0 100644 --- a/src/com/android/settings/display/SmartAutoRotateController.java +++ b/src/com/android/settings/display/SmartAutoRotateController.java @@ -47,7 +47,6 @@ import com.android.settings.core.TogglePreferenceController; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager; -import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager; /** * SmartAutoRotateController controls whether auto rotation is enabled @@ -75,7 +74,7 @@ public class SmartAutoRotateController extends TogglePreferenceController implem } }; - private final DeviceStateRotationLockSettingsManager mDeviceStateAutoRotateSettingsManager; + private final DeviceStateAutoRotateSettingManager mDeviceStateAutoRotateSettingsManager; private final DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener mDeviceStateAutoRotateSettingListener = () -> updateState(mPreference); private RotationPolicy.RotationPolicyListener mRotationPolicyListener; @@ -85,8 +84,9 @@ public class SmartAutoRotateController extends TogglePreferenceController implem mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(); mPrivacyManager = SensorPrivacyManager.getInstance(context); mPowerManager = context.getSystemService(PowerManager.class); - mDeviceStateAutoRotateSettingsManager = DeviceStateRotationLockSettingsManager.getInstance( - context); + mDeviceStateAutoRotateSettingsManager = + DeviceStateAutoRotateSettingManagerProvider.getSingletonInstance( + context); } @Override diff --git a/src/com/android/settings/network/telephony/satellite/SatelliteSettingsPreferenceCategoryController.java b/src/com/android/settings/network/telephony/satellite/SatelliteSettingsPreferenceCategoryController.java index f2fb347852c..d0cb1bcc5ca 100644 --- a/src/com/android/settings/network/telephony/satellite/SatelliteSettingsPreferenceCategoryController.java +++ b/src/com/android/settings/network/telephony/satellite/SatelliteSettingsPreferenceCategoryController.java @@ -159,6 +159,7 @@ public class SatelliteSettingsPreferenceCategoryController @Override public void onResult(Boolean result) { mIsSatelliteSupported.set(result); + Log.d(TAG, "Satellite requestIsSupported : " + result); SatelliteSettingsPreferenceCategoryController.this.displayPreference(); } }); diff --git a/src/com/android/settings/supervision/SupervisionSafeSearchPreference.kt b/src/com/android/settings/supervision/SupervisionSafeSearchPreference.kt new file mode 100644 index 00000000000..617a3451207 --- /dev/null +++ b/src/com/android/settings/supervision/SupervisionSafeSearchPreference.kt @@ -0,0 +1,106 @@ +/* + * 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.supervision + +import android.content.Context +import androidx.preference.Preference +import com.android.settings.R +import com.android.settingslib.datastore.KeyValueStore +import com.android.settingslib.datastore.Permissions +import com.android.settingslib.datastore.SettingsSecureStore +import com.android.settingslib.metadata.BooleanValuePreference +import com.android.settingslib.metadata.PreferenceMetadata +import com.android.settingslib.metadata.ReadWritePermit +import com.android.settingslib.metadata.SensitivityLevel +import com.android.settingslib.preference.PreferenceBinding +import com.android.settingslib.preference.forEachRecursively +import com.android.settingslib.widget.SelectorWithWidgetPreference + +/** Base class of web content filters SafeSearch preferences. */ +sealed class SupervisionSafeSearchPreference : + BooleanValuePreference, SelectorWithWidgetPreference.OnClickListener, PreferenceBinding { + override fun storage(context: Context): KeyValueStore = SettingsSecureStore.get(context) + + override fun getReadPermissions(context: Context) = Permissions.EMPTY + + override fun getWritePermissions(context: Context) = Permissions.EMPTY + + override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) = + ReadWritePermit.ALLOW + + override fun getWritePermit( + context: Context, + value: Boolean?, + callingPid: Int, + callingUid: Int, + ) = ReadWritePermit.DISALLOW + + override val sensitivityLevel + get() = SensitivityLevel.NO_SENSITIVITY + + override fun createWidget(context: Context) = SelectorWithWidgetPreference(context) + + override fun onRadioButtonClicked(emiter: SelectorWithWidgetPreference) { + emiter.parent?.forEachRecursively { + if (it is SelectorWithWidgetPreference) { + it.isChecked = it == emiter + } + } + } + + override fun bind(preference: Preference, metadata: PreferenceMetadata) { + super.bind(preference, metadata) + (preference as SelectorWithWidgetPreference).also { + // TODO(b/401568995): Set the isChecked value using stored values. + it.isChecked = (it.key == SupervisionSearchFilterOffPreference.KEY) + it.setOnClickListener(this) + } + } +} + +/** The SafeSearch filter on preference. */ +class SupervisionSearchFilterOnPreference : SupervisionSafeSearchPreference() { + + override val key + get() = KEY + + override val title + get() = R.string.supervision_web_content_filters_search_filter_on_title + + override val summary + get() = R.string.supervision_web_content_filters_search_filter_on_summary + + companion object { + const val KEY = "web_content_filters_search_filter_on" + } +} + +/** The SafeSearch filter off preference. */ +class SupervisionSearchFilterOffPreference : SupervisionSafeSearchPreference() { + + override val key + get() = KEY + + override val title + get() = R.string.supervision_web_content_filters_search_filter_off_title + + override val summary + get() = R.string.supervision_web_content_filters_search_filter_off_summary + + companion object { + const val KEY = "web_content_filters_search_filter_off" + } +} diff --git a/src/com/android/settings/supervision/SupervisionSafeSitesDataStore.kt b/src/com/android/settings/supervision/SupervisionSafeSitesDataStore.kt new file mode 100644 index 00000000000..4f283b8c22c --- /dev/null +++ b/src/com/android/settings/supervision/SupervisionSafeSitesDataStore.kt @@ -0,0 +1,75 @@ +/* + * 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.supervision + +import android.content.Context +import android.provider.Settings.Secure.BROWSER_CONTENT_FILTERS_ENABLED +import com.android.settingslib.datastore.AbstractKeyedDataObservable +import com.android.settingslib.datastore.HandlerExecutor +import com.android.settingslib.datastore.KeyValueStore +import com.android.settingslib.datastore.KeyedObserver +import com.android.settingslib.datastore.SettingsSecureStore +import com.android.settingslib.datastore.SettingsStore + +/** Datastore of the safe sites preference. */ +@Suppress("UNCHECKED_CAST") +class SupervisionSafeSitesDataStore( + private val context: Context, + private val settingsStore: SettingsStore = SettingsSecureStore.get(context), +) : AbstractKeyedDataObservable(), KeyedObserver, KeyValueStore { + + override fun contains(key: String) = + key == SupervisionBlockExplicitSitesPreference.KEY || + key == SupervisionAllowAllSitesPreference.KEY + + override fun getValue(key: String, valueType: Class): T? { + val settingValue = (settingsStore.getBoolean(BROWSER_CONTENT_FILTERS_ENABLED) == true) + return when (key) { + SupervisionAllowAllSitesPreference.KEY -> !settingValue + + SupervisionBlockExplicitSitesPreference.KEY -> settingValue + + else -> null + } + as T? + } + + override fun setValue(key: String, valueType: Class, value: T?) { + if (value !is Boolean) return + when (key) { + SupervisionAllowAllSitesPreference.KEY -> + settingsStore.setBoolean(BROWSER_CONTENT_FILTERS_ENABLED, !value) + + SupervisionBlockExplicitSitesPreference.KEY -> + settingsStore.setBoolean(BROWSER_CONTENT_FILTERS_ENABLED, value) + } + } + + override fun onFirstObserverAdded() { + // observe the underlying storage key + settingsStore.addObserver(BROWSER_CONTENT_FILTERS_ENABLED, this, HandlerExecutor.main) + } + + override fun onKeyChanged(key: String, reason: Int) { + // forward data change to preference hierarchy key + notifyChange(SupervisionBlockExplicitSitesPreference.KEY, reason) + notifyChange(SupervisionAllowAllSitesPreference.KEY, reason) + } + + override fun onLastObserverRemoved() { + settingsStore.removeObserver(BROWSER_CONTENT_FILTERS_ENABLED, this) + } +} diff --git a/src/com/android/settings/supervision/SupervisionSafeSitesPreference.kt b/src/com/android/settings/supervision/SupervisionSafeSitesPreference.kt index aaa5a333a86..d78afb299ac 100644 --- a/src/com/android/settings/supervision/SupervisionSafeSitesPreference.kt +++ b/src/com/android/settings/supervision/SupervisionSafeSitesPreference.kt @@ -18,9 +18,7 @@ package com.android.settings.supervision import android.content.Context import androidx.preference.Preference import com.android.settings.R -import com.android.settingslib.datastore.KeyValueStore import com.android.settingslib.datastore.Permissions -import com.android.settingslib.datastore.SettingsSecureStore import com.android.settingslib.metadata.BooleanValuePreference import com.android.settingslib.metadata.PreferenceMetadata import com.android.settingslib.metadata.ReadWritePermit @@ -30,9 +28,10 @@ import com.android.settingslib.preference.forEachRecursively import com.android.settingslib.widget.SelectorWithWidgetPreference /** Base class of web content filters Safe sites preferences. */ -sealed class SupervisionSafeSitesPreference : - BooleanValuePreference, SelectorWithWidgetPreference.OnClickListener, PreferenceBinding { - override fun storage(context: Context): KeyValueStore = SettingsSecureStore.get(context) +sealed class SupervisionSafeSitesPreference( + protected val dataStore: SupervisionSafeSitesDataStore +) : BooleanValuePreference, SelectorWithWidgetPreference.OnClickListener, PreferenceBinding { + override fun storage(context: Context) = dataStore override fun getReadPermissions(context: Context) = Permissions.EMPTY @@ -64,15 +63,15 @@ sealed class SupervisionSafeSitesPreference : override fun bind(preference: Preference, metadata: PreferenceMetadata) { super.bind(preference, metadata) (preference as SelectorWithWidgetPreference).also { - // TODO(b/401568468): Set the isChecked value using stored values. - it.isChecked = (it.key == SupervisionAllowAllSitesPreference.KEY) + it.isChecked = (dataStore.getBoolean(it.key) == true) it.setOnClickListener(this) } } } /** The "Try to block explicit sites" preference. */ -class SupervisionBlockExplicitSitesPreference : SupervisionSafeSitesPreference() { +class SupervisionBlockExplicitSitesPreference(dataStore: SupervisionSafeSitesDataStore) : + SupervisionSafeSitesPreference(dataStore) { override val key get() = KEY @@ -89,7 +88,8 @@ class SupervisionBlockExplicitSitesPreference : SupervisionSafeSitesPreference() } /** The "Allow all sites" preference. */ -class SupervisionAllowAllSitesPreference : SupervisionSafeSitesPreference() { +class SupervisionAllowAllSitesPreference(dataStore: SupervisionSafeSitesDataStore) : + SupervisionSafeSitesPreference(dataStore) { override val key get() = KEY diff --git a/src/com/android/settings/supervision/SupervisionWebContentFiltersScreen.kt b/src/com/android/settings/supervision/SupervisionWebContentFiltersScreen.kt index 0a2891b5c5e..994165a8b36 100644 --- a/src/com/android/settings/supervision/SupervisionWebContentFiltersScreen.kt +++ b/src/com/android/settings/supervision/SupervisionWebContentFiltersScreen.kt @@ -47,14 +47,23 @@ class SupervisionWebContentFiltersScreen : PreferenceScreenCreator { R.string.supervision_web_content_filters_browser_title, ) += { - +SupervisionBlockExplicitSitesPreference() - +SupervisionAllowAllSitesPreference() + val dataStore = SupervisionSafeSitesDataStore(context) + +SupervisionBlockExplicitSitesPreference(dataStore) + +SupervisionAllowAllSitesPreference(dataStore) + } + +PreferenceCategory( + SEARCH_RADIO_BUTTON_GROUP, + R.string.supervision_web_content_filters_search_title, + ) += + { + +SupervisionSearchFilterOnPreference() + +SupervisionSearchFilterOffPreference() } - // TODO(b/401569571) implement the SafeSearch group. } companion object { const val KEY = "supervision_web_content_filters" internal const val BROWSER_RADIO_BUTTON_GROUP = "browser_radio_button_group" + internal const val SEARCH_RADIO_BUTTON_GROUP = "search_radio_button_group" } } diff --git a/tests/robotests/src/com/android/settings/accessibility/LockScreenRotationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/LockScreenRotationPreferenceControllerTest.java index c98ad3df5a3..88623ac6889 100644 --- a/tests/robotests/src/com/android/settings/accessibility/LockScreenRotationPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/LockScreenRotationPreferenceControllerTest.java @@ -16,9 +16,14 @@ package com.android.settings.accessibility; +import static com.android.settings.testutils.DeviceStateAutoRotateSettingTestUtils.setDeviceStateRotationLockEnabled; + import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.when; + import android.content.Context; +import android.content.res.Resources; import android.os.UserHandle; import android.provider.Settings; @@ -26,12 +31,14 @@ import androidx.preference.SwitchPreference; import com.android.internal.view.RotationPolicy; import com.android.settings.core.BasePreferenceController; -import com.android.settings.testutils.shadow.ShadowDeviceStateRotationLockSettingsManager; import com.android.settings.testutils.shadow.ShadowRotationPolicy; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; @@ -41,48 +48,45 @@ import org.robolectric.annotation.Config; com.android.settings.testutils.shadow.ShadowSystemSettings.class, }) public class LockScreenRotationPreferenceControllerTest { - + @Mock + private Resources mResources; private Context mContext; private SwitchPreference mPreference; private LockScreenRotationPreferenceController mController; @Before public void setUp() { - mContext = RuntimeEnvironment.application; + MockitoAnnotations.initMocks(this); + mContext = Mockito.spy(RuntimeEnvironment.application); mPreference = new SwitchPreference(mContext); + when(mContext.getResources()).thenReturn(mResources); + mController = new LockScreenRotationPreferenceController(mContext, "lock_screen"); } @Test - @Config(shadows = { - ShadowRotationPolicy.class, - ShadowDeviceStateRotationLockSettingsManager.class - }) + @Config(shadows = {ShadowRotationPolicy.class}) public void getAvailabilityStatus_supportedRotation_shouldReturnAvailable() { ShadowRotationPolicy.setRotationSupported(true /* supported */); + setDeviceStateRotationLockEnabled(false, mResources); assertThat(mController.getAvailabilityStatus()).isEqualTo( BasePreferenceController.AVAILABLE); } @Test - @Config(shadows = { - ShadowRotationPolicy.class, - ShadowDeviceStateRotationLockSettingsManager.class - }) + @Config(shadows = {ShadowRotationPolicy.class}) public void getAvailabilityStatus_deviceStateRotationEnabled_returnsUnsupported() { ShadowRotationPolicy.setRotationSupported(true /* supported */); - ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(true); + setDeviceStateRotationLockEnabled(true, mResources); assertThat(mController.getAvailabilityStatus()).isEqualTo( BasePreferenceController.UNSUPPORTED_ON_DEVICE); } @Test - @Config(shadows = { - ShadowRotationPolicy.class, - ShadowDeviceStateRotationLockSettingsManager.class - }) public void getAvailabilityStatus_unsupportedRotation_shouldReturnUnsupportedOnDevice() { + @Config(shadows = {ShadowRotationPolicy.class}) + public void getAvailabilityStatus_unsupportedRotation_shouldReturnUnsupportedOnDevice() { ShadowRotationPolicy.setRotationSupported(false /* supported */); assertThat(mController.getAvailabilityStatus()).isEqualTo( diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothKeyMissingReceiverTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothKeyMissingReceiverTest.java index a183d8d7e68..42d7105c6f3 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothKeyMissingReceiverTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothKeyMissingReceiverTest.java @@ -127,6 +127,7 @@ public class BluetoothKeyMissingReceiverTest { public void broadcastReceiver_background_showNotification() { Intent intent = spy(new Intent(BluetoothDevice.ACTION_KEY_MISSING)); when(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)).thenReturn(mBluetoothDevice); + when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); BluetoothKeyMissingReceiver bluetoothKeyMissingReceiver = getReceiver(intent); bluetoothKeyMissingReceiver.onReceive(mContext, intent); @@ -141,6 +142,7 @@ public class BluetoothKeyMissingReceiverTest { when(mLocalBtManager.isForegroundActivity()).thenReturn(true); Intent intent = spy(new Intent(BluetoothDevice.ACTION_KEY_MISSING)); when(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)).thenReturn(mBluetoothDevice); + when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); BluetoothKeyMissingReceiver bluetoothKeyMissingReceiver = getReceiver(intent); bluetoothKeyMissingReceiver.onReceive(mContext, intent); diff --git a/tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java b/tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java index 8859ebd97e5..176bfa8f573 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/UtilsTest.java @@ -18,22 +18,29 @@ package com.android.settings.bluetooth; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.bluetooth.BluetoothCsipSetCoordinator; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeBroadcastReceiveState; import android.content.Context; +import android.os.SystemProperties; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.testutils.FakeFeatureFactory; -import com.android.settings.testutils.shadow.ShadowBluetoothUtils; +import com.android.settingslib.bluetooth.A2dpProfile; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; +import com.android.settingslib.bluetooth.HeadsetProfile; +import com.android.settingslib.bluetooth.HearingAidProfile; +import com.android.settingslib.bluetooth.LeAudioProfile; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; import com.android.settingslib.bluetooth.LocalBluetoothManager; @@ -41,8 +48,8 @@ import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; -import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -52,10 +59,8 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; @RunWith(RobolectricTestRunner.class) -@Config(shadows = {ShadowBluetoothUtils.class}) public class UtilsTest { private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25; private static final String TEMP_BOND_METADATA = @@ -73,6 +78,14 @@ public class UtilsTest { @Mock private LocalBluetoothLeBroadcastAssistant mAssistant; @Mock + private A2dpProfile mA2dpProfile; + @Mock + private HeadsetProfile mHeadsetProfile; + @Mock + private LeAudioProfile mLeAudioProfile; + @Mock + private HearingAidProfile mHearingAidProfile; + @Mock private CachedBluetoothDeviceManager mDeviceManager; private MetricsFeatureProvider mMetricsFeatureProvider; @@ -80,17 +93,14 @@ public class UtilsTest { @Before public void setUp() { mMetricsFeatureProvider = FakeFeatureFactory.setupForTest().getMetricsFeatureProvider(); - ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager; - mLocalBtManager = Utils.getLocalBtManager(mContext); when(mLocalBtManager.getProfileManager()).thenReturn(mProfileManager); when(mLocalBtManager.getCachedDeviceManager()).thenReturn(mDeviceManager); when(mProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast); when(mProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant); - } - - @After - public void tearDown() { - ShadowBluetoothUtils.reset(); + when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile); + when(mProfileManager.getHeadsetProfile()).thenReturn(mHeadsetProfile); + when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile); + when(mProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile); } @Test @@ -170,4 +180,148 @@ public class UtilsTest { when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(state)); assertThat(Utils.shouldBlockPairingInAudioSharing(mLocalBtManager)).isTrue(); } + + @Test + public void enableLeAudioProfile_multipleDeviceInGroup() { + CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class); + CachedBluetoothDevice cachedDevice2 = mock(CachedBluetoothDevice.class); + CachedBluetoothDevice cachedDevice3 = mock(CachedBluetoothDevice.class); + BluetoothDevice device1 = mock(BluetoothDevice.class); + BluetoothDevice device2 = mock(BluetoothDevice.class); + BluetoothDevice device3 = mock(BluetoothDevice.class); + when(cachedDevice1.getDevice()).thenReturn(device1); + when(cachedDevice2.getDevice()).thenReturn(device2); + when(cachedDevice3.getDevice()).thenReturn(device3); + when(cachedDevice1.getMemberDevice()).thenReturn(ImmutableSet.of(cachedDevice2)); + when(mDeviceManager.getCachedDevicesCopy()) + .thenReturn(ImmutableList.of(cachedDevice1, cachedDevice3)); + when(cachedDevice1.getGroupId()).thenReturn(1); + when(cachedDevice2.getGroupId()).thenReturn(1); + when(cachedDevice3.getGroupId()).thenReturn(2); + when(cachedDevice1.getProfiles()) + .thenReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile, mLeAudioProfile)); + when(cachedDevice2.getProfiles()).thenReturn(ImmutableList.of(mLeAudioProfile)); + when(cachedDevice3.getProfiles()) + .thenReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile, mLeAudioProfile)); + + Utils.setLeAudioEnabled(mLocalBtManager, cachedDevice2, true); + + verify(mLeAudioProfile).setEnabled(device1, true); + verify(mLeAudioProfile).setEnabled(device2, true); + verify(mHearingAidProfile).setEnabled(device1, false); + verify(mAssistant).setEnabled(device1, true); + verify(mLeAudioProfile, never()).setEnabled(eq(device3), anyBoolean()); + verify(mA2dpProfile, never()).setEnabled(eq(device3), anyBoolean()); + verify(mHeadsetProfile, never()).setEnabled(eq(device3), anyBoolean()); + } + + @Test + public void enableLeAudioProfile_dualModeEnabled_a2dpAndHfpNotChanged() { + SystemProperties.set("persist.bluetooth.enable_dual_mode_audio", "true"); + CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class); + BluetoothDevice device1 = mock(BluetoothDevice.class); + when(cachedDevice1.getDevice()).thenReturn(device1); + when(cachedDevice1.getGroupId()).thenReturn(BluetoothCsipSetCoordinator.GROUP_ID_INVALID); + when(cachedDevice1.getProfiles()) + .thenReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile, mLeAudioProfile)); + when(mA2dpProfile.isEnabled(device1)).thenReturn(true); + when(mHeadsetProfile.isEnabled(device1)).thenReturn(true); + + Utils.setLeAudioEnabled(mLocalBtManager, cachedDevice1, true); + + verify(mLeAudioProfile).setEnabled(device1, true); + verify(mA2dpProfile, never()).setEnabled(device1, false); + verify(mHeadsetProfile, never()).setEnabled(device1, false); + } + + @Test + public void enableLeAudioProfile_dualModeDisabled_disableA2dpAndHfp() { + SystemProperties.set("persist.bluetooth.enable_dual_mode_audio", "false"); + CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class); + BluetoothDevice device1 = mock(BluetoothDevice.class); + when(cachedDevice1.getDevice()).thenReturn(device1); + when(cachedDevice1.getGroupId()).thenReturn(BluetoothCsipSetCoordinator.GROUP_ID_INVALID); + when(cachedDevice1.getProfiles()) + .thenReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile, mLeAudioProfile)); + when(mA2dpProfile.isEnabled(device1)).thenReturn(true); + when(mHeadsetProfile.isEnabled(device1)).thenReturn(true); + + Utils.setLeAudioEnabled(mLocalBtManager, cachedDevice1, true); + + verify(mLeAudioProfile).setEnabled(device1, true); + verify(mA2dpProfile).setEnabled(device1, false); + verify(mHeadsetProfile).setEnabled(device1, false); + } + + @Test + public void disableLeAudioProfile_multipleDeviceInGroup() { + CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class); + CachedBluetoothDevice cachedDevice2 = mock(CachedBluetoothDevice.class); + CachedBluetoothDevice cachedDevice3 = mock(CachedBluetoothDevice.class); + BluetoothDevice device1 = mock(BluetoothDevice.class); + BluetoothDevice device2 = mock(BluetoothDevice.class); + BluetoothDevice device3 = mock(BluetoothDevice.class); + when(cachedDevice1.getDevice()).thenReturn(device1); + when(cachedDevice2.getDevice()).thenReturn(device2); + when(cachedDevice3.getDevice()).thenReturn(device3); + when(cachedDevice1.getMemberDevice()).thenReturn(ImmutableSet.of(cachedDevice2)); + when(mDeviceManager.getCachedDevicesCopy()) + .thenReturn(ImmutableList.of(cachedDevice1, cachedDevice3)); + when(cachedDevice1.getGroupId()).thenReturn(1); + when(cachedDevice2.getGroupId()).thenReturn(1); + when(cachedDevice3.getGroupId()).thenReturn(2); + when(cachedDevice1.getProfiles()) + .thenReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile, mLeAudioProfile)); + when(cachedDevice2.getProfiles()).thenReturn(ImmutableList.of(mLeAudioProfile)); + when(cachedDevice3.getProfiles()) + .thenReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile, mLeAudioProfile)); + + Utils.setLeAudioEnabled(mLocalBtManager, cachedDevice2, false); + + verify(mLeAudioProfile).setEnabled(device1, false); + verify(mLeAudioProfile).setEnabled(device2, false); + verify(mHearingAidProfile).setEnabled(device1, true); + verify(mAssistant).setEnabled(device1, false); + verify(mLeAudioProfile, never()).setEnabled(eq(device3), anyBoolean()); + verify(mA2dpProfile, never()).setEnabled(eq(device3), anyBoolean()); + verify(mHeadsetProfile, never()).setEnabled(eq(device3), anyBoolean()); + } + + @Test + public void disableLeAudioProfile_dualModeEnabled_a2dpAndHfpNotChanged() { + SystemProperties.set("persist.bluetooth.enable_dual_mode_audio", "true"); + CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class); + BluetoothDevice device1 = mock(BluetoothDevice.class); + when(cachedDevice1.getDevice()).thenReturn(device1); + when(cachedDevice1.getGroupId()).thenReturn(BluetoothCsipSetCoordinator.GROUP_ID_INVALID); + when(cachedDevice1.getProfiles()) + .thenReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile, mLeAudioProfile)); + when(mA2dpProfile.isEnabled(device1)).thenReturn(false); + when(mHeadsetProfile.isEnabled(device1)).thenReturn(false); + + Utils.setLeAudioEnabled(mLocalBtManager, cachedDevice1, false); + + verify(mLeAudioProfile).setEnabled(device1, false); + verify(mA2dpProfile, never()).setEnabled(device1, true); + verify(mHeadsetProfile, never()).setEnabled(device1, true); + } + + @Test + public void disableLeAudioProfile_dualModeDisabled_enableA2dpAndHfp() { + SystemProperties.set("persist.bluetooth.enable_dual_mode_audio", "false"); + CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class); + BluetoothDevice device1 = mock(BluetoothDevice.class); + when(cachedDevice1.getDevice()).thenReturn(device1); + when(cachedDevice1.getGroupId()).thenReturn(BluetoothCsipSetCoordinator.GROUP_ID_INVALID); + when(cachedDevice1.getProfiles()) + .thenReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile, mLeAudioProfile)); + when(mA2dpProfile.isEnabled(device1)).thenReturn(false); + when(mHeadsetProfile.isEnabled(device1)).thenReturn(false); + + Utils.setLeAudioEnabled(mLocalBtManager, cachedDevice1, false); + + verify(mLeAudioProfile).setEnabled(device1, false); + verify(mA2dpProfile).setEnabled(device1, true); + verify(mHeadsetProfile).setEnabled(device1, true); + } } diff --git a/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragmentTest.java b/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragmentTest.java index d1c32a2281f..6fce9a83390 100644 --- a/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragmentTest.java +++ b/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragmentTest.java @@ -39,7 +39,6 @@ import android.hardware.devicestate.DeviceStateManager; import com.android.settings.R; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager; import org.junit.Before; import org.junit.Test; @@ -144,16 +143,15 @@ public class DeviceStateAutoRotateDetailsFragmentTest { } private void enableDeviceStateSettableRotationStates(String[] settableStates, - String[] settableStatesDescriptions) { + String[] settableStatesDescriptions) { when(mResources.getStringArray( com.android.internal.R.array.config_perDeviceStateRotationLockDefaults)).thenReturn( settableStates); when(mResources.getStringArray( R.array.config_settableAutoRotationDeviceStatesDescriptions)).thenReturn( settableStatesDescriptions); - DeviceStateRotationLockSettingsManager.resetInstance(); - DeviceStateRotationLockSettingsManager.getInstance(mContext) - .resetStateForTesting(mResources); + DeviceStateAutoRotateSettingManagerProvider.resetInstance(); + when(mContext.getResources()).thenReturn(mResources); } // Sets up posture mappings for PosturesHelper diff --git a/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateOverviewControllerTest.java b/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateOverviewControllerTest.java index a5416e70413..4c2c694a4bd 100644 --- a/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateOverviewControllerTest.java +++ b/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateOverviewControllerTest.java @@ -18,30 +18,48 @@ package com.android.settings.display; import static com.android.settings.core.BasePreferenceController.AVAILABLE; import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE; +import static com.android.settings.testutils.DeviceStateAutoRotateSettingTestUtils.setDeviceStateRotationLockEnabled; import static com.google.common.truth.Truth.assertThat; -import com.android.settings.testutils.shadow.ShadowDeviceStateRotationLockSettingsManager; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.res.Resources; + import com.android.settings.testutils.shadow.ShadowRotationPolicy; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; @RunWith(RobolectricTestRunner.class) -@Config(shadows = {ShadowRotationPolicy.class, ShadowDeviceStateRotationLockSettingsManager.class}) +@Config(shadows = {ShadowRotationPolicy.class}) public class DeviceStateAutoRotateOverviewControllerTest { + @Mock + private Resources mResources; + private DeviceStateAutoRotateOverviewController mController; - private final DeviceStateAutoRotateOverviewController mController = - new DeviceStateAutoRotateOverviewController( - RuntimeEnvironment.application, "device_state_auto_rotate"); + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + Context context = Mockito.spy(RuntimeEnvironment.application); + when(context.getResources()).thenReturn(mResources); + + mController = new DeviceStateAutoRotateOverviewController( + context, "device_state_auto_rotate"); + } @Test public void getAvailabilityStatus_rotationAndDeviceStateRotationEnabled_returnsAvailable() { ShadowRotationPolicy.setRotationSupported(true); - ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(true); + setDeviceStateRotationLockEnabled(true, mResources); int availability = mController.getAvailabilityStatus(); @@ -51,7 +69,7 @@ public class DeviceStateAutoRotateOverviewControllerTest { @Test public void getAvailabilityStatus_rotationNotSupported_returnsUnsupportedOnDevice() { ShadowRotationPolicy.setRotationSupported(false); - ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(true); + setDeviceStateRotationLockEnabled(true, mResources); int availability = mController.getAvailabilityStatus(); @@ -61,7 +79,7 @@ public class DeviceStateAutoRotateOverviewControllerTest { @Test public void getAvailabilityStatus_deviceStateRotationNotSupported_returnsUnsupportedOnDevice() { ShadowRotationPolicy.setRotationSupported(true); - ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(false); + setDeviceStateRotationLockEnabled(false, mResources); int availability = mController.getAvailabilityStatus(); diff --git a/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateSettingControllerTest.java b/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateSettingControllerTest.java index cb1be85f881..63a4af21e91 100644 --- a/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateSettingControllerTest.java +++ b/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateSettingControllerTest.java @@ -18,14 +18,17 @@ package com.android.settings.display; import static com.android.settings.core.BasePreferenceController.AVAILABLE; import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE; +import static com.android.settings.testutils.DeviceStateAutoRotateSettingTestUtils.setDeviceStateRotationLockEnabled; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.app.settings.SettingsEnums; import android.content.Context; +import android.content.res.Resources; import android.hardware.devicestate.DeviceState; import android.hardware.devicestate.DeviceStateManager; @@ -34,10 +37,9 @@ import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; import com.android.settings.R; -import com.android.settings.testutils.shadow.ShadowDeviceStateRotationLockSettingsManager; import com.android.settings.testutils.shadow.ShadowRotationPolicy; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; -import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager; +import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager; import com.android.settingslib.search.SearchIndexableRaw; import org.junit.Before; @@ -54,10 +56,7 @@ import java.util.ArrayList; import java.util.List; @RunWith(RobolectricTestRunner.class) -@Config(shadows = { - ShadowRotationPolicy.class, - ShadowDeviceStateRotationLockSettingsManager.class -}) +@Config(shadows = {ShadowRotationPolicy.class}) public class DeviceStateAutoRotateSettingControllerTest { private static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState( @@ -66,10 +65,11 @@ public class DeviceStateAutoRotateSettingControllerTest { private static final int DEFAULT_ORDER = -10; private final Context mContext = Mockito.spy(RuntimeEnvironment.application); - private DeviceStateRotationLockSettingsManager mAutoRotateSettingsManager; + private DeviceStateAutoRotateSettingManager mAutoRotateSettingsManager; @Mock private MetricsFeatureProvider mMetricsFeatureProvider; @Mock private DeviceStateManager mDeviceStateManager; + @Mock private Resources mResources; private DeviceStateAutoRotateSettingController mController; @@ -78,11 +78,14 @@ public class DeviceStateAutoRotateSettingControllerTest { MockitoAnnotations.initMocks(this); doReturn(mContext).when(mContext).getApplicationContext(); + when(mContext.getResources()).thenReturn(mResources); doReturn(mDeviceStateManager).when(mContext).getSystemService(DeviceStateManager.class); doReturn(List.of(DEFAULT_DEVICE_STATE)).when( mDeviceStateManager).getSupportedDeviceStates(); + setDeviceStateRotationLockEnabled(false, mResources); mAutoRotateSettingsManager = - DeviceStateRotationLockSettingsManager.getInstance(mContext); + DeviceStateAutoRotateSettingManagerProvider.getSingletonInstance(mContext); + mController = new DeviceStateAutoRotateSettingController( mContext, DEFAULT_DEVICE_STATE.getIdentifier(), @@ -108,7 +111,7 @@ public class DeviceStateAutoRotateSettingControllerTest { @Test public void getAvailabilityStatus_rotationAndDeviceStateRotationEnabled_returnsAvailable() { ShadowRotationPolicy.setRotationSupported(true); - ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(true); + setDeviceStateRotationLockEnabled(true, mResources); int availability = mController.getAvailabilityStatus(); @@ -118,7 +121,7 @@ public class DeviceStateAutoRotateSettingControllerTest { @Test public void getAvailabilityStatus_deviceStateRotationDisabled_returnsUnsupported() { ShadowRotationPolicy.setRotationSupported(true); - ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(false); + setDeviceStateRotationLockEnabled(false, mResources); int availability = mController.getAvailabilityStatus(); @@ -128,7 +131,7 @@ public class DeviceStateAutoRotateSettingControllerTest { @Test public void getAvailabilityStatus_rotationDisabled_returnsUnsupported() { ShadowRotationPolicy.setRotationSupported(false); - ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(true); + setDeviceStateRotationLockEnabled(true, mResources); int availability = mController.getAvailabilityStatus(); diff --git a/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateSettingManagerProviderTest.kt b/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateSettingManagerProviderTest.kt new file mode 100644 index 00000000000..f2e59c5a005 --- /dev/null +++ b/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateSettingManagerProviderTest.kt @@ -0,0 +1,111 @@ +/* + * 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.display; + +import android.content.Context +import android.content.res.Resources +import android.hardware.devicestate.DeviceStateManager +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags +import android.platform.test.flag.junit.SetFlagsRule +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.internal.R +import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManagerImpl +import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager +import com.android.window.flags.Flags +import com.google.common.truth.Truth.assertThat +import org.junit.After +import org.junit.Before +import org.junit.Assert.assertNotSame +import org.junit.Assert.assertSame +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.junit.MockitoJUnit +import org.mockito.Mockito.`when` as whenever + +@SmallTest +@RunWith(AndroidJUnit4::class) +class DeviceStateAutoRotateSettingManagerProviderTest { + + @get:Rule + val setFlagsRule: SetFlagsRule = SetFlagsRule() + @get:Rule + val rule = MockitoJUnit.rule() + + @Mock + private lateinit var mockContext: Context + @Mock + private lateinit var mockDeviceStateManager: DeviceStateManager + @Mock + private lateinit var mockResources: Resources + + private val context: Context = ApplicationProvider.getApplicationContext() + + @Before + fun setup() { + whenever(mockContext.contentResolver).thenReturn(context.contentResolver) + whenever(mockContext.getSystemService(DeviceStateManager::class.java)).thenReturn( + mockDeviceStateManager + ) + whenever(mockContext.resources).thenReturn(mockResources) + whenever(mockResources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults)) + .thenReturn(arrayOf()) + } + + @After + fun tearDown() { + DeviceStateAutoRotateSettingManagerProvider.resetInstance() + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR) + fun getSingletonInstance_refactorFlagEnabled_returnsRefactoredManager() { + val manager = DeviceStateAutoRotateSettingManagerProvider.getSingletonInstance(mockContext) + + assertThat(manager).isInstanceOf(DeviceStateAutoRotateSettingManagerImpl::class.java) + } + + @Test + @DisableFlags(Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR) + fun getSingletonInstance_refactorFlagDisabled_returnsLegacyManager() { + val manager = DeviceStateAutoRotateSettingManagerProvider.getSingletonInstance(mockContext) + + assertThat(manager).isInstanceOf(DeviceStateRotationLockSettingsManager::class.java) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR) + fun getSingletonInstance_resetInstance_returnsNewInstance() { + val manager1 = DeviceStateAutoRotateSettingManagerProvider.getSingletonInstance(mockContext) + DeviceStateAutoRotateSettingManagerProvider.resetInstance() + val manager2 = DeviceStateAutoRotateSettingManagerProvider.getSingletonInstance(mockContext) + + assertNotSame(manager1, manager2) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR) + fun getSingletonInstance_getInstanceTwice_returnsSameInstance() { + val manager1 = DeviceStateAutoRotateSettingManagerProvider.getSingletonInstance(mockContext) + val manager2 = DeviceStateAutoRotateSettingManagerProvider.getSingletonInstance(mockContext) + + assertSame(manager1, manager2) + } +} diff --git a/tests/robotests/src/com/android/settings/display/SmartAutoRotateControllerTest.java b/tests/robotests/src/com/android/settings/display/SmartAutoRotateControllerTest.java index e2542b0d55b..a1eb89c249a 100644 --- a/tests/robotests/src/com/android/settings/display/SmartAutoRotateControllerTest.java +++ b/tests/robotests/src/com/android/settings/display/SmartAutoRotateControllerTest.java @@ -19,6 +19,7 @@ package com.android.settings.display; import static com.android.settings.core.BasePreferenceController.AVAILABLE; import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING; import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE; +import static com.android.settings.testutils.DeviceStateAutoRotateSettingTestUtils.setDeviceStateRotationLockEnabled; import static com.google.common.truth.Truth.assertThat; @@ -33,6 +34,7 @@ import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; +import android.content.res.Resources; import android.hardware.devicestate.DeviceState; import android.hardware.devicestate.DeviceStateManager; import android.os.UserHandle; @@ -41,11 +43,11 @@ import android.provider.Settings; import androidx.preference.Preference; import com.android.settings.testutils.ResolveInfoBuilder; -import com.android.settings.testutils.shadow.ShadowDeviceStateRotationLockSettingsManager; +import com.android.settings.testutils.shadow.ShadowDeviceStateAutoRotateSettingManager; import com.android.settings.testutils.shadow.ShadowRotationPolicy; import com.android.settings.testutils.shadow.ShadowSensorPrivacyManager; import com.android.settings.testutils.shadow.ShadowSystemSettings; -import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager; +import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager; import org.junit.Before; import org.junit.Test; @@ -73,17 +75,21 @@ public class SmartAutoRotateControllerTest { private Preference mPreference; @Mock private DeviceStateManager mDeviceStateManager; + @Mock + private Resources mResources; private ContentResolver mContentResolver; - private DeviceStateRotationLockSettingsManager mDeviceStateAutoRotateSettingsManager; + private DeviceStateAutoRotateSettingManager mDeviceStateAutoRotateSettingManager; @Before public void setUp() { MockitoAnnotations.initMocks(this); final Context context = Mockito.spy(RuntimeEnvironment.application); mContentResolver = RuntimeEnvironment.application.getContentResolver(); + mResources = Mockito.spy(RuntimeEnvironment.application.getResources()); when(context.getPackageManager()).thenReturn(mPackageManager); when(context.getContentResolver()).thenReturn(mContentResolver); + when(context.getResources()).thenReturn(mResources); doReturn(PACKAGE_NAME).when(mPackageManager).getRotationResolverPackageName(); doReturn(PackageManager.PERMISSION_GRANTED).when(mPackageManager).checkPermission( Manifest.permission.CAMERA, PACKAGE_NAME); @@ -91,8 +97,9 @@ public class SmartAutoRotateControllerTest { doReturn(context).when(context).getApplicationContext(); doReturn(mDeviceStateManager).when(context).getSystemService(DeviceStateManager.class); doReturn(getDeviceStateList()).when(mDeviceStateManager).getSupportedDeviceStates(); - mDeviceStateAutoRotateSettingsManager = DeviceStateRotationLockSettingsManager.getInstance( - context); + setDeviceStateRotationLockEnabled(false, mResources); + mDeviceStateAutoRotateSettingManager = + DeviceStateAutoRotateSettingManagerProvider.getSingletonInstance(context); mController = Mockito.spy(new SmartAutoRotateController(context, "test_key")); when(mController.isCameraLocked()).thenReturn(false); @@ -144,7 +151,7 @@ public class SmartAutoRotateControllerTest { @Test @Config(shadows = { - ShadowDeviceStateRotationLockSettingsManager.class, + ShadowDeviceStateAutoRotateSettingManager.class, ShadowRotationPolicy.class }) public void getAvailabilityStatus_deviceStateRotationLocked_returnDisableDependentSetting() { @@ -158,7 +165,7 @@ public class SmartAutoRotateControllerTest { @Test @Config(shadows = { - ShadowDeviceStateRotationLockSettingsManager.class, + ShadowDeviceStateAutoRotateSettingManager.class, ShadowRotationPolicy.class }) public void getAvailabilityStatus_deviceStateRotationUnlocked_returnAvailable() { @@ -182,18 +189,18 @@ public class SmartAutoRotateControllerTest { private void enableDeviceStateRotation() { ShadowRotationPolicy.setRotationSupported(true); - ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(true); + setDeviceStateRotationLockEnabled(true, mResources); } private void lockDeviceStateRotation() { - ShadowDeviceStateRotationLockSettingsManager shadowManager = - Shadow.extract(mDeviceStateAutoRotateSettingsManager); + ShadowDeviceStateAutoRotateSettingManager shadowManager = + Shadow.extract(mDeviceStateAutoRotateSettingManager); shadowManager.setRotationLockedForAllStates(true); } private void unlockDeviceStateRotation() { - ShadowDeviceStateRotationLockSettingsManager shadowManager = - Shadow.extract(mDeviceStateAutoRotateSettingsManager); + ShadowDeviceStateAutoRotateSettingManager shadowManager = + Shadow.extract(mDeviceStateAutoRotateSettingManager); shadowManager.setRotationLockedForAllStates(false); } diff --git a/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceControllerTest.java index 9f1b5d4357f..1fb4703fc7c 100644 --- a/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceControllerTest.java @@ -18,6 +18,8 @@ package com.android.settings.display; import static android.provider.Settings.Secure.CAMERA_AUTOROTATE; +import static com.android.settings.testutils.DeviceStateAutoRotateSettingTestUtils.setDeviceStateRotationLockEnabled; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -40,7 +42,6 @@ import com.android.settings.R; import com.android.settings.core.BasePreferenceController; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.ResolveInfoBuilder; -import com.android.settings.testutils.shadow.ShadowDeviceStateRotationLockSettingsManager; import com.android.settings.testutils.shadow.ShadowSensorPrivacyManager; import com.android.settings.testutils.shadow.ShadowSystemSettings; @@ -57,8 +58,7 @@ import org.robolectric.annotation.Config; @RunWith(RobolectricTestRunner.class) @Config(shadows = { ShadowSystemSettings.class, - ShadowSensorPrivacyManager.class, - ShadowDeviceStateRotationLockSettingsManager.class + ShadowSensorPrivacyManager.class }) public class SmartAutoRotatePreferenceControllerTest { @@ -104,7 +104,7 @@ public class SmartAutoRotatePreferenceControllerTest { new SmartAutoRotatePreferenceController(mContext, "smart_auto_rotate")); when(mController.isCameraLocked()).thenReturn(false); when(mController.isPowerSaveMode()).thenReturn(false); - ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(false); + setDeviceStateRotationLockEnabled(false, mResources); } @Test @@ -213,7 +213,7 @@ public class SmartAutoRotatePreferenceControllerTest { @Test public void getAvailabilityStatus_deviceStateRotationEnabled_returnsUnsupported() { enableAutoRotationPreference(); - ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(true); + setDeviceStateRotationLockEnabled(true, mResources); assertThat(mController.getAvailabilityStatus()).isEqualTo( BasePreferenceController.UNSUPPORTED_ON_DEVICE); diff --git a/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceFragmentTest.java index 16155384cf8..731cffb8719 100644 --- a/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceFragmentTest.java +++ b/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceFragmentTest.java @@ -27,6 +27,7 @@ import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED import static com.android.settings.display.SmartAutoRotatePreferenceFragment.AUTO_ROTATE_MAIN_SWITCH_PREFERENCE_KEY; import static com.android.settings.display.SmartAutoRotatePreferenceFragment.AUTO_ROTATE_SWITCH_PREFERENCE_KEY; +import static com.android.settings.testutils.DeviceStateAutoRotateSettingTestUtils.setDeviceStateRotationLockEnabled; import static com.google.common.truth.Truth.assertThat; @@ -55,10 +56,8 @@ import androidx.preference.Preference; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.testutils.ResolveInfoBuilder; -import com.android.settings.testutils.shadow.ShadowDeviceStateRotationLockSettingsManager; import com.android.settings.testutils.shadow.ShadowRotationPolicy; import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager; import org.junit.Before; import org.junit.Test; @@ -75,7 +74,6 @@ import java.util.Set; @RunWith(RobolectricTestRunner.class) @Config(shadows = { com.android.settings.testutils.shadow.ShadowFragment.class, - ShadowDeviceStateRotationLockSettingsManager.class, ShadowRotationPolicy.class }) public class SmartAutoRotatePreferenceFragmentTest { @@ -174,7 +172,7 @@ public class SmartAutoRotatePreferenceFragmentTest { @Test public void createHeader_faceDetectionSupported_switchBarIsEnabled() { - ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(false); + setDeviceStateRotationLockEnabled(false, mResources); mFragment.createHeader(mActivity); verify(mRotateMainSwitchPreference, never()).setVisible(false); @@ -184,7 +182,7 @@ public class SmartAutoRotatePreferenceFragmentTest { @Test public void createHeader_deviceStateRotationSupported_switchBarIsDisabled() { ShadowRotationPolicy.setRotationSupported(true); - ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(true); + setDeviceStateRotationLockEnabled(true, mResources); mFragment.createHeader(mActivity); @@ -258,15 +256,14 @@ public class SmartAutoRotatePreferenceFragmentTest { private void enableDeviceStateSettableRotationStates( String[] settableStates, String[] settableStatesDescriptions) { when(mResources.getStringArray( - com.android.internal.R.array.config_perDeviceStateRotationLockDefaults)) + com.android.internal.R.array.config_perDeviceStateRotationLockDefaults)) .thenReturn(settableStates); when(mResources.getStringArray(R.array.config_settableAutoRotationDeviceStatesDescriptions)) .thenReturn(settableStatesDescriptions); when(mResources.getBoolean(R.bool.config_auto_rotate_face_detection_available)) .thenReturn(true); - DeviceStateRotationLockSettingsManager.resetInstance(); - DeviceStateRotationLockSettingsManager.getInstance(mContext) - .resetStateForTesting(mResources); + DeviceStateAutoRotateSettingManagerProvider.resetInstance(); + when(mContext.getResources()).thenReturn(mResources); } // Sets up posture mappings for PosturesHelper diff --git a/tests/robotests/src/com/android/settings/supervision/SupervisionSafeSearchPreferenceTest.kt b/tests/robotests/src/com/android/settings/supervision/SupervisionSafeSearchPreferenceTest.kt new file mode 100644 index 00000000000..371cca3f2b5 --- /dev/null +++ b/tests/robotests/src/com/android/settings/supervision/SupervisionSafeSearchPreferenceTest.kt @@ -0,0 +1,60 @@ +/* + * 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.supervision + +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settings.R +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class SupervisionSafeSearchPreferenceTest { + private val context: Context = ApplicationProvider.getApplicationContext() + + private val searchFilterOnPreference = SupervisionSearchFilterOnPreference() + + private val searchFilterOffPreference = SupervisionSearchFilterOffPreference() + + @Test + fun getTitle_filterOn() { + assertThat(searchFilterOnPreference.title) + .isEqualTo(R.string.supervision_web_content_filters_search_filter_on_title) + } + + + @Test + fun getSummary_filterOn() { + assertThat(searchFilterOnPreference.summary) + .isEqualTo(R.string.supervision_web_content_filters_search_filter_on_summary) + } + + @Test + fun getTitle_filterOff() { + assertThat(searchFilterOffPreference.title) + .isEqualTo(R.string.supervision_web_content_filters_search_filter_off_title) + } + + @Test + fun getSummary_filterOff() { + assertThat(searchFilterOffPreference.summary) + .isEqualTo( + R.string.supervision_web_content_filters_search_filter_off_summary + ) + } +} diff --git a/tests/robotests/src/com/android/settings/supervision/SupervisionSafeSitesPreferenceTest.kt b/tests/robotests/src/com/android/settings/supervision/SupervisionSafeSitesPreferenceTest.kt index 5be7a1167e4..a3aca69cf32 100644 --- a/tests/robotests/src/com/android/settings/supervision/SupervisionSafeSitesPreferenceTest.kt +++ b/tests/robotests/src/com/android/settings/supervision/SupervisionSafeSitesPreferenceTest.kt @@ -16,20 +16,33 @@ package com.android.settings.supervision import android.content.Context +import android.provider.Settings +import android.provider.Settings.Secure.BROWSER_CONTENT_FILTERS_ENABLED +import android.provider.Settings.SettingNotFoundException import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settings.R +import com.android.settingslib.preference.createAndBindWidget +import com.android.settingslib.widget.SelectorWithWidgetPreference import com.google.common.truth.Truth.assertThat +import org.junit.Assert.assertThrows +import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class SupervisionSafeSitesPreferenceTest { private val context: Context = ApplicationProvider.getApplicationContext() + private lateinit var dataStore: SupervisionSafeSitesDataStore + private lateinit var allowAllSitesPreference: SupervisionAllowAllSitesPreference + private lateinit var blockExplicitSitesPreference: SupervisionBlockExplicitSitesPreference - private val allowAllSitesPreference = SupervisionAllowAllSitesPreference() - - private val blockExplicitSitesPreference = SupervisionBlockExplicitSitesPreference() + @Before + fun setUp() { + dataStore = SupervisionSafeSitesDataStore(context) + allowAllSitesPreference = SupervisionAllowAllSitesPreference(dataStore) + blockExplicitSitesPreference = SupervisionBlockExplicitSitesPreference(dataStore) + } @Test fun getTitle_allowAllSites() { @@ -50,4 +63,64 @@ class SupervisionSafeSitesPreferenceTest { R.string.supervision_web_content_filters_browser_block_explicit_sites_summary ) } + + @Test + fun allowAllSitesIsChecked_whenNoValueIsSet() { + assertThrows(SettingNotFoundException::class.java) { + Settings.Secure.getInt(context.getContentResolver(), BROWSER_CONTENT_FILTERS_ENABLED) + } + assertThat(getBlockExplicitSitesWidget().isChecked).isFalse() + assertThat(getAllowAllSitesWidget().isChecked).isTrue() + } + + @Test + fun blockExplicitSitesIsChecked_whenPreviouslyEnabled() { + Settings.Secure.putInt(context.getContentResolver(), BROWSER_CONTENT_FILTERS_ENABLED, 1) + assertThat(getAllowAllSitesWidget().isChecked).isFalse() + assertThat(getBlockExplicitSitesWidget().isChecked).isTrue() + } + + @Test + fun clickBlockExplicitSites_enablesFilter() { + Settings.Secure.putInt(context.getContentResolver(), BROWSER_CONTENT_FILTERS_ENABLED, 0) + val blockExplicitSitesWidget = getBlockExplicitSitesWidget() + assertThat(blockExplicitSitesWidget.isChecked).isFalse() + + blockExplicitSitesWidget.performClick() + + assertThat( + Settings.Secure.getInt( + context.getContentResolver(), + BROWSER_CONTENT_FILTERS_ENABLED, + ) + ) + .isEqualTo(1) + assertThat(blockExplicitSitesWidget.isChecked).isTrue() + } + + @Test + fun clickAllowAllSites_disablesFilter() { + Settings.Secure.putInt(context.getContentResolver(), BROWSER_CONTENT_FILTERS_ENABLED, 1) + val allowAllSitesWidget = getAllowAllSitesWidget() + assertThat(allowAllSitesWidget.isChecked).isFalse() + + allowAllSitesWidget.performClick() + + assertThat( + Settings.Secure.getInt( + context.getContentResolver(), + BROWSER_CONTENT_FILTERS_ENABLED, + ) + ) + .isEqualTo(0) + assertThat(allowAllSitesWidget.isChecked).isTrue() + } + + private fun getBlockExplicitSitesWidget(): SelectorWithWidgetPreference { + return blockExplicitSitesPreference.createAndBindWidget(context) + } + + private fun getAllowAllSitesWidget(): SelectorWithWidgetPreference { + return allowAllSitesPreference.createAndBindWidget(context) + } } diff --git a/tests/robotests/src/com/android/settings/supervision/SupervisionWebContentFiltersScreenTest.kt b/tests/robotests/src/com/android/settings/supervision/SupervisionWebContentFiltersScreenTest.kt index 351cbdeaa19..31bdbd29405 100644 --- a/tests/robotests/src/com/android/settings/supervision/SupervisionWebContentFiltersScreenTest.kt +++ b/tests/robotests/src/com/android/settings/supervision/SupervisionWebContentFiltersScreenTest.kt @@ -15,18 +15,32 @@ */ package com.android.settings.supervision +import android.app.supervision.flags.Flags import android.content.Context +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags +import android.platform.test.flag.junit.SetFlagsRule +import androidx.fragment.app.testing.FragmentScenario import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settings.R +import com.android.settingslib.widget.SelectorWithWidgetPreference import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class SupervisionWebContentFiltersScreenTest { + @get:Rule val setFlagsRule = SetFlagsRule() private val context: Context = ApplicationProvider.getApplicationContext() - private val supervisionWebContentFiltersScreen = SupervisionWebContentFiltersScreen() + private lateinit var supervisionWebContentFiltersScreen: SupervisionWebContentFiltersScreen + + @Before + fun setUp() { + supervisionWebContentFiltersScreen = SupervisionWebContentFiltersScreen() + } @Test fun key() { @@ -39,4 +53,40 @@ class SupervisionWebContentFiltersScreenTest { assertThat(supervisionWebContentFiltersScreen.title) .isEqualTo(R.string.supervision_web_content_filters_title) } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_WEB_CONTENT_FILTERS_SCREEN) + fun flagEnabled() { + assertThat(supervisionWebContentFiltersScreen.isFlagEnabled(context)).isTrue() + } + + @Test + @DisableFlags(Flags.FLAG_ENABLE_WEB_CONTENT_FILTERS_SCREEN) + fun flagDisabled() { + assertThat(supervisionWebContentFiltersScreen.isFlagEnabled(context)).isFalse() + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_WEB_CONTENT_FILTERS_SCREEN) + fun switchSafeSitesPreferences() { + FragmentScenario.launchInContainer(supervisionWebContentFiltersScreen.fragmentClass()) + .onFragment { fragment -> + val allowAllSitesPreference = + fragment.findPreference( + SupervisionAllowAllSitesPreference.KEY + )!! + val blockExplicitSitesPreference = + fragment.findPreference( + SupervisionBlockExplicitSitesPreference.KEY + )!! + + assertThat(allowAllSitesPreference.isChecked).isTrue() + assertThat(blockExplicitSitesPreference.isChecked).isFalse() + + blockExplicitSitesPreference.performClick() + + assertThat(blockExplicitSitesPreference.isChecked).isTrue() + assertThat(allowAllSitesPreference.isChecked).isFalse() + } + } } diff --git a/tests/robotests/src/com/android/settings/testutils/DeviceStateAutoRotateSettingTestUtils.java b/tests/robotests/src/com/android/settings/testutils/DeviceStateAutoRotateSettingTestUtils.java new file mode 100644 index 00000000000..3359b2f9dc5 --- /dev/null +++ b/tests/robotests/src/com/android/settings/testutils/DeviceStateAutoRotateSettingTestUtils.java @@ -0,0 +1,41 @@ +/* + * 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.testutils; + +import static org.mockito.Mockito.when; + +import android.content.res.Resources; + +/** + * Helper for testing device state auto rotate setting + */ +public class DeviceStateAutoRotateSettingTestUtils { + + /** + * Mock {@link mockResources} to return device state auto rotate enabled or disabled based on + * value passed for {@link enable}. + */ + public static void setDeviceStateRotationLockEnabled(boolean enable, Resources mockResources) { + String[] perDeviceStateRotationLockDefaults = new String[0]; + if (enable) { + perDeviceStateRotationLockDefaults = new String[]{"test_value"}; + } + when(mockResources.getStringArray( + com.android.internal.R.array.config_perDeviceStateRotationLockDefaults)) + .thenReturn(perDeviceStateRotationLockDefaults); + } +} diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDeviceStateRotationLockSettingsManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDeviceStateAutoRotateSettingManager.java similarity index 73% rename from tests/robotests/src/com/android/settings/testutils/shadow/ShadowDeviceStateRotationLockSettingsManager.java rename to tests/robotests/src/com/android/settings/testutils/shadow/ShadowDeviceStateAutoRotateSettingManager.java index ed266e3b23e..b44d79e3acc 100644 --- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDeviceStateRotationLockSettingsManager.java +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDeviceStateAutoRotateSettingManager.java @@ -16,28 +16,16 @@ package com.android.settings.testutils.shadow; -import android.content.Context; - import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; @Implements(DeviceStateRotationLockSettingsManager.class) -public class ShadowDeviceStateRotationLockSettingsManager { +public class ShadowDeviceStateAutoRotateSettingManager { - private static boolean sDeviceStateRotationLockEnabled; private boolean mIsRotationLockedForAllStates; - @Implementation - public static boolean isDeviceStateRotationLockEnabled(Context context) { - return sDeviceStateRotationLockEnabled; - } - - public static void setDeviceStateRotationLockEnabled(boolean enabled) { - sDeviceStateRotationLockEnabled = enabled; - } - @Implementation public boolean isRotationLockedForAllStates() { return mIsRotationLockedForAllStates; diff --git a/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowBluetoothUtils.java b/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowBluetoothUtils.java index 4dca7490e1a..72de74672f6 100644 --- a/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowBluetoothUtils.java +++ b/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowBluetoothUtils.java @@ -18,24 +18,48 @@ package com.android.settings.testutils.shadow; import android.content.Context; +import androidx.annotation.NonNull; + import com.android.settings.bluetooth.Utils; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.LocalBluetoothManager; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; import org.robolectric.annotation.Resetter; +import java.util.HashMap; +import java.util.Map; + /** Robolectric shadow for the bluetooth utils. */ @Implements(Utils.class) public class ShadowBluetoothUtils { public static LocalBluetoothManager sLocalBluetoothManager; + private static final Map sLeAudioState = new HashMap<>(); @Implementation protected static LocalBluetoothManager getLocalBtManager(Context context) { return sLocalBluetoothManager; } + /** Sets le audio state for the device. */ + @Implementation + public static void setLeAudioEnabled( + @NonNull LocalBluetoothManager manager, + @NonNull CachedBluetoothDevice cachedDevice, + boolean enable) { + sLeAudioState.put(cachedDevice, enable); + } + + /** Checks whether le audio is enabled for the device. */ + public static boolean isLeAudioEnabled(@NonNull CachedBluetoothDevice cachedDevice) { + if (sLeAudioState.containsKey(cachedDevice)) { + return sLeAudioState.get(cachedDevice); + } + return false; + } + /** Resets the local bluetooth manager to null. */ @Resetter public static void reset() {