diff --git a/res/layout/audio_sharing_password_dialog.xml b/res/layout/audio_sharing_password_dialog.xml index f1a78bcaad4..2bdf505290d 100644 --- a/res/layout/audio_sharing_password_dialog.xml +++ b/res/layout/audio_sharing_password_dialog.xml @@ -53,8 +53,8 @@ diff --git a/res/navigation/privatespace_main_context_nav.xml b/res/navigation/privatespace_main_context_nav.xml index 3eb57d3ecda..e20eaff5206 100644 --- a/res/navigation/privatespace_main_context_nav.xml +++ b/res/navigation/privatespace_main_context_nav.xml @@ -35,6 +35,9 @@ + + Couldn\u2019t set up a private space Try Again + + Exit + + Private space isn\u2019t available.\nView possible causes + + View possible causes Choose a new lock for private space? diff --git a/res/xml/mobile_network_settings.xml b/res/xml/mobile_network_settings.xml index 6c0f7b5450c..63d8a3d7b50 100644 --- a/res/xml/mobile_network_settings.xml +++ b/res/xml/mobile_network_settings.xml @@ -85,9 +85,10 @@ android:summary="@string/auto_data_switch_summary" settings:controller="com.android.settings.network.telephony.AutoDataSwitchPreferenceController"/> + > mGroupedConnectedDevices = new HashMap<>(); + Map> mGroupedConnectedDevices = new HashMap<>(); private List mDeviceItemsInSharingSession = new ArrayList<>(); private final AtomicBoolean mCallbacksRegistered = new AtomicBoolean(false); @@ -210,17 +210,18 @@ public class AudioSharingCallAudioPreferenceController extends AudioSharingBaseP "Skip set fallback active device: unchanged"); return; } - List devices = + List devices = mGroupedConnectedDevices.getOrDefault( item.getGroupId(), ImmutableList.of()); CachedBluetoothDevice lead = - AudioSharingUtils.getLeadDevice(devices); + AudioSharingUtils.getLeadDevice( + mCacheManager, devices); if (lead != null) { Log.d( TAG, "Set fallback active device: " + lead.getDevice() - .getAnonymizedAddress()); + .getAnonymizedAddress()); lead.setActive(); logCallAudioDeviceChange(currentGroupId, lead); } else { @@ -347,8 +348,8 @@ public class AudioSharingCallAudioPreferenceController extends AudioSharingBaseP */ private void updateSummary() { updateDeviceItemsInSharingSession(); - int fallbackActiveGroupId = BluetoothUtils.getPrimaryGroupIdForBroadcast( - mContext.getContentResolver()); + int fallbackActiveGroupId = + BluetoothUtils.getPrimaryGroupIdForBroadcast(mContext.getContentResolver()); if (fallbackActiveGroupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) { for (AudioSharingDeviceItem item : mDeviceItemsInSharingSession) { if (item.getGroupId() == fallbackActiveGroupId) { diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java index 472cb440063..4ee405d0f35 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java @@ -16,6 +16,8 @@ package com.android.settings.connecteddevice.audiosharing; +import static java.util.stream.Collectors.toList; + import android.app.settings.SettingsEnums; import android.bluetooth.BluetoothCsipSetCoordinator; import android.bluetooth.BluetoothDevice; @@ -38,6 +40,7 @@ import com.android.settings.dashboard.DashboardFragment; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; import com.android.settingslib.bluetooth.LocalBluetoothManager; @@ -49,14 +52,14 @@ import com.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.concurrent.Executor; public class AudioSharingDialogHandler { - private static final String TAG = "AudioSharingDialogHandler"; + private static final String TAG = "AudioSharingDlgHandler"; private final Context mContext; private final Fragment mHostFragment; @Nullable private final LocalBluetoothManager mLocalBtManager; + @Nullable private final CachedBluetoothDeviceManager mDeviceManager; @Nullable private final LocalBluetoothLeBroadcast mBroadcast; @Nullable private final LocalBluetoothLeBroadcastAssistant mAssistant; private final MetricsFeatureProvider mMetricsFeatureProvider; @@ -163,6 +166,7 @@ public class AudioSharingDialogHandler { mContext = context; mHostFragment = fragment; mLocalBtManager = Utils.getLocalBluetoothManager(context); + mDeviceManager = mLocalBtManager != null ? mLocalBtManager.getCachedDeviceManager() : null; mBroadcast = mLocalBtManager != null ? mLocalBtManager.getProfileManager().getLeAudioBroadcastProfile() @@ -212,7 +216,7 @@ public class AudioSharingDialogHandler { if (isBroadcasting) { // Show stop audio sharing dialog when an ineligible (non LE audio) remote device // connected during a sharing session. - Map> groupedDevices = + Map> groupedDevices = AudioSharingUtils.fetchConnectedDevicesByGroupId(mLocalBtManager); List deviceItemsInSharingSession = AudioSharingUtils.buildOrderedConnectedLeadAudioSharingDeviceItem( @@ -256,19 +260,19 @@ public class AudioSharingDialogHandler { @NonNull CachedBluetoothDevice cachedDevice, boolean isBroadcasting, boolean userTriggered) { - Map> groupedDevices = + Map> groupedDevices = AudioSharingUtils.fetchConnectedDevicesByGroupId(mLocalBtManager); BluetoothDevice btDevice = cachedDevice.getDevice(); String deviceAddress = btDevice == null ? "" : btDevice.getAnonymizedAddress(); + int groupId = BluetoothUtils.getGroupId(cachedDevice); if (isBroadcasting) { // If another device within the same is already in the sharing session, add source to // the device automatically. - int groupId = BluetoothUtils.getGroupId(cachedDevice); if (groupedDevices.containsKey(groupId) && groupedDevices.get(groupId).stream() .anyMatch( device -> - BluetoothUtils.hasConnectedBroadcastSource( + BluetoothUtils.hasConnectedBroadcastSourceForBtDevice( device, mLocalBtManager))) { Log.d( TAG, @@ -352,14 +356,17 @@ public class AudioSharingDialogHandler { } else { // Build a list of AudioSharingDeviceItem for connected devices other than cachedDevice. List deviceItems = new ArrayList<>(); - for (List devices : groupedDevices.values()) { + for (Map.Entry> entry : groupedDevices.entrySet()) { + if (entry.getKey() == groupId) continue; // Use random device in the group within the sharing session to represent the group. - CachedBluetoothDevice device = devices.get(0); - if (BluetoothUtils.getGroupId(device) - == BluetoothUtils.getGroupId(cachedDevice)) { - continue; + for (BluetoothDevice device : entry.getValue()) { + CachedBluetoothDevice cDevice = + mDeviceManager != null ? mDeviceManager.findDevice(device) : null; + if (cDevice != null) { + deviceItems.add(AudioSharingUtils.buildAudioSharingDeviceItem(cDevice)); + break; + } } - deviceItems.add(AudioSharingUtils.buildAudioSharingDeviceItem(device)); } // Show audio sharing join dialog when the second eligible (LE audio) remote // device connect and no sharing session. @@ -368,13 +375,10 @@ public class AudioSharingDialogHandler { new AudioSharingJoinDialogFragment.DialogEventListener() { @Override public void onShareClick() { - mTargetSinks = new ArrayList<>(); - for (List devices : - groupedDevices.values()) { - for (CachedBluetoothDevice device : devices) { - mTargetSinks.add(device.getDevice()); - } - } + mTargetSinks = + groupedDevices.values().stream() + .flatMap(items -> items.stream()) + .collect(toList()); Log.d(TAG, "Start broadcast with sinks = " + mTargetSinks.size()); if (mBroadcast != null) { mBroadcast.startPrivateBroadcast(); @@ -493,7 +497,7 @@ public class AudioSharingDialogHandler { } private void removeSourceForGroup( - int groupId, Map> groupedDevices) { + int groupId, Map> groupedDevices) { if (mAssistant == null) { Log.d(TAG, "Fail to add source due to null profiles, group = " + groupId); return; @@ -503,8 +507,6 @@ public class AudioSharingDialogHandler { return; } groupedDevices.getOrDefault(groupId, ImmutableList.of()).stream() - .map(CachedBluetoothDevice::getDevice) - .filter(Objects::nonNull) .forEach( device -> { for (BluetoothLeBroadcastReceiveState source : @@ -515,7 +517,7 @@ public class AudioSharingDialogHandler { } private void addSourceForGroup( - int groupId, Map> groupedDevices) { + int groupId, Map> groupedDevices) { if (mBroadcast == null || mAssistant == null) { Log.d(TAG, "Fail to add source due to null profiles, group = " + groupId); return; @@ -525,8 +527,6 @@ public class AudioSharingDialogHandler { return; } groupedDevices.getOrDefault(groupId, ImmutableList.of()).stream() - .map(CachedBluetoothDevice::getDevice) - .filter(Objects::nonNull) .forEach( device -> mAssistant.addSource( diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java index 3c0faba5db8..8396e48afd1 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java @@ -48,7 +48,6 @@ import com.android.settings.core.BasePreferenceController; import com.android.settings.overlay.FeatureFactory; import com.android.settings.widget.SettingsMainSwitchBar; import com.android.settingslib.bluetooth.BluetoothUtils; -import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; import com.android.settingslib.bluetooth.LocalBluetoothManager; @@ -63,17 +62,15 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Objects; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; public class AudioSharingSwitchBarController extends BasePreferenceController implements DefaultLifecycleObserver, - OnCheckedChangeListener, - LocalBluetoothProfileManager.ServiceListener { - private static final String TAG = "AudioSharingSwitchBarCtl"; + OnCheckedChangeListener, + LocalBluetoothProfileManager.ServiceListener { + private static final String TAG = "AudioSharingSwitchCtlr"; private static final String PREF_KEY = "audio_sharing_main_switch"; interface OnAudioSharingStateChangedListener { @@ -103,7 +100,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController private final Executor mExecutor; private final MetricsFeatureProvider mMetricsFeatureProvider; private final OnAudioSharingStateChangedListener mListener; - private Map> mGroupedConnectedDevices = new HashMap<>(); + private Map> mGroupedConnectedDevices = new HashMap<>(); private List mTargetActiveSinks = new ArrayList<>(); private List mDeviceItemsForSharing = new ArrayList<>(); @VisibleForTesting IntentFilter mIntentFilter; @@ -341,8 +338,8 @@ public class AudioSharingSwitchBarController extends BasePreferenceController // FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST is always true in // prod. We can turn off the flag for debug purpose. if (FeatureFlagUtils.isEnabled( - mContext, - FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST) + mContext, + FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST) && mAssistant.getAllConnectedDevices().isEmpty()) { // Pop up dialog to ask users to connect at least one lea buds before audio sharing. AudioSharingUtils.postOnMainThread( @@ -454,13 +451,11 @@ public class AudioSharingSwitchBarController extends BasePreferenceController mDeviceItemsForSharing = new ArrayList<>(deviceItems); mTargetActiveSinks = new ArrayList<>(); if (!deviceItems.isEmpty() && deviceItems.get(0).isActive()) { - for (CachedBluetoothDevice device : + // If active device exists for audio sharing, share to it + // automatically once the broadcast is started. + mTargetActiveSinks = mGroupedConnectedDevices.getOrDefault( - deviceItems.get(0).getGroupId(), ImmutableList.of())) { - // If active device exists for audio sharing, share to it - // automatically once the broadcast is started. - mTargetActiveSinks.add(device.getDevice()); - } + deviceItems.get(0).getGroupId(), ImmutableList.of()); mDeviceItemsForSharing.remove(0); } if (mBroadcast != null) { @@ -488,7 +483,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController boolean isStateReady = isBluetoothOn() && AudioSharingUtils.isAudioSharingProfileReady( - mProfileManager); + mProfileManager); AudioSharingUtils.postOnMainThread( mContext, () -> { @@ -541,12 +536,8 @@ public class AudioSharingSwitchBarController extends BasePreferenceController @Override public void onItemClick(@NonNull AudioSharingDeviceItem item) { AudioSharingUtils.addSourceToTargetSinks( - mGroupedConnectedDevices - .getOrDefault(item.getGroupId(), ImmutableList.of()) - .stream() - .map(CachedBluetoothDevice::getDevice) - .filter(Objects::nonNull) - .collect(Collectors.toList()), + mGroupedConnectedDevices.getOrDefault( + item.getGroupId(), ImmutableList.of()), mBtManager); mGroupedConnectedDevices.clear(); mDeviceItemsForSharing.clear(); @@ -575,8 +566,8 @@ public class AudioSharingSwitchBarController extends BasePreferenceController @NonNull ViewGroup host, @NonNull View view, @NonNull AccessibilityEvent event) { if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED && (event.getContentChangeTypes() - & AccessibilityEvent.CONTENT_CHANGE_TYPE_ENABLED) - != 0) { + & AccessibilityEvent.CONTENT_CHANGE_TYPE_ENABLED) + != 0) { Log.d(TAG, "Skip accessibility event for CONTENT_CHANGE_TYPE_ENABLED"); return false; } diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java index 50f9c9a8cc9..0c2dc367171 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java @@ -22,6 +22,8 @@ import static com.android.settings.connecteddevice.audiosharing.AudioSharingUtil import static com.android.settings.connecteddevice.audiosharing.AudioSharingUtils.MetricKey.METRIC_KEY_SOURCE_PAGE_ID; import static com.android.settings.connecteddevice.audiosharing.AudioSharingUtils.MetricKey.METRIC_KEY_USER_TRIGGERED; +import static java.util.stream.Collectors.toList; + import android.bluetooth.BluetoothCsipSetCoordinator; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeBroadcastMetadata; @@ -44,10 +46,11 @@ import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import com.android.settingslib.bluetooth.VolumeControlProfile; import java.util.ArrayList; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; +import java.util.Objects; public class AudioSharingUtils { private static final String TAG = "AudioSharingUtils"; @@ -62,15 +65,15 @@ public class AudioSharingUtils { } /** - * Fetch {@link CachedBluetoothDevice}s connected to the broadcast assistant. The devices are - * grouped by CSIP group id. + * Fetch {@link BluetoothDevice}s connected to the broadcast assistant. The devices are grouped + * by CSIP group id. * * @param localBtManager The BT manager to provide BT functions. * @return A map of connected devices grouped by CSIP group id. */ - public static Map> fetchConnectedDevicesByGroupId( + public static Map> fetchConnectedDevicesByGroupId( @Nullable LocalBluetoothManager localBtManager) { - Map> groupedDevices = new HashMap<>(); + Map> groupedDevices = new HashMap<>(); if (localBtManager == null) { Log.d(TAG, "Skip fetchConnectedDevicesByGroupId due to bt manager is null"); return groupedDevices; @@ -99,7 +102,7 @@ public class AudioSharingUtils { if (!groupedDevices.containsKey(groupId)) { groupedDevices.put(groupId, new ArrayList<>()); } - groupedDevices.get(groupId).add(cachedDevice); + groupedDevices.get(groupId).add(device); } if (DEBUG) { Log.d(TAG, "fetchConnectedDevicesByGroupId: " + groupedDevices); @@ -122,11 +125,16 @@ public class AudioSharingUtils { */ public static List buildOrderedConnectedLeadDevices( @Nullable LocalBluetoothManager localBtManager, - Map> groupedConnectedDevices, + Map> groupedConnectedDevices, boolean filterByInSharing) { List orderedDevices = new ArrayList<>(); - for (List devices : groupedConnectedDevices.values()) { - CachedBluetoothDevice leadDevice = getLeadDevice(devices); + if (localBtManager == null) { + Log.d(TAG, "Skip buildOrderedConnectedLeadDevices due to bt manager is null"); + return orderedDevices; + } + CachedBluetoothDeviceManager deviceManager = localBtManager.getCachedDeviceManager(); + for (List devices : groupedConnectedDevices.values()) { + CachedBluetoothDevice leadDevice = getLeadDevice(deviceManager, devices); if (leadDevice == null) { Log.d(TAG, "Skip due to no lead device"); continue; @@ -141,52 +149,39 @@ public class AudioSharingUtils { } orderedDevices.add(leadDevice); } - orderedDevices.sort( - (CachedBluetoothDevice d1, CachedBluetoothDevice d2) -> { - // Active above not inactive - int comparison = - (isActiveLeAudioDevice(d2) ? 1 : 0) - - (isActiveLeAudioDevice(d1) ? 1 : 0); - if (comparison != 0) return comparison; - // Bonded above not bonded - comparison = - (d2.getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0) - - (d1.getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0); - if (comparison != 0) return comparison; - // Bond timestamp available above unavailable - comparison = - (d2.getBondTimestamp() != null ? 1 : 0) - - (d1.getBondTimestamp() != null ? 1 : 0); - if (comparison != 0) return comparison; - // Order by bond timestamp if it is available - // Otherwise order by device name - return d1.getBondTimestamp() != null - ? d1.getBondTimestamp().compareTo(d2.getBondTimestamp()) - : d1.getName().compareTo(d2.getName()); - }); + orderedDevices.sort(sCachedDeviceComparator); return orderedDevices; } /** * Get the lead device from a list of devices with same group id. * + * @param deviceManager CachedBluetoothDeviceManager * @param devices A list of devices with same group id. * @return The lead device */ @Nullable public static CachedBluetoothDevice getLeadDevice( - @NonNull List devices) { - if (devices.isEmpty()) return null; - for (CachedBluetoothDevice device : devices) { - if (!device.getMemberDevice().isEmpty()) { - return device; + @Nullable CachedBluetoothDeviceManager deviceManager, + @NonNull List devices) { + if (deviceManager == null || devices.isEmpty()) return null; + List cachedDevices = + devices.stream() + .map(device -> deviceManager.findDevice(device)) + .filter(Objects::nonNull) + .collect(toList()); + for (CachedBluetoothDevice cachedDevice : cachedDevices) { + if (!cachedDevice.getMemberDevice().isEmpty()) { + return cachedDevice; } } - CachedBluetoothDevice leadDevice = devices.get(0); + CachedBluetoothDevice leadDevice = cachedDevices.isEmpty() ? null : cachedDevices.get(0); Log.d( TAG, "No lead device in the group, pick arbitrary device as the lead: " - + leadDevice.getDevice().getAnonymizedAddress()); + + (leadDevice == null + ? "null" + : leadDevice.getDevice().getAnonymizedAddress())); return leadDevice; } @@ -206,13 +201,13 @@ public class AudioSharingUtils { @NonNull public static List buildOrderedConnectedLeadAudioSharingDeviceItem( @Nullable LocalBluetoothManager localBtManager, - Map> groupedConnectedDevices, + Map> groupedConnectedDevices, boolean filterByInSharing) { return buildOrderedConnectedLeadDevices( localBtManager, groupedConnectedDevices, filterByInSharing) .stream() .map(AudioSharingUtils::buildAudioSharingDeviceItem) - .collect(Collectors.toList()); + .collect(toList()); } /** Build {@link AudioSharingDeviceItem} from {@link CachedBluetoothDevice}. */ @@ -361,4 +356,27 @@ public class AudioSharingUtils { Pair.create(METRIC_KEY_CANDIDATE_DEVICE_COUNT.ordinal(), candidateDeviceCount) }; } + + private static final Comparator sCachedDeviceComparator = + (CachedBluetoothDevice d1, CachedBluetoothDevice d2) -> { + // Active above not inactive + int comparison = + (isActiveLeAudioDevice(d2) ? 1 : 0) - (isActiveLeAudioDevice(d1) ? 1 : 0); + if (comparison != 0) return comparison; + // Bonded above not bonded + comparison = + (d2.getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0) + - (d1.getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0); + if (comparison != 0) return comparison; + // Bond timestamp available above unavailable + comparison = + (d2.getBondTimestamp() != null ? 1 : 0) + - (d1.getBondTimestamp() != null ? 1 : 0); + if (comparison != 0) return comparison; + // Order by bond timestamp if it is available + // Otherwise order by device name + return d1.getBondTimestamp() != null + ? d1.getBondTimestamp().compareTo(d2.getBondTimestamp()) + : d1.getName().compareTo(d2.getName()); + }; } diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt b/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt index ca50fd0e758..d1800715302 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt +++ b/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt @@ -23,6 +23,7 @@ import com.android.settings.R import com.android.settings.network.SubscriptionUtil import com.android.settings.network.telephony.MmsMessagePreferenceController.Companion.MmsMessageSearchItem import com.android.settings.network.telephony.NrAdvancedCallingPreferenceController.Companion.NrAdvancedCallingSearchItem +import com.android.settings.network.telephony.RoamingPreferenceController.Companion.RoamingSearchItem import com.android.settings.spa.SpaSearchLanding.BundleValue import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingFragment import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingKey @@ -111,6 +112,7 @@ class MobileNetworkSettingsSearchIndex( fun createSearchItems(context: Context): List = listOf( + RoamingSearchItem(context), MmsMessageSearchItem(context), NrAdvancedCallingSearchItem(context), ) diff --git a/src/com/android/settings/network/telephony/RoamingPreferenceController.kt b/src/com/android/settings/network/telephony/RoamingPreferenceController.kt index 2529d41324e..7633677dcb4 100644 --- a/src/com/android/settings/network/telephony/RoamingPreferenceController.kt +++ b/src/com/android/settings/network/telephony/RoamingPreferenceController.kt @@ -29,6 +29,7 @@ import androidx.compose.ui.res.stringResource import androidx.fragment.app.FragmentManager import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.settings.R +import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem import com.android.settings.spa.preference.ComposePreferenceController import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel import com.android.settingslib.spaprivileged.model.enterprise.Restrictions @@ -47,6 +48,7 @@ constructor( private var telephonyManager = context.getSystemService(TelephonyManager::class.java)!! private val carrierConfigRepository = CarrierConfigRepository(context) + private val roamingSearchItem = RoamingSearchItem(context) fun init(fragmentManager: FragmentManager, subId: Int) { this.fragmentManager = fragmentManager @@ -54,14 +56,8 @@ constructor( telephonyManager = telephonyManager.createForSubscriptionId(subId) } - override fun getAvailabilityStatus(): Int { - if (!SubscriptionManager.isValidSubscriptionId(subId)) return CONDITIONALLY_UNAVAILABLE - val isForceHomeNetwork = - carrierConfigRepository.getBoolean( - subId, CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL) - - return if (isForceHomeNetwork) CONDITIONALLY_UNAVAILABLE else AVAILABLE - } + override fun getAvailabilityStatus() = + if (roamingSearchItem.isAvailable(subId)) AVAILABLE else CONDITIONALLY_UNAVAILABLE @Composable override fun Content() { @@ -101,5 +97,17 @@ constructor( companion object { private const val DIALOG_TAG = "MobileDataDialog" + + class RoamingSearchItem(context: Context) : MobileNetworkSettingsSearchItem { + override val key = "button_roaming_key" + override val title: String = context.getString(R.string.roaming) + + private val carrierConfigRepository = CarrierConfigRepository(context) + + override fun isAvailable(subId: Int): Boolean = + SubscriptionManager.isValidSubscriptionId(subId) && + !carrierConfigRepository.getBoolean( + subId, CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL) + } } } diff --git a/src/com/android/settings/password/PasswordUtils.java b/src/com/android/settings/password/PasswordUtils.java index a54df94fa90..8c8afc2e9ef 100644 --- a/src/com/android/settings/password/PasswordUtils.java +++ b/src/com/android/settings/password/PasswordUtils.java @@ -116,7 +116,7 @@ public final class PasswordUtils extends com.android.settingslib.Utils { final ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - lp.leftMargin = layoutTitleParams.leftMargin; + lp.setMarginStart(layoutTitleParams.leftMargin); lp.topMargin = (int) context.getResources().getDimensionPixelSize( R.dimen.screen_lock_options_button_margin_top); optButton.setPadding(0, 0, 0, 0); diff --git a/src/com/android/settings/privatespace/PrivateProfileCreationRestrictedError.java b/src/com/android/settings/privatespace/PrivateProfileCreationRestrictedError.java new file mode 100644 index 00000000000..d2bdb8cbe79 --- /dev/null +++ b/src/com/android/settings/privatespace/PrivateProfileCreationRestrictedError.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.privatespace; + +import android.app.Activity; +import android.app.settings.SettingsEnums; +import android.os.Bundle; +import android.text.util.Linkify; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.activity.OnBackPressedCallback; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.settings.R; +import com.android.settings.core.InstrumentedFragment; + +import com.google.android.setupcompat.template.FooterBarMixin; +import com.google.android.setupcompat.template.FooterButton; +import com.google.android.setupdesign.GlifLayout; + +import java.util.regex.Pattern; + +public class PrivateProfileCreationRestrictedError extends InstrumentedFragment { + private static final String TAG = "PrivateSpaceCreationErr"; + + @NonNull + @Override + public View onCreateView( + @NonNull LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + GlifLayout rootView = + (GlifLayout) + inflater.inflate(R.layout.privatespace_creation_error, container, false); + final FooterBarMixin mixin = rootView.getMixin(FooterBarMixin.class); + mixin.setPrimaryButton( + new FooterButton.Builder(getContext()) + .setText(R.string.private_space_exit_label) + .setListener(onExit()) + .setButtonType(FooterButton.ButtonType.NEXT) + .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary) + .build()); + OnBackPressedCallback callback = + new OnBackPressedCallback(true /* enabled by default */) { + @Override + public void handleOnBackPressed() { + // Handle the back button event. We intentionally don't want to allow back + // button to work in this screen during the setup flow. + } + }; + requireActivity().getOnBackPressedDispatcher().addCallback(this, callback); + rootView.setDescriptionText(R.string.private_space_error_description); + TextView textView = rootView.getDescriptionTextView(); + Pattern pattern = Pattern.compile(getString(R.string.private_space_error_causes_text)); + Linkify.addLinks( + textView, + pattern, + getString(R.string.private_space_learn_more_url)); + + return rootView; + } + + @Override + public int getMetricsCategory() { + return SettingsEnums.PRIVATE_SPACE_SETUP_SPACE_CREATION_ERROR; + } + + private View.OnClickListener onExit() { + return v -> { + Activity activity = getActivity(); + if (activity != null) { + mMetricsFeatureProvider.action( + getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_CANCEL_CREATE_SPACE); + Log.i(TAG, "private space setup exited"); + activity.finish(); + } + }; + } +} + diff --git a/src/com/android/settings/privatespace/PrivateSpaceCreationFragment.java b/src/com/android/settings/privatespace/PrivateSpaceCreationFragment.java index ce85d7238cc..0bfedbd394e 100644 --- a/src/com/android/settings/privatespace/PrivateSpaceCreationFragment.java +++ b/src/com/android/settings/privatespace/PrivateSpaceCreationFragment.java @@ -29,6 +29,7 @@ import android.net.NetworkInfo; import android.os.Bundle; import android.os.Handler; import android.os.Looper; +import android.os.UserManager; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -49,6 +50,7 @@ public class PrivateSpaceCreationFragment extends InstrumentedFragment { private static final String TAG = "PrivateSpaceCreateFrag"; private static final int PRIVATE_SPACE_CREATE_POST_DELAY_MS = 1000; private static final int PRIVATE_SPACE_ACCOUNT_LOGIN_POST_DELAY_MS = 5000; + private static final int PRIVATE_SPACE_SETUP_NO_ERROR = 0; private static final Handler sHandler = new Handler(Looper.getMainLooper()); private Runnable mRunnable = () -> { @@ -122,6 +124,11 @@ public class PrivateSpaceCreationFragment extends InstrumentedFragment { Log.i(TAG, "Private Space created"); mMetricsFeatureProvider.action( getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_SPACE_CREATED, true); + if (android.multiuser.Flags.showDifferentCreationErrorForUnsupportedDevices()) { + mMetricsFeatureProvider.action( + getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_SPACE_ERRORS, + PRIVATE_SPACE_SETUP_NO_ERROR); + } if (isConnectedToInternet()) { registerReceiver(); sHandler.postDelayed( @@ -132,8 +139,18 @@ public class PrivateSpaceCreationFragment extends InstrumentedFragment { } } else { mMetricsFeatureProvider.action( - getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_SPACE_CREATED, false); - showPrivateSpaceErrorScreen(); + getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_SPACE_CREATED, + false); + if (android.multiuser.Flags.showDifferentCreationErrorForUnsupportedDevices()) { + int errorCode = PrivateSpaceMaintainer.getInstance( + getActivity()).getPrivateSpaceCreateError(); + mMetricsFeatureProvider.action( + getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_SPACE_ERRORS, + errorCode); + showPrivateSpaceErrorScreen(errorCode); + } else { + showPrivateSpaceErrorScreen(); + } } } @@ -147,6 +164,16 @@ public class PrivateSpaceCreationFragment extends InstrumentedFragment { .navigate(R.id.action_create_profile_error); } + private void showPrivateSpaceErrorScreen(int errorCode) { + if (errorCode == UserManager.USER_OPERATION_ERROR_USER_RESTRICTED + || errorCode == UserManager.USER_OPERATION_ERROR_PRIVATE_PROFILE) { + NavHostFragment.findNavController(PrivateSpaceCreationFragment.this) + .navigate(R.id.action_create_profile_error_restrict); + } else { + showPrivateSpaceErrorScreen(); + } + } + /** Returns true if device has an active internet connection, false otherwise. */ private boolean isConnectedToInternet() { ConnectivityManager cm = diff --git a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java index ec044daacbf..dd6a4bb6ab4 100644 --- a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java +++ b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java @@ -60,6 +60,7 @@ public class PrivateSpaceMaintainer { private final Context mContext; private final UserManager mUserManager; private final ActivityManager mActivityManager; + private int mErrorCode; @GuardedBy("this") private UserHandle mUserHandle; private final KeyguardManager mKeyguardManager; @@ -111,6 +112,9 @@ public class PrivateSpaceMaintainer { userName, USER_TYPE_PROFILE_PRIVATE, new ArraySet<>()); } catch (Exception e) { Log.e(TAG, "Error creating private space", e); + if (android.multiuser.Flags.showDifferentCreationErrorForUnsupportedDevices()) { + mErrorCode = ((UserManager.UserOperationException) e).getUserOperationResult(); + } return false; } @@ -312,6 +316,11 @@ public class PrivateSpaceMaintainer { return mUserManager.canAddPrivateProfile() || doesPrivateSpaceExist(); } + /** Returns the error code for private space creation failure*/ + public int getPrivateSpaceCreateError() { + return mErrorCode; + } + /** Returns true if private space exists and is running, otherwise returns false */ @VisibleForTesting synchronized boolean isPrivateProfileRunning() { diff --git a/src/com/android/settings/wifi/WepNetworksPreferenceController.kt b/src/com/android/settings/wifi/WepNetworksPreferenceController.kt index 2ce4bcd4d4f..bad7201128c 100644 --- a/src/com/android/settings/wifi/WepNetworksPreferenceController.kt +++ b/src/com/android/settings/wifi/WepNetworksPreferenceController.kt @@ -42,6 +42,8 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.asExecutor import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOn /** Controller that controls whether the WEP network can be connected. */ class WepNetworksPreferenceController(context: Context, preferenceKey: String) : @@ -49,68 +51,74 @@ class WepNetworksPreferenceController(context: Context, preferenceKey: String) : var wifiManager = context.getSystemService(WifiManager::class.java)!! - override fun getAvailabilityStatus() = if (Flags.androidVWifiApi()) AVAILABLE - else UNSUPPORTED_ON_DEVICE + override fun getAvailabilityStatus() = + if (Flags.androidVWifiApi()) AVAILABLE else UNSUPPORTED_ON_DEVICE @Composable override fun Content() { - val checked by wepAllowedFlow.flow.collectAsStateWithLifecycle(initialValue = null) + val isWepSupported: Boolean? = + isWepSupportedFlow.collectAsStateWithLifecycle(initialValue = null).value + val isWepAllowed: Boolean? = + wepAllowedFlow.flow.collectAsStateWithLifecycle(initialValue = null).value var openDialog by rememberSaveable { mutableStateOf(false) } - val wifiInfo = wifiManager.connectionInfo - SwitchPreference(object : SwitchPreferenceModel { - override val title = stringResource(R.string.wifi_allow_wep_networks) - override val summary = { getSummary() } - override val checked = { checked } - override val changeable: () -> Boolean - get() = { carrierAllowed } - override val onCheckedChange: (Boolean) -> Unit = { newChecked -> - if (!newChecked && wifiInfo.currentSecurityType == WifiEntry.SECURITY_WEP) { - openDialog = true - } else { - wifiManager.setWepAllowed(newChecked) - wepAllowedFlow.override(newChecked) + SwitchPreference( + object : SwitchPreferenceModel { + override val title = stringResource(R.string.wifi_allow_wep_networks) + override val summary = { getSummary(isWepSupported) } + override val checked = { + if (isWepSupported == true) isWepAllowed else isWepSupported } - } - }) + override val changeable: () -> Boolean + get() = { isWepSupported == true } + + override val onCheckedChange: (Boolean) -> Unit = { newChecked -> + val wifiInfo = wifiManager.connectionInfo + if (!newChecked && wifiInfo.currentSecurityType == WifiEntry.SECURITY_WEP) { + openDialog = true + } else { + wifiManager.setWepAllowed(newChecked) + wepAllowedFlow.override(newChecked) + } + } + }) if (openDialog) { SettingsAlertDialogWithIcon( onDismissRequest = { openDialog = false }, - confirmButton = AlertDialogButton( - stringResource(R.string.sim_action_yes) - ) { - wifiManager.setWepAllowed(false) - wepAllowedFlow.override(false) - openDialog = false - }, + confirmButton = + AlertDialogButton(stringResource(R.string.sim_action_yes)) { + wifiManager.setWepAllowed(false) + wepAllowedFlow.override(false) + openDialog = false + }, dismissButton = - AlertDialogButton( - stringResource(R.string.wifi_cancel) - ) { openDialog = false }, + AlertDialogButton(stringResource(R.string.wifi_cancel)) { openDialog = false }, title = stringResource(R.string.wifi_settings_wep_networks_disconnect_title), text = { Text( stringResource(R.string.wifi_settings_wep_networks_disconnect_summary), modifier = Modifier.fillMaxWidth(), - textAlign = TextAlign.Center + textAlign = TextAlign.Center, ) }) } } - override fun getSummary(): String = mContext.getString( - if (carrierAllowed) { - R.string.wifi_allow_wep_networks_summary - } else { - R.string.wifi_allow_wep_networks_summary_carrier_not_allow - } - ) + private fun getSummary(isWepSupported: Boolean?): String = + mContext.getString( + when (isWepSupported) { + true -> R.string.wifi_allow_wep_networks_summary + false -> R.string.wifi_allow_wep_networks_summary_carrier_not_allow + null -> R.string.summary_placeholder + }) - private val carrierAllowed: Boolean - get() = wifiManager.isWepSupported + private val isWepSupportedFlow = + flow { emit(wifiManager.isWepSupported) }.flowOn(Dispatchers.Default) - val wepAllowedFlow = OverridableFlow(callbackFlow { - wifiManager.queryWepAllowed(Dispatchers.Default.asExecutor(), ::trySend) + val wepAllowedFlow = + OverridableFlow( + callbackFlow { + wifiManager.queryWepAllowed(Dispatchers.Default.asExecutor(), ::trySend) - awaitClose { } - }) -} \ No newline at end of file + awaitClose {} + }) +} diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamConfirmDialogTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamConfirmDialogTest.java index e00a146244d..9c83fa6f8ee 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamConfirmDialogTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamConfirmDialogTest.java @@ -23,7 +23,6 @@ import static com.android.settings.connecteddevice.audiosharing.audiostreams.Aud import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; diff --git a/tests/spa_unit/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProviderTest.kt index 6a6b91b2922..5ced84b0b5b 100644 --- a/tests/spa_unit/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProviderTest.kt +++ b/tests/spa_unit/src/com/android/settings/spa/app/backgroundinstall/BackgroundInstalledAppsPageProviderTest.kt @@ -37,45 +37,32 @@ import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest -import org.junit.Before 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.junit.MockitoRule import org.mockito.kotlin.any import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.spy import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) class BackgroundInstalledAppsPageProviderTest { - @get:Rule - val composeTestRule = createComposeRule() + @get:Rule val composeTestRule = createComposeRule() - @get:Rule - val mockito: MockitoRule = MockitoJUnit.rule() + private val mockPackageManager = mock() - private val context: Context = ApplicationProvider.getApplicationContext() + private val context: Context = + spy(ApplicationProvider.getApplicationContext()) { + on { packageManager } doReturn mockPackageManager + } - @Mock - private lateinit var mockContext: Context - - @Mock - private lateinit var mockPackageManager: PackageManager - - @Mock - private lateinit var mockBackgroundInstallControlService: IBackgroundInstallControlService - - private var packageInfoFlagsCaptor = argumentCaptor() + private val mockBackgroundInstallControlService = mock() private val fakeNavControllerWrapper = FakeNavControllerWrapper() - @Before - fun setup() { - whenever(mockContext.packageManager).thenReturn(mockPackageManager) - } @Test fun allAppListPageProvider_name() { assertThat(BackgroundInstalledAppsPageProvider.name) @@ -108,7 +95,7 @@ class BackgroundInstalledAppsPageProviderTest { setInjectEntry(false) - composeTestRule.onNodeWithText("0 apps").assertIsDisplayed() + composeTestRule.waitUntilExists(hasText("0 apps")) } @Test @@ -200,7 +187,8 @@ class BackgroundInstalledAppsPageProviderTest { @Test fun backgroundInstalledAppsWithGroupingListModel_transform() = runTest { - val listModel = BackgroundInstalledAppsWithGroupingListModel(mockContext) + val packageInfoFlagsCaptor = argumentCaptor() + val listModel = BackgroundInstalledAppsWithGroupingListModel(context) whenever(mockPackageManager.getPackageInfoAsUser( eq(TEST_PACKAGE_NAME), packageInfoFlagsCaptor.capture(), @@ -218,7 +206,7 @@ class BackgroundInstalledAppsPageProviderTest { @Test fun backgroundInstalledAppsWithGroupingListModel_filter() = runTest { - val listModel = BackgroundInstalledAppsWithGroupingListModel(mockContext) + val listModel = BackgroundInstalledAppsWithGroupingListModel(context) listModel.setBackgroundInstallControlService(mockBackgroundInstallControlService) whenever(mockBackgroundInstallControlService.getBackgroundInstalledPackages( PackageManager.MATCH_ALL.toLong(), diff --git a/tests/spa_unit/src/com/android/settings/wifi/WepNetworksPreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/wifi/WepNetworksPreferenceControllerTest.kt index 49e6a171efd..9183096fd7b 100644 --- a/tests/spa_unit/src/com/android/settings/wifi/WepNetworksPreferenceControllerTest.kt +++ b/tests/spa_unit/src/com/android/settings/wifi/WepNetworksPreferenceControllerTest.kt @@ -17,6 +17,7 @@ package com.android.settings.wifi import android.content.Context +import android.net.wifi.WifiInfo import android.net.wifi.WifiManager import androidx.compose.ui.test.assertIsOff import androidx.compose.ui.test.assertIsOn @@ -30,7 +31,6 @@ import androidx.preference.PreferenceManager import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settings.R -import com.android.settings.dashboard.DashboardFragment import com.android.settings.spa.preference.ComposePreference import com.android.settingslib.spa.testutils.onDialogText import com.android.wifitrackerlib.WifiEntry @@ -45,29 +45,31 @@ import org.mockito.kotlin.doAnswer import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.spy +import org.mockito.kotlin.stub @RunWith(AndroidJUnit4::class) class WepNetworksPreferenceControllerTest { - @get:Rule - val composeTestRule = createComposeRule() + @get:Rule val composeTestRule = createComposeRule() private var wepAllowed = true - private var mockWifiInfo = mock { - on { it.currentSecurityType } doReturn WifiEntry.SECURITY_EAP - on { it.ssid } doReturn SSID - } - - private var mockWifiManager = mock { - on { queryWepAllowed(any(), any()) } doAnswer { - @Suppress("UNCHECKED_CAST") - val consumer = it.arguments[1] as Consumer - consumer.accept(wepAllowed) + private var mockWifiInfo = + mock { + on { currentSecurityType } doReturn WifiEntry.SECURITY_EAP + on { ssid } doReturn SSID + } + + private var mockWifiManager = + mock { + on { queryWepAllowed(any(), any()) } doAnswer + { + @Suppress("UNCHECKED_CAST") val consumer = it.arguments[1] as Consumer + consumer.accept(wepAllowed) + } + on { isWepSupported } doReturn true + on { connectionInfo } doReturn mockWifiInfo } - on { it.isWepSupported } doReturn true - on { it.connectionInfo } doReturn mockWifiInfo - } private var context: Context = spy(ApplicationProvider.getApplicationContext()) { @@ -85,74 +87,101 @@ class WepNetworksPreferenceControllerTest { } @Test - fun wepAllowedTrue_turnOn() { + fun isChecked_wepSupportedAndAllowed_isOn() { + mockWifiManager.stub { on { isWepSupported } doReturn true } wepAllowed = true - composeTestRule.setContent { - controller.Content() - } - composeTestRule.onNodeWithText(context.getString(R.string.wifi_allow_wep_networks)) + composeTestRule.setContent { controller.Content() } + + composeTestRule + .onNodeWithText(context.getString(R.string.wifi_allow_wep_networks)) .assertIsOn() } @Test - fun wepAllowedFalse_turnOff() { + fun isChecked_wepSupportedAndNotAllowed_isOff() { + mockWifiManager.stub { on { isWepSupported } doReturn true } wepAllowed = false - composeTestRule.setContent { - controller.Content() - } - composeTestRule.onNodeWithText(context.getString(R.string.wifi_allow_wep_networks)) + composeTestRule.setContent { controller.Content() } + + composeTestRule + .onNodeWithText(context.getString(R.string.wifi_allow_wep_networks)) + .assertIsOff() + } + + @Test + fun isChecked_wepNotSupportedAndAllowed_isOff() { + mockWifiManager.stub { on { isWepSupported } doReturn false } + wepAllowed = true + + composeTestRule.setContent { controller.Content() } + + composeTestRule + .onNodeWithText(context.getString(R.string.wifi_allow_wep_networks)) + .assertIsOff() + } + + @Test + fun isChecked_wepNotSupportedAndNotAllowed_isOff() { + mockWifiManager.stub { on { isWepSupported } doReturn false } + wepAllowed = false + + composeTestRule.setContent { controller.Content() } + + composeTestRule + .onNodeWithText(context.getString(R.string.wifi_allow_wep_networks)) .assertIsOff() } @Test fun onClick_turnOn() { wepAllowed = false - composeTestRule.setContent { - controller.Content() - } + composeTestRule.setContent { controller.Content() } composeTestRule.onRoot().performClick() - composeTestRule.onNodeWithText(context.getString(R.string.wifi_allow_wep_networks)) + composeTestRule + .onNodeWithText(context.getString(R.string.wifi_allow_wep_networks)) .assertIsOn() } @Test fun onClick_turnOff() { wepAllowed = true - composeTestRule.setContent { - controller.Content() - } + composeTestRule.setContent { controller.Content() } composeTestRule.onRoot().performClick() - composeTestRule.onNodeWithText(context.getString(R.string.wifi_allow_wep_networks)) + composeTestRule + .onNodeWithText(context.getString(R.string.wifi_allow_wep_networks)) .assertIsOff() } @Test fun whenClick_wepAllowed_openDialog() { wepAllowed = true - Mockito.`when`(mockWifiInfo.currentSecurityType).thenReturn(WifiEntry.SECURITY_WEP) - composeTestRule.setContent { - controller.Content() + mockWifiInfo.stub { + on { currentSecurityType } doReturn WifiEntry.SECURITY_WEP } + composeTestRule.setContent { controller.Content() } composeTestRule.onRoot().performClick() - composeTestRule.onDialogText(context.getString(R.string.wifi_disconnect_button_text)) + composeTestRule + .onDialogText(context.getString(R.string.wifi_disconnect_button_text)) .isDisplayed() } @Test fun whenClick_wepDisallowed_openDialog() { wepAllowed = false - Mockito.`when`(mockWifiInfo.currentSecurityType).thenReturn(WifiEntry.SECURITY_WEP) - composeTestRule.setContent { - controller.Content() + mockWifiInfo.stub { + on { currentSecurityType } doReturn WifiEntry.SECURITY_WEP } + composeTestRule.setContent { controller.Content() } + composeTestRule.onRoot().performClick() - composeTestRule.onDialogText(context.getString(R.string.wifi_disconnect_button_text)) + composeTestRule + .onDialogText(context.getString(R.string.wifi_disconnect_button_text)) .isNotDisplayed() } @@ -160,4 +189,4 @@ class WepNetworksPreferenceControllerTest { const val TEST_KEY = "test_key" const val SSID = "ssid" } -} \ No newline at end of file +} diff --git a/tests/unit/src/com/android/settings/localepicker/TermsOfAddressCategoryControllerTest.java b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressCategoryControllerTest.java index e316b2556c6..b025abdf06a 100644 --- a/tests/unit/src/com/android/settings/localepicker/TermsOfAddressCategoryControllerTest.java +++ b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressCategoryControllerTest.java @@ -17,13 +17,11 @@ package com.android.settings.localepicker; import static com.android.settings.core.BasePreferenceController.AVAILABLE; -import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.spy; -import android.app.settings.SettingsEnums; import android.content.Context; import android.os.Looper; @@ -66,14 +64,6 @@ public class TermsOfAddressCategoryControllerTest { Locale.setDefault(mCacheLocale); } - @Test - public void getAvailabilityStatus_returnUnavailable() { - Locale.setDefault(Locale.forLanguageTag("fr-CA")); - - assertThat(mTermsOfAddressCategoryController.getAvailabilityStatus()).isEqualTo( - CONDITIONALLY_UNAVAILABLE); - } - @Test public void getAvailabilityStatus_returnAvailable() { Locale.setDefault(Locale.forLanguageTag("fr-FR"));