Snap for 12185670 from b22a9d03af to 24Q4-release

Change-Id: Id9cd31c17443a3785cff9e8c89f9262676d283ef
This commit is contained in:
Android Build Coastguard Worker
2024-08-05 23:21:24 +00:00
21 changed files with 416 additions and 236 deletions

View File

@@ -53,8 +53,8 @@
<CheckBox
android:id="@+id/audio_sharing_stream_password_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="20dp" />
</LinearLayout>

View File

@@ -35,6 +35,9 @@
<action
android:id="@+id/action_set_lock_fragment"
app:destination="@id/ps_profile_lock_fragment"/>
<action
android:id="@+id/action_create_profile_error_restrict"
app:destination="@id/ps_profile_error_restricted_fragment"/>
</fragment>
<fragment android:id="@+id/ps_profile_error_fragment"
android:name="com.android.settings.privatespace.PrivateProfileCreationError"
@@ -67,6 +70,9 @@
android:id="@+id/action_lock_success_fragment"
app:destination="@id/ps_pre_finish_delay_fragment"/>
</fragment>
<fragment android:id="@+id/ps_profile_error_restricted_fragment"
android:name="com.android.settings.privatespace.PrivateProfileCreationRestrictedError"
android:label="fragment_ps_error_exit"/>
<action android:id="@+id/action_pre_finish_delay_fragment"
app:destination="@id/ps_pre_finish_delay_fragment"/>
<action android:id="@+id/action_advance_login_error"

View File

@@ -1374,6 +1374,12 @@
<string name="private_space_error_screen_title">Couldn\u2019t set up a private space</string>
<!-- Label for button to retry creating private space again on creation error. [CHAR LIMIT=30] -->
<string name="private_space_tryagain_label">Try Again</string>
<!-- Label for button to exit private space setup on creation error. [CHAR LIMIT=30] -->
<string name="private_space_exit_label">Exit</string>
<!-- Description in Private space error page with a link to the Help Center page[CHAR LIMIT=NONE] -->
<string name="private_space_error_description">Private space isn\u2019t available.\nView possible causes</string>
<!-- Text in Private space error page that points to view possible error causes [CHAR LIMIT=40] -->
<string name="private_space_error_causes_text">View possible causes</string>
<!-- Title for private space lock setup screen. [CHAR LIMIT=90] -->
<string name="private_space_lockscreen_title">Choose a new lock for private space?</string>
<!-- Summary for the private space lock setup screen. [CHAR LIMIT=NONE] -->

View File

@@ -85,9 +85,10 @@
android:summary="@string/auto_data_switch_summary"
settings:controller="com.android.settings.network.telephony.AutoDataSwitchPreferenceController"/>
<!-- Settings search is handled by RoamingSearchItem. -->
<com.android.settings.spa.preference.ComposePreference
android:key="button_roaming_key"
android:title="@string/roaming"
settings:searchable="false"
settings:controller="com.android.settings.network.telephony.RoamingPreferenceController"/>
<Preference

View File

@@ -50,7 +50,6 @@ public class BluetoothDetailsPairOtherController extends BluetoothDetailsControl
CachedBluetoothDevice device,
Lifecycle lifecycle) {
super(context, fragment, device, lifecycle);
lifecycle.addObserver(this);
}
@Override

View File

@@ -106,7 +106,6 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
mProfileManager = mManager.getProfileManager();
mCachedDevice = device;
mAllOfCachedDevices = Utils.getAllOfCachedBluetoothDevices(mManager, mCachedDevice);
lifecycle.addObserver(this);
}
@Override

View File

@@ -66,7 +66,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
/** PreferenceController to control the dialog to choose the active device for calls and alarms */
public class AudioSharingCallAudioPreferenceController extends AudioSharingBasePreferenceController
implements BluetoothCallback {
private static final String TAG = "CallsAndAlarmsPreferenceController";
private static final String TAG = "CallAudioPrefController";
private static final String PREF_KEY = "calls_and_alarms";
@VisibleForTesting
@@ -85,7 +85,7 @@ public class AudioSharingCallAudioPreferenceController extends AudioSharingBaseP
private final ContentObserver mSettingsObserver;
private final MetricsFeatureProvider mMetricsFeatureProvider;
@Nullable private Fragment mFragment;
Map<Integer, List<CachedBluetoothDevice>> mGroupedConnectedDevices = new HashMap<>();
Map<Integer, List<BluetoothDevice>> mGroupedConnectedDevices = new HashMap<>();
private List<AudioSharingDeviceItem> 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<CachedBluetoothDevice> devices =
List<BluetoothDevice> 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) {

View File

@@ -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<Integer, List<CachedBluetoothDevice>> groupedDevices =
Map<Integer, List<BluetoothDevice>> groupedDevices =
AudioSharingUtils.fetchConnectedDevicesByGroupId(mLocalBtManager);
List<AudioSharingDeviceItem> deviceItemsInSharingSession =
AudioSharingUtils.buildOrderedConnectedLeadAudioSharingDeviceItem(
@@ -256,19 +260,19 @@ public class AudioSharingDialogHandler {
@NonNull CachedBluetoothDevice cachedDevice,
boolean isBroadcasting,
boolean userTriggered) {
Map<Integer, List<CachedBluetoothDevice>> groupedDevices =
Map<Integer, List<BluetoothDevice>> 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<AudioSharingDeviceItem> deviceItems = new ArrayList<>();
for (List<CachedBluetoothDevice> devices : groupedDevices.values()) {
for (Map.Entry<Integer, List<BluetoothDevice>> 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<CachedBluetoothDevice> 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<Integer, List<CachedBluetoothDevice>> groupedDevices) {
int groupId, Map<Integer, List<BluetoothDevice>> 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<Integer, List<CachedBluetoothDevice>> groupedDevices) {
int groupId, Map<Integer, List<BluetoothDevice>> 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(

View File

@@ -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<Integer, List<CachedBluetoothDevice>> mGroupedConnectedDevices = new HashMap<>();
private Map<Integer, List<BluetoothDevice>> mGroupedConnectedDevices = new HashMap<>();
private List<BluetoothDevice> mTargetActiveSinks = new ArrayList<>();
private List<AudioSharingDeviceItem> 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;
}

View File

@@ -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<Integer, List<CachedBluetoothDevice>> fetchConnectedDevicesByGroupId(
public static Map<Integer, List<BluetoothDevice>> fetchConnectedDevicesByGroupId(
@Nullable LocalBluetoothManager localBtManager) {
Map<Integer, List<CachedBluetoothDevice>> groupedDevices = new HashMap<>();
Map<Integer, List<BluetoothDevice>> 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<CachedBluetoothDevice> buildOrderedConnectedLeadDevices(
@Nullable LocalBluetoothManager localBtManager,
Map<Integer, List<CachedBluetoothDevice>> groupedConnectedDevices,
Map<Integer, List<BluetoothDevice>> groupedConnectedDevices,
boolean filterByInSharing) {
List<CachedBluetoothDevice> orderedDevices = new ArrayList<>();
for (List<CachedBluetoothDevice> 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<BluetoothDevice> 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<CachedBluetoothDevice> devices) {
if (devices.isEmpty()) return null;
for (CachedBluetoothDevice device : devices) {
if (!device.getMemberDevice().isEmpty()) {
return device;
@Nullable CachedBluetoothDeviceManager deviceManager,
@NonNull List<BluetoothDevice> devices) {
if (deviceManager == null || devices.isEmpty()) return null;
List<CachedBluetoothDevice> 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<AudioSharingDeviceItem> buildOrderedConnectedLeadAudioSharingDeviceItem(
@Nullable LocalBluetoothManager localBtManager,
Map<Integer, List<CachedBluetoothDevice>> groupedConnectedDevices,
Map<Integer, List<BluetoothDevice>> 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<CachedBluetoothDevice> 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());
};
}

View File

@@ -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<MobileNetworkSettingsSearchItem> =
listOf(
RoamingSearchItem(context),
MmsMessageSearchItem(context),
NrAdvancedCallingSearchItem(context),
)

View File

@@ -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)
}
}
}

View File

@@ -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);

View File

@@ -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();
}
};
}
}

View File

@@ -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 =

View File

@@ -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() {

View File

@@ -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 { }
})
}
awaitClose {}
})
}

View File

@@ -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;

View File

@@ -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<PackageManager>()
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<PackageManager.PackageInfoFlags>()
private val mockBackgroundInstallControlService = mock<IBackgroundInstallControlService>()
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<PackageManager.PackageInfoFlags>()
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(),

View File

@@ -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<android.net.wifi.WifiInfo> {
on { it.currentSecurityType } doReturn WifiEntry.SECURITY_EAP
on { it.ssid } doReturn SSID
}
private var mockWifiManager = mock<WifiManager> {
on { queryWepAllowed(any(), any()) } doAnswer {
@Suppress("UNCHECKED_CAST")
val consumer = it.arguments[1] as Consumer<Boolean>
consumer.accept(wepAllowed)
private var mockWifiInfo =
mock<WifiInfo> {
on { currentSecurityType } doReturn WifiEntry.SECURITY_EAP
on { ssid } doReturn SSID
}
private var mockWifiManager =
mock<WifiManager> {
on { queryWepAllowed(any(), any()) } doAnswer
{
@Suppress("UNCHECKED_CAST") val consumer = it.arguments[1] as Consumer<Boolean>
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"
}
}
}

View File

@@ -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"));