Snap for 11343021 from c36baa0e8d to 24Q2-release
Change-Id: I624b9ae0bdad5c0b76df84c765def6239c3bffa8
This commit is contained in:
@@ -4603,6 +4603,22 @@
|
||||
android:value="true" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="Settings$AudioStreamConfirmDialogActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/Transparent"
|
||||
android:permission="android.permission.BLUETOOTH_CONNECT"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize">
|
||||
<intent-filter android:priority="1">
|
||||
<action android:name="android.settings.AUDIO_STREAM_DIALOG" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
||||
android:value="com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamConfirmDialog" />
|
||||
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
|
||||
android:value="true" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".Settings$PreviouslyConnectedDeviceActivity"
|
||||
android:label="@string/connected_device_saved_title"
|
||||
|
||||
@@ -6,3 +6,10 @@ flag {
|
||||
description: "Gates whether to offload bluetooth operations to background thread"
|
||||
bug: "305636727"
|
||||
}
|
||||
|
||||
flag {
|
||||
name: "enable_bluetooth_profile_toggle_visibility_checker"
|
||||
namespace: "pixel_cross_device_control"
|
||||
description: "Gates whether to enable checker for bluetooth profile toggle visibility"
|
||||
bug: "321178209"
|
||||
}
|
||||
@@ -103,9 +103,11 @@
|
||||
<Switch
|
||||
android:id="@+id/phonebook_sharing_message_confirm_pin"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_weight="0"
|
||||
android:gravity="center_vertical" />
|
||||
android:gravity="center_vertical"
|
||||
android:contentDescription="@string/bluetooth_pairing_phonebook_toggle_text"
|
||||
android:switchMinWidth="48dp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@@ -11538,7 +11538,7 @@
|
||||
<!-- Button on the dual sim onboarding to go to next page. [CHAR LIMIT=30] -->
|
||||
<string name="sim_onboarding_next">Next</string>
|
||||
<!-- Text on the progressbar of dual sim onboarding for turning sim on. [CHAR LIMIT=30] -->
|
||||
<string name="sim_onboarding_profressbar_turning_sim_on">Turning on <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g>…</string>
|
||||
<string name="sim_onboarding_progressbar_turning_sim_on">Turning on <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g>…</string>
|
||||
<!-- Title of service provider name(SPN) at mobile network settings page. [CHAR LIMIT=30] -->
|
||||
<string name="mobile_network_spn_title">Mobile network</string>
|
||||
<!-- Title of phone number at mobile network settings page. [CHAR LIMIT=30] -->
|
||||
|
||||
@@ -78,14 +78,14 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_weight="1"
|
||||
android:visibility="gone"/>
|
||||
android:visibility="invisible"/>
|
||||
<Button
|
||||
android:id="@+id/right_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginRight="16dp"
|
||||
android:visibility="gone"/>
|
||||
android:visibility="invisible"/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -399,6 +399,7 @@ public class Settings extends SettingsActivity {
|
||||
public static class StylusUsiDetailsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class BluetoothBroadcastActivity extends SettingsActivity { /* empty */ }
|
||||
public static class BluetoothFindBroadcastsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class AudioStreamConfirmDialogActivity extends SettingsActivity { /* empty */ }
|
||||
public static class WifiCallingDisclaimerActivity extends SettingsActivity { /* empty */ }
|
||||
public static class MobileNetworkListActivity extends SettingsActivity {}
|
||||
public static class PowerMenuSettingsActivity extends SettingsActivity {}
|
||||
|
||||
@@ -37,6 +37,8 @@ import androidx.preference.TwoStatePreference;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.SettingsUIDeviceConfig;
|
||||
import com.android.settings.flags.Flags;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settingslib.bluetooth.A2dpProfile;
|
||||
import com.android.settingslib.bluetooth.BluetoothUtils;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
@@ -49,11 +51,14 @@ import com.android.settingslib.bluetooth.MapProfile;
|
||||
import com.android.settingslib.bluetooth.PanProfile;
|
||||
import com.android.settingslib.bluetooth.PbapServerProfile;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
import com.android.settingslib.utils.ThreadUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* This class adds switches for toggling the individual profiles that a Bluetooth device
|
||||
@@ -79,6 +84,8 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
|
||||
private static final String LE_AUDIO_TOGGLE_VISIBLE_PROPERTY =
|
||||
"persist.bluetooth.leaudio.toggle_visible";
|
||||
|
||||
private final AtomicReference<Set<String>> mInvisiblePreferenceKey = new AtomicReference<>();
|
||||
|
||||
private LocalBluetoothManager mManager;
|
||||
private LocalBluetoothProfileManager mProfileManager;
|
||||
private CachedBluetoothDevice mCachedDevice;
|
||||
@@ -547,6 +554,22 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
|
||||
*/
|
||||
@Override
|
||||
protected void refresh() {
|
||||
if (Flags.enableBluetoothProfileToggleVisibilityChecker()) {
|
||||
ThreadUtils.postOnBackgroundThread(
|
||||
() -> {
|
||||
mInvisiblePreferenceKey.set(
|
||||
FeatureFactory.getFeatureFactory()
|
||||
.getBluetoothFeatureProvider()
|
||||
.getInvisibleProfilePreferenceKeys(
|
||||
mContext, mCachedDevice.getDevice()));
|
||||
ThreadUtils.postOnMainThread(this::refreshUi);
|
||||
});
|
||||
} else {
|
||||
refreshUi();
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshUi() {
|
||||
for (LocalBluetoothProfile profile : getProfiles()) {
|
||||
if (profile == null || !profile.isProfileReady()) {
|
||||
continue;
|
||||
@@ -577,6 +600,16 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
|
||||
preference.setSelectable(false);
|
||||
mProfilesContainer.addPreference(preference);
|
||||
}
|
||||
|
||||
if (Flags.enableBluetoothProfileToggleVisibilityChecker()) {
|
||||
Set<String> invisibleKeys = mInvisiblePreferenceKey.get();
|
||||
if (invisibleKeys != null) {
|
||||
for (int i = 0; i < mProfilesContainer.getPreferenceCount(); ++i) {
|
||||
Preference pref = mProfilesContainer.getPreference(i);
|
||||
pref.setVisible(pref.isVisible() && !invisibleKeys.contains(pref.getKey()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -27,6 +27,7 @@ import androidx.preference.Preference;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Provider for bluetooth related features.
|
||||
@@ -73,4 +74,14 @@ public interface BluetoothFeatureProvider {
|
||||
* @return the extra bluetooth preference list
|
||||
*/
|
||||
List<Preference> getBluetoothExtraOptions(Context context, CachedBluetoothDevice device);
|
||||
|
||||
/**
|
||||
* Gets the bluetooth profile preference keys which should be hidden in the device details page.
|
||||
*
|
||||
* @param context Context
|
||||
* @param bluetoothDevice the bluetooth device
|
||||
* @return the profiles which should be hidden
|
||||
*/
|
||||
Set<String> getInvisibleProfilePreferenceKeys(
|
||||
Context context, BluetoothDevice bluetoothDevice);
|
||||
}
|
||||
|
||||
@@ -29,8 +29,10 @@ import com.android.settingslib.bluetooth.BluetoothUtils;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Impl of {@link BluetoothFeatureProvider}
|
||||
@@ -65,4 +67,10 @@ public class BluetoothFeatureProviderImpl implements BluetoothFeatureProvider {
|
||||
CachedBluetoothDevice device) {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getInvisibleProfilePreferenceKeys(
|
||||
Context context, BluetoothDevice bluetoothDevice) {
|
||||
return ImmutableSet.of();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,16 +139,20 @@ public class AudioSharingCompatibilityPreferenceController extends TogglePrefere
|
||||
|
||||
@Override
|
||||
public boolean isChecked() {
|
||||
// TODO: return real compatibility config.
|
||||
return false;
|
||||
return mBroadcast != null && mBroadcast.getImproveCompatibility();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setChecked(boolean isChecked) {
|
||||
if (mBroadcast == null) {
|
||||
if (mBroadcast == null || mBroadcast.getImproveCompatibility() == isChecked) {
|
||||
Log.d(
|
||||
TAG,
|
||||
"Skip setting improveCompatibility, unchanged = "
|
||||
+ (mBroadcast.getImproveCompatibility() == isChecked));
|
||||
return false;
|
||||
}
|
||||
// TODO: set real compatibility config.
|
||||
mBroadcast.setImproveCompatibility(isChecked);
|
||||
// TODO: call updateBroadcast once framework change ready.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ import android.bluetooth.BluetoothLeBroadcast;
|
||||
import android.bluetooth.BluetoothLeBroadcastAssistant;
|
||||
import android.bluetooth.BluetoothLeBroadcastMetadata;
|
||||
import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
||||
import android.bluetooth.BluetoothLeBroadcastSubgroupSettings;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
@@ -419,7 +418,7 @@ public class AudioSharingDevicePreferenceController extends BasePreferenceContro
|
||||
if (isBroadcasting()) {
|
||||
// Show stop audio sharing dialog when an ineligible (non LE audio) remote device
|
||||
// connected during a sharing session.
|
||||
ThreadUtils.postOnMainThread(
|
||||
postOnMainThread(
|
||||
() -> {
|
||||
closeOpeningDialogs();
|
||||
AudioSharingStopDialogFragment.show(
|
||||
@@ -443,8 +442,9 @@ public class AudioSharingDevicePreferenceController extends BasePreferenceContro
|
||||
Map<Integer, List<CachedBluetoothDevice>> groupedDevices =
|
||||
AudioSharingUtils.fetchConnectedDevicesByGroupId(mLocalBtManager);
|
||||
if (isBroadcasting()) {
|
||||
if (groupedDevices.containsKey(cachedDevice.getGroupId())
|
||||
&& groupedDevices.get(cachedDevice.getGroupId()).stream()
|
||||
int groupId = AudioSharingUtils.getGroupId(cachedDevice);
|
||||
if (groupedDevices.containsKey(groupId)
|
||||
&& groupedDevices.get(groupId).stream()
|
||||
.anyMatch(
|
||||
device ->
|
||||
AudioSharingUtils.hasBroadcastSource(
|
||||
@@ -464,7 +464,7 @@ public class AudioSharingDevicePreferenceController extends BasePreferenceContro
|
||||
// Show audio sharing switch dialog when the third eligible (LE audio) remote device
|
||||
// connected during a sharing session.
|
||||
if (deviceItemsInSharingSession.size() >= 2) {
|
||||
ThreadUtils.postOnMainThread(
|
||||
postOnMainThread(
|
||||
() -> {
|
||||
closeOpeningDialogs();
|
||||
AudioSharingDisconnectDialogFragment.show(
|
||||
@@ -495,7 +495,7 @@ public class AudioSharingDevicePreferenceController extends BasePreferenceContro
|
||||
} else {
|
||||
// Show audio sharing join dialog when the first or second eligible (LE audio)
|
||||
// remote device connected during a sharing session.
|
||||
ThreadUtils.postOnMainThread(
|
||||
postOnMainThread(
|
||||
() -> {
|
||||
closeOpeningDialogs();
|
||||
AudioSharingJoinDialogFragment.show(
|
||||
@@ -516,7 +516,8 @@ public class AudioSharingDevicePreferenceController extends BasePreferenceContro
|
||||
for (List<CachedBluetoothDevice> devices : groupedDevices.values()) {
|
||||
// Use random device in the group within the sharing session to represent the group.
|
||||
CachedBluetoothDevice device = devices.get(0);
|
||||
if (device.getGroupId() == cachedDevice.getGroupId()) {
|
||||
if (AudioSharingUtils.getGroupId(device)
|
||||
== AudioSharingUtils.getGroupId(cachedDevice)) {
|
||||
continue;
|
||||
}
|
||||
deviceItems.add(AudioSharingUtils.buildAudioSharingDeviceItem(device));
|
||||
@@ -524,7 +525,7 @@ public class AudioSharingDevicePreferenceController extends BasePreferenceContro
|
||||
// Show audio sharing join dialog when the second eligible (LE audio) remote
|
||||
// device connect and no sharing session.
|
||||
if (deviceItems.size() == 1) {
|
||||
ThreadUtils.postOnMainThread(
|
||||
postOnMainThread(
|
||||
() -> {
|
||||
closeOpeningDialogs();
|
||||
AudioSharingJoinDialogFragment.show(
|
||||
@@ -539,8 +540,7 @@ public class AudioSharingDevicePreferenceController extends BasePreferenceContro
|
||||
mTargetSinks.add(device.getDevice());
|
||||
}
|
||||
}
|
||||
mBroadcast.startPrivateBroadcast(
|
||||
BluetoothLeBroadcastSubgroupSettings.QUALITY_HIGH);
|
||||
mBroadcast.startPrivateBroadcast();
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -601,4 +601,8 @@ public class AudioSharingDevicePreferenceController extends BasePreferenceContro
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void postOnMainThread(@NonNull Runnable runnable) {
|
||||
mContext.getMainExecutor().execute(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,11 +77,12 @@ public class AudioSharingDeviceVolumeGroupController extends AudioSharingBasePre
|
||||
CachedBluetoothDevice cachedDevice =
|
||||
mLocalBtManager.getCachedDeviceManager().findDevice(device);
|
||||
if (cachedDevice == null) return;
|
||||
mValueMap.put(cachedDevice.getGroupId(), volume);
|
||||
int groupId = AudioSharingUtils.getGroupId(cachedDevice);
|
||||
mValueMap.put(groupId, volume);
|
||||
for (AudioSharingDeviceVolumePreference preference : mVolumePreferences) {
|
||||
if (preference.getCachedDevice() != null
|
||||
&& preference.getCachedDevice().getGroupId()
|
||||
== cachedDevice.getGroupId()) {
|
||||
&& AudioSharingUtils.getGroupId(preference.getCachedDevice())
|
||||
== groupId) {
|
||||
// If the callback return invalid volume, try to
|
||||
// get the volume from AudioManager.STREAM_MUSIC
|
||||
int finalVolume = getAudioVolumeIfNeeded(volume);
|
||||
@@ -270,7 +271,7 @@ public class AudioSharingDeviceVolumeGroupController extends AudioSharingBasePre
|
||||
if (volumePref.getProgress() > 0) return;
|
||||
CachedBluetoothDevice device = volumePref.getCachedDevice();
|
||||
if (device == null) return;
|
||||
int volume = mValueMap.getOrDefault(device.getGroupId(), -1);
|
||||
int volume = mValueMap.getOrDefault(AudioSharingUtils.getGroupId(device), -1);
|
||||
// If the volume is invalid, try to get the volume from AudioManager.STREAM_MUSIC
|
||||
int finalVolume = getAudioVolumeIfNeeded(volume);
|
||||
Log.d(
|
||||
|
||||
@@ -22,7 +22,6 @@ import android.bluetooth.BluetoothLeBroadcast;
|
||||
import android.bluetooth.BluetoothLeBroadcastAssistant;
|
||||
import android.bluetooth.BluetoothLeBroadcastMetadata;
|
||||
import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
||||
import android.bluetooth.BluetoothLeBroadcastSubgroupSettings;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -339,7 +338,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
||||
}
|
||||
mDeviceItemsForSharing.remove(0);
|
||||
}
|
||||
mBroadcast.startPrivateBroadcast(BluetoothLeBroadcastSubgroupSettings.QUALITY_HIGH);
|
||||
mBroadcast.startPrivateBroadcast();
|
||||
}
|
||||
|
||||
private void stopAudioSharing() {
|
||||
|
||||
@@ -29,10 +29,11 @@ import com.android.settings.flags.Flags;
|
||||
import com.android.settingslib.bluetooth.BluetoothUtils;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||
import com.android.settingslib.bluetooth.LeAudioProfile;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.settingslib.utils.ThreadUtils;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothProfile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@@ -57,10 +58,16 @@ public class AudioSharingUtils {
|
||||
public static Map<Integer, List<CachedBluetoothDevice>> fetchConnectedDevicesByGroupId(
|
||||
LocalBluetoothManager localBtManager) {
|
||||
Map<Integer, List<CachedBluetoothDevice>> groupedDevices = new HashMap<>();
|
||||
if (localBtManager == null) {
|
||||
Log.d(TAG, "Skip fetchConnectedDevicesByGroupId due to bt manager is null");
|
||||
return groupedDevices;
|
||||
}
|
||||
LocalBluetoothLeBroadcastAssistant assistant =
|
||||
localBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
|
||||
if (assistant == null) return groupedDevices;
|
||||
// TODO: filter out devices with le audio disabled.
|
||||
if (assistant == null) {
|
||||
Log.d(TAG, "Skip fetchConnectedDevicesByGroupId due to assistant profile is null");
|
||||
return groupedDevices;
|
||||
}
|
||||
List<BluetoothDevice> connectedDevices = assistant.getConnectedDevices();
|
||||
CachedBluetoothDeviceManager cacheManager = localBtManager.getCachedDeviceManager();
|
||||
for (BluetoothDevice device : connectedDevices) {
|
||||
@@ -69,7 +76,7 @@ public class AudioSharingUtils {
|
||||
Log.d(TAG, "Skip device due to not being cached: " + device.getAnonymizedAddress());
|
||||
continue;
|
||||
}
|
||||
int groupId = cachedDevice.getGroupId();
|
||||
int groupId = getGroupId(cachedDevice);
|
||||
if (groupId == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
|
||||
Log.d(
|
||||
TAG,
|
||||
@@ -105,9 +112,6 @@ public class AudioSharingUtils {
|
||||
Map<Integer, List<CachedBluetoothDevice>> groupedConnectedDevices,
|
||||
boolean filterByInSharing) {
|
||||
List<CachedBluetoothDevice> orderedDevices = new ArrayList<>();
|
||||
LocalBluetoothLeBroadcastAssistant assistant =
|
||||
localBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
|
||||
if (assistant == null) return orderedDevices;
|
||||
for (List<CachedBluetoothDevice> devices : groupedConnectedDevices.values()) {
|
||||
CachedBluetoothDevice leadDevice = null;
|
||||
for (CachedBluetoothDevice device : devices) {
|
||||
@@ -191,7 +195,7 @@ public class AudioSharingUtils {
|
||||
CachedBluetoothDevice cachedDevice) {
|
||||
return new AudioSharingDeviceItem(
|
||||
cachedDevice.getName(),
|
||||
cachedDevice.getGroupId(),
|
||||
getGroupId(cachedDevice),
|
||||
isActiveLeAudioDevice(cachedDevice));
|
||||
}
|
||||
|
||||
@@ -204,19 +208,36 @@ public class AudioSharingUtils {
|
||||
*/
|
||||
public static boolean hasBroadcastSource(
|
||||
CachedBluetoothDevice cachedDevice, LocalBluetoothManager localBtManager) {
|
||||
if (localBtManager == null) {
|
||||
Log.d(TAG, "Skip check hasBroadcastSource due to bt manager is null");
|
||||
return false;
|
||||
}
|
||||
LocalBluetoothLeBroadcastAssistant assistant =
|
||||
localBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
|
||||
if (assistant == null) {
|
||||
Log.d(TAG, "Skip check hasBroadcastSource due to assistant profile is null");
|
||||
return false;
|
||||
}
|
||||
List<BluetoothLeBroadcastReceiveState> sourceList =
|
||||
assistant.getAllSources(cachedDevice.getDevice());
|
||||
if (!sourceList.isEmpty()) return true;
|
||||
if (!sourceList.isEmpty()) {
|
||||
Log.d(
|
||||
TAG,
|
||||
"Lead device has broadcast source, device = "
|
||||
+ cachedDevice.getDevice().getAnonymizedAddress());
|
||||
return true;
|
||||
}
|
||||
// Return true if member device is in broadcast.
|
||||
for (CachedBluetoothDevice device : cachedDevice.getMemberDevice()) {
|
||||
List<BluetoothLeBroadcastReceiveState> list =
|
||||
assistant.getAllSources(device.getDevice());
|
||||
if (!list.isEmpty()) return true;
|
||||
if (!list.isEmpty()) {
|
||||
Log.d(
|
||||
TAG,
|
||||
"Member device has broadcast source, device = "
|
||||
+ device.getDevice().getAnonymizedAddress());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -257,8 +278,8 @@ public class AudioSharingUtils {
|
||||
|
||||
/** Toast message on main thread. */
|
||||
public static void toastMessage(Context context, String message) {
|
||||
ThreadUtils.postOnMainThread(
|
||||
() -> Toast.makeText(context, message, Toast.LENGTH_LONG).show());
|
||||
context.getMainExecutor()
|
||||
.execute(() -> Toast.makeText(context, message, Toast.LENGTH_LONG).show());
|
||||
}
|
||||
|
||||
/** Returns if the le audio sharing is enabled. */
|
||||
@@ -273,7 +294,10 @@ public class AudioSharingUtils {
|
||||
|
||||
/** Automatically update active device if needed. */
|
||||
public static void updateActiveDeviceIfNeeded(LocalBluetoothManager localBtManager) {
|
||||
if (localBtManager == null) return;
|
||||
if (localBtManager == null) {
|
||||
Log.d(TAG, "Skip updateActiveDeviceIfNeeded due to bt manager is null");
|
||||
return;
|
||||
}
|
||||
Map<Integer, List<CachedBluetoothDevice>> groupedConnectedDevices =
|
||||
fetchConnectedDevicesByGroupId(localBtManager);
|
||||
List<CachedBluetoothDevice> devicesInSharing =
|
||||
@@ -283,6 +307,7 @@ public class AudioSharingUtils {
|
||||
List<BluetoothDevice> devices =
|
||||
BluetoothAdapter.getDefaultAdapter().getMostRecentlyConnectedDevices();
|
||||
CachedBluetoothDevice targetDevice = null;
|
||||
// Find the earliest connected device in sharing session.
|
||||
int targetDeviceIdx = -1;
|
||||
for (CachedBluetoothDevice device : devicesInSharing) {
|
||||
if (devices.contains(device.getDevice())) {
|
||||
@@ -299,6 +324,14 @@ public class AudioSharingUtils {
|
||||
"updateActiveDeviceIfNeeded, set active device: "
|
||||
+ targetDevice.getDevice().getAnonymizedAddress());
|
||||
targetDevice.setActive();
|
||||
} else {
|
||||
Log.d(
|
||||
TAG,
|
||||
"updateActiveDeviceIfNeeded, skip set active device: "
|
||||
+ (targetDevice == null
|
||||
? "null"
|
||||
: (targetDevice.getDevice().getAnonymizedAddress()
|
||||
+ " is already active")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,9 +345,38 @@ public class AudioSharingUtils {
|
||||
|
||||
/** Stops the latest broadcast. */
|
||||
public static void stopBroadcasting(LocalBluetoothManager manager) {
|
||||
if (manager == null) return;
|
||||
if (manager == null) {
|
||||
Log.d(TAG, "Skip stop broadcasting due to bt manager is null");
|
||||
return;
|
||||
}
|
||||
LocalBluetoothLeBroadcast broadcast =
|
||||
manager.getProfileManager().getLeAudioBroadcastProfile();
|
||||
if (broadcast == null) {
|
||||
Log.d(TAG, "Skip stop broadcasting due to broadcast profile is null");
|
||||
}
|
||||
broadcast.stopBroadcast(broadcast.getLatestBroadcastId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get CSIP group id for {@link CachedBluetoothDevice}.
|
||||
*
|
||||
* <p>If CachedBluetoothDevice#getGroupId is invalid, fetch group id from
|
||||
* LeAudioProfile#getGroupId.
|
||||
*/
|
||||
public static int getGroupId(CachedBluetoothDevice cachedDevice) {
|
||||
int groupId = cachedDevice.getGroupId();
|
||||
String anonymizedAddress = cachedDevice.getDevice().getAnonymizedAddress();
|
||||
if (groupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
|
||||
Log.d(TAG, "getGroupId by CSIP profile for device: " + anonymizedAddress);
|
||||
return groupId;
|
||||
}
|
||||
for (LocalBluetoothProfile profile : cachedDevice.getProfiles()) {
|
||||
if (profile instanceof LeAudioProfile) {
|
||||
Log.d(TAG, "getGroupId by LEA profile for device: " + anonymizedAddress);
|
||||
return ((LeAudioProfile) profile).getGroupId(cachedDevice.getDevice());
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "getGroupId return invalid id for device: " + anonymizedAddress);
|
||||
return BluetoothCsipSetCoordinator.GROUP_ID_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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.connecteddevice.audiosharing.audiostreams;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.bluetooth.BluetoothLeBroadcastMetadata;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.bluetooth.Utils;
|
||||
import com.android.settings.connecteddevice.audiosharing.audiostreams.qrcode.QrCodeScanModeFragment;
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||
import com.android.settingslib.bluetooth.BluetoothLeBroadcastMetadataExt;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
public class AudioStreamConfirmDialog extends InstrumentedDialogFragment {
|
||||
public static final String KEY_BROADCAST_METADATA = "key_broadcast_metadata";
|
||||
private static final String TAG = "AudioStreamConfirmDialog";
|
||||
private Activity mActivity;
|
||||
private String mBroadcastMetadataStr;
|
||||
private BluetoothLeBroadcastMetadata mBroadcastMetadata;
|
||||
private boolean mIsRequestValid = false;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setShowsDialog(true);
|
||||
mActivity = getActivity();
|
||||
if (mActivity == null) {
|
||||
Log.w(TAG, "onCreate() mActivity is null!");
|
||||
return;
|
||||
}
|
||||
mBroadcastMetadataStr =
|
||||
mActivity.getIntent().getStringExtra(QrCodeScanModeFragment.KEY_BROADCAST_METADATA);
|
||||
if (Strings.isNullOrEmpty(mBroadcastMetadataStr)) {
|
||||
Log.w(TAG, "onCreate() mBroadcastMetadataStr is null or empty!");
|
||||
return;
|
||||
}
|
||||
mBroadcastMetadata =
|
||||
BluetoothLeBroadcastMetadataExt.INSTANCE.convertToBroadcastMetadata(
|
||||
mBroadcastMetadataStr);
|
||||
if (mBroadcastMetadata == null) {
|
||||
Log.w(TAG, "onCreate() mBroadcastMetadata is null!");
|
||||
} else {
|
||||
// Warm up LE_AUDIO_BROADCAST_ASSISTANT service
|
||||
Utils.getLocalBluetoothManager(mActivity);
|
||||
mIsRequestValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
return mIsRequestValid ? getConfirmDialog() : getErrorDialog();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
// TODO(chelseahao): update metrics id
|
||||
return 0;
|
||||
}
|
||||
|
||||
private Dialog getConfirmDialog() {
|
||||
return new AudioStreamsDialogFragment.DialogBuilder(mActivity)
|
||||
.setTitle("Listen to audio stream")
|
||||
.setSubTitle1(mBroadcastMetadata.getBroadcastName())
|
||||
.setSubTitle2(
|
||||
"The audio stream will play on the active LE audio device. Use this device"
|
||||
+ " to control the volume.")
|
||||
.setLeftButtonText("Cancel")
|
||||
.setLeftButtonOnClickListener(
|
||||
unused -> {
|
||||
dismiss();
|
||||
mActivity.finish();
|
||||
})
|
||||
.setRightButtonText("Listen")
|
||||
.setRightButtonOnClickListener(
|
||||
unused -> {
|
||||
launchAudioStreamsActivity();
|
||||
dismiss();
|
||||
mActivity.finish();
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
private Dialog getErrorDialog() {
|
||||
return new AudioStreamsDialogFragment.DialogBuilder(mActivity)
|
||||
.setTitle("Can't listen to audio stream")
|
||||
.setSubTitle1("Can't play this audio stream. Learn more")
|
||||
.setRightButtonText("Close")
|
||||
.setRightButtonOnClickListener(
|
||||
unused -> {
|
||||
dismiss();
|
||||
mActivity.finish();
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
private void launchAudioStreamsActivity() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(KEY_BROADCAST_METADATA, mBroadcastMetadataStr);
|
||||
|
||||
new SubSettingLauncher(mActivity)
|
||||
.setTitleRes(R.string.bluetooth_find_broadcast_title)
|
||||
.setDestination(AudioStreamsDashboardFragment.class.getName())
|
||||
.setArguments(bundle)
|
||||
.setSourceMetricsCategory(SettingsEnums.PAGE_UNKNOWN)
|
||||
.launch();
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,8 @@ import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settingslib.bluetooth.BluetoothLeBroadcastMetadataExt;
|
||||
import com.android.settingslib.bluetooth.BluetoothUtils;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
public class AudioStreamsDashboardFragment extends DashboardFragment {
|
||||
private static final String TAG = "AudioStreamsDashboardFrag";
|
||||
private static final boolean DEBUG = BluetoothUtils.D;
|
||||
@@ -72,6 +74,21 @@ public class AudioStreamsDashboardFragment extends DashboardFragment {
|
||||
use(AudioStreamsScanQrCodeController.class).setFragment(this);
|
||||
mAudioStreamsProgressCategoryController = use(AudioStreamsProgressCategoryController.class);
|
||||
mAudioStreamsProgressCategoryController.setFragment(this);
|
||||
|
||||
if (getArguments() != null) {
|
||||
String broadcastMetadataStr =
|
||||
getArguments().getString(AudioStreamConfirmDialog.KEY_BROADCAST_METADATA);
|
||||
if (!Strings.isNullOrEmpty(broadcastMetadataStr)) {
|
||||
BluetoothLeBroadcastMetadata broadcastMetadata =
|
||||
BluetoothLeBroadcastMetadataExt.INSTANCE.convertToBroadcastMetadata(
|
||||
broadcastMetadataStr);
|
||||
if (broadcastMetadata == null) {
|
||||
Log.w(TAG, "onAttach() broadcastMetadata is null!");
|
||||
} else {
|
||||
mAudioStreamsProgressCategoryController.setSourceFromQrCode(broadcastMetadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -84,6 +84,7 @@ import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFrag
|
||||
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
|
||||
import com.android.settings.connecteddevice.NfcAndPaymentFragment;
|
||||
import com.android.settings.connecteddevice.PreviouslyConnectedDeviceDashboardFragment;
|
||||
import com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamConfirmDialog;
|
||||
import com.android.settings.connecteddevice.stylus.StylusUsiDetailsFragment;
|
||||
import com.android.settings.connecteddevice.usb.UsbDetailsFragment;
|
||||
import com.android.settings.datausage.DataSaverSummary;
|
||||
@@ -349,6 +350,7 @@ public class SettingsGateway {
|
||||
DataUsageList.class.getName(),
|
||||
ToggleBackupSettingFragment.class.getName(),
|
||||
PreviouslyConnectedDeviceDashboardFragment.class.getName(),
|
||||
AudioStreamConfirmDialog.class.getName(),
|
||||
BatterySaverScheduleSettings.class.getName(),
|
||||
MobileNetworkListFragment.class.getName(),
|
||||
PowerMenuSettings.class.getName(),
|
||||
|
||||
126
src/com/android/settings/network/SimOnboardingService.kt
Normal file
126
src/com/android/settings/network/SimOnboardingService.kt
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.network
|
||||
|
||||
import android.content.Context
|
||||
import android.telephony.SubscriptionInfo
|
||||
import android.telephony.SubscriptionManager
|
||||
import android.telephony.TelephonyManager
|
||||
import android.telephony.UiccCardInfo
|
||||
import android.telephony.UiccSlotInfo
|
||||
import android.util.Log
|
||||
import com.android.settingslib.utils.ThreadUtils
|
||||
|
||||
|
||||
private const val TAG = "SimOnboardingService"
|
||||
private const val INVALID = -1
|
||||
|
||||
class SimOnboardingService {
|
||||
var subscriptionManager:SubscriptionManager? = null
|
||||
var telephonyManager:TelephonyManager? = null
|
||||
|
||||
var targetSubId: Int = INVALID
|
||||
var targetSubInfo: SubscriptionInfo? = null
|
||||
var availableSubInfoList: List<SubscriptionInfo> = listOf()
|
||||
var activeSubInfoList: List<SubscriptionInfo> = listOf()
|
||||
var slotInfoList: List<UiccSlotInfo> = listOf()
|
||||
var uiccCardInfoList: List<UiccCardInfo> = listOf()
|
||||
var selectedSubInfoList: MutableList<SubscriptionInfo> = mutableListOf()
|
||||
var targetPrimarySimCalls: Int = -1
|
||||
var targetPrimarySimTexts: Int = -1
|
||||
var targetPrimarySimMobileData: Int = -1
|
||||
var isMultipleEnabledProfilesSupported: Boolean = false
|
||||
get() {
|
||||
if (uiccCardInfoList.isEmpty()) {
|
||||
Log.w(TAG, "UICC cards info list is empty.")
|
||||
return false
|
||||
}
|
||||
return uiccCardInfoList.stream()
|
||||
.anyMatch { cardInfo: UiccCardInfo -> cardInfo.isMultipleEnabledProfilesSupported }
|
||||
}
|
||||
var renameMutableMap : MutableMap<Int, String> = mutableMapOf()
|
||||
|
||||
fun isValid(): Boolean {
|
||||
return targetSubId != INVALID
|
||||
&& targetSubInfo != null
|
||||
&& activeSubInfoList.isNotEmpty()
|
||||
&& slotInfoList.isNotEmpty()
|
||||
&& selectedSubInfoList.isNotEmpty()
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
targetSubId = -1
|
||||
targetSubInfo = null
|
||||
availableSubInfoList = listOf()
|
||||
activeSubInfoList = listOf()
|
||||
slotInfoList = listOf()
|
||||
uiccCardInfoList = listOf()
|
||||
selectedSubInfoList = mutableListOf()
|
||||
targetPrimarySimCalls = -1
|
||||
targetPrimarySimTexts = -1
|
||||
targetPrimarySimMobileData = -1
|
||||
renameMutableMap.clear()
|
||||
}
|
||||
|
||||
fun initData(inputTargetSubId:Int,context: Context) {
|
||||
targetSubId = inputTargetSubId
|
||||
subscriptionManager = context.getSystemService(SubscriptionManager::class.java)
|
||||
telephonyManager = context.getSystemService(TelephonyManager::class.java)
|
||||
|
||||
ThreadUtils.postOnBackgroundThread {
|
||||
activeSubInfoList = SubscriptionUtil.getActiveSubscriptions(subscriptionManager)
|
||||
availableSubInfoList = SubscriptionUtil.getAvailableSubscriptions(context)
|
||||
targetSubInfo = availableSubInfoList.find { subInfo -> subInfo.subscriptionId == targetSubId }
|
||||
Log.d(
|
||||
TAG, "targetSubId: $targetSubId" + ", targetSubInfo: $targetSubInfo" +
|
||||
". activeSubInfoList: $activeSubInfoList"
|
||||
)
|
||||
slotInfoList = telephonyManager?.uiccSlotsInfo?.toList() ?: listOf()
|
||||
Log.d(TAG, "slotInfoList: $slotInfoList.")
|
||||
uiccCardInfoList = telephonyManager?.uiccCardsInfo!!
|
||||
Log.d(TAG, "uiccCardInfoList: $uiccCardInfoList")
|
||||
|
||||
Log.d(TAG, "isMultipleEnabledProfilesSupported: $isMultipleEnabledProfilesSupported")
|
||||
}
|
||||
}
|
||||
|
||||
fun getSelectableSubscriptionInfo(): List<SubscriptionInfo> {
|
||||
var list: MutableList<SubscriptionInfo> = mutableListOf()
|
||||
list.addAll(activeSubInfoList)
|
||||
if (!list.contains(targetSubInfo)) {
|
||||
targetSubInfo?.let { list.add(it) }
|
||||
}
|
||||
|
||||
Log.d(TAG, "list: $list")
|
||||
return list.toList()
|
||||
}
|
||||
|
||||
fun addItemForRenaming(subInfo: SubscriptionInfo, newName: String) {
|
||||
if (subInfo.displayName == newName) {
|
||||
return
|
||||
}
|
||||
renameMutableMap[subInfo.subscriptionId] = newName
|
||||
}
|
||||
|
||||
fun getSubscriptionInfoDisplayName(subInfo: SubscriptionInfo): String {
|
||||
return renameMutableMap[subInfo.subscriptionId] ?: subInfo.displayName.toString()
|
||||
}
|
||||
|
||||
fun startActivatingSim(callback:() -> Unit){
|
||||
// TODO: start to activate sim
|
||||
}
|
||||
}
|
||||
@@ -42,12 +42,14 @@ import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.internal.telephony.MccTable;
|
||||
import com.android.internal.telephony.flags.Flags;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.flags.Flags;
|
||||
import com.android.settings.network.helper.SelectableSubscriptions;
|
||||
import com.android.settings.network.helper.SubscriptionAnnotation;
|
||||
import com.android.settings.network.telephony.DeleteEuiccSubscriptionDialogActivity;
|
||||
import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity;
|
||||
import com.android.settings.spa.SpaActivity;
|
||||
import com.android.settings.spa.network.SimOnboardingPageProvider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -335,8 +337,16 @@ public class SubscriptionUtil {
|
||||
|
||||
if (duplicateOriginalNames.contains(info.originalName)) {
|
||||
// This may return null, if the user cannot view the phone number itself.
|
||||
final String phoneNumber = getBidiFormattedPhoneNumber(context,
|
||||
info.subscriptionInfo);
|
||||
String phoneNumber = "";
|
||||
try {
|
||||
final SubscriptionManager subscriptionManager = context.getSystemService(
|
||||
SubscriptionManager.class);
|
||||
phoneNumber = subscriptionManager.getPhoneNumber(infoSubId);
|
||||
} catch (IllegalStateException
|
||||
| SecurityException
|
||||
| UnsupportedOperationException e) {
|
||||
Log.w(TAG, "get number error." + e);
|
||||
}
|
||||
String lastFourDigits = "";
|
||||
if (phoneNumber != null) {
|
||||
lastFourDigits = (phoneNumber.length() > 4)
|
||||
@@ -535,6 +545,11 @@ public class SubscriptionUtil {
|
||||
Log.i(TAG, "Unable to toggle subscription due to invalid subscription ID.");
|
||||
return;
|
||||
}
|
||||
if (enable && Flags.isDualSimOnboardingEnabled()) {
|
||||
String route = SimOnboardingPageProvider.INSTANCE.getRoute(subId);
|
||||
SpaActivity.startSpaActivity(context, route);
|
||||
return;
|
||||
}
|
||||
context.startActivity(ToggleSubscriptionDialogActivity.getIntent(context, subId, enable));
|
||||
}
|
||||
|
||||
@@ -814,7 +829,7 @@ public class SubscriptionUtil {
|
||||
private static boolean isEmbeddedSubscriptionVisible(@NonNull SubscriptionInfo subInfo) {
|
||||
if (subInfo.isEmbedded()
|
||||
&& (subInfo.getProfileClass() == PROFILE_CLASS_PROVISIONING
|
||||
|| (Flags.oemEnabledSatelliteFlag()
|
||||
|| (com.android.internal.telephony.flags.Flags.oemEnabledSatelliteFlag()
|
||||
&& subInfo.isOnlyNonTerrestrialNetwork()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,10 @@ import androidx.navigation.navArgument
|
||||
import com.android.settings.R
|
||||
import com.android.settings.network.apn.ApnNetworkTypes.getNetworkTypeDisplayNames
|
||||
import com.android.settings.network.apn.ApnNetworkTypes.getNetworkTypeSelectedOptionsState
|
||||
import com.android.settings.network.apn.ApnTypes.APN_TYPES_OPTIONS
|
||||
import com.android.settings.network.apn.ApnTypes.APN_TYPE_MMS
|
||||
import com.android.settings.network.apn.ApnTypes.getApnTypeSelectedOptionsState
|
||||
import com.android.settings.network.apn.ApnTypes.updateApnType
|
||||
import com.android.settingslib.spa.framework.common.SettingsPageProvider
|
||||
import com.android.settingslib.spa.framework.compose.LocalNavController
|
||||
import com.android.settingslib.spa.framework.theme.SettingsDimension
|
||||
@@ -100,6 +104,9 @@ fun ApnPage(apnDataInit: ApnData, apnDataCur: MutableState<ApnData>, uriInit: Ur
|
||||
val networkTypeSelectedOptionsState = remember {
|
||||
getNetworkTypeSelectedOptionsState(apnData.networkType)
|
||||
}
|
||||
var apnTypeSelectedOptionsState = remember {
|
||||
getApnTypeSelectedOptionsState(apnData.apnType)
|
||||
}
|
||||
val navController = LocalNavController.current
|
||||
var valid: String?
|
||||
RegularScaffold(
|
||||
@@ -191,37 +198,50 @@ fun ApnPage(apnDataInit: ApnData, apnDataCur: MutableState<ApnData>, uriInit: Ur
|
||||
label = stringResource(R.string.apn_server),
|
||||
enabled = apnData.serverEnabled
|
||||
) { apnData = apnData.copy(server = it) }
|
||||
SettingsOutlinedTextField(
|
||||
value = apnData.mmsc,
|
||||
label = stringResource(R.string.apn_mmsc),
|
||||
errorMessage = validateMMSC(apnData.validEnabled, apnData.mmsc, context),
|
||||
enabled = apnData.mmscEnabled
|
||||
) { apnData = apnData.copy(mmsc = it) }
|
||||
SettingsOutlinedTextField(
|
||||
value = apnData.mmsProxy,
|
||||
label = stringResource(R.string.apn_mms_proxy),
|
||||
enabled = apnData.mmsProxyEnabled
|
||||
) { apnData = apnData.copy(mmsProxy = it) }
|
||||
SettingsOutlinedTextField(
|
||||
value = apnData.mmsPort,
|
||||
label = stringResource(R.string.apn_mms_port),
|
||||
enabled = apnData.mmsPortEnabled
|
||||
) { apnData = apnData.copy(mmsPort = it) }
|
||||
SettingsExposedDropdownMenuCheckBox(
|
||||
label = stringResource(R.string.apn_type),
|
||||
options = APN_TYPES_OPTIONS,
|
||||
selectedOptionsState = apnTypeSelectedOptionsState,
|
||||
enabled = apnData.apnTypeEnabled,
|
||||
errorMessage = validateAPNType(
|
||||
apnData.validEnabled, apnData.apnType,
|
||||
apnData.customizedConfig.readOnlyApnTypes, context
|
||||
)
|
||||
) {
|
||||
val apnType = updateApnType(
|
||||
apnTypeSelectedOptionsState,
|
||||
apnData.customizedConfig.defaultApnTypes,
|
||||
apnData.customizedConfig.readOnlyApnTypes
|
||||
)
|
||||
apnTypeSelectedOptionsState = getApnTypeSelectedOptionsState(apnType)
|
||||
apnData = apnData.copy(
|
||||
apnType = apnType
|
||||
)
|
||||
}
|
||||
if (apnTypeSelectedOptionsState.contains(APN_TYPES_OPTIONS.indexOf(APN_TYPE_MMS))) {
|
||||
SettingsOutlinedTextField(
|
||||
value = apnData.mmsc,
|
||||
label = stringResource(R.string.apn_mmsc),
|
||||
errorMessage = validateMMSC(apnData.validEnabled, apnData.mmsc, context),
|
||||
enabled = apnData.mmscEnabled
|
||||
) { apnData = apnData.copy(mmsc = it) }
|
||||
SettingsOutlinedTextField(
|
||||
value = apnData.mmsProxy,
|
||||
label = stringResource(R.string.apn_mms_proxy),
|
||||
enabled = apnData.mmsProxyEnabled
|
||||
) { apnData = apnData.copy(mmsProxy = it) }
|
||||
SettingsOutlinedTextField(
|
||||
value = apnData.mmsPort,
|
||||
label = stringResource(R.string.apn_mms_port),
|
||||
enabled = apnData.mmsPortEnabled
|
||||
) { apnData = apnData.copy(mmsPort = it) }
|
||||
}
|
||||
SettingsExposedDropdownMenuBox(
|
||||
label = stringResource(R.string.apn_auth_type),
|
||||
options = authTypeOptions,
|
||||
selectedOptionIndex = apnData.authType,
|
||||
enabled = apnData.authTypeEnabled,
|
||||
) { apnData = apnData.copy(authType = it) }
|
||||
SettingsOutlinedTextField(
|
||||
value = apnData.apnType,
|
||||
label = stringResource(R.string.apn_type),
|
||||
enabled = apnData.apnTypeEnabled,
|
||||
errorMessage = validateAPNType(
|
||||
apnData.validEnabled, apnData.apnType,
|
||||
apnData.customizedConfig.readOnlyApnTypes, context
|
||||
)
|
||||
) { apnData = apnData.copy(apnType = updateApnType(apnData.copy(apnType = it))) }
|
||||
SettingsExposedDropdownMenuBox(
|
||||
label = stringResource(R.string.apn_protocol),
|
||||
options = apnProtocolOptions,
|
||||
@@ -234,6 +254,13 @@ fun ApnPage(apnDataInit: ApnData, apnDataCur: MutableState<ApnData>, uriInit: Ur
|
||||
selectedOptionIndex = apnData.apnRoaming,
|
||||
enabled = apnData.apnRoamingEnabled
|
||||
) { apnData = apnData.copy(apnRoaming = it) }
|
||||
SettingsExposedDropdownMenuCheckBox(
|
||||
label = stringResource(R.string.network_type),
|
||||
options = getNetworkTypeDisplayNames(),
|
||||
selectedOptionsState = networkTypeSelectedOptionsState,
|
||||
emptyVal = stringResource(R.string.network_type_unspecified),
|
||||
enabled = apnData.networkTypeEnabled
|
||||
) {}
|
||||
SwitchPreference(
|
||||
object : SwitchPreferenceModel {
|
||||
override val title = context.resources.getString(R.string.carrier_enabled)
|
||||
@@ -244,13 +271,6 @@ fun ApnPage(apnDataInit: ApnData, apnDataCur: MutableState<ApnData>, uriInit: Ur
|
||||
}
|
||||
}
|
||||
)
|
||||
SettingsExposedDropdownMenuCheckBox(
|
||||
label = stringResource(R.string.network_type),
|
||||
options = getNetworkTypeDisplayNames(),
|
||||
selectedOptionsState = networkTypeSelectedOptionsState,
|
||||
emptyVal = stringResource(R.string.network_type_unspecified),
|
||||
enabled = apnData.networkTypeEnabled
|
||||
) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,12 @@ import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import com.android.internal.util.ArrayUtils
|
||||
import com.android.settings.R
|
||||
import com.android.settings.network.apn.ApnNetworkTypes.getNetworkType
|
||||
import com.android.settings.network.apn.ApnTypes.APN_TYPES
|
||||
import com.android.settings.network.apn.ApnTypes.APN_TYPE_ALL
|
||||
import com.android.settings.network.apn.ApnTypes.APN_TYPE_EMERGENCY
|
||||
import com.android.settings.network.apn.ApnTypes.APN_TYPE_IA
|
||||
import com.android.settings.network.apn.ApnTypes.APN_TYPE_IMS
|
||||
import com.android.settings.network.apn.ApnTypes.APN_TYPE_MCX
|
||||
import java.util.Locale
|
||||
|
||||
data class ApnData(
|
||||
@@ -113,67 +119,6 @@ data class CustomizedConfig(
|
||||
val defaultApnRoamingProtocol: String = "",
|
||||
)
|
||||
|
||||
/**
|
||||
* APN types for data connections. These are usage categories for an APN
|
||||
* entry. One APN entry may support multiple APN types, eg, a single APN
|
||||
* may service regular internet traffic ("default") as well as MMS-specific
|
||||
* connections.<br></br>
|
||||
* APN_TYPE_ALL is a special type to indicate that this APN entry can
|
||||
* service all data connections.
|
||||
*/
|
||||
const val APN_TYPE_ALL = "*"
|
||||
|
||||
/** APN type for default data traffic */
|
||||
const val APN_TYPE_DEFAULT = "default"
|
||||
|
||||
/** APN type for MMS traffic */
|
||||
const val APN_TYPE_MMS = "mms"
|
||||
|
||||
/** APN type for SUPL assisted GPS */
|
||||
const val APN_TYPE_SUPL = "supl"
|
||||
|
||||
/** APN type for DUN traffic */
|
||||
const val APN_TYPE_DUN = "dun"
|
||||
|
||||
/** APN type for HiPri traffic */
|
||||
const val APN_TYPE_HIPRI = "hipri"
|
||||
|
||||
/** APN type for FOTA */
|
||||
const val APN_TYPE_FOTA = "fota"
|
||||
|
||||
/** APN type for IMS */
|
||||
const val APN_TYPE_IMS = "ims"
|
||||
|
||||
/** APN type for CBS */
|
||||
const val APN_TYPE_CBS = "cbs"
|
||||
|
||||
/** APN type for IA Initial Attach APN */
|
||||
const val APN_TYPE_IA = "ia"
|
||||
|
||||
/** APN type for Emergency PDN. This is not an IA apn, but is used
|
||||
* for access to carrier services in an emergency call situation. */
|
||||
const val APN_TYPE_EMERGENCY = "emergency"
|
||||
|
||||
/** APN type for Mission Critical Services */
|
||||
const val APN_TYPE_MCX = "mcx"
|
||||
|
||||
/** APN type for XCAP */
|
||||
const val APN_TYPE_XCAP = "xcap"
|
||||
val APN_TYPES = arrayOf(
|
||||
APN_TYPE_DEFAULT,
|
||||
APN_TYPE_MMS,
|
||||
APN_TYPE_SUPL,
|
||||
APN_TYPE_DUN,
|
||||
APN_TYPE_HIPRI,
|
||||
APN_TYPE_FOTA,
|
||||
APN_TYPE_IMS,
|
||||
APN_TYPE_CBS,
|
||||
APN_TYPE_IA,
|
||||
APN_TYPE_EMERGENCY,
|
||||
APN_TYPE_MCX,
|
||||
APN_TYPE_XCAP
|
||||
)
|
||||
|
||||
/**
|
||||
* Initialize ApnData according to the arguments.
|
||||
* @param arguments The data passed in when the user calls PageProvider.
|
||||
@@ -483,25 +428,6 @@ fun hasAllApns(apnTypes: List<String>): Boolean {
|
||||
private fun normalizeApnType(apnType: String): String =
|
||||
apnType.trim().lowercase(Locale.getDefault())
|
||||
|
||||
fun updateApnType(apnData: ApnData): String {
|
||||
return if (apnData.apnType == "" && apnData.customizedConfig.defaultApnTypes.isNotEmpty())
|
||||
getEditableApnType(apnData)
|
||||
else
|
||||
apnData.apnType
|
||||
}
|
||||
|
||||
private fun getEditableApnType(apnData: ApnData): String {
|
||||
val customizedConfig = apnData.customizedConfig
|
||||
return customizedConfig.defaultApnTypes.filterNot { apnType ->
|
||||
customizedConfig.readOnlyApnTypes.contains(apnType) || apnType in listOf(
|
||||
APN_TYPE_IA,
|
||||
APN_TYPE_EMERGENCY,
|
||||
APN_TYPE_MCX,
|
||||
APN_TYPE_IMS,
|
||||
)
|
||||
}.joinToString()
|
||||
}
|
||||
|
||||
fun deleteApn(uri: Uri, context: Context) {
|
||||
val contentResolver = context.contentResolver
|
||||
contentResolver.delete(uri, null, null)
|
||||
|
||||
143
src/com/android/settings/network/apn/ApnTypes.kt
Normal file
143
src/com/android/settings/network/apn/ApnTypes.kt
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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.network.apn
|
||||
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
|
||||
object ApnTypes {
|
||||
/**
|
||||
* APN types for data connections. These are usage categories for an APN
|
||||
* entry. One APN entry may support multiple APN types, eg, a single APN
|
||||
* may service regular internet traffic ("default") as well as MMS-specific
|
||||
* connections.<br></br>
|
||||
* APN_TYPE_ALL is a special type to indicate that this APN entry can
|
||||
* service all data connections.
|
||||
*/
|
||||
const val APN_TYPE_ALL = "*"
|
||||
|
||||
/** APN type for default data traffic */
|
||||
const val APN_TYPE_DEFAULT = "default"
|
||||
|
||||
/** APN type for MMS traffic */
|
||||
const val APN_TYPE_MMS = "mms"
|
||||
|
||||
/** APN type for SUPL assisted GPS */
|
||||
const val APN_TYPE_SUPL = "supl"
|
||||
|
||||
/** APN type for DUN traffic */
|
||||
const val APN_TYPE_DUN = "dun"
|
||||
|
||||
/** APN type for HiPri traffic */
|
||||
const val APN_TYPE_HIPRI = "hipri"
|
||||
|
||||
/** APN type for FOTA */
|
||||
const val APN_TYPE_FOTA = "fota"
|
||||
|
||||
/** APN type for IMS */
|
||||
const val APN_TYPE_IMS = "ims"
|
||||
|
||||
/** APN type for CBS */
|
||||
const val APN_TYPE_CBS = "cbs"
|
||||
|
||||
/** APN type for IA Initial Attach APN */
|
||||
const val APN_TYPE_IA = "ia"
|
||||
|
||||
/** APN type for Emergency PDN. This is not an IA apn, but is used
|
||||
* for access to carrier services in an emergency call situation. */
|
||||
const val APN_TYPE_EMERGENCY = "emergency"
|
||||
|
||||
/** APN type for Mission Critical Services */
|
||||
const val APN_TYPE_MCX = "mcx"
|
||||
|
||||
/** APN type for XCAP */
|
||||
const val APN_TYPE_XCAP = "xcap"
|
||||
|
||||
/** APN type for VSIM */
|
||||
const val APN_TYPE_VSIM = "vsim"
|
||||
|
||||
/** APN type for BIP */
|
||||
const val APN_TYPE_BIP = "bip"
|
||||
|
||||
/** APN type for ENTERPRISE */
|
||||
const val APN_TYPE_ENTERPRISE = "enterprise"
|
||||
|
||||
val APN_TYPES = arrayOf(
|
||||
APN_TYPE_DEFAULT,
|
||||
APN_TYPE_MMS,
|
||||
APN_TYPE_SUPL,
|
||||
APN_TYPE_DUN,
|
||||
APN_TYPE_HIPRI,
|
||||
APN_TYPE_FOTA,
|
||||
APN_TYPE_IMS,
|
||||
APN_TYPE_CBS,
|
||||
APN_TYPE_IA,
|
||||
APN_TYPE_EMERGENCY,
|
||||
APN_TYPE_MCX,
|
||||
APN_TYPE_XCAP,
|
||||
APN_TYPE_VSIM,
|
||||
APN_TYPE_BIP,
|
||||
APN_TYPE_ENTERPRISE
|
||||
)
|
||||
|
||||
val APN_TYPES_OPTIONS = listOf(APN_TYPE_ALL) + APN_TYPES
|
||||
|
||||
fun getApnTypeSelectedOptionsState(apnType: String): SnapshotStateList<Int> {
|
||||
val apnTypeSelectedOptionsState = mutableStateListOf<Int>()
|
||||
if (apnType.contains(APN_TYPE_ALL))
|
||||
APN_TYPES_OPTIONS.forEachIndexed { index, _ ->
|
||||
apnTypeSelectedOptionsState.add(index)
|
||||
}
|
||||
else {
|
||||
APN_TYPES_OPTIONS.forEachIndexed { index, type ->
|
||||
if (apnType.contains(type)) {
|
||||
apnTypeSelectedOptionsState.add(index)
|
||||
}
|
||||
}
|
||||
if (apnTypeSelectedOptionsState.size == APN_TYPES.size)
|
||||
apnTypeSelectedOptionsState.add(APN_TYPES_OPTIONS.indexOf(APN_TYPE_ALL))
|
||||
}
|
||||
return apnTypeSelectedOptionsState
|
||||
}
|
||||
|
||||
fun updateApnType(
|
||||
apnTypeSelectedOptionsState: SnapshotStateList<Int>,
|
||||
defaultApnTypes: List<String>,
|
||||
readOnlyApnTypes: List<String>
|
||||
): String {
|
||||
val apnType = apnTypeSelectedOptionsState.joinToString { APN_TYPES_OPTIONS[it] }
|
||||
if (apnType.contains(APN_TYPE_ALL)) return APN_TYPE_ALL
|
||||
return if (apnType == "" && defaultApnTypes.isNotEmpty())
|
||||
getEditableApnType(defaultApnTypes, readOnlyApnTypes)
|
||||
else
|
||||
apnType
|
||||
}
|
||||
|
||||
private fun getEditableApnType(
|
||||
defaultApnTypes: List<String>,
|
||||
readOnlyApnTypes: List<String>
|
||||
): String {
|
||||
return defaultApnTypes.filterNot { apnType ->
|
||||
readOnlyApnTypes.contains(apnType) || apnType in listOf(
|
||||
APN_TYPE_IA,
|
||||
APN_TYPE_EMERGENCY,
|
||||
APN_TYPE_MCX,
|
||||
APN_TYPE_IMS,
|
||||
)
|
||||
}.joinToString()
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,9 @@ package com.android.settings.notification.zen;
|
||||
import static android.app.NotificationManager.EXTRA_AUTOMATIC_RULE_ID;
|
||||
|
||||
import android.app.AutomaticZenRule;
|
||||
import android.app.Flags;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
@@ -100,10 +102,21 @@ public abstract class ZenModeRuleSettingsBase extends ZenModeSettingsBase {
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(ZenCustomRuleSettings.RULE_ID, mId);
|
||||
|
||||
// When modes_api flag is on, we skip the radio button screen distinguishing
|
||||
// between "default" and "custom" and take users directly to the custom
|
||||
// settings screen.
|
||||
String destination = ZenCustomRuleSettings.class.getName();
|
||||
int sourceMetricsCategory = 0;
|
||||
if (Flags.modesApi()) {
|
||||
// From ZenRuleCustomPolicyPreferenceController#launchCustomSettings
|
||||
destination = ZenCustomRuleConfigSettings.class.getName();
|
||||
sourceMetricsCategory = SettingsEnums.ZEN_CUSTOM_RULE_SOUND_SETTINGS;
|
||||
}
|
||||
new SubSettingLauncher(mContext)
|
||||
.setDestination(ZenCustomRuleSettings.class.getName())
|
||||
.setDestination(destination)
|
||||
.setArguments(bundle)
|
||||
.setSourceMetricsCategory(0) // TODO
|
||||
.setSourceMetricsCategory(sourceMetricsCategory)
|
||||
.launch();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ import static com.android.settings.privatespace.PrivateSpaceSetupActivity.ACCOUN
|
||||
import static com.android.settings.privatespace.PrivateSpaceSetupActivity.EXTRA_ACTION_TYPE;
|
||||
import static com.android.settings.privatespace.PrivateSpaceSetupActivity.SET_LOCK_ACTION;
|
||||
|
||||
import android.app.KeyguardManager;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
@@ -37,6 +36,7 @@ import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SetupWizardUtils;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
@@ -52,9 +52,10 @@ public class PrivateProfileContextHelperActivity extends FragmentActivity {
|
||||
private final ActivityResultLauncher<Intent> mAddAccountToPrivateProfile =
|
||||
registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(), this::onAccountAdded);
|
||||
private final ActivityResultLauncher<Intent> mVerifyDeviceLock =
|
||||
private final ActivityResultLauncher<Intent> mSetNewPrivateProfileLock =
|
||||
registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(), this::onSetDeviceNewLock);
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
this::onSetNewProfileLockActionCompleted);
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@@ -88,7 +89,7 @@ public class PrivateProfileContextHelperActivity extends FragmentActivity {
|
||||
intent.putExtra(
|
||||
EXTRA_KEY_CHOOSE_LOCK_SCREEN_DESCRIPTION,
|
||||
R.string.private_space_lock_setup_description);
|
||||
mVerifyDeviceLock.launch(intent);
|
||||
mSetNewPrivateProfileLock.launch(intent);
|
||||
}
|
||||
|
||||
private void onAccountAdded(@Nullable ActivityResult result) {
|
||||
@@ -102,10 +103,12 @@ public class PrivateProfileContextHelperActivity extends FragmentActivity {
|
||||
finish();
|
||||
}
|
||||
|
||||
private void onSetDeviceNewLock(@Nullable ActivityResult result) {
|
||||
// TODO(b/307281644) : Verify this for biometrics and check result code after new
|
||||
// Authentication changes are merged.
|
||||
if (result != null && getSystemService(KeyguardManager.class).isDeviceSecure()) {
|
||||
private void onSetNewProfileLockActionCompleted(@Nullable ActivityResult result) {
|
||||
LockPatternUtils lockPatternUtils =
|
||||
FeatureFactory.getFeatureFactory()
|
||||
.getSecurityFeatureProvider()
|
||||
.getLockPatternUtils(this);
|
||||
if (result != null && lockPatternUtils.isSeparateProfileChallengeEnabled(getUserId())) {
|
||||
Log.i(TAG, "separate private space lock setup success");
|
||||
setResult(RESULT_OK);
|
||||
} else {
|
||||
|
||||
@@ -47,6 +47,7 @@ import com.android.settings.spa.development.UsageStatsPageProvider
|
||||
import com.android.settings.spa.development.compat.PlatformCompatAppListPageProvider
|
||||
import com.android.settings.spa.home.HomePageProvider
|
||||
import com.android.settings.spa.network.NetworkAndInternetPageProvider
|
||||
import com.android.settings.spa.network.SimOnboardingPageProvider
|
||||
import com.android.settings.spa.notification.AppListNotificationsPageProvider
|
||||
import com.android.settings.spa.notification.NotificationMainPageProvider
|
||||
import com.android.settings.spa.system.AppLanguagesPageProvider
|
||||
@@ -114,6 +115,7 @@ open class SettingsSpaEnvironment(context: Context) : SpaEnvironment(context) {
|
||||
StorageAppListPageProvider.Apps,
|
||||
StorageAppListPageProvider.Games,
|
||||
ApnEditPageProvider,
|
||||
SimOnboardingPageProvider,
|
||||
)
|
||||
|
||||
override val logger = if (FeatureFlagUtils.isEnabled(
|
||||
|
||||
113
src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
Normal file
113
src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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.spa.network
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.SignalCellularAlt
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.android.settings.R
|
||||
import com.android.settings.network.SimOnboardingService
|
||||
import com.android.settings.network.SubscriptionUtil
|
||||
import com.android.settingslib.spa.framework.theme.SettingsDimension
|
||||
import com.android.settingslib.spa.widget.dialog.AlertDialogButton
|
||||
import com.android.settingslib.spa.widget.dialog.rememberAlertDialogPresenter
|
||||
import com.android.settingslib.spa.widget.editor.SettingsOutlinedTextField
|
||||
|
||||
import com.android.settingslib.spa.widget.preference.Preference
|
||||
import com.android.settingslib.spa.widget.preference.PreferenceModel
|
||||
import com.android.settingslib.spa.widget.scaffold.BottomAppBarButton
|
||||
import com.android.settingslib.spa.widget.scaffold.SuwScaffold
|
||||
import com.android.settingslib.spa.widget.ui.SettingsBody
|
||||
|
||||
/**
|
||||
* the sim onboarding label compose
|
||||
*/
|
||||
@Composable
|
||||
fun SimOnboardingLabelSimImpl(
|
||||
nextAction: () -> Unit,
|
||||
cancelAction: () -> Unit,
|
||||
onboardingService: SimOnboardingService
|
||||
) {
|
||||
SuwScaffold(
|
||||
imageVector = Icons.Outlined.SignalCellularAlt,
|
||||
title = stringResource(R.string.sim_onboarding_label_sim_title),
|
||||
actionButton = BottomAppBarButton(
|
||||
stringResource(R.string.sim_onboarding_next),
|
||||
nextAction
|
||||
),
|
||||
dismissButton = BottomAppBarButton(
|
||||
stringResource(R.string.cancel),
|
||||
cancelAction
|
||||
),
|
||||
) {
|
||||
labelSimBody(onboardingService)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun labelSimBody(onboardingService: SimOnboardingService) {
|
||||
Column(Modifier.padding(SettingsDimension.itemPadding)) {
|
||||
SettingsBody(stringResource(R.string.sim_onboarding_label_sim_msg))
|
||||
}
|
||||
|
||||
for (subInfo in onboardingService.getSelectableSubscriptionInfo()) {
|
||||
var titleSimName by remember {
|
||||
mutableStateOf(
|
||||
onboardingService.getSubscriptionInfoDisplayName(subInfo)
|
||||
)
|
||||
}
|
||||
var summaryNumber = subInfo.number
|
||||
// TODO using the SubscriptionUtil.getFormattedPhoneNumber
|
||||
val alertDialogPresenter = rememberAlertDialogPresenter(
|
||||
confirmButton = AlertDialogButton(
|
||||
stringResource(R.string.mobile_network_sim_name_rename)
|
||||
) {
|
||||
onboardingService.addItemForRenaming(subInfo, titleSimName)
|
||||
},
|
||||
dismissButton = AlertDialogButton(stringResource(R.string.cancel)) {
|
||||
titleSimName = onboardingService.getSubscriptionInfoDisplayName(subInfo)
|
||||
},
|
||||
title = stringResource(R.string.sim_onboarding_label_sim_dialog_title),
|
||||
text = {
|
||||
Text(summaryNumber)
|
||||
SettingsOutlinedTextField(
|
||||
value = titleSimName,
|
||||
label = stringResource(R.string.sim_onboarding_label_sim_dialog_label),
|
||||
enabled = true
|
||||
) {
|
||||
titleSimName = it
|
||||
}
|
||||
},
|
||||
)
|
||||
Preference(object : PreferenceModel {
|
||||
override val title = titleSimName
|
||||
override val summary: () -> String
|
||||
get() = { summaryNumber }
|
||||
override val onClick = alertDialogPresenter::open
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.spa.network
|
||||
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navArgument
|
||||
import com.android.settings.R
|
||||
import com.android.settings.network.SimOnboardingService
|
||||
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
|
||||
import com.android.settingslib.spa.framework.common.SettingsPageProvider
|
||||
import com.android.settingslib.spa.framework.common.createSettingsPage
|
||||
import com.android.settingslib.spa.framework.compose.navigator
|
||||
|
||||
import com.android.settingslib.spa.widget.preference.Preference
|
||||
import com.android.settingslib.spa.widget.preference.PreferenceModel
|
||||
|
||||
const val SUB_ID = "subId"
|
||||
|
||||
enum class SimOnboardingScreen(val stringResId: Int) {
|
||||
LabelSim(R.string.sim_onboarding_label_sim_title),
|
||||
SelectSim(R.string.sim_onboarding_select_sim_title),
|
||||
PrimarySim(R.string.sim_onboarding_primary_sim_title)
|
||||
}
|
||||
|
||||
/**
|
||||
* Showing the sim onboarding which is the process flow of sim switching on.
|
||||
*/
|
||||
object SimOnboardingPageProvider : SettingsPageProvider {
|
||||
override val name = "SimOnboardingPageProvider"
|
||||
override val parameter = listOf(
|
||||
navArgument(SUB_ID) { type = NavType.IntType },
|
||||
)
|
||||
|
||||
private val owner = createSettingsPage()
|
||||
@VisibleForTesting
|
||||
var onboardingService: SimOnboardingService = SimOnboardingService()
|
||||
|
||||
fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner = owner)
|
||||
.setUiLayoutFn {
|
||||
// never using
|
||||
Preference(object : PreferenceModel {
|
||||
override val title = name
|
||||
override val onClick = navigator(getRoute(-1))
|
||||
})
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Page(arguments: Bundle?) {
|
||||
initServiceData(arguments!!.getInt(SUB_ID))
|
||||
PageImpl(onboardingService,rememberNavController())
|
||||
}
|
||||
|
||||
fun getRoute(
|
||||
subId: Int
|
||||
): String = "${name}/$subId"
|
||||
|
||||
@Composable
|
||||
fun initServiceData(targetSubId: Int) {
|
||||
onboardingService.initData(targetSubId, LocalContext.current)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Context.getActivity(): Activity? = when (this) {
|
||||
is Activity -> this
|
||||
is ContextWrapper -> baseContext.getActivity()
|
||||
else -> null
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PageImpl(onboardingService:SimOnboardingService,navHostController: NavHostController) {
|
||||
val context = LocalContext.current
|
||||
var previousPageOfOnboarding: () -> Unit = { context.getActivity()?.finish() }
|
||||
|
||||
NavHost(
|
||||
navController = navHostController,
|
||||
startDestination = SimOnboardingScreen.LabelSim.name
|
||||
) {
|
||||
composable(route = SimOnboardingScreen.LabelSim.name) {
|
||||
val nextPage =
|
||||
// Adding more conditions
|
||||
if (onboardingService.isMultipleEnabledProfilesSupported) {
|
||||
SimOnboardingScreen.SelectSim.name
|
||||
} else {
|
||||
SimOnboardingScreen.PrimarySim.name
|
||||
}
|
||||
SimOnboardingLabelSimImpl(
|
||||
nextAction = { navHostController.navigate(nextPage) },
|
||||
cancelAction = previousPageOfOnboarding,
|
||||
onboardingService = onboardingService
|
||||
)
|
||||
}
|
||||
composable(route = SimOnboardingScreen.PrimarySim.name) {
|
||||
SimOnboardingPrimarySimImpl(
|
||||
nextAction = {
|
||||
//go back and activate sim
|
||||
},
|
||||
cancelAction = previousPageOfOnboarding,
|
||||
onboardingService = onboardingService
|
||||
)
|
||||
}
|
||||
composable(route = SimOnboardingScreen.SelectSim.name) {
|
||||
SimOnboardingSelectSimImpl(
|
||||
nextAction = { navHostController.navigate(SimOnboardingScreen.PrimarySim.name) },
|
||||
cancelAction = previousPageOfOnboarding,
|
||||
onboardingService = onboardingService
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
160
src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt
Normal file
160
src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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.spa.network
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.Message
|
||||
import androidx.compose.material.icons.outlined.DataUsage
|
||||
import androidx.compose.material.icons.outlined.SignalCellularAlt
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableIntState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import com.android.settings.R
|
||||
import com.android.settings.network.SimOnboardingService
|
||||
import com.android.settingslib.spa.framework.theme.SettingsDimension
|
||||
import com.android.settingslib.spa.widget.preference.ListPreference
|
||||
import com.android.settingslib.spa.widget.preference.ListPreferenceModel
|
||||
import com.android.settingslib.spa.widget.preference.ListPreferenceOption
|
||||
import com.android.settingslib.spa.widget.preference.SwitchPreference
|
||||
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
|
||||
import com.android.settingslib.spa.widget.scaffold.BottomAppBarButton
|
||||
import com.android.settingslib.spa.widget.scaffold.SuwScaffold
|
||||
import com.android.settingslib.spa.widget.ui.SettingsBody
|
||||
import com.android.settingslib.spa.widget.ui.SettingsIcon
|
||||
|
||||
/**
|
||||
* the sim onboarding primary sim compose
|
||||
*/
|
||||
@Composable
|
||||
fun SimOnboardingPrimarySimImpl(
|
||||
nextAction: () -> Unit,
|
||||
cancelAction: () -> Unit,
|
||||
onboardingService: SimOnboardingService
|
||||
) {
|
||||
SuwScaffold(
|
||||
imageVector = Icons.Outlined.SignalCellularAlt,
|
||||
title = stringResource(id = R.string.sim_onboarding_primary_sim_title),
|
||||
actionButton = BottomAppBarButton(
|
||||
stringResource(id = R.string.done),
|
||||
nextAction
|
||||
),
|
||||
dismissButton = BottomAppBarButton(
|
||||
stringResource(id = R.string.cancel),
|
||||
cancelAction
|
||||
),
|
||||
) {
|
||||
primarySimBody(onboardingService)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun primarySimBody(onboardingService: SimOnboardingService) {
|
||||
//TODO: Load the status from the frameworks
|
||||
var callsSelectedId = rememberSaveable { mutableIntStateOf(1) }
|
||||
var textsSelectedId = rememberSaveable { mutableIntStateOf(1) }
|
||||
var mobileDataSelectedId = rememberSaveable { mutableIntStateOf(1) }
|
||||
var automaticDataChecked by rememberSaveable { mutableStateOf(true) }
|
||||
|
||||
Column(Modifier.padding(SettingsDimension.itemPadding)) {
|
||||
SettingsBody(stringResource(id = R.string.sim_onboarding_primary_sim_msg))
|
||||
}
|
||||
var selectableSubscriptionInfo = onboardingService.getSelectableSubscriptionInfo()
|
||||
var list = listOf(ListPreferenceOption(id = -1, text = "Loading"))
|
||||
if (selectableSubscriptionInfo.size >= 2) {
|
||||
list = listOf(
|
||||
ListPreferenceOption(
|
||||
id = selectableSubscriptionInfo[0].subscriptionId,
|
||||
text = "${selectableSubscriptionInfo[0].displayName}"
|
||||
),
|
||||
ListPreferenceOption(
|
||||
id = selectableSubscriptionInfo[1].subscriptionId,
|
||||
text = "${selectableSubscriptionInfo[1].displayName}"
|
||||
),
|
||||
ListPreferenceOption(
|
||||
id = -1,
|
||||
text = stringResource(id = R.string.sim_calls_ask_first_prefs_title)
|
||||
),
|
||||
)
|
||||
} else {
|
||||
// set all of primary sim items' enable as false and showing that sim.
|
||||
}
|
||||
createPrimarySimListPreference(
|
||||
stringResource(id = R.string.primary_sim_calls_title),
|
||||
list,
|
||||
callsSelectedId,
|
||||
ImageVector.vectorResource(R.drawable.ic_phone),
|
||||
true
|
||||
)
|
||||
createPrimarySimListPreference(
|
||||
stringResource(id = R.string.primary_sim_texts_title),
|
||||
list,
|
||||
textsSelectedId,
|
||||
Icons.AutoMirrored.Outlined.Message,
|
||||
true
|
||||
)
|
||||
createPrimarySimListPreference(
|
||||
stringResource(id = R.string.mobile_data_settings_title),
|
||||
list,
|
||||
mobileDataSelectedId,
|
||||
Icons.Outlined.DataUsage,
|
||||
true
|
||||
)
|
||||
|
||||
val autoDataTitle = stringResource(id = R.string.primary_sim_automatic_data_title)
|
||||
val autoDataSummary = stringResource(id = R.string.primary_sim_automatic_data_msg)
|
||||
SwitchPreference(remember {
|
||||
object : SwitchPreferenceModel {
|
||||
override val title = autoDataTitle
|
||||
override val summary = { autoDataSummary }
|
||||
override val checked = { automaticDataChecked }
|
||||
override val onCheckedChange =
|
||||
{ newChecked: Boolean -> automaticDataChecked = newChecked }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun createPrimarySimListPreference(
|
||||
title: String,
|
||||
list: List<ListPreferenceOption>,
|
||||
selectedId: MutableIntState,
|
||||
icon: ImageVector,
|
||||
enable: Boolean
|
||||
) = ListPreference(remember {
|
||||
object : ListPreferenceModel {
|
||||
override val title = title
|
||||
override val options = list
|
||||
override val selectedId = selectedId
|
||||
override val onIdSelected: (id: Int) -> Unit = { selectedId.intValue = it }
|
||||
override val icon = @Composable {
|
||||
SettingsIcon(icon)
|
||||
}
|
||||
override val enabled: () -> Boolean
|
||||
get() = { enable }
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.spa.network
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.SignalCellularAlt
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.android.settings.R
|
||||
import com.android.settings.network.SimOnboardingService
|
||||
import com.android.settingslib.spa.framework.theme.SettingsDimension
|
||||
import com.android.settingslib.spa.widget.preference.CheckboxPreference
|
||||
import com.android.settingslib.spa.widget.preference.CheckboxPreferenceModel
|
||||
|
||||
import com.android.settingslib.spa.widget.scaffold.BottomAppBarButton
|
||||
import com.android.settingslib.spa.widget.scaffold.SuwScaffold
|
||||
import com.android.settingslib.spa.widget.ui.SettingsBody
|
||||
|
||||
/**
|
||||
* the sim onboarding select sim compose
|
||||
*/
|
||||
@Composable
|
||||
fun SimOnboardingSelectSimImpl(
|
||||
nextAction: () -> Unit,
|
||||
cancelAction: () -> Unit,
|
||||
onboardingService: SimOnboardingService
|
||||
) {
|
||||
SuwScaffold(
|
||||
imageVector = Icons.Outlined.SignalCellularAlt,
|
||||
title = stringResource(id = R.string.sim_onboarding_select_sim_title),
|
||||
actionButton = BottomAppBarButton(
|
||||
stringResource(id = R.string.sim_onboarding_next),
|
||||
nextAction
|
||||
),
|
||||
dismissButton = BottomAppBarButton(
|
||||
stringResource(id = R.string.cancel),
|
||||
cancelAction
|
||||
),
|
||||
) {
|
||||
selectSimBody(onboardingService)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun selectSimBody(onboardingService: SimOnboardingService) {
|
||||
Column(Modifier.padding(SettingsDimension.itemPadding)) {
|
||||
SettingsBody(stringResource(id = R.string.sim_onboarding_select_sim_msg))
|
||||
}
|
||||
for (subInfo in onboardingService.getSelectableSubscriptionInfo()) {
|
||||
var title = onboardingService.getSubscriptionInfoDisplayName(subInfo)
|
||||
var summaryNumber =
|
||||
subInfo.number // TODO using the SubscriptionUtil.getFormattedPhoneNumber
|
||||
var changeable = subInfo.isActive
|
||||
var checked by rememberSaveable { mutableStateOf(!subInfo.isActive) }
|
||||
|
||||
CheckboxPreference(remember {
|
||||
object : CheckboxPreferenceModel {
|
||||
override val title = title
|
||||
override val summary: () -> String
|
||||
get() = { summaryNumber }
|
||||
override val checked = { checked }
|
||||
override val changeable = { changeable }
|
||||
override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package com.android.settings.bluetooth;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
@@ -28,23 +29,33 @@ import android.bluetooth.BluetoothClass;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.Context;
|
||||
import android.platform.test.flag.junit.CheckFlagsRule;
|
||||
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
|
||||
import android.platform.test.flag.junit.SetFlagsRule;
|
||||
import android.sysprop.BluetoothProperties;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.SwitchPreference;
|
||||
import androidx.preference.SwitchPreferenceCompat;
|
||||
|
||||
import com.android.settings.flags.Flags;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothDevice;
|
||||
import com.android.settingslib.R;
|
||||
import com.android.settingslib.bluetooth.A2dpProfile;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||
import com.android.settingslib.bluetooth.LeAudioProfile;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothProfile;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
||||
import com.android.settingslib.bluetooth.MapProfile;
|
||||
import com.android.settingslib.bluetooth.PbapServerProfile;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
@@ -59,30 +70,41 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Ignore
|
||||
@Config(shadows = ShadowBluetoothDevice.class)
|
||||
public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsControllerTestBase {
|
||||
@Rule
|
||||
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
|
||||
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||
|
||||
private static final String LE_DEVICE_MODEL = "le_audio_headset";
|
||||
private static final String NON_LE_DEVICE_MODEL = "non_le_audio_headset";
|
||||
private BluetoothDetailsProfilesController mController;
|
||||
private List<LocalBluetoothProfile> mConnectableProfiles;
|
||||
private PreferenceCategory mProfiles;
|
||||
private BluetoothFeatureProvider mFeatureProvider;
|
||||
|
||||
@Mock
|
||||
private LocalBluetoothManager mLocalManager;
|
||||
@Mock
|
||||
private LocalBluetoothProfileManager mProfileManager;
|
||||
@Mock
|
||||
private CachedBluetoothDeviceManager mCachedBluetoothDeviceManager;
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
super.setUp();
|
||||
|
||||
FakeFeatureFactory fakeFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||
mFeatureProvider = fakeFeatureFactory.getBluetoothFeatureProvider();
|
||||
|
||||
mProfiles = spy(new PreferenceCategory(mContext));
|
||||
when(mProfiles.getPreferenceManager()).thenReturn(mPreferenceManager);
|
||||
|
||||
mConnectableProfiles = new ArrayList<>();
|
||||
when(mLocalManager.getProfileManager()).thenReturn(mProfileManager);
|
||||
when(mLocalManager.getCachedDeviceManager()).thenReturn(mCachedBluetoothDeviceManager);
|
||||
when(mCachedBluetoothDeviceManager.getCachedDevicesCopy())
|
||||
.thenReturn(ImmutableList.of(mCachedDevice));
|
||||
when(mCachedDevice.getConnectableProfiles()).thenAnswer(invocation ->
|
||||
new ArrayList<>(mConnectableProfiles)
|
||||
);
|
||||
@@ -196,25 +218,26 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
|
||||
return profile;
|
||||
}
|
||||
|
||||
/** Returns the list of SwitchPreference objects added to the screen - there should be one per
|
||||
* Bluetooth profile.
|
||||
/**
|
||||
* Returns the list of SwitchPreferenceCompat objects added to the screen - there should be one
|
||||
* per Bluetooth profile.
|
||||
*/
|
||||
private List<SwitchPreference> getProfileSwitches(boolean expectOnlyMConnectable) {
|
||||
private List<SwitchPreferenceCompat> getProfileSwitches(boolean expectOnlyMConnectable) {
|
||||
if (expectOnlyMConnectable) {
|
||||
assertThat(mConnectableProfiles).isNotEmpty();
|
||||
assertThat(mProfiles.getPreferenceCount() - 1).isEqualTo(mConnectableProfiles.size());
|
||||
}
|
||||
List<SwitchPreference> result = new ArrayList<>();
|
||||
List<SwitchPreferenceCompat> result = new ArrayList<>();
|
||||
for (int i = 0; i < mProfiles.getPreferenceCount(); i++) {
|
||||
final Preference preference = mProfiles.getPreference(i);
|
||||
if (preference instanceof SwitchPreference) {
|
||||
result.add((SwitchPreference) preference);
|
||||
if (preference instanceof SwitchPreferenceCompat) {
|
||||
result.add((SwitchPreferenceCompat) preference);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void verifyProfileSwitchTitles(List<SwitchPreference> switches) {
|
||||
private void verifyProfileSwitchTitles(List<SwitchPreferenceCompat> switches) {
|
||||
for (int i = 0; i < switches.size(); i++) {
|
||||
String expectedTitle =
|
||||
mContext.getString(mConnectableProfiles.get(i).getNameResource(mDevice));
|
||||
@@ -234,7 +257,7 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
|
||||
addFakeProfile(com.android.settingslib.R.string.bluetooth_profile_a2dp, true);
|
||||
addFakeProfile(com.android.settingslib.R.string.bluetooth_profile_headset, false);
|
||||
showScreen(mController);
|
||||
List<SwitchPreference> switches = getProfileSwitches(true);
|
||||
List<SwitchPreferenceCompat> switches = getProfileSwitches(true);
|
||||
verifyProfileSwitchTitles(switches);
|
||||
assertThat(switches.get(0).isChecked()).isTrue();
|
||||
assertThat(switches.get(1).isChecked()).isFalse();
|
||||
@@ -260,8 +283,8 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
|
||||
addFakeProfile(com.android.settingslib.R.string.bluetooth_profile_a2dp, true);
|
||||
addFakeProfile(com.android.settingslib.R.string.bluetooth_profile_headset, true);
|
||||
showScreen(mController);
|
||||
List<SwitchPreference> switches = getProfileSwitches(true);
|
||||
SwitchPreference pref = switches.get(0);
|
||||
List<SwitchPreferenceCompat> switches = getProfileSwitches(true);
|
||||
SwitchPreferenceCompat pref = switches.get(0);
|
||||
|
||||
// Clicking the pref should cause the profile to become not-preferred.
|
||||
assertThat(pref.isChecked()).isTrue();
|
||||
@@ -296,14 +319,16 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
|
||||
PbapServerProfile psp = mock(PbapServerProfile.class);
|
||||
when(psp.getNameResource(mDevice))
|
||||
.thenReturn(com.android.settingslib.R.string.bluetooth_profile_pbap);
|
||||
when(psp.getSummaryResourceForDevice(mDevice))
|
||||
.thenReturn(R.string.bluetooth_profile_pbap_summary);
|
||||
when(psp.toString()).thenReturn(PbapServerProfile.NAME);
|
||||
when(psp.isProfileReady()).thenReturn(true);
|
||||
when(mProfileManager.getPbapProfile()).thenReturn(psp);
|
||||
|
||||
showScreen(mController);
|
||||
List<SwitchPreference> switches = getProfileSwitches(false);
|
||||
List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
|
||||
assertThat(switches.size()).isEqualTo(1);
|
||||
SwitchPreference pref = switches.get(0);
|
||||
SwitchPreferenceCompat pref = switches.get(0);
|
||||
assertThat(pref.getTitle()).isEqualTo(
|
||||
mContext.getString(com.android.settingslib.R.string.bluetooth_profile_pbap));
|
||||
assertThat(pref.isChecked()).isTrue();
|
||||
@@ -321,14 +346,16 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
|
||||
PbapServerProfile psp = mock(PbapServerProfile.class);
|
||||
when(psp.getNameResource(mDevice))
|
||||
.thenReturn(com.android.settingslib.R.string.bluetooth_profile_pbap);
|
||||
when(psp.getSummaryResourceForDevice(mDevice))
|
||||
.thenReturn(R.string.bluetooth_profile_pbap_summary);
|
||||
when(psp.toString()).thenReturn(PbapServerProfile.NAME);
|
||||
when(psp.isProfileReady()).thenReturn(true);
|
||||
when(mProfileManager.getPbapProfile()).thenReturn(psp);
|
||||
|
||||
showScreen(mController);
|
||||
List<SwitchPreference> switches = getProfileSwitches(false);
|
||||
List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
|
||||
assertThat(switches.size()).isEqualTo(1);
|
||||
SwitchPreference pref = switches.get(0);
|
||||
SwitchPreferenceCompat pref = switches.get(0);
|
||||
assertThat(pref.getTitle()).isEqualTo(
|
||||
mContext.getString(com.android.settingslib.R.string.bluetooth_profile_pbap));
|
||||
assertThat(pref.isChecked()).isFalse();
|
||||
@@ -350,9 +377,9 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
|
||||
when(mProfileManager.getProfileByName(eq(mapProfile.toString()))).thenReturn(mapProfile);
|
||||
mDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_REJECTED);
|
||||
showScreen(mController);
|
||||
List<SwitchPreference> switches = getProfileSwitches(false);
|
||||
List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
|
||||
assertThat(switches.size()).isEqualTo(1);
|
||||
SwitchPreference pref = switches.get(0);
|
||||
SwitchPreferenceCompat pref = switches.get(0);
|
||||
assertThat(pref.getTitle()).isEqualTo(
|
||||
mContext.getString(com.android.settingslib.R.string.bluetooth_profile_map));
|
||||
assertThat(pref.isChecked()).isFalse();
|
||||
@@ -379,8 +406,8 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
|
||||
return profile;
|
||||
}
|
||||
|
||||
private SwitchPreference getHighQualityAudioPref() {
|
||||
return (SwitchPreference) mScreen.findPreference(
|
||||
private SwitchPreferenceCompat getHighQualityAudioPref() {
|
||||
return (SwitchPreferenceCompat) mScreen.findPreference(
|
||||
BluetoothDetailsProfilesController.HIGH_QUALITY_AUDIO_PREF_TAG);
|
||||
}
|
||||
|
||||
@@ -389,7 +416,7 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
|
||||
setupDevice(makeDefaultDeviceConfig());
|
||||
addMockA2dpProfile(true, true, true);
|
||||
showScreen(mController);
|
||||
SwitchPreference pref = getHighQualityAudioPref();
|
||||
SwitchPreferenceCompat pref = getHighQualityAudioPref();
|
||||
assertThat(pref.getKey()).isEqualTo(
|
||||
BluetoothDetailsProfilesController.HIGH_QUALITY_AUDIO_PREF_TAG);
|
||||
|
||||
@@ -407,7 +434,7 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
|
||||
addMockA2dpProfile(true, false, false);
|
||||
showScreen(mController);
|
||||
assertThat(mProfiles.getPreferenceCount()).isEqualTo(2);
|
||||
SwitchPreference pref = (SwitchPreference) mProfiles.getPreference(0);
|
||||
SwitchPreferenceCompat pref = (SwitchPreferenceCompat) mProfiles.getPreference(0);
|
||||
assertThat(pref.getKey())
|
||||
.isNotEqualTo(BluetoothDetailsProfilesController.HIGH_QUALITY_AUDIO_PREF_TAG);
|
||||
assertThat(pref.getTitle()).isEqualTo(
|
||||
@@ -420,7 +447,7 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
|
||||
addMockA2dpProfile(true, true, true);
|
||||
when(mCachedDevice.isBusy()).thenReturn(true);
|
||||
showScreen(mController);
|
||||
SwitchPreference pref = getHighQualityAudioPref();
|
||||
SwitchPreferenceCompat pref = getHighQualityAudioPref();
|
||||
assertThat(pref.isEnabled()).isFalse();
|
||||
}
|
||||
|
||||
@@ -433,14 +460,14 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
|
||||
|
||||
// Disabling media audio should cause the high quality audio switch to disappear, but not
|
||||
// the regular audio one.
|
||||
SwitchPreference audioPref =
|
||||
(SwitchPreference) mScreen.findPreference(audioProfile.toString());
|
||||
SwitchPreferenceCompat audioPref =
|
||||
(SwitchPreferenceCompat) mScreen.findPreference(audioProfile.toString());
|
||||
audioPref.performClick();
|
||||
verify(audioProfile).setEnabled(mDevice, false);
|
||||
when(audioProfile.isEnabled(mDevice)).thenReturn(false);
|
||||
mController.onDeviceAttributesChanged();
|
||||
assertThat(audioPref.isVisible()).isTrue();
|
||||
SwitchPreference highQualityAudioPref = getHighQualityAudioPref();
|
||||
SwitchPreferenceCompat highQualityAudioPref = getHighQualityAudioPref();
|
||||
assertThat(highQualityAudioPref.isVisible()).isFalse();
|
||||
|
||||
// And re-enabling media audio should make high quality switch to reappear.
|
||||
@@ -457,8 +484,8 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
|
||||
setupDevice(makeDefaultDeviceConfig());
|
||||
A2dpProfile audioProfile = addMockA2dpProfile(false, true, true);
|
||||
showScreen(mController);
|
||||
SwitchPreference audioPref = mScreen.findPreference(audioProfile.toString());
|
||||
SwitchPreference highQualityAudioPref = getHighQualityAudioPref();
|
||||
SwitchPreferenceCompat audioPref = mScreen.findPreference(audioProfile.toString());
|
||||
SwitchPreferenceCompat highQualityAudioPref = getHighQualityAudioPref();
|
||||
assertThat(audioPref).isNotNull();
|
||||
assertThat(audioPref.isChecked()).isFalse();
|
||||
assertThat(highQualityAudioPref).isNotNull();
|
||||
@@ -489,4 +516,46 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
|
||||
assertThat(mController.isModelNameInAllowList(null)).isFalse();
|
||||
assertThat(mController.isModelNameInAllowList(NON_LE_DEVICE_MODEL)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prefKeyInBlockingList_hideToggle() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_BLUETOOTH_PROFILE_TOGGLE_VISIBILITY_CHECKER);
|
||||
setupDevice(makeDefaultDeviceConfig());
|
||||
|
||||
LeAudioProfile leAudioProfile = mock(LeAudioProfile.class);
|
||||
when(leAudioProfile.getNameResource(mDevice))
|
||||
.thenReturn(com.android.settingslib.R.string.bluetooth_profile_le_audio);
|
||||
when(leAudioProfile.isProfileReady()).thenReturn(true);
|
||||
when(leAudioProfile.toString()).thenReturn("LE_AUDIO");
|
||||
when(mProfileManager.getLeAudioProfile()).thenReturn(leAudioProfile);
|
||||
when(mFeatureProvider.getInvisibleProfilePreferenceKeys(any(), any()))
|
||||
.thenReturn(ImmutableSet.of("LE_AUDIO"));
|
||||
mConnectableProfiles.add(leAudioProfile);
|
||||
|
||||
showScreen(mController);
|
||||
|
||||
List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
|
||||
assertThat(switches.get(0).isVisible()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prefKeyNotInBlockingList_showToggle() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_BLUETOOTH_PROFILE_TOGGLE_VISIBILITY_CHECKER);
|
||||
setupDevice(makeDefaultDeviceConfig());
|
||||
|
||||
LeAudioProfile leAudioProfile = mock(LeAudioProfile.class);
|
||||
when(leAudioProfile.getNameResource(mDevice))
|
||||
.thenReturn(com.android.settingslib.R.string.bluetooth_profile_le_audio);
|
||||
when(leAudioProfile.isProfileReady()).thenReturn(true);
|
||||
when(leAudioProfile.toString()).thenReturn("LE_AUDIO");
|
||||
when(mProfileManager.getLeAudioProfile()).thenReturn(leAudioProfile);
|
||||
when(mFeatureProvider.getInvisibleProfilePreferenceKeys(any(), any()))
|
||||
.thenReturn(ImmutableSet.of("A2DP"));
|
||||
mConnectableProfiles.add(leAudioProfile);
|
||||
|
||||
showScreen(mController);
|
||||
|
||||
List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
|
||||
assertThat(switches.get(0).isVisible()).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.content.pm.UserProperties;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.widget.TextView;
|
||||
@@ -42,16 +43,19 @@ import com.android.settingslib.drawer.Tile;
|
||||
import com.google.android.collect.Lists;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class ProfileSelectDialogTest {
|
||||
@Rule
|
||||
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||
|
||||
private static final UserHandle NORMAL_USER = new UserHandle(1111);
|
||||
private static final UserHandle REMOVED_USER = new UserHandle(2222);
|
||||
@@ -67,11 +71,12 @@ public class ProfileSelectDialogTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
|
||||
final UserInfo userInfo = new UserInfo(
|
||||
NORMAL_USER.getIdentifier(), "test_user", UserInfo.FLAG_RESTRICTED);
|
||||
when(mUserManager.getUserInfo(NORMAL_USER.getIdentifier())).thenReturn(userInfo);
|
||||
final UserProperties userProperties = new UserProperties.Builder().build();
|
||||
when(mUserManager.getUserProperties(NORMAL_USER)).thenReturn(userProperties);
|
||||
mActivityInfo = new ActivityInfo();
|
||||
mActivityInfo.packageName = "pkg";
|
||||
mActivityInfo.name = "cls";
|
||||
@@ -89,7 +94,6 @@ public class ProfileSelectDialogTest {
|
||||
verify(mUserManager, never()).getUserInfo(NORMAL_USER.getIdentifier());
|
||||
}
|
||||
|
||||
@Ignore("b/313569889")
|
||||
@Test
|
||||
public void updateUserHandlesIfNeeded_Remove() {
|
||||
final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
|
||||
@@ -105,7 +109,6 @@ public class ProfileSelectDialogTest {
|
||||
verify(mUserManager, times(2)).getUserInfo(REMOVED_USER.getIdentifier());
|
||||
}
|
||||
|
||||
@Ignore("b/313569889")
|
||||
@Test
|
||||
public void updateUserHandlesIfNeeded_removesCloneProfile() {
|
||||
final UserInfo userInfo = new UserInfo(CLONE_USER.getIdentifier(), "clone_user", null,
|
||||
@@ -122,7 +125,6 @@ public class ProfileSelectDialogTest {
|
||||
verify(mUserManager, times(1)).getUserInfo(CLONE_USER.getIdentifier());
|
||||
}
|
||||
|
||||
@Ignore("b/313569889")
|
||||
@Test
|
||||
public void updatePendingIntentsIfNeeded_removesUsersWithNoPendingIntentsAndCloneProfile() {
|
||||
final UserInfo userInfo = new UserInfo(CLONE_USER.getIdentifier(), "clone_user", null,
|
||||
|
||||
@@ -50,9 +50,9 @@ class ApnEditPageProviderTest {
|
||||
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
private val apnName = "apn_name"
|
||||
private val mmsc = "mmsc"
|
||||
private val mmsProxy = "mms_proxy"
|
||||
private val apnType = "apn_type"
|
||||
private val proxy = "proxy"
|
||||
private val port = "port"
|
||||
private val apnType = context.resources.getString(R.string.apn_type)
|
||||
private val apnRoaming = "IPv4"
|
||||
private val apnEnable = context.resources.getString(R.string.carrier_enabled)
|
||||
private val apnProtocolOptions =
|
||||
@@ -61,8 +61,8 @@ class ApnEditPageProviderTest {
|
||||
private val passwordTitle = context.resources.getString(R.string.apn_password)
|
||||
private val apnInit = ApnData(
|
||||
name = apnName,
|
||||
mmsc = mmsc,
|
||||
mmsProxy = mmsProxy,
|
||||
proxy = proxy,
|
||||
port = port,
|
||||
apnType = apnType,
|
||||
apnRoaming = apnProtocolOptions.indexOf(apnRoaming),
|
||||
apnEnable = true
|
||||
@@ -94,23 +94,23 @@ class ApnEditPageProviderTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mmsc_displayed() {
|
||||
fun proxy_displayed() {
|
||||
composeTestRule.setContent {
|
||||
ApnPage(apnInit, remember { apnData }, uri)
|
||||
}
|
||||
composeTestRule.onRoot().onChild().onChildAt(0)
|
||||
.performScrollToNode(hasText(mmsc, true))
|
||||
composeTestRule.onNodeWithText(mmsc, true).assertIsDisplayed()
|
||||
.performScrollToNode(hasText(proxy, true))
|
||||
composeTestRule.onNodeWithText(proxy, true).assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mms_proxy_displayed() {
|
||||
fun port_displayed() {
|
||||
composeTestRule.setContent {
|
||||
ApnPage(apnInit, remember { apnData }, uri)
|
||||
}
|
||||
composeTestRule.onRoot().onChild().onChildAt(0)
|
||||
.performScrollToNode(hasText(mmsProxy, true))
|
||||
composeTestRule.onNodeWithText(mmsProxy, true).assertIsDisplayed()
|
||||
.performScrollToNode(hasText(port, true))
|
||||
composeTestRule.onNodeWithText(port, true).assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* 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.spa.network
|
||||
|
||||
import android.content.Context
|
||||
import android.telephony.SubscriptionInfo
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settings.R
|
||||
import com.android.settings.network.SimOnboardingService
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.stub
|
||||
import org.mockito.kotlin.verify
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class SimOnboardingLabelSimTest {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
private var mockSimOnboardingService = mock<SimOnboardingService> {
|
||||
on { targetSubId }.doReturn(-1)
|
||||
on { targetSubInfo }.doReturn(null)
|
||||
on { availableSubInfoList }.doReturn(listOf())
|
||||
on { activeSubInfoList }.doReturn(listOf())
|
||||
on { slotInfoList }.doReturn(listOf())
|
||||
on { uiccCardInfoList }.doReturn(listOf())
|
||||
on { selectedSubInfoList }.doReturn(mutableListOf())
|
||||
|
||||
on { targetPrimarySimCalls }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
|
||||
on { targetPrimarySimTexts }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
|
||||
on { targetPrimarySimMobileData }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
|
||||
}
|
||||
|
||||
private val nextAction: () -> Unit = mock()
|
||||
private val cancelAction: () -> Unit = mock()
|
||||
|
||||
@Test
|
||||
fun simOnboardingLabelSimImpl_showTitle() {
|
||||
composeTestRule.setContent {
|
||||
SimOnboardingLabelSimImpl(nextAction, cancelAction, mockSimOnboardingService)
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_label_sim_title))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun simOnboardingLabelSimImpl_showSubTitle() {
|
||||
composeTestRule.setContent {
|
||||
SimOnboardingLabelSimImpl(nextAction, cancelAction, mockSimOnboardingService)
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_label_sim_msg))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun simOnboardingLabelSimImpl_clickNextAction_verifyNextAction() {
|
||||
composeTestRule.setContent {
|
||||
SimOnboardingLabelSimImpl(nextAction, cancelAction, mockSimOnboardingService)
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_next))
|
||||
.performClick()
|
||||
|
||||
verify(nextAction)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun simOnboardingLabelSimImpl_clickCancelAction_verifyCancelAction() {
|
||||
composeTestRule.setContent {
|
||||
SimOnboardingLabelSimImpl(nextAction, cancelAction, mockSimOnboardingService)
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.cancel))
|
||||
.performClick()
|
||||
|
||||
verify(cancelAction)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun simOnboardingLabelSimImpl_showItem_show3Items() {
|
||||
mockSimOnboardingService.stub {
|
||||
on { targetSubId }.doReturn(SUB_ID_1)
|
||||
on { targetSubInfo }.doReturn(SUB_INFO_1)
|
||||
on { availableSubInfoList }.doReturn(listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_3))
|
||||
on { activeSubInfoList }.doReturn(listOf(SUB_INFO_2, SUB_INFO_3))
|
||||
on { getSelectableSubscriptionInfo() }.doReturn(
|
||||
listOf(
|
||||
SUB_INFO_1,
|
||||
SUB_INFO_2,
|
||||
SUB_INFO_3
|
||||
)
|
||||
)
|
||||
on { getSubscriptionInfoDisplayName(SUB_INFO_1) }.doReturn(DISPLAY_NAME_1)
|
||||
on { getSubscriptionInfoDisplayName(SUB_INFO_2) }.doReturn(DISPLAY_NAME_2)
|
||||
on { getSubscriptionInfoDisplayName(SUB_INFO_3) }.doReturn(DISPLAY_NAME_3)
|
||||
}
|
||||
|
||||
composeTestRule.setContent {
|
||||
SimOnboardingLabelSimImpl(nextAction, cancelAction, mockSimOnboardingService)
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(DISPLAY_NAME_1).assertIsDisplayed()
|
||||
composeTestRule.onNodeWithText(NUMBER_1).assertIsDisplayed()
|
||||
composeTestRule.onNodeWithText(DISPLAY_NAME_2).assertIsDisplayed()
|
||||
composeTestRule.onNodeWithText(NUMBER_2).assertIsDisplayed()
|
||||
composeTestRule.onNodeWithText(DISPLAY_NAME_3).assertIsDisplayed()
|
||||
composeTestRule.onNodeWithText(NUMBER_3).assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun simOnboardingLabelSimImpl_showDialog_checkTitle() {
|
||||
mockSimOnboardingService.stub {
|
||||
on { targetSubId }.doReturn(SUB_ID_1)
|
||||
on { targetSubInfo }.doReturn(SUB_INFO_1)
|
||||
on { availableSubInfoList }.doReturn(listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_3))
|
||||
on { activeSubInfoList }.doReturn(listOf(SUB_INFO_2, SUB_INFO_3))
|
||||
on { getSelectableSubscriptionInfo() }.doReturn(
|
||||
listOf(
|
||||
SUB_INFO_1,
|
||||
SUB_INFO_2,
|
||||
SUB_INFO_3
|
||||
)
|
||||
)
|
||||
on { getSubscriptionInfoDisplayName(SUB_INFO_1) }.doReturn(DISPLAY_NAME_1)
|
||||
on { getSubscriptionInfoDisplayName(SUB_INFO_2) }.doReturn(DISPLAY_NAME_2)
|
||||
on { getSubscriptionInfoDisplayName(SUB_INFO_3) }.doReturn(DISPLAY_NAME_3)
|
||||
}
|
||||
|
||||
composeTestRule.setContent {
|
||||
SimOnboardingLabelSimImpl(nextAction, cancelAction, mockSimOnboardingService)
|
||||
}
|
||||
|
||||
|
||||
composeTestRule.onNodeWithText(DISPLAY_NAME_1).performClick()
|
||||
|
||||
composeTestRule.onNodeWithText(
|
||||
context.getString(R.string.sim_onboarding_label_sim_dialog_title)
|
||||
)
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val SUB_ID_1 = 1
|
||||
const val SUB_ID_2 = 2
|
||||
const val SUB_ID_3 = 3
|
||||
const val DISPLAY_NAME_1 = "Sub 1"
|
||||
const val DISPLAY_NAME_2 = "Sub 2"
|
||||
const val DISPLAY_NAME_3 = "Sub 3"
|
||||
const val NUMBER_1 = "000000001"
|
||||
const val NUMBER_2 = "000000002"
|
||||
const val NUMBER_3 = "000000003"
|
||||
const val PRIMARY_SIM_ASK_EVERY_TIME = -1
|
||||
|
||||
val SUB_INFO_1: SubscriptionInfo = SubscriptionInfo.Builder().apply {
|
||||
setId(SUB_ID_1)
|
||||
setDisplayName(DISPLAY_NAME_1)
|
||||
setNumber(NUMBER_1)
|
||||
}.build()
|
||||
|
||||
val SUB_INFO_2: SubscriptionInfo = SubscriptionInfo.Builder().apply {
|
||||
setId(SUB_ID_2)
|
||||
setDisplayName(DISPLAY_NAME_2)
|
||||
setNumber(NUMBER_2)
|
||||
}.build()
|
||||
|
||||
val SUB_INFO_3: SubscriptionInfo = SubscriptionInfo.Builder().apply {
|
||||
setId(SUB_ID_3)
|
||||
setDisplayName(DISPLAY_NAME_3)
|
||||
setNumber(NUMBER_3)
|
||||
}.build()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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.spa.network
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settings.R
|
||||
import com.android.settings.network.SimOnboardingService
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.stub
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class SimOnboardingPageProviderTest {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
private var mockSimOnboardingService = mock<SimOnboardingService> {
|
||||
on { targetSubId }.doReturn(SUB_ID)
|
||||
on { targetSubInfo }.doReturn(null)
|
||||
on { availableSubInfoList }.doReturn(listOf())
|
||||
on { activeSubInfoList }.doReturn(listOf())
|
||||
on { slotInfoList }.doReturn(listOf())
|
||||
on { uiccCardInfoList }.doReturn(listOf())
|
||||
on { selectedSubInfoList }.doReturn(mutableListOf())
|
||||
|
||||
on { targetPrimarySimCalls }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
|
||||
on { targetPrimarySimTexts }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
|
||||
on { targetPrimarySimMobileData }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun simOnboardingPageProvider_name() {
|
||||
assertThat(SimOnboardingPageProvider.name).isEqualTo("SimOnboardingPageProvider")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun simOnboardingPage_labelSim() {
|
||||
composeTestRule.setContent {
|
||||
val navHostController = rememberNavController()
|
||||
PageImpl(mockSimOnboardingService, navHostController)
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_label_sim_title))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun simOnboardingPage_nextAction_fromLabelSimToPrimarySim() {
|
||||
mockSimOnboardingService.stub {
|
||||
on { isMultipleEnabledProfilesSupported }.thenReturn(false)
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
val navHostController = rememberNavController()
|
||||
PageImpl(mockSimOnboardingService, navHostController)
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_next))
|
||||
.performClick()
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_primary_sim_title))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun simOnboardingPage_nextAction_fromLabelSimToSelectSim() {
|
||||
mockSimOnboardingService.stub {
|
||||
on { isMultipleEnabledProfilesSupported }.thenReturn(true)
|
||||
}
|
||||
|
||||
composeTestRule.setContent {
|
||||
val navHostController = rememberNavController()
|
||||
PageImpl(mockSimOnboardingService, navHostController)
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_next))
|
||||
.performClick()
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_select_sim_title))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun simOnboardingPage_nextAction_fromSelectSimToPrimarySim() {
|
||||
composeTestRule.setContent {
|
||||
val navHostController = rememberNavController()
|
||||
PageImpl(mockSimOnboardingService, navHostController)
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_next))
|
||||
.performClick()
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_primary_sim_title))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val SUB_ID = 1
|
||||
const val PRIMARY_SIM_ASK_EVERY_TIME = -1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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.spa.network
|
||||
|
||||
import android.content.Context
|
||||
import android.telephony.SubscriptionInfo
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settings.R
|
||||
import com.android.settings.network.SimOnboardingService
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.stub
|
||||
import org.mockito.kotlin.verify
|
||||
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class SimOnboardingPrimarySimTest {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
private var mockSimOnboardingService = mock<SimOnboardingService> {
|
||||
on { targetSubId }.doReturn(-1)
|
||||
on { targetSubInfo }.doReturn(null)
|
||||
on { availableSubInfoList }.doReturn(listOf())
|
||||
on { activeSubInfoList }.doReturn(listOf())
|
||||
on { slotInfoList }.doReturn(listOf())
|
||||
on { uiccCardInfoList }.doReturn(listOf())
|
||||
on { selectedSubInfoList }.doReturn(mutableListOf())
|
||||
|
||||
on { targetPrimarySimCalls }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
|
||||
on { targetPrimarySimTexts }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
|
||||
on { targetPrimarySimMobileData }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
|
||||
}
|
||||
|
||||
private val nextAction: () -> Unit = mock()
|
||||
private val cancelAction: () -> Unit = mock()
|
||||
|
||||
@Test
|
||||
fun simOnboardingPrimarySimImpl_showTitle() {
|
||||
composeTestRule.setContent {
|
||||
SimOnboardingPrimarySimImpl(nextAction, cancelAction, mockSimOnboardingService)
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_primary_sim_title))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun simOnboardingPrimarySimImpl_showSubTitle() {
|
||||
composeTestRule.setContent {
|
||||
SimOnboardingPrimarySimImpl(nextAction, cancelAction, mockSimOnboardingService)
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_primary_sim_msg))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun simOnboardingPrimarySimImpl_clickCancelAction_verifyCancelAction() {
|
||||
composeTestRule.setContent {
|
||||
SimOnboardingPrimarySimImpl(nextAction, cancelAction, mockSimOnboardingService)
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.cancel))
|
||||
.performClick()
|
||||
|
||||
verify(cancelAction)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val SUB_ID_1 = 1
|
||||
const val SUB_ID_2 = 2
|
||||
const val SUB_ID_3 = 3
|
||||
const val DISPLAY_NAME_1 = "Sub 1"
|
||||
const val DISPLAY_NAME_2 = "Sub 2"
|
||||
const val DISPLAY_NAME_3 = "Sub 3"
|
||||
const val NUMBER_1 = "000000001"
|
||||
const val NUMBER_2 = "000000002"
|
||||
const val NUMBER_3 = "000000003"
|
||||
const val PRIMARY_SIM_ASK_EVERY_TIME = -1
|
||||
|
||||
val SUB_INFO_1: SubscriptionInfo = SubscriptionInfo.Builder().apply {
|
||||
setId(SUB_ID_1)
|
||||
setDisplayName(DISPLAY_NAME_1)
|
||||
setNumber(NUMBER_1)
|
||||
}.build()
|
||||
|
||||
val SUB_INFO_2: SubscriptionInfo = SubscriptionInfo.Builder().apply {
|
||||
setId(SUB_ID_2)
|
||||
setDisplayName(DISPLAY_NAME_2)
|
||||
setNumber(NUMBER_2)
|
||||
}.build()
|
||||
|
||||
val SUB_INFO_3: SubscriptionInfo = SubscriptionInfo.Builder().apply {
|
||||
setId(SUB_ID_3)
|
||||
setDisplayName(DISPLAY_NAME_3)
|
||||
setNumber(NUMBER_3)
|
||||
}.build()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* 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.spa.network
|
||||
|
||||
import android.content.Context
|
||||
import android.telephony.SubscriptionInfo
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settings.R
|
||||
import com.android.settings.network.SimOnboardingService
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.stub
|
||||
import org.mockito.kotlin.verify
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class SimOnboardingSelectSimTest {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
private var mockSimOnboardingService = mock<SimOnboardingService> {
|
||||
on { targetSubId }.doReturn(-1)
|
||||
on { targetSubInfo }.doReturn(null)
|
||||
on { availableSubInfoList }.doReturn(listOf())
|
||||
on { activeSubInfoList }.doReturn(listOf())
|
||||
on { slotInfoList }.doReturn(listOf())
|
||||
on { uiccCardInfoList }.doReturn(listOf())
|
||||
on { selectedSubInfoList }.doReturn(mutableListOf())
|
||||
|
||||
on { targetPrimarySimCalls }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
|
||||
on { targetPrimarySimTexts }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
|
||||
on { targetPrimarySimMobileData }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
|
||||
}
|
||||
|
||||
private val nextAction: () -> Unit = mock()
|
||||
private val cancelAction: () -> Unit = mock()
|
||||
|
||||
@Test
|
||||
fun simOnboardingSelectSimImpl_showTitle() {
|
||||
composeTestRule.setContent {
|
||||
SimOnboardingSelectSimImpl(nextAction, cancelAction, mockSimOnboardingService)
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_select_sim_title))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun simOnboardingSelectSimImpl_showSubTitle() {
|
||||
composeTestRule.setContent {
|
||||
SimOnboardingSelectSimImpl(nextAction, cancelAction, mockSimOnboardingService)
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_select_sim_msg))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun simOnboardingSelectSimImpl_clickNextAction_verifyNextAction() {
|
||||
composeTestRule.setContent {
|
||||
SimOnboardingSelectSimImpl(nextAction, cancelAction, mockSimOnboardingService)
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_next))
|
||||
.performClick()
|
||||
|
||||
verify(nextAction)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun simOnboardingSelectSimImpl_clickCancelAction_verifyCancelAction() {
|
||||
composeTestRule.setContent {
|
||||
SimOnboardingSelectSimImpl(nextAction, cancelAction, mockSimOnboardingService)
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.cancel))
|
||||
.performClick()
|
||||
|
||||
verify(cancelAction)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun simOnboardingSelectSimImpl_showItem_show3Items() {
|
||||
mockSimOnboardingService.stub {
|
||||
on { targetSubId }.doReturn(SUB_ID_1)
|
||||
on { targetSubInfo }.doReturn(SUB_INFO_1)
|
||||
on { availableSubInfoList }.doReturn(listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_3))
|
||||
on { activeSubInfoList }.doReturn(listOf(SUB_INFO_2, SUB_INFO_3))
|
||||
on { getSelectableSubscriptionInfo() }.doReturn(
|
||||
listOf(
|
||||
SUB_INFO_1,
|
||||
SUB_INFO_2,
|
||||
SUB_INFO_3
|
||||
)
|
||||
)
|
||||
on { getSubscriptionInfoDisplayName(SUB_INFO_1) }.doReturn(DISPLAY_NAME_1)
|
||||
on { getSubscriptionInfoDisplayName(SUB_INFO_2) }.doReturn(DISPLAY_NAME_2)
|
||||
on { getSubscriptionInfoDisplayName(SUB_INFO_3) }.doReturn(DISPLAY_NAME_3)
|
||||
}
|
||||
|
||||
composeTestRule.setContent {
|
||||
SimOnboardingSelectSimImpl(nextAction, cancelAction, mockSimOnboardingService)
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(DISPLAY_NAME_1).assertIsDisplayed()
|
||||
composeTestRule.onNodeWithText(NUMBER_1).assertIsDisplayed()
|
||||
composeTestRule.onNodeWithText(DISPLAY_NAME_2).assertIsDisplayed()
|
||||
composeTestRule.onNodeWithText(NUMBER_2).assertIsDisplayed()
|
||||
composeTestRule.onNodeWithText(DISPLAY_NAME_3).assertIsDisplayed()
|
||||
composeTestRule.onNodeWithText(NUMBER_3).assertIsDisplayed()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val SUB_ID_1 = 1
|
||||
const val SUB_ID_2 = 2
|
||||
const val SUB_ID_3 = 3
|
||||
const val DISPLAY_NAME_1 = "Sub 1"
|
||||
const val DISPLAY_NAME_2 = "Sub 2"
|
||||
const val DISPLAY_NAME_3 = "Sub 3"
|
||||
const val NUMBER_1 = "000000001"
|
||||
const val NUMBER_2 = "000000002"
|
||||
const val NUMBER_3 = "000000003"
|
||||
const val PRIMARY_SIM_ASK_EVERY_TIME = -1
|
||||
|
||||
val SUB_INFO_1: SubscriptionInfo = SubscriptionInfo.Builder().apply {
|
||||
setId(SUB_ID_1)
|
||||
setDisplayName(DISPLAY_NAME_1)
|
||||
setNumber(NUMBER_1)
|
||||
}.build()
|
||||
|
||||
val SUB_INFO_2: SubscriptionInfo = SubscriptionInfo.Builder().apply {
|
||||
setId(SUB_ID_2)
|
||||
setDisplayName(DISPLAY_NAME_2)
|
||||
setNumber(NUMBER_2)
|
||||
}.build()
|
||||
|
||||
val SUB_INFO_3: SubscriptionInfo = SubscriptionInfo.Builder().apply {
|
||||
setId(SUB_ID_3)
|
||||
setDisplayName(DISPLAY_NAME_3)
|
||||
setNumber(NUMBER_3)
|
||||
}.build()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user