Snap for 12185670 from b22a9d03af
to 24Q4-release
Change-Id: Id9cd31c17443a3785cff9e8c89f9262676d283ef
This commit is contained in:
@@ -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>
|
||||
|
@@ -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"
|
||||
|
@@ -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] -->
|
||||
|
@@ -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
|
||||
|
@@ -50,7 +50,6 @@ public class BluetoothDetailsPairOtherController extends BluetoothDetailsControl
|
||||
CachedBluetoothDevice device,
|
||||
Lifecycle lifecycle) {
|
||||
super(context, fragment, device, lifecycle);
|
||||
lifecycle.addObserver(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -106,7 +106,6 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
|
||||
mProfileManager = mManager.getProfileManager();
|
||||
mCachedDevice = device;
|
||||
mAllOfCachedDevices = Utils.getAllOfCachedBluetoothDevices(mManager, mCachedDevice);
|
||||
lifecycle.addObserver(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -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) {
|
||||
|
@@ -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(
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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());
|
||||
};
|
||||
}
|
||||
|
@@ -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),
|
||||
)
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -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 =
|
||||
|
@@ -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() {
|
||||
|
@@ -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 {}
|
||||
})
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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(),
|
||||
|
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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"));
|
||||
|
Reference in New Issue
Block a user