Snap for 12185670 from b22a9d03af
to 24Q4-release
Change-Id: Id9cd31c17443a3785cff9e8c89f9262676d283ef
This commit is contained in:
@@ -53,8 +53,8 @@
|
|||||||
|
|
||||||
<CheckBox
|
<CheckBox
|
||||||
android:id="@+id/audio_sharing_stream_password_checkbox"
|
android:id="@+id/audio_sharing_stream_password_checkbox"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="48dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="48dp"
|
||||||
android:layout_marginTop="24dp"
|
android:layout_marginTop="24dp"
|
||||||
android:layout_marginEnd="20dp" />
|
android:layout_marginEnd="20dp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@@ -35,6 +35,9 @@
|
|||||||
<action
|
<action
|
||||||
android:id="@+id/action_set_lock_fragment"
|
android:id="@+id/action_set_lock_fragment"
|
||||||
app:destination="@id/ps_profile_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>
|
||||||
<fragment android:id="@+id/ps_profile_error_fragment"
|
<fragment android:id="@+id/ps_profile_error_fragment"
|
||||||
android:name="com.android.settings.privatespace.PrivateProfileCreationError"
|
android:name="com.android.settings.privatespace.PrivateProfileCreationError"
|
||||||
@@ -67,6 +70,9 @@
|
|||||||
android:id="@+id/action_lock_success_fragment"
|
android:id="@+id/action_lock_success_fragment"
|
||||||
app:destination="@id/ps_pre_finish_delay_fragment"/>
|
app:destination="@id/ps_pre_finish_delay_fragment"/>
|
||||||
</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"
|
<action android:id="@+id/action_pre_finish_delay_fragment"
|
||||||
app:destination="@id/ps_pre_finish_delay_fragment"/>
|
app:destination="@id/ps_pre_finish_delay_fragment"/>
|
||||||
<action android:id="@+id/action_advance_login_error"
|
<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>
|
<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] -->
|
<!-- Label for button to retry creating private space again on creation error. [CHAR LIMIT=30] -->
|
||||||
<string name="private_space_tryagain_label">Try Again</string>
|
<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] -->
|
<!-- Title for private space lock setup screen. [CHAR LIMIT=90] -->
|
||||||
<string name="private_space_lockscreen_title">Choose a new lock for private space?</string>
|
<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] -->
|
<!-- Summary for the private space lock setup screen. [CHAR LIMIT=NONE] -->
|
||||||
|
@@ -85,9 +85,10 @@
|
|||||||
android:summary="@string/auto_data_switch_summary"
|
android:summary="@string/auto_data_switch_summary"
|
||||||
settings:controller="com.android.settings.network.telephony.AutoDataSwitchPreferenceController"/>
|
settings:controller="com.android.settings.network.telephony.AutoDataSwitchPreferenceController"/>
|
||||||
|
|
||||||
|
<!-- Settings search is handled by RoamingSearchItem. -->
|
||||||
<com.android.settings.spa.preference.ComposePreference
|
<com.android.settings.spa.preference.ComposePreference
|
||||||
android:key="button_roaming_key"
|
android:key="button_roaming_key"
|
||||||
android:title="@string/roaming"
|
settings:searchable="false"
|
||||||
settings:controller="com.android.settings.network.telephony.RoamingPreferenceController"/>
|
settings:controller="com.android.settings.network.telephony.RoamingPreferenceController"/>
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
|
@@ -50,7 +50,6 @@ public class BluetoothDetailsPairOtherController extends BluetoothDetailsControl
|
|||||||
CachedBluetoothDevice device,
|
CachedBluetoothDevice device,
|
||||||
Lifecycle lifecycle) {
|
Lifecycle lifecycle) {
|
||||||
super(context, fragment, device, lifecycle);
|
super(context, fragment, device, lifecycle);
|
||||||
lifecycle.addObserver(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -106,7 +106,6 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
|
|||||||
mProfileManager = mManager.getProfileManager();
|
mProfileManager = mManager.getProfileManager();
|
||||||
mCachedDevice = device;
|
mCachedDevice = device;
|
||||||
mAllOfCachedDevices = Utils.getAllOfCachedBluetoothDevices(mManager, mCachedDevice);
|
mAllOfCachedDevices = Utils.getAllOfCachedBluetoothDevices(mManager, mCachedDevice);
|
||||||
lifecycle.addObserver(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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 */
|
/** PreferenceController to control the dialog to choose the active device for calls and alarms */
|
||||||
public class AudioSharingCallAudioPreferenceController extends AudioSharingBasePreferenceController
|
public class AudioSharingCallAudioPreferenceController extends AudioSharingBasePreferenceController
|
||||||
implements BluetoothCallback {
|
implements BluetoothCallback {
|
||||||
private static final String TAG = "CallsAndAlarmsPreferenceController";
|
private static final String TAG = "CallAudioPrefController";
|
||||||
private static final String PREF_KEY = "calls_and_alarms";
|
private static final String PREF_KEY = "calls_and_alarms";
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -85,7 +85,7 @@ public class AudioSharingCallAudioPreferenceController extends AudioSharingBaseP
|
|||||||
private final ContentObserver mSettingsObserver;
|
private final ContentObserver mSettingsObserver;
|
||||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
||||||
@Nullable private Fragment mFragment;
|
@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 List<AudioSharingDeviceItem> mDeviceItemsInSharingSession = new ArrayList<>();
|
||||||
private final AtomicBoolean mCallbacksRegistered = new AtomicBoolean(false);
|
private final AtomicBoolean mCallbacksRegistered = new AtomicBoolean(false);
|
||||||
|
|
||||||
@@ -210,11 +210,12 @@ public class AudioSharingCallAudioPreferenceController extends AudioSharingBaseP
|
|||||||
"Skip set fallback active device: unchanged");
|
"Skip set fallback active device: unchanged");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<CachedBluetoothDevice> devices =
|
List<BluetoothDevice> devices =
|
||||||
mGroupedConnectedDevices.getOrDefault(
|
mGroupedConnectedDevices.getOrDefault(
|
||||||
item.getGroupId(), ImmutableList.of());
|
item.getGroupId(), ImmutableList.of());
|
||||||
CachedBluetoothDevice lead =
|
CachedBluetoothDevice lead =
|
||||||
AudioSharingUtils.getLeadDevice(devices);
|
AudioSharingUtils.getLeadDevice(
|
||||||
|
mCacheManager, devices);
|
||||||
if (lead != null) {
|
if (lead != null) {
|
||||||
Log.d(
|
Log.d(
|
||||||
TAG,
|
TAG,
|
||||||
@@ -347,8 +348,8 @@ public class AudioSharingCallAudioPreferenceController extends AudioSharingBaseP
|
|||||||
*/
|
*/
|
||||||
private void updateSummary() {
|
private void updateSummary() {
|
||||||
updateDeviceItemsInSharingSession();
|
updateDeviceItemsInSharingSession();
|
||||||
int fallbackActiveGroupId = BluetoothUtils.getPrimaryGroupIdForBroadcast(
|
int fallbackActiveGroupId =
|
||||||
mContext.getContentResolver());
|
BluetoothUtils.getPrimaryGroupIdForBroadcast(mContext.getContentResolver());
|
||||||
if (fallbackActiveGroupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
|
if (fallbackActiveGroupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
|
||||||
for (AudioSharingDeviceItem item : mDeviceItemsInSharingSession) {
|
for (AudioSharingDeviceItem item : mDeviceItemsInSharingSession) {
|
||||||
if (item.getGroupId() == fallbackActiveGroupId) {
|
if (item.getGroupId() == fallbackActiveGroupId) {
|
||||||
|
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.settings.connecteddevice.audiosharing;
|
package com.android.settings.connecteddevice.audiosharing;
|
||||||
|
|
||||||
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
import android.bluetooth.BluetoothCsipSetCoordinator;
|
import android.bluetooth.BluetoothCsipSetCoordinator;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
@@ -38,6 +40,7 @@ import com.android.settings.dashboard.DashboardFragment;
|
|||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
import com.android.settingslib.bluetooth.BluetoothUtils;
|
import com.android.settingslib.bluetooth.BluetoothUtils;
|
||||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||||
|
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||||
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
|
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
|
||||||
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
|
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
|
||||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||||
@@ -49,14 +52,14 @@ import com.google.common.collect.ImmutableList;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
public class AudioSharingDialogHandler {
|
public class AudioSharingDialogHandler {
|
||||||
private static final String TAG = "AudioSharingDialogHandler";
|
private static final String TAG = "AudioSharingDlgHandler";
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final Fragment mHostFragment;
|
private final Fragment mHostFragment;
|
||||||
@Nullable private final LocalBluetoothManager mLocalBtManager;
|
@Nullable private final LocalBluetoothManager mLocalBtManager;
|
||||||
|
@Nullable private final CachedBluetoothDeviceManager mDeviceManager;
|
||||||
@Nullable private final LocalBluetoothLeBroadcast mBroadcast;
|
@Nullable private final LocalBluetoothLeBroadcast mBroadcast;
|
||||||
@Nullable private final LocalBluetoothLeBroadcastAssistant mAssistant;
|
@Nullable private final LocalBluetoothLeBroadcastAssistant mAssistant;
|
||||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
||||||
@@ -163,6 +166,7 @@ public class AudioSharingDialogHandler {
|
|||||||
mContext = context;
|
mContext = context;
|
||||||
mHostFragment = fragment;
|
mHostFragment = fragment;
|
||||||
mLocalBtManager = Utils.getLocalBluetoothManager(context);
|
mLocalBtManager = Utils.getLocalBluetoothManager(context);
|
||||||
|
mDeviceManager = mLocalBtManager != null ? mLocalBtManager.getCachedDeviceManager() : null;
|
||||||
mBroadcast =
|
mBroadcast =
|
||||||
mLocalBtManager != null
|
mLocalBtManager != null
|
||||||
? mLocalBtManager.getProfileManager().getLeAudioBroadcastProfile()
|
? mLocalBtManager.getProfileManager().getLeAudioBroadcastProfile()
|
||||||
@@ -212,7 +216,7 @@ public class AudioSharingDialogHandler {
|
|||||||
if (isBroadcasting) {
|
if (isBroadcasting) {
|
||||||
// Show stop audio sharing dialog when an ineligible (non LE audio) remote device
|
// Show stop audio sharing dialog when an ineligible (non LE audio) remote device
|
||||||
// connected during a sharing session.
|
// connected during a sharing session.
|
||||||
Map<Integer, List<CachedBluetoothDevice>> groupedDevices =
|
Map<Integer, List<BluetoothDevice>> groupedDevices =
|
||||||
AudioSharingUtils.fetchConnectedDevicesByGroupId(mLocalBtManager);
|
AudioSharingUtils.fetchConnectedDevicesByGroupId(mLocalBtManager);
|
||||||
List<AudioSharingDeviceItem> deviceItemsInSharingSession =
|
List<AudioSharingDeviceItem> deviceItemsInSharingSession =
|
||||||
AudioSharingUtils.buildOrderedConnectedLeadAudioSharingDeviceItem(
|
AudioSharingUtils.buildOrderedConnectedLeadAudioSharingDeviceItem(
|
||||||
@@ -256,19 +260,19 @@ public class AudioSharingDialogHandler {
|
|||||||
@NonNull CachedBluetoothDevice cachedDevice,
|
@NonNull CachedBluetoothDevice cachedDevice,
|
||||||
boolean isBroadcasting,
|
boolean isBroadcasting,
|
||||||
boolean userTriggered) {
|
boolean userTriggered) {
|
||||||
Map<Integer, List<CachedBluetoothDevice>> groupedDevices =
|
Map<Integer, List<BluetoothDevice>> groupedDevices =
|
||||||
AudioSharingUtils.fetchConnectedDevicesByGroupId(mLocalBtManager);
|
AudioSharingUtils.fetchConnectedDevicesByGroupId(mLocalBtManager);
|
||||||
BluetoothDevice btDevice = cachedDevice.getDevice();
|
BluetoothDevice btDevice = cachedDevice.getDevice();
|
||||||
String deviceAddress = btDevice == null ? "" : btDevice.getAnonymizedAddress();
|
String deviceAddress = btDevice == null ? "" : btDevice.getAnonymizedAddress();
|
||||||
|
int groupId = BluetoothUtils.getGroupId(cachedDevice);
|
||||||
if (isBroadcasting) {
|
if (isBroadcasting) {
|
||||||
// If another device within the same is already in the sharing session, add source to
|
// If another device within the same is already in the sharing session, add source to
|
||||||
// the device automatically.
|
// the device automatically.
|
||||||
int groupId = BluetoothUtils.getGroupId(cachedDevice);
|
|
||||||
if (groupedDevices.containsKey(groupId)
|
if (groupedDevices.containsKey(groupId)
|
||||||
&& groupedDevices.get(groupId).stream()
|
&& groupedDevices.get(groupId).stream()
|
||||||
.anyMatch(
|
.anyMatch(
|
||||||
device ->
|
device ->
|
||||||
BluetoothUtils.hasConnectedBroadcastSource(
|
BluetoothUtils.hasConnectedBroadcastSourceForBtDevice(
|
||||||
device, mLocalBtManager))) {
|
device, mLocalBtManager))) {
|
||||||
Log.d(
|
Log.d(
|
||||||
TAG,
|
TAG,
|
||||||
@@ -352,14 +356,17 @@ public class AudioSharingDialogHandler {
|
|||||||
} else {
|
} else {
|
||||||
// Build a list of AudioSharingDeviceItem for connected devices other than cachedDevice.
|
// Build a list of AudioSharingDeviceItem for connected devices other than cachedDevice.
|
||||||
List<AudioSharingDeviceItem> deviceItems = new ArrayList<>();
|
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.
|
// Use random device in the group within the sharing session to represent the group.
|
||||||
CachedBluetoothDevice device = devices.get(0);
|
for (BluetoothDevice device : entry.getValue()) {
|
||||||
if (BluetoothUtils.getGroupId(device)
|
CachedBluetoothDevice cDevice =
|
||||||
== BluetoothUtils.getGroupId(cachedDevice)) {
|
mDeviceManager != null ? mDeviceManager.findDevice(device) : null;
|
||||||
continue;
|
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
|
// Show audio sharing join dialog when the second eligible (LE audio) remote
|
||||||
// device connect and no sharing session.
|
// device connect and no sharing session.
|
||||||
@@ -368,13 +375,10 @@ public class AudioSharingDialogHandler {
|
|||||||
new AudioSharingJoinDialogFragment.DialogEventListener() {
|
new AudioSharingJoinDialogFragment.DialogEventListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onShareClick() {
|
public void onShareClick() {
|
||||||
mTargetSinks = new ArrayList<>();
|
mTargetSinks =
|
||||||
for (List<CachedBluetoothDevice> devices :
|
groupedDevices.values().stream()
|
||||||
groupedDevices.values()) {
|
.flatMap(items -> items.stream())
|
||||||
for (CachedBluetoothDevice device : devices) {
|
.collect(toList());
|
||||||
mTargetSinks.add(device.getDevice());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Log.d(TAG, "Start broadcast with sinks = " + mTargetSinks.size());
|
Log.d(TAG, "Start broadcast with sinks = " + mTargetSinks.size());
|
||||||
if (mBroadcast != null) {
|
if (mBroadcast != null) {
|
||||||
mBroadcast.startPrivateBroadcast();
|
mBroadcast.startPrivateBroadcast();
|
||||||
@@ -493,7 +497,7 @@ public class AudioSharingDialogHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void removeSourceForGroup(
|
private void removeSourceForGroup(
|
||||||
int groupId, Map<Integer, List<CachedBluetoothDevice>> groupedDevices) {
|
int groupId, Map<Integer, List<BluetoothDevice>> groupedDevices) {
|
||||||
if (mAssistant == null) {
|
if (mAssistant == null) {
|
||||||
Log.d(TAG, "Fail to add source due to null profiles, group = " + groupId);
|
Log.d(TAG, "Fail to add source due to null profiles, group = " + groupId);
|
||||||
return;
|
return;
|
||||||
@@ -503,8 +507,6 @@ public class AudioSharingDialogHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
groupedDevices.getOrDefault(groupId, ImmutableList.of()).stream()
|
groupedDevices.getOrDefault(groupId, ImmutableList.of()).stream()
|
||||||
.map(CachedBluetoothDevice::getDevice)
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.forEach(
|
.forEach(
|
||||||
device -> {
|
device -> {
|
||||||
for (BluetoothLeBroadcastReceiveState source :
|
for (BluetoothLeBroadcastReceiveState source :
|
||||||
@@ -515,7 +517,7 @@ public class AudioSharingDialogHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void addSourceForGroup(
|
private void addSourceForGroup(
|
||||||
int groupId, Map<Integer, List<CachedBluetoothDevice>> groupedDevices) {
|
int groupId, Map<Integer, List<BluetoothDevice>> groupedDevices) {
|
||||||
if (mBroadcast == null || mAssistant == null) {
|
if (mBroadcast == null || mAssistant == null) {
|
||||||
Log.d(TAG, "Fail to add source due to null profiles, group = " + groupId);
|
Log.d(TAG, "Fail to add source due to null profiles, group = " + groupId);
|
||||||
return;
|
return;
|
||||||
@@ -525,8 +527,6 @@ public class AudioSharingDialogHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
groupedDevices.getOrDefault(groupId, ImmutableList.of()).stream()
|
groupedDevices.getOrDefault(groupId, ImmutableList.of()).stream()
|
||||||
.map(CachedBluetoothDevice::getDevice)
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.forEach(
|
.forEach(
|
||||||
device ->
|
device ->
|
||||||
mAssistant.addSource(
|
mAssistant.addSource(
|
||||||
|
@@ -48,7 +48,6 @@ import com.android.settings.core.BasePreferenceController;
|
|||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
import com.android.settings.widget.SettingsMainSwitchBar;
|
import com.android.settings.widget.SettingsMainSwitchBar;
|
||||||
import com.android.settingslib.bluetooth.BluetoothUtils;
|
import com.android.settingslib.bluetooth.BluetoothUtils;
|
||||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
|
||||||
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
|
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
|
||||||
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
|
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
|
||||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||||
@@ -63,17 +62,15 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class AudioSharingSwitchBarController extends BasePreferenceController
|
public class AudioSharingSwitchBarController extends BasePreferenceController
|
||||||
implements DefaultLifecycleObserver,
|
implements DefaultLifecycleObserver,
|
||||||
OnCheckedChangeListener,
|
OnCheckedChangeListener,
|
||||||
LocalBluetoothProfileManager.ServiceListener {
|
LocalBluetoothProfileManager.ServiceListener {
|
||||||
private static final String TAG = "AudioSharingSwitchBarCtl";
|
private static final String TAG = "AudioSharingSwitchCtlr";
|
||||||
private static final String PREF_KEY = "audio_sharing_main_switch";
|
private static final String PREF_KEY = "audio_sharing_main_switch";
|
||||||
|
|
||||||
interface OnAudioSharingStateChangedListener {
|
interface OnAudioSharingStateChangedListener {
|
||||||
@@ -103,7 +100,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
|||||||
private final Executor mExecutor;
|
private final Executor mExecutor;
|
||||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
||||||
private final OnAudioSharingStateChangedListener mListener;
|
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<BluetoothDevice> mTargetActiveSinks = new ArrayList<>();
|
||||||
private List<AudioSharingDeviceItem> mDeviceItemsForSharing = new ArrayList<>();
|
private List<AudioSharingDeviceItem> mDeviceItemsForSharing = new ArrayList<>();
|
||||||
@VisibleForTesting IntentFilter mIntentFilter;
|
@VisibleForTesting IntentFilter mIntentFilter;
|
||||||
@@ -454,13 +451,11 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
|||||||
mDeviceItemsForSharing = new ArrayList<>(deviceItems);
|
mDeviceItemsForSharing = new ArrayList<>(deviceItems);
|
||||||
mTargetActiveSinks = new ArrayList<>();
|
mTargetActiveSinks = new ArrayList<>();
|
||||||
if (!deviceItems.isEmpty() && deviceItems.get(0).isActive()) {
|
if (!deviceItems.isEmpty() && deviceItems.get(0).isActive()) {
|
||||||
for (CachedBluetoothDevice device :
|
|
||||||
mGroupedConnectedDevices.getOrDefault(
|
|
||||||
deviceItems.get(0).getGroupId(), ImmutableList.of())) {
|
|
||||||
// If active device exists for audio sharing, share to it
|
// If active device exists for audio sharing, share to it
|
||||||
// automatically once the broadcast is started.
|
// automatically once the broadcast is started.
|
||||||
mTargetActiveSinks.add(device.getDevice());
|
mTargetActiveSinks =
|
||||||
}
|
mGroupedConnectedDevices.getOrDefault(
|
||||||
|
deviceItems.get(0).getGroupId(), ImmutableList.of());
|
||||||
mDeviceItemsForSharing.remove(0);
|
mDeviceItemsForSharing.remove(0);
|
||||||
}
|
}
|
||||||
if (mBroadcast != null) {
|
if (mBroadcast != null) {
|
||||||
@@ -541,12 +536,8 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
|||||||
@Override
|
@Override
|
||||||
public void onItemClick(@NonNull AudioSharingDeviceItem item) {
|
public void onItemClick(@NonNull AudioSharingDeviceItem item) {
|
||||||
AudioSharingUtils.addSourceToTargetSinks(
|
AudioSharingUtils.addSourceToTargetSinks(
|
||||||
mGroupedConnectedDevices
|
mGroupedConnectedDevices.getOrDefault(
|
||||||
.getOrDefault(item.getGroupId(), ImmutableList.of())
|
item.getGroupId(), ImmutableList.of()),
|
||||||
.stream()
|
|
||||||
.map(CachedBluetoothDevice::getDevice)
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.collect(Collectors.toList()),
|
|
||||||
mBtManager);
|
mBtManager);
|
||||||
mGroupedConnectedDevices.clear();
|
mGroupedConnectedDevices.clear();
|
||||||
mDeviceItemsForSharing.clear();
|
mDeviceItemsForSharing.clear();
|
||||||
|
@@ -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_SOURCE_PAGE_ID;
|
||||||
import static com.android.settings.connecteddevice.audiosharing.AudioSharingUtils.MetricKey.METRIC_KEY_USER_TRIGGERED;
|
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.BluetoothCsipSetCoordinator;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.bluetooth.BluetoothLeBroadcastMetadata;
|
import android.bluetooth.BluetoothLeBroadcastMetadata;
|
||||||
@@ -44,10 +46,11 @@ import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
|||||||
import com.android.settingslib.bluetooth.VolumeControlProfile;
|
import com.android.settingslib.bluetooth.VolumeControlProfile;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class AudioSharingUtils {
|
public class AudioSharingUtils {
|
||||||
private static final String TAG = "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
|
* Fetch {@link BluetoothDevice}s connected to the broadcast assistant. The devices are grouped
|
||||||
* grouped by CSIP group id.
|
* by CSIP group id.
|
||||||
*
|
*
|
||||||
* @param localBtManager The BT manager to provide BT functions.
|
* @param localBtManager The BT manager to provide BT functions.
|
||||||
* @return A map of connected devices grouped by CSIP group id.
|
* @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) {
|
@Nullable LocalBluetoothManager localBtManager) {
|
||||||
Map<Integer, List<CachedBluetoothDevice>> groupedDevices = new HashMap<>();
|
Map<Integer, List<BluetoothDevice>> groupedDevices = new HashMap<>();
|
||||||
if (localBtManager == null) {
|
if (localBtManager == null) {
|
||||||
Log.d(TAG, "Skip fetchConnectedDevicesByGroupId due to bt manager is null");
|
Log.d(TAG, "Skip fetchConnectedDevicesByGroupId due to bt manager is null");
|
||||||
return groupedDevices;
|
return groupedDevices;
|
||||||
@@ -99,7 +102,7 @@ public class AudioSharingUtils {
|
|||||||
if (!groupedDevices.containsKey(groupId)) {
|
if (!groupedDevices.containsKey(groupId)) {
|
||||||
groupedDevices.put(groupId, new ArrayList<>());
|
groupedDevices.put(groupId, new ArrayList<>());
|
||||||
}
|
}
|
||||||
groupedDevices.get(groupId).add(cachedDevice);
|
groupedDevices.get(groupId).add(device);
|
||||||
}
|
}
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "fetchConnectedDevicesByGroupId: " + groupedDevices);
|
Log.d(TAG, "fetchConnectedDevicesByGroupId: " + groupedDevices);
|
||||||
@@ -122,11 +125,16 @@ public class AudioSharingUtils {
|
|||||||
*/
|
*/
|
||||||
public static List<CachedBluetoothDevice> buildOrderedConnectedLeadDevices(
|
public static List<CachedBluetoothDevice> buildOrderedConnectedLeadDevices(
|
||||||
@Nullable LocalBluetoothManager localBtManager,
|
@Nullable LocalBluetoothManager localBtManager,
|
||||||
Map<Integer, List<CachedBluetoothDevice>> groupedConnectedDevices,
|
Map<Integer, List<BluetoothDevice>> groupedConnectedDevices,
|
||||||
boolean filterByInSharing) {
|
boolean filterByInSharing) {
|
||||||
List<CachedBluetoothDevice> orderedDevices = new ArrayList<>();
|
List<CachedBluetoothDevice> orderedDevices = new ArrayList<>();
|
||||||
for (List<CachedBluetoothDevice> devices : groupedConnectedDevices.values()) {
|
if (localBtManager == null) {
|
||||||
CachedBluetoothDevice leadDevice = getLeadDevice(devices);
|
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) {
|
if (leadDevice == null) {
|
||||||
Log.d(TAG, "Skip due to no lead device");
|
Log.d(TAG, "Skip due to no lead device");
|
||||||
continue;
|
continue;
|
||||||
@@ -141,52 +149,39 @@ public class AudioSharingUtils {
|
|||||||
}
|
}
|
||||||
orderedDevices.add(leadDevice);
|
orderedDevices.add(leadDevice);
|
||||||
}
|
}
|
||||||
orderedDevices.sort(
|
orderedDevices.sort(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());
|
|
||||||
});
|
|
||||||
return orderedDevices;
|
return orderedDevices;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the lead device from a list of devices with same group id.
|
* 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.
|
* @param devices A list of devices with same group id.
|
||||||
* @return The lead device
|
* @return The lead device
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public static CachedBluetoothDevice getLeadDevice(
|
public static CachedBluetoothDevice getLeadDevice(
|
||||||
@NonNull List<CachedBluetoothDevice> devices) {
|
@Nullable CachedBluetoothDeviceManager deviceManager,
|
||||||
if (devices.isEmpty()) return null;
|
@NonNull List<BluetoothDevice> devices) {
|
||||||
for (CachedBluetoothDevice device : devices) {
|
if (deviceManager == null || devices.isEmpty()) return null;
|
||||||
if (!device.getMemberDevice().isEmpty()) {
|
List<CachedBluetoothDevice> cachedDevices =
|
||||||
return device;
|
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(
|
Log.d(
|
||||||
TAG,
|
TAG,
|
||||||
"No lead device in the group, pick arbitrary device as the lead: "
|
"No lead device in the group, pick arbitrary device as the lead: "
|
||||||
+ leadDevice.getDevice().getAnonymizedAddress());
|
+ (leadDevice == null
|
||||||
|
? "null"
|
||||||
|
: leadDevice.getDevice().getAnonymizedAddress()));
|
||||||
return leadDevice;
|
return leadDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,13 +201,13 @@ public class AudioSharingUtils {
|
|||||||
@NonNull
|
@NonNull
|
||||||
public static List<AudioSharingDeviceItem> buildOrderedConnectedLeadAudioSharingDeviceItem(
|
public static List<AudioSharingDeviceItem> buildOrderedConnectedLeadAudioSharingDeviceItem(
|
||||||
@Nullable LocalBluetoothManager localBtManager,
|
@Nullable LocalBluetoothManager localBtManager,
|
||||||
Map<Integer, List<CachedBluetoothDevice>> groupedConnectedDevices,
|
Map<Integer, List<BluetoothDevice>> groupedConnectedDevices,
|
||||||
boolean filterByInSharing) {
|
boolean filterByInSharing) {
|
||||||
return buildOrderedConnectedLeadDevices(
|
return buildOrderedConnectedLeadDevices(
|
||||||
localBtManager, groupedConnectedDevices, filterByInSharing)
|
localBtManager, groupedConnectedDevices, filterByInSharing)
|
||||||
.stream()
|
.stream()
|
||||||
.map(AudioSharingUtils::buildAudioSharingDeviceItem)
|
.map(AudioSharingUtils::buildAudioSharingDeviceItem)
|
||||||
.collect(Collectors.toList());
|
.collect(toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Build {@link AudioSharingDeviceItem} from {@link CachedBluetoothDevice}. */
|
/** Build {@link AudioSharingDeviceItem} from {@link CachedBluetoothDevice}. */
|
||||||
@@ -361,4 +356,27 @@ public class AudioSharingUtils {
|
|||||||
Pair.create(METRIC_KEY_CANDIDATE_DEVICE_COUNT.ordinal(), candidateDeviceCount)
|
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.SubscriptionUtil
|
||||||
import com.android.settings.network.telephony.MmsMessagePreferenceController.Companion.MmsMessageSearchItem
|
import com.android.settings.network.telephony.MmsMessagePreferenceController.Companion.MmsMessageSearchItem
|
||||||
import com.android.settings.network.telephony.NrAdvancedCallingPreferenceController.Companion.NrAdvancedCallingSearchItem
|
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.BundleValue
|
||||||
import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingFragment
|
import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingFragment
|
||||||
import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingKey
|
import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingKey
|
||||||
@@ -111,6 +112,7 @@ class MobileNetworkSettingsSearchIndex(
|
|||||||
|
|
||||||
fun createSearchItems(context: Context): List<MobileNetworkSettingsSearchItem> =
|
fun createSearchItems(context: Context): List<MobileNetworkSettingsSearchItem> =
|
||||||
listOf(
|
listOf(
|
||||||
|
RoamingSearchItem(context),
|
||||||
MmsMessageSearchItem(context),
|
MmsMessageSearchItem(context),
|
||||||
NrAdvancedCallingSearchItem(context),
|
NrAdvancedCallingSearchItem(context),
|
||||||
)
|
)
|
||||||
|
@@ -29,6 +29,7 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
|
import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem
|
||||||
import com.android.settings.spa.preference.ComposePreferenceController
|
import com.android.settings.spa.preference.ComposePreferenceController
|
||||||
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
|
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
|
||||||
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
|
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
|
||||||
@@ -47,6 +48,7 @@ constructor(
|
|||||||
|
|
||||||
private var telephonyManager = context.getSystemService(TelephonyManager::class.java)!!
|
private var telephonyManager = context.getSystemService(TelephonyManager::class.java)!!
|
||||||
private val carrierConfigRepository = CarrierConfigRepository(context)
|
private val carrierConfigRepository = CarrierConfigRepository(context)
|
||||||
|
private val roamingSearchItem = RoamingSearchItem(context)
|
||||||
|
|
||||||
fun init(fragmentManager: FragmentManager, subId: Int) {
|
fun init(fragmentManager: FragmentManager, subId: Int) {
|
||||||
this.fragmentManager = fragmentManager
|
this.fragmentManager = fragmentManager
|
||||||
@@ -54,14 +56,8 @@ constructor(
|
|||||||
telephonyManager = telephonyManager.createForSubscriptionId(subId)
|
telephonyManager = telephonyManager.createForSubscriptionId(subId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getAvailabilityStatus(): Int {
|
override fun getAvailabilityStatus() =
|
||||||
if (!SubscriptionManager.isValidSubscriptionId(subId)) return CONDITIONALLY_UNAVAILABLE
|
if (roamingSearchItem.isAvailable(subId)) AVAILABLE else CONDITIONALLY_UNAVAILABLE
|
||||||
val isForceHomeNetwork =
|
|
||||||
carrierConfigRepository.getBoolean(
|
|
||||||
subId, CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL)
|
|
||||||
|
|
||||||
return if (isForceHomeNetwork) CONDITIONALLY_UNAVAILABLE else AVAILABLE
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun Content() {
|
override fun Content() {
|
||||||
@@ -101,5 +97,17 @@ constructor(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val DIALOG_TAG = "MobileDataDialog"
|
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(
|
final ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT);
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
lp.leftMargin = layoutTitleParams.leftMargin;
|
lp.setMarginStart(layoutTitleParams.leftMargin);
|
||||||
lp.topMargin = (int) context.getResources().getDimensionPixelSize(
|
lp.topMargin = (int) context.getResources().getDimensionPixelSize(
|
||||||
R.dimen.screen_lock_options_button_margin_top);
|
R.dimen.screen_lock_options_button_margin_top);
|
||||||
optButton.setPadding(0, 0, 0, 0);
|
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.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
import android.os.UserManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -49,6 +50,7 @@ public class PrivateSpaceCreationFragment extends InstrumentedFragment {
|
|||||||
private static final String TAG = "PrivateSpaceCreateFrag";
|
private static final String TAG = "PrivateSpaceCreateFrag";
|
||||||
private static final int PRIVATE_SPACE_CREATE_POST_DELAY_MS = 1000;
|
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_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 static final Handler sHandler = new Handler(Looper.getMainLooper());
|
||||||
private Runnable mRunnable =
|
private Runnable mRunnable =
|
||||||
() -> {
|
() -> {
|
||||||
@@ -122,6 +124,11 @@ public class PrivateSpaceCreationFragment extends InstrumentedFragment {
|
|||||||
Log.i(TAG, "Private Space created");
|
Log.i(TAG, "Private Space created");
|
||||||
mMetricsFeatureProvider.action(
|
mMetricsFeatureProvider.action(
|
||||||
getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_SPACE_CREATED, true);
|
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()) {
|
if (isConnectedToInternet()) {
|
||||||
registerReceiver();
|
registerReceiver();
|
||||||
sHandler.postDelayed(
|
sHandler.postDelayed(
|
||||||
@@ -132,10 +139,20 @@ public class PrivateSpaceCreationFragment extends InstrumentedFragment {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mMetricsFeatureProvider.action(
|
mMetricsFeatureProvider.action(
|
||||||
getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_SPACE_CREATED, false);
|
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();
|
showPrivateSpaceErrorScreen();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getMetricsCategory() {
|
public int getMetricsCategory() {
|
||||||
@@ -147,6 +164,16 @@ public class PrivateSpaceCreationFragment extends InstrumentedFragment {
|
|||||||
.navigate(R.id.action_create_profile_error);
|
.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. */
|
/** Returns true if device has an active internet connection, false otherwise. */
|
||||||
private boolean isConnectedToInternet() {
|
private boolean isConnectedToInternet() {
|
||||||
ConnectivityManager cm =
|
ConnectivityManager cm =
|
||||||
|
@@ -60,6 +60,7 @@ public class PrivateSpaceMaintainer {
|
|||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final UserManager mUserManager;
|
private final UserManager mUserManager;
|
||||||
private final ActivityManager mActivityManager;
|
private final ActivityManager mActivityManager;
|
||||||
|
private int mErrorCode;
|
||||||
@GuardedBy("this")
|
@GuardedBy("this")
|
||||||
private UserHandle mUserHandle;
|
private UserHandle mUserHandle;
|
||||||
private final KeyguardManager mKeyguardManager;
|
private final KeyguardManager mKeyguardManager;
|
||||||
@@ -111,6 +112,9 @@ public class PrivateSpaceMaintainer {
|
|||||||
userName, USER_TYPE_PROFILE_PRIVATE, new ArraySet<>());
|
userName, USER_TYPE_PROFILE_PRIVATE, new ArraySet<>());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "Error creating private space", e);
|
Log.e(TAG, "Error creating private space", e);
|
||||||
|
if (android.multiuser.Flags.showDifferentCreationErrorForUnsupportedDevices()) {
|
||||||
|
mErrorCode = ((UserManager.UserOperationException) e).getUserOperationResult();
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,6 +316,11 @@ public class PrivateSpaceMaintainer {
|
|||||||
return mUserManager.canAddPrivateProfile() || doesPrivateSpaceExist();
|
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 */
|
/** Returns true if private space exists and is running, otherwise returns false */
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
synchronized boolean isPrivateProfileRunning() {
|
synchronized boolean isPrivateProfileRunning() {
|
||||||
|
@@ -42,6 +42,8 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.asExecutor
|
import kotlinx.coroutines.asExecutor
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
import kotlinx.coroutines.flow.callbackFlow
|
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. */
|
/** Controller that controls whether the WEP network can be connected. */
|
||||||
class WepNetworksPreferenceController(context: Context, preferenceKey: String) :
|
class WepNetworksPreferenceController(context: Context, preferenceKey: String) :
|
||||||
@@ -49,21 +51,28 @@ class WepNetworksPreferenceController(context: Context, preferenceKey: String) :
|
|||||||
|
|
||||||
var wifiManager = context.getSystemService(WifiManager::class.java)!!
|
var wifiManager = context.getSystemService(WifiManager::class.java)!!
|
||||||
|
|
||||||
override fun getAvailabilityStatus() = if (Flags.androidVWifiApi()) AVAILABLE
|
override fun getAvailabilityStatus() =
|
||||||
else UNSUPPORTED_ON_DEVICE
|
if (Flags.androidVWifiApi()) AVAILABLE else UNSUPPORTED_ON_DEVICE
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun Content() {
|
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) }
|
var openDialog by rememberSaveable { mutableStateOf(false) }
|
||||||
val wifiInfo = wifiManager.connectionInfo
|
SwitchPreference(
|
||||||
SwitchPreference(object : SwitchPreferenceModel {
|
object : SwitchPreferenceModel {
|
||||||
override val title = stringResource(R.string.wifi_allow_wep_networks)
|
override val title = stringResource(R.string.wifi_allow_wep_networks)
|
||||||
override val summary = { getSummary() }
|
override val summary = { getSummary(isWepSupported) }
|
||||||
override val checked = { checked }
|
override val checked = {
|
||||||
|
if (isWepSupported == true) isWepAllowed else isWepSupported
|
||||||
|
}
|
||||||
override val changeable: () -> Boolean
|
override val changeable: () -> Boolean
|
||||||
get() = { carrierAllowed }
|
get() = { isWepSupported == true }
|
||||||
|
|
||||||
override val onCheckedChange: (Boolean) -> Unit = { newChecked ->
|
override val onCheckedChange: (Boolean) -> Unit = { newChecked ->
|
||||||
|
val wifiInfo = wifiManager.connectionInfo
|
||||||
if (!newChecked && wifiInfo.currentSecurityType == WifiEntry.SECURITY_WEP) {
|
if (!newChecked && wifiInfo.currentSecurityType == WifiEntry.SECURITY_WEP) {
|
||||||
openDialog = true
|
openDialog = true
|
||||||
} else {
|
} else {
|
||||||
@@ -75,42 +84,41 @@ class WepNetworksPreferenceController(context: Context, preferenceKey: String) :
|
|||||||
if (openDialog) {
|
if (openDialog) {
|
||||||
SettingsAlertDialogWithIcon(
|
SettingsAlertDialogWithIcon(
|
||||||
onDismissRequest = { openDialog = false },
|
onDismissRequest = { openDialog = false },
|
||||||
confirmButton = AlertDialogButton(
|
confirmButton =
|
||||||
stringResource(R.string.sim_action_yes)
|
AlertDialogButton(stringResource(R.string.sim_action_yes)) {
|
||||||
) {
|
|
||||||
wifiManager.setWepAllowed(false)
|
wifiManager.setWepAllowed(false)
|
||||||
wepAllowedFlow.override(false)
|
wepAllowedFlow.override(false)
|
||||||
openDialog = false
|
openDialog = false
|
||||||
},
|
},
|
||||||
dismissButton =
|
dismissButton =
|
||||||
AlertDialogButton(
|
AlertDialogButton(stringResource(R.string.wifi_cancel)) { openDialog = false },
|
||||||
stringResource(R.string.wifi_cancel)
|
|
||||||
) { openDialog = false },
|
|
||||||
title = stringResource(R.string.wifi_settings_wep_networks_disconnect_title),
|
title = stringResource(R.string.wifi_settings_wep_networks_disconnect_title),
|
||||||
text = {
|
text = {
|
||||||
Text(
|
Text(
|
||||||
stringResource(R.string.wifi_settings_wep_networks_disconnect_summary),
|
stringResource(R.string.wifi_settings_wep_networks_disconnect_summary),
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
textAlign = TextAlign.Center
|
textAlign = TextAlign.Center,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSummary(): String = mContext.getString(
|
private fun getSummary(isWepSupported: Boolean?): String =
|
||||||
if (carrierAllowed) {
|
mContext.getString(
|
||||||
R.string.wifi_allow_wep_networks_summary
|
when (isWepSupported) {
|
||||||
} else {
|
true -> R.string.wifi_allow_wep_networks_summary
|
||||||
R.string.wifi_allow_wep_networks_summary_carrier_not_allow
|
false -> R.string.wifi_allow_wep_networks_summary_carrier_not_allow
|
||||||
}
|
null -> R.string.summary_placeholder
|
||||||
)
|
})
|
||||||
|
|
||||||
private val carrierAllowed: Boolean
|
private val isWepSupportedFlow =
|
||||||
get() = wifiManager.isWepSupported
|
flow { emit(wifiManager.isWepSupported) }.flowOn(Dispatchers.Default)
|
||||||
|
|
||||||
val wepAllowedFlow = OverridableFlow(callbackFlow {
|
val wepAllowedFlow =
|
||||||
|
OverridableFlow(
|
||||||
|
callbackFlow {
|
||||||
wifiManager.queryWepAllowed(Dispatchers.Default.asExecutor(), ::trySend)
|
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 com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
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.first
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
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.any
|
||||||
import org.mockito.kotlin.argumentCaptor
|
import org.mockito.kotlin.argumentCaptor
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
import org.mockito.kotlin.eq
|
import org.mockito.kotlin.eq
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import org.mockito.kotlin.spy
|
||||||
import org.mockito.kotlin.whenever
|
import org.mockito.kotlin.whenever
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class BackgroundInstalledAppsPageProviderTest {
|
class BackgroundInstalledAppsPageProviderTest {
|
||||||
@get:Rule
|
@get:Rule val composeTestRule = createComposeRule()
|
||||||
val composeTestRule = createComposeRule()
|
|
||||||
|
|
||||||
@get:Rule
|
private val mockPackageManager = mock<PackageManager>()
|
||||||
val mockito: MockitoRule = MockitoJUnit.rule()
|
|
||||||
|
|
||||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
private val context: Context =
|
||||||
|
spy(ApplicationProvider.getApplicationContext()) {
|
||||||
|
on { packageManager } doReturn mockPackageManager
|
||||||
|
}
|
||||||
|
|
||||||
@Mock
|
private val mockBackgroundInstallControlService = mock<IBackgroundInstallControlService>()
|
||||||
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 fakeNavControllerWrapper = FakeNavControllerWrapper()
|
private val fakeNavControllerWrapper = FakeNavControllerWrapper()
|
||||||
|
|
||||||
@Before
|
|
||||||
fun setup() {
|
|
||||||
whenever(mockContext.packageManager).thenReturn(mockPackageManager)
|
|
||||||
}
|
|
||||||
@Test
|
@Test
|
||||||
fun allAppListPageProvider_name() {
|
fun allAppListPageProvider_name() {
|
||||||
assertThat(BackgroundInstalledAppsPageProvider.name)
|
assertThat(BackgroundInstalledAppsPageProvider.name)
|
||||||
@@ -108,7 +95,7 @@ class BackgroundInstalledAppsPageProviderTest {
|
|||||||
|
|
||||||
setInjectEntry(false)
|
setInjectEntry(false)
|
||||||
|
|
||||||
composeTestRule.onNodeWithText("0 apps").assertIsDisplayed()
|
composeTestRule.waitUntilExists(hasText("0 apps"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -200,7 +187,8 @@ class BackgroundInstalledAppsPageProviderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun backgroundInstalledAppsWithGroupingListModel_transform() = runTest {
|
fun backgroundInstalledAppsWithGroupingListModel_transform() = runTest {
|
||||||
val listModel = BackgroundInstalledAppsWithGroupingListModel(mockContext)
|
val packageInfoFlagsCaptor = argumentCaptor<PackageManager.PackageInfoFlags>()
|
||||||
|
val listModel = BackgroundInstalledAppsWithGroupingListModel(context)
|
||||||
whenever(mockPackageManager.getPackageInfoAsUser(
|
whenever(mockPackageManager.getPackageInfoAsUser(
|
||||||
eq(TEST_PACKAGE_NAME),
|
eq(TEST_PACKAGE_NAME),
|
||||||
packageInfoFlagsCaptor.capture(),
|
packageInfoFlagsCaptor.capture(),
|
||||||
@@ -218,7 +206,7 @@ class BackgroundInstalledAppsPageProviderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun backgroundInstalledAppsWithGroupingListModel_filter() = runTest {
|
fun backgroundInstalledAppsWithGroupingListModel_filter() = runTest {
|
||||||
val listModel = BackgroundInstalledAppsWithGroupingListModel(mockContext)
|
val listModel = BackgroundInstalledAppsWithGroupingListModel(context)
|
||||||
listModel.setBackgroundInstallControlService(mockBackgroundInstallControlService)
|
listModel.setBackgroundInstallControlService(mockBackgroundInstallControlService)
|
||||||
whenever(mockBackgroundInstallControlService.getBackgroundInstalledPackages(
|
whenever(mockBackgroundInstallControlService.getBackgroundInstalledPackages(
|
||||||
PackageManager.MATCH_ALL.toLong(),
|
PackageManager.MATCH_ALL.toLong(),
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
package com.android.settings.wifi
|
package com.android.settings.wifi
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.net.wifi.WifiInfo
|
||||||
import android.net.wifi.WifiManager
|
import android.net.wifi.WifiManager
|
||||||
import androidx.compose.ui.test.assertIsOff
|
import androidx.compose.ui.test.assertIsOff
|
||||||
import androidx.compose.ui.test.assertIsOn
|
import androidx.compose.ui.test.assertIsOn
|
||||||
@@ -30,7 +31,6 @@ import androidx.preference.PreferenceManager
|
|||||||
import androidx.test.core.app.ApplicationProvider
|
import androidx.test.core.app.ApplicationProvider
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.android.settings.dashboard.DashboardFragment
|
|
||||||
import com.android.settings.spa.preference.ComposePreference
|
import com.android.settings.spa.preference.ComposePreference
|
||||||
import com.android.settingslib.spa.testutils.onDialogText
|
import com.android.settingslib.spa.testutils.onDialogText
|
||||||
import com.android.wifitrackerlib.WifiEntry
|
import com.android.wifitrackerlib.WifiEntry
|
||||||
@@ -45,28 +45,30 @@ import org.mockito.kotlin.doAnswer
|
|||||||
import org.mockito.kotlin.doReturn
|
import org.mockito.kotlin.doReturn
|
||||||
import org.mockito.kotlin.mock
|
import org.mockito.kotlin.mock
|
||||||
import org.mockito.kotlin.spy
|
import org.mockito.kotlin.spy
|
||||||
|
import org.mockito.kotlin.stub
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class WepNetworksPreferenceControllerTest {
|
class WepNetworksPreferenceControllerTest {
|
||||||
|
|
||||||
@get:Rule
|
@get:Rule val composeTestRule = createComposeRule()
|
||||||
val composeTestRule = createComposeRule()
|
|
||||||
|
|
||||||
private var wepAllowed = true
|
private var wepAllowed = true
|
||||||
|
|
||||||
private var mockWifiInfo = mock<android.net.wifi.WifiInfo> {
|
private var mockWifiInfo =
|
||||||
on { it.currentSecurityType } doReturn WifiEntry.SECURITY_EAP
|
mock<WifiInfo> {
|
||||||
on { it.ssid } doReturn SSID
|
on { currentSecurityType } doReturn WifiEntry.SECURITY_EAP
|
||||||
|
on { ssid } doReturn SSID
|
||||||
}
|
}
|
||||||
|
|
||||||
private var mockWifiManager = mock<WifiManager> {
|
private var mockWifiManager =
|
||||||
on { queryWepAllowed(any(), any()) } doAnswer {
|
mock<WifiManager> {
|
||||||
@Suppress("UNCHECKED_CAST")
|
on { queryWepAllowed(any(), any()) } doAnswer
|
||||||
val consumer = it.arguments[1] as Consumer<Boolean>
|
{
|
||||||
|
@Suppress("UNCHECKED_CAST") val consumer = it.arguments[1] as Consumer<Boolean>
|
||||||
consumer.accept(wepAllowed)
|
consumer.accept(wepAllowed)
|
||||||
}
|
}
|
||||||
on { it.isWepSupported } doReturn true
|
on { isWepSupported } doReturn true
|
||||||
on { it.connectionInfo } doReturn mockWifiInfo
|
on { connectionInfo } doReturn mockWifiInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
private var context: Context =
|
private var context: Context =
|
||||||
@@ -85,74 +87,101 @@ class WepNetworksPreferenceControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun wepAllowedTrue_turnOn() {
|
fun isChecked_wepSupportedAndAllowed_isOn() {
|
||||||
|
mockWifiManager.stub { on { isWepSupported } doReturn true }
|
||||||
wepAllowed = 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()
|
.assertIsOn()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun wepAllowedFalse_turnOff() {
|
fun isChecked_wepSupportedAndNotAllowed_isOff() {
|
||||||
|
mockWifiManager.stub { on { isWepSupported } doReturn true }
|
||||||
wepAllowed = false
|
wepAllowed = false
|
||||||
composeTestRule.setContent {
|
|
||||||
controller.Content()
|
composeTestRule.setContent { controller.Content() }
|
||||||
|
|
||||||
|
composeTestRule
|
||||||
|
.onNodeWithText(context.getString(R.string.wifi_allow_wep_networks))
|
||||||
|
.assertIsOff()
|
||||||
}
|
}
|
||||||
|
|
||||||
composeTestRule.onNodeWithText(context.getString(R.string.wifi_allow_wep_networks))
|
@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()
|
.assertIsOff()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun onClick_turnOn() {
|
fun onClick_turnOn() {
|
||||||
wepAllowed = false
|
wepAllowed = false
|
||||||
composeTestRule.setContent {
|
composeTestRule.setContent { controller.Content() }
|
||||||
controller.Content()
|
|
||||||
}
|
|
||||||
|
|
||||||
composeTestRule.onRoot().performClick()
|
composeTestRule.onRoot().performClick()
|
||||||
composeTestRule.onNodeWithText(context.getString(R.string.wifi_allow_wep_networks))
|
composeTestRule
|
||||||
|
.onNodeWithText(context.getString(R.string.wifi_allow_wep_networks))
|
||||||
.assertIsOn()
|
.assertIsOn()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun onClick_turnOff() {
|
fun onClick_turnOff() {
|
||||||
wepAllowed = true
|
wepAllowed = true
|
||||||
composeTestRule.setContent {
|
composeTestRule.setContent { controller.Content() }
|
||||||
controller.Content()
|
|
||||||
}
|
|
||||||
|
|
||||||
composeTestRule.onRoot().performClick()
|
composeTestRule.onRoot().performClick()
|
||||||
composeTestRule.onNodeWithText(context.getString(R.string.wifi_allow_wep_networks))
|
composeTestRule
|
||||||
|
.onNodeWithText(context.getString(R.string.wifi_allow_wep_networks))
|
||||||
.assertIsOff()
|
.assertIsOff()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun whenClick_wepAllowed_openDialog() {
|
fun whenClick_wepAllowed_openDialog() {
|
||||||
wepAllowed = true
|
wepAllowed = true
|
||||||
Mockito.`when`(mockWifiInfo.currentSecurityType).thenReturn(WifiEntry.SECURITY_WEP)
|
mockWifiInfo.stub {
|
||||||
composeTestRule.setContent {
|
on { currentSecurityType } doReturn WifiEntry.SECURITY_WEP
|
||||||
controller.Content()
|
|
||||||
}
|
}
|
||||||
|
composeTestRule.setContent { controller.Content() }
|
||||||
|
|
||||||
composeTestRule.onRoot().performClick()
|
composeTestRule.onRoot().performClick()
|
||||||
composeTestRule.onDialogText(context.getString(R.string.wifi_disconnect_button_text))
|
composeTestRule
|
||||||
|
.onDialogText(context.getString(R.string.wifi_disconnect_button_text))
|
||||||
.isDisplayed()
|
.isDisplayed()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun whenClick_wepDisallowed_openDialog() {
|
fun whenClick_wepDisallowed_openDialog() {
|
||||||
wepAllowed = false
|
wepAllowed = false
|
||||||
Mockito.`when`(mockWifiInfo.currentSecurityType).thenReturn(WifiEntry.SECURITY_WEP)
|
mockWifiInfo.stub {
|
||||||
composeTestRule.setContent {
|
on { currentSecurityType } doReturn WifiEntry.SECURITY_WEP
|
||||||
controller.Content()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
composeTestRule.setContent { controller.Content() }
|
||||||
|
|
||||||
composeTestRule.onRoot().performClick()
|
composeTestRule.onRoot().performClick()
|
||||||
composeTestRule.onDialogText(context.getString(R.string.wifi_disconnect_button_text))
|
composeTestRule
|
||||||
|
.onDialogText(context.getString(R.string.wifi_disconnect_button_text))
|
||||||
.isNotDisplayed()
|
.isNotDisplayed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -17,13 +17,11 @@
|
|||||||
package com.android.settings.localepicker;
|
package com.android.settings.localepicker;
|
||||||
|
|
||||||
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
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 com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
|
|
||||||
import android.app.settings.SettingsEnums;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
|
||||||
@@ -66,14 +64,6 @@ public class TermsOfAddressCategoryControllerTest {
|
|||||||
Locale.setDefault(mCacheLocale);
|
Locale.setDefault(mCacheLocale);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getAvailabilityStatus_returnUnavailable() {
|
|
||||||
Locale.setDefault(Locale.forLanguageTag("fr-CA"));
|
|
||||||
|
|
||||||
assertThat(mTermsOfAddressCategoryController.getAvailabilityStatus()).isEqualTo(
|
|
||||||
CONDITIONALLY_UNAVAILABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getAvailabilityStatus_returnAvailable() {
|
public void getAvailabilityStatus_returnAvailable() {
|
||||||
Locale.setDefault(Locale.forLanguageTag("fr-FR"));
|
Locale.setDefault(Locale.forLanguageTag("fr-FR"));
|
||||||
|
Reference in New Issue
Block a user