diff --git a/color-check-baseline.xml b/color-check-baseline.xml index 75d4aba8304..15b497632f6 100644 --- a/color-check-baseline.xml +++ b/color-check-baseline.xml @@ -1,658 +1,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + diff --git a/res/layout/dialog_audio_sharing_loading_state.xml b/res/layout/dialog_audio_sharing_progress.xml similarity index 100% rename from res/layout/dialog_audio_sharing_loading_state.xml rename to res/layout/dialog_audio_sharing_progress.xml diff --git a/res/values/strings.xml b/res/values/strings.xml index c586c3fb7b5..fb95aa3bd39 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -12141,8 +12141,8 @@ Satellite messaging is included with your account Satellite messaging isn\u2019t included with your account - - Add satellite messaging + + Learn More How it works diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java b/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java index 86f090e8be5..f1e12a4a6fe 100644 --- a/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java +++ b/src/com/android/settings/bluetooth/BluetoothDevicePairingDetailBase.java @@ -72,7 +72,7 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere private final Handler mHandler = new Handler(Looper.getMainLooper()); private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); @Nullable - private AlertDialog mLoadingDialog = null; + private AlertDialog mProgressDialog = null; @VisibleForTesting boolean mShouldTriggerAudioSharingShareThenPairFlow = false; private CopyOnWriteArrayList mDevicesWithMetadataChangedListener = @@ -89,7 +89,7 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere // In share then pair flow, we have to wait on this page till the device is connected. // The BluetoothDevicePreference summary will be blank for seconds between "Pairing..." and // "Connecting..." To help users better understand the process, we listen to metadata change - // as well and show a loading dialog with "Connecting to ...." once BluetoothDevice.getState() + // as well and show a progress dialog with "Connecting to ...." once BluetoothDevice.getState() // gets to BOND_BONDED. final BluetoothAdapter.OnMetadataChangedListener mMetadataListener = new BluetoothAdapter.OnMetadataChangedListener() { @@ -97,7 +97,7 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere public void onMetadataChanged(@NonNull BluetoothDevice device, int key, @Nullable byte[] value) { Log.d(getLogTag(), "onMetadataChanged device = " + device + ", key = " + key); - if (mShouldTriggerAudioSharingShareThenPairFlow && mLoadingDialog == null + if (mShouldTriggerAudioSharingShareThenPairFlow && mProgressDialog == null && device.getBondState() == BluetoothDevice.BOND_BONDED && mSelectedList.contains(device)) { triggerAudioSharingShareThenPairFlow(device); @@ -355,7 +355,7 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere return; } mJustBonded = device; - // Show connecting device loading state + // Show connecting device progress String aliasName = device.getAlias(); String deviceName = TextUtils.isEmpty(aliasName) ? device.getAddress() : aliasName; @@ -387,9 +387,9 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere // TODO: use DialogFragment private void showConnectingDialog(@NonNull String message) { postOnMainThread(() -> { - if (mLoadingDialog != null) { + if (mProgressDialog != null) { Log.d(getLogTag(), "showConnectingDialog, is already showing"); - TextView textView = mLoadingDialog.findViewById(R.id.message); + TextView textView = mProgressDialog.findViewById(R.id.message); if (textView != null && !message.equals(textView.getText().toString())) { Log.d(getLogTag(), "showConnectingDialog, update message"); // TODO: use string res once finalized @@ -401,7 +401,7 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); LayoutInflater inflater = LayoutInflater.from(builder.getContext()); View customView = inflater.inflate( - R.layout.dialog_audio_sharing_loading_state, /* root= */ + R.layout.dialog_audio_sharing_progress, /* root= */ null); TextView textView = customView.findViewById(R.id.message); if (textView != null) { @@ -410,15 +410,15 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere } AlertDialog dialog = builder.setView(customView).setCancelable(false).create(); dialog.setCanceledOnTouchOutside(false); - mLoadingDialog = dialog; + mProgressDialog = dialog; dialog.show(); }); } private void dismissConnectingDialog() { postOnMainThread(() -> { - if (mLoadingDialog != null) { - mLoadingDialog.dismiss(); + if (mProgressDialog != null) { + mProgressDialog.dismiss(); } }); } diff --git a/src/com/android/settings/bluetooth/BluetoothSliceBuilder.java b/src/com/android/settings/bluetooth/BluetoothSliceBuilder.java index fd8ef1fd82a..f4060d62dfd 100644 --- a/src/com/android/settings/bluetooth/BluetoothSliceBuilder.java +++ b/src/com/android/settings/bluetooth/BluetoothSliceBuilder.java @@ -26,6 +26,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; import android.provider.SettingsSlicesContract; +import android.util.Log; import androidx.core.graphics.drawable.IconCompat; import androidx.slice.Slice; @@ -36,10 +37,16 @@ import androidx.slice.builders.SliceAction; import com.android.settings.R; import com.android.settings.SubSettings; import com.android.settings.connecteddevice.BluetoothDashboardFragment; +import com.android.settings.network.SatelliteRepository; import com.android.settings.slices.CustomSliceRegistry; import com.android.settings.slices.SliceBroadcastReceiver; import com.android.settings.slices.SliceBuilderUtils; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + /** * Utility class to build a Bluetooth Slice, and handle all associated actions. */ @@ -80,18 +87,34 @@ public class BluetoothSliceBuilder { final PendingIntent primaryAction = getPrimaryAction(context); final SliceAction primarySliceAction = SliceAction.createDeeplink(primaryAction, icon, ListBuilder.ICON_IMAGE, title); - final SliceAction toggleSliceAction = SliceAction.createToggle(toggleAction, - null /* actionTitle */, isBluetoothEnabled); + RowBuilder rowBuilder = new RowBuilder(); + rowBuilder.setTitle(title); + rowBuilder.setPrimaryAction(primarySliceAction); + if (!isSatelliteOn(context)) { + final SliceAction toggleSliceAction = SliceAction.createToggle(toggleAction, + null /* actionTitle */, isBluetoothEnabled); + rowBuilder.addEndItem(toggleSliceAction); + } return new ListBuilder(context, CustomSliceRegistry.BLUETOOTH_URI, ListBuilder.INFINITY) .setAccentColor(color) - .addRow(new RowBuilder() - .setTitle(title) - .addEndItem(toggleSliceAction) - .setPrimaryAction(primarySliceAction)) + .addRow(rowBuilder) .build(); } + private static boolean isSatelliteOn(Context context) { + boolean result = false; + SatelliteRepository satelliteRepository = new SatelliteRepository(context); + try { + result = satelliteRepository.requestIsSessionStarted( + Executors.newSingleThreadExecutor()).get(3000, TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + Log.e(TAG, "Error to get satellite status : " + e); + } + + return result; + } + public static Intent getIntent(Context context) { final String screenTitle = context.getText(R.string.bluetooth_settings_title).toString(); final Uri contentUri = new Uri.Builder().appendPath( diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingRetryDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingErrorDialogFragment.java similarity index 89% rename from src/com/android/settings/connecteddevice/audiosharing/AudioSharingRetryDialogFragment.java rename to src/com/android/settings/connecteddevice/audiosharing/AudioSharingErrorDialogFragment.java index 822e0537149..95b9bc36b92 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingRetryDialogFragment.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingErrorDialogFragment.java @@ -29,8 +29,8 @@ import androidx.fragment.app.FragmentManager; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settingslib.bluetooth.BluetoothUtils; -public class AudioSharingRetryDialogFragment extends InstrumentedDialogFragment { - private static final String TAG = "AudioSharingRetryDialog"; +public class AudioSharingErrorDialogFragment extends InstrumentedDialogFragment { + private static final String TAG = "AudioSharingErrorDialog"; @Override public int getMetricsCategory() { @@ -39,7 +39,7 @@ public class AudioSharingRetryDialogFragment extends InstrumentedDialogFragment } /** - * Display the {@link AudioSharingRetryDialogFragment} dialog. + * Display the {@link AudioSharingErrorDialogFragment} dialog. * * @param host The Fragment this dialog will be hosted. */ @@ -57,8 +57,8 @@ public class AudioSharingRetryDialogFragment extends InstrumentedDialogFragment Log.d(TAG, "Dialog is showing, return."); return; } - Log.d(TAG, "Show up the retry dialog."); - AudioSharingRetryDialogFragment dialogFrag = new AudioSharingRetryDialogFragment(); + Log.d(TAG, "Show up the error dialog."); + AudioSharingErrorDialogFragment dialogFrag = new AudioSharingErrorDialogFragment(); dialogFrag.show(manager, TAG); } diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingLoadingStateDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingProgressDialogFragment.java similarity index 91% rename from src/com/android/settings/connecteddevice/audiosharing/AudioSharingLoadingStateDialogFragment.java rename to src/com/android/settings/connecteddevice/audiosharing/AudioSharingProgressDialogFragment.java index 8706590c362..53bfcf8f17c 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingLoadingStateDialogFragment.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingProgressDialogFragment.java @@ -40,8 +40,8 @@ import com.google.common.base.Strings; import java.util.concurrent.TimeUnit; -public class AudioSharingLoadingStateDialogFragment extends InstrumentedDialogFragment { - private static final String TAG = "AudioSharingLoadingDlg"; +public class AudioSharingProgressDialogFragment extends InstrumentedDialogFragment { + private static final String TAG = "AudioSharingProgressDlg"; private static final String BUNDLE_KEY_MESSAGE = "bundle_key_message"; private static final long AUTO_DISMISS_TIME_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(15); @@ -58,7 +58,7 @@ public class AudioSharingLoadingStateDialogFragment extends InstrumentedDialogFr } /** - * Display the {@link AudioSharingLoadingStateDialogFragment} dialog. + * Display the {@link AudioSharingProgressDialogFragment} dialog. * * @param host The Fragment this dialog will be hosted by. * @param message The content to be shown on the dialog. @@ -85,16 +85,15 @@ public class AudioSharingLoadingStateDialogFragment extends InstrumentedDialogFr return; } sMessage = message; - Log.d(TAG, "Show up the loading dialog."); + Log.d(TAG, "Show up the progress dialog."); Bundle args = new Bundle(); args.putString(BUNDLE_KEY_MESSAGE, message); - AudioSharingLoadingStateDialogFragment dialogFrag = - new AudioSharingLoadingStateDialogFragment(); + AudioSharingProgressDialogFragment dialogFrag = new AudioSharingProgressDialogFragment(); dialogFrag.setArguments(args); dialogFrag.show(manager, TAG); } - /** Dismiss the {@link AudioSharingLoadingStateDialogFragment} dialog. */ + /** Dismiss the {@link AudioSharingProgressDialogFragment} dialog. */ public static void dismiss(@Nullable Fragment host) { if (host == null || !BluetoothUtils.isAudioSharingEnabled()) return; final FragmentManager manager; @@ -119,7 +118,7 @@ public class AudioSharingLoadingStateDialogFragment extends InstrumentedDialogFr String message = args.getString(BUNDLE_KEY_MESSAGE, ""); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); LayoutInflater inflater = LayoutInflater.from(builder.getContext()); - View customView = inflater.inflate(R.layout.dialog_audio_sharing_loading_state, /* root= */ + View customView = inflater.inflate(R.layout.dialog_audio_sharing_progress, /* root= */ null); TextView textView = customView.findViewById(R.id.message); if (!Strings.isNullOrEmpty(message)) textView.setText(message); diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java index 395647ca84a..ebc8cecadbf 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java @@ -41,7 +41,9 @@ import android.widget.CompoundButton.OnCheckedChangeListener; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.UiThread; import androidx.annotation.VisibleForTesting; +import androidx.fragment.app.DialogFragment; import androidx.fragment.app.Fragment; import androidx.lifecycle.DefaultLifecycleObserver; import androidx.lifecycle.LifecycleOwner; @@ -69,7 +71,6 @@ import com.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; @@ -113,14 +114,21 @@ public class AudioSharingSwitchBarController extends BasePreferenceController private final Executor mExecutor; private final MetricsFeatureProvider mMetricsFeatureProvider; private final OnAudioSharingStateChangedListener mListener; + @VisibleForTesting IntentFilter mIntentFilter; private Map> mGroupedConnectedDevices = new HashMap<>(); @Nullable private AudioSharingDeviceItem mTargetActiveItem; private List mDeviceItemsForSharing = new ArrayList<>(); - @VisibleForTesting IntentFilter mIntentFilter; private final AtomicBoolean mCallbacksRegistered = new AtomicBoolean(false); private AtomicInteger mIntentHandleStage = new AtomicInteger(StartIntentHandleStage.TO_HANDLE.ordinal()); + // The sinks in adding source process. We show the progress dialog based on this list. private CopyOnWriteArrayList mSinksInAdding = new CopyOnWriteArrayList<>(); + // The primary/active sinks in adding source process. + // To avoid users advance to share then pair flow before the primary/active sinks successfully + // join the audio sharing, we will wait for the process complete for this list of sinks and then + // popup audio sharing dialog with options to pair new device. + private CopyOnWriteArrayList mSinksToWaitFor = new CopyOnWriteArrayList<>(); + private AtomicBoolean mStoppingSharing = new AtomicBoolean(false); @VisibleForTesting BroadcastReceiver mReceiver = @@ -153,6 +161,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController public void onBroadcastStartFailed(int reason) { Log.d(TAG, "onBroadcastStartFailed(), reason = " + reason); updateSwitch(); + showErrorDialog(); mMetricsFeatureProvider.action( mContext, SettingsEnums.ACTION_AUDIO_SHARING_START_FAILED, @@ -178,7 +187,10 @@ public class AudioSharingSwitchBarController extends BasePreferenceController + reason + ", broadcastId = " + broadcastId); + mStoppingSharing.compareAndSet(true, false); updateSwitch(); + AudioSharingUtils.postOnMainThread(mContext, + () -> dismissStaleDialogsOtherThanErrorDialog()); AudioSharingUtils.toastMessage( mContext, mContext.getString(R.string.audio_sharing_sharing_stopped_label)); @@ -219,7 +231,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController TAG, "Skip handleOnBroadcastReady: null assistant or " + "sink has active local source."); - cleanUp(); + cleanUpStatesForStartSharing(); return; } handleOnBroadcastReady(); @@ -264,17 +276,14 @@ public class AudioSharingSwitchBarController extends BasePreferenceController + source + ", reason = " + reason); - mMetricsFeatureProvider.action( - mContext, - SettingsEnums.ACTION_AUDIO_SHARING_JOIN_FAILED, - SettingsEnums.AUDIO_SHARING_SETTINGS); - AudioSharingUtils.toastMessage( - mContext, - String.format( - Locale.US, - "Fail to add source to %s reason %d", - sink.getAddress(), - reason)); + if (mSinksInAdding.contains(sink)) { + stopAudioSharing(); + showErrorDialog(); + mMetricsFeatureProvider.action( + mContext, + SettingsEnums.ACTION_AUDIO_SHARING_JOIN_FAILED, + SettingsEnums.AUDIO_SHARING_SETTINGS); + } } @Override @@ -298,13 +307,33 @@ public class AudioSharingSwitchBarController extends BasePreferenceController @NonNull BluetoothDevice sink, int sourceId, @NonNull BluetoothLeBroadcastReceiveState state) { + if (mStoppingSharing.get()) { + Log.d(TAG, "Skip onReceiveStateChanged, stopping broadcast"); + return; + } if (BluetoothUtils.isConnected(state)) { if (mSinksInAdding.contains(sink)) { mSinksInAdding.remove(sink); } - dismissLoadingStateDialogIfNeeded(); + dismissProgressDialogIfNeeded(); Log.d(TAG, "onReceiveStateChanged() connected, sink = " + sink + ", remaining sinks = " + mSinksInAdding); + if (mSinksToWaitFor.contains(sink)) { + mSinksToWaitFor.remove(sink); + if (mSinksToWaitFor.isEmpty()) { + // To avoid users advance to share then pair flow before the + // primary/active sinks successfully join the audio sharing, + // popup dialog till adding source complete for mSinksToWaitFor. + Pair[] eventData = + AudioSharingUtils.buildAudioSharingDialogEventData( + SettingsEnums.AUDIO_SHARING_SETTINGS, + SettingsEnums.DIALOG_AUDIO_SHARING_ADD_DEVICE, + /* userTriggered= */ false, + /* deviceCountInSharing= */ 1, + /* candidateDeviceCount= */ 0); + showAudioSharingDialog(eventData); + } + } } } }; @@ -411,6 +440,8 @@ public class AudioSharingSwitchBarController extends BasePreferenceController return; } stopAudioSharing(); + mMetricsFeatureProvider.action( + mContext, SettingsEnums.ACTION_AUDIO_SHARING_MAIN_SWITCH_OFF); } } @@ -542,7 +573,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController mSinksInAdding.clear(); // TODO: use string res once finalized. AudioSharingUtils.postOnMainThread(mContext, - () -> AudioSharingLoadingStateDialogFragment.show(mFragment, + () -> AudioSharingProgressDialogFragment.show(mFragment, "Starting audio stream...")); mMetricsFeatureProvider.action( mContext, @@ -553,9 +584,14 @@ public class AudioSharingSwitchBarController extends BasePreferenceController private void stopAudioSharing() { if (mBroadcast != null) { - mBroadcast.stopBroadcast(mBroadcast.getLatestBroadcastId()); - mMetricsFeatureProvider.action( - mContext, SettingsEnums.ACTION_AUDIO_SHARING_MAIN_SWITCH_OFF); + int broadcastId = mBroadcast.getLatestBroadcastId(); + if (broadcastId != -1) { + mBroadcast.stopBroadcast(broadcastId); + mStoppingSharing.compareAndSet(false, true); + mSinksInAdding.clear(); + mSinksToWaitFor.clear(); + } + cleanUpStatesForStartSharing(); } } @@ -617,11 +653,22 @@ public class AudioSharingSwitchBarController extends BasePreferenceController /* userTriggered= */ false, /* deviceCountInSharing= */ targetActiveSinks.isEmpty() ? 0 : 1, /* candidateDeviceCount= */ mDeviceItemsForSharing.size()); + // Auto add primary/active sinks w/o user interactions. if (!targetActiveSinks.isEmpty() && mTargetActiveItem != null) { Log.d(TAG, "handleOnBroadcastReady: automatically add source to active sinks."); addSourceToTargetSinks(targetActiveSinks, mTargetActiveItem.getName()); + // To avoid users advance to share then pair flow before the primary/active sinks + // successfully join the audio sharing, save the primary/active sinks in mSinksToWaitFor + // and popup dialog till adding source complete for these sinks. + if (mDeviceItemsForSharing.isEmpty()) { + mSinksToWaitFor.clear(); + mSinksToWaitFor.addAll(targetActiveSinks); + } mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING); mTargetActiveItem = null; + // When audio sharing page is brought up by intent with EXTRA_START_LE_AUDIO_SHARING + // == true, plus there is one active lea headset and one connected lea headset, we + // should auto add these sinks without user interactions. if (mIntentHandleStage.compareAndSet( StartIntentHandleStage.HANDLE_AUTO_ADD.ordinal(), StartIntentHandleStage.HANDLED.ordinal()) @@ -631,31 +678,42 @@ public class AudioSharingSwitchBarController extends BasePreferenceController List targetSinks = mGroupedConnectedDevices.getOrDefault( target.getGroupId(), ImmutableList.of()); addSourceToTargetSinks(targetSinks, target.getName()); - cleanUp(); + cleanUpStatesForStartSharing(); // TODO: Add metric for auto add by intent return; } } + // Still mark intent as handled if early returned due to preconditions not met mIntentHandleStage.compareAndSet( StartIntentHandleStage.HANDLE_AUTO_ADD.ordinal(), StartIntentHandleStage.HANDLED.ordinal()); if (mFragment == null) { Log.d(TAG, "handleOnBroadcastReady: dialog fail to show due to null fragment."); - dismissLoadingStateDialogIfNeeded(); - cleanUp(); + // Clean up states before early return. + dismissProgressDialogIfNeeded(); + cleanUpStatesForStartSharing(); return; } - showDialog(eventData); + // To avoid users advance to share then pair flow before the primary/active sinks + // successfully join the audio sharing, popup dialog till adding source complete for + // mSinksToWaitFor. + if (mSinksToWaitFor.isEmpty() && !mStoppingSharing.get()) { + showAudioSharingDialog(eventData); + } } - private void showDialog(Pair[] eventData) { + private void showAudioSharingDialog(Pair[] eventData) { + if (!BluetoothUtils.isBroadcasting(mBtManager)) { + Log.d(TAG, "Skip showAudioSharingDialog, broadcast is stopped"); + return; + } AudioSharingDialogFragment.DialogEventListener listener = new AudioSharingDialogFragment.DialogEventListener() { @Override public void onPositiveClick() { - // Could go to other pages, dismiss the loading dialog. - dismissLoadingStateDialogIfNeeded(); - cleanUp(); + // Could go to other pages, dismiss the progress dialog. + dismissProgressDialogIfNeeded(); + cleanUpStatesForStartSharing(); } @Override @@ -663,14 +721,14 @@ public class AudioSharingSwitchBarController extends BasePreferenceController List targetSinks = mGroupedConnectedDevices.getOrDefault( item.getGroupId(), ImmutableList.of()); addSourceToTargetSinks(targetSinks, item.getName()); - cleanUp(); + cleanUpStatesForStartSharing(); } @Override public void onCancelClick() { - // Could go to other pages, dismiss the loading dialog. - dismissLoadingStateDialogIfNeeded(); - cleanUp(); + // Could go to other pages, dismiss the progress dialog. + dismissProgressDialogIfNeeded(); + cleanUpStatesForStartSharing(); } }; AudioSharingUtils.postOnMainThread( @@ -684,6 +742,36 @@ public class AudioSharingSwitchBarController extends BasePreferenceController }); } + private void showErrorDialog() { + AudioSharingUtils.postOnMainThread(mContext, + () -> { + // Remove all stale dialogs before showing error dialog + dismissStaleDialogsOtherThanErrorDialog(); + AudioSharingErrorDialogFragment.show(mFragment); + }); + } + + @UiThread + private void dismissStaleDialogsOtherThanErrorDialog() { + List fragments = new ArrayList(); + try { + if (mFragment != null) { + fragments = + mFragment.getChildFragmentManager().getFragments(); + } + } catch (Exception e) { + Log.e(TAG, "Fail to dismiss stale dialogs: " + e.getMessage()); + } + for (Fragment fragment : fragments) { + if (fragment != null && fragment instanceof DialogFragment + && !(fragment instanceof AudioSharingErrorDialogFragment) + && ((DialogFragment) fragment).getDialog() != null) { + Log.d(TAG, "Remove stale dialog = " + fragment.getTag()); + ((DialogFragment) fragment).dismiss(); + } + } + } + private static final class MainSwitchAccessibilityDelegate extends View.AccessibilityDelegate { @Override public boolean onRequestSendAccessibilityEvent( @@ -742,25 +830,25 @@ public class AudioSharingSwitchBarController extends BasePreferenceController private void addSourceToTargetSinks(List targetActiveSinks, @NonNull String sinkName) { mSinksInAdding.addAll(targetActiveSinks); - AudioSharingUtils.addSourceToTargetSinks(targetActiveSinks, mBtManager); // TODO: move to res once finalized - String loadingMessage = "Sharing with " + sinkName + "..."; - showLoadingStateDialog(loadingMessage); + String progressMessage = "Sharing with " + sinkName + "..."; + showProgressDialog(progressMessage); + AudioSharingUtils.addSourceToTargetSinks(targetActiveSinks, mBtManager); } - private void showLoadingStateDialog(@NonNull String loadingMessage) { + private void showProgressDialog(@NonNull String progressMessage) { AudioSharingUtils.postOnMainThread(mContext, - () -> AudioSharingLoadingStateDialogFragment.show(mFragment, loadingMessage)); + () -> AudioSharingProgressDialogFragment.show(mFragment, progressMessage)); } - private void dismissLoadingStateDialogIfNeeded() { + private void dismissProgressDialogIfNeeded() { if (mSinksInAdding.isEmpty()) { AudioSharingUtils.postOnMainThread(mContext, - () -> AudioSharingLoadingStateDialogFragment.dismiss(mFragment)); + () -> AudioSharingProgressDialogFragment.dismiss(mFragment)); } } - private void cleanUp() { + private void cleanUpStatesForStartSharing() { mGroupedConnectedDevices.clear(); mDeviceItemsForSharing.clear(); } diff --git a/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreference.kt b/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreference.kt new file mode 100644 index 00000000000..fb27dd814c8 --- /dev/null +++ b/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreference.kt @@ -0,0 +1,57 @@ +/* + * 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.deviceinfo.firmwareversion + +import android.content.Context +import android.os.SystemProperties +import androidx.preference.Preference +import com.android.settings.R +import com.android.settings.Utils +import com.android.settingslib.metadata.PreferenceAvailabilityProvider +import com.android.settingslib.metadata.PreferenceMetadata +import com.android.settingslib.metadata.PreferenceSummaryProvider +import com.android.settingslib.preference.PreferenceBinding + +// LINT.IfChange +class BasebandVersionPreference : + PreferenceMetadata, + PreferenceSummaryProvider, + PreferenceAvailabilityProvider, + PreferenceBinding { + + override val key: String + get() = "base_band" + + override val title: Int + get() = R.string.baseband_version + + override fun getSummary(context: Context): CharSequence? = + SystemProperties.get(BASEBAND_PROPERTY, context.getString(R.string.device_info_default)) + + override fun isAvailable(context: Context) = !Utils.isWifiOnly(context) + + override fun bind(preference: Preference, metadata: PreferenceMetadata) { + super.bind(preference, metadata) + preference.isSelectable = false + preference.isCopyingEnabled = true + } + + companion object { + const val BASEBAND_PROPERTY: String = "gsm.version.baseband" + } +} +// LINT.ThenChange(BasebandVersionPreferenceController.java) diff --git a/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceController.java b/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceController.java index dd3d560282a..235638433be 100644 --- a/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceController.java +++ b/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceController.java @@ -25,6 +25,7 @@ import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.core.BasePreferenceController; +// LINT.IfChange public class BasebandVersionPreferenceController extends BasePreferenceController { @VisibleForTesting @@ -45,3 +46,4 @@ public class BasebandVersionPreferenceController extends BasePreferenceControlle mContext.getString(R.string.device_info_default)); } } +// LINT.ThenChange(BasebandVersionPreference.kt) diff --git a/src/com/android/settings/deviceinfo/firmwareversion/FirmwareVersionScreen.kt b/src/com/android/settings/deviceinfo/firmwareversion/FirmwareVersionScreen.kt index 58fdefe6e4a..0908f9eaa85 100644 --- a/src/com/android/settings/deviceinfo/firmwareversion/FirmwareVersionScreen.kt +++ b/src/com/android/settings/deviceinfo/firmwareversion/FirmwareVersionScreen.kt @@ -50,9 +50,9 @@ class FirmwareVersionScreen : PreferenceScreenCreator, PreferenceSummaryProvider +PreferenceWidget("os_firmware_version", R.string.firmware_version) +PreferenceWidget("security_key", R.string.security_patch) +PreferenceWidget("module_version", R.string.module_version) - +PreferenceWidget("base_band", R.string.baseband_version) - +PreferenceWidget("kernel_version", R.string.kernel_version) - +PreferenceWidget("os_build_number", R.string.build_number) + +BasebandVersionPreference() + +KernelVersionPreference() + +SimpleBuildNumberPreference() } private class PreferenceWidget(override val key: String, override val title: Int) : diff --git a/src/com/android/settings/deviceinfo/firmwareversion/KernelVersionPreference.kt b/src/com/android/settings/deviceinfo/firmwareversion/KernelVersionPreference.kt new file mode 100644 index 00000000000..789117852b5 --- /dev/null +++ b/src/com/android/settings/deviceinfo/firmwareversion/KernelVersionPreference.kt @@ -0,0 +1,45 @@ +/* + * 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.deviceinfo.firmwareversion + +import android.content.Context +import androidx.preference.Preference +import com.android.settings.R +import com.android.settingslib.DeviceInfoUtils +import com.android.settingslib.metadata.PreferenceMetadata +import com.android.settingslib.metadata.PreferenceSummaryProvider +import com.android.settingslib.preference.PreferenceBinding + +// LINT.IfChange +class KernelVersionPreference : PreferenceMetadata, PreferenceSummaryProvider, PreferenceBinding { + + override val key: String + get() = "kernel_version" + + override val title: Int + get() = R.string.kernel_version + + override fun getSummary(context: Context): CharSequence? = + DeviceInfoUtils.getFormattedKernelVersion(context) + + override fun bind(preference: Preference, metadata: PreferenceMetadata) { + super.bind(preference, metadata) + preference.isSelectable = false + preference.isCopyingEnabled = true + } +} +// LINT.ThenChange(KernelVersionPreferenceController.java) diff --git a/src/com/android/settings/deviceinfo/firmwareversion/KernelVersionPreferenceController.java b/src/com/android/settings/deviceinfo/firmwareversion/KernelVersionPreferenceController.java index 0500c89371a..7a3bdafb5e7 100644 --- a/src/com/android/settings/deviceinfo/firmwareversion/KernelVersionPreferenceController.java +++ b/src/com/android/settings/deviceinfo/firmwareversion/KernelVersionPreferenceController.java @@ -21,6 +21,7 @@ import android.content.Context; import com.android.settings.core.BasePreferenceController; import com.android.settingslib.DeviceInfoUtils; +// LINT.IfChange public class KernelVersionPreferenceController extends BasePreferenceController { public KernelVersionPreferenceController(Context context, String preferenceKey) { @@ -37,3 +38,4 @@ public class KernelVersionPreferenceController extends BasePreferenceController return DeviceInfoUtils.getFormattedKernelVersion(mContext); } } +// LINT.ThenChange(KernelVersionPreference.kt) diff --git a/src/com/android/settings/deviceinfo/firmwareversion/SimpleBuildNumberPreference.kt b/src/com/android/settings/deviceinfo/firmwareversion/SimpleBuildNumberPreference.kt new file mode 100644 index 00000000000..62cc2f985e7 --- /dev/null +++ b/src/com/android/settings/deviceinfo/firmwareversion/SimpleBuildNumberPreference.kt @@ -0,0 +1,52 @@ +/* + * 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.deviceinfo.firmwareversion + +import android.content.Context +import android.os.Build +import android.text.BidiFormatter +import android.view.View.LAYOUT_DIRECTION_RTL +import androidx.preference.Preference +import com.android.settings.R +import com.android.settingslib.metadata.PreferenceMetadata +import com.android.settingslib.metadata.PreferenceSummaryProvider +import com.android.settingslib.preference.PreferenceBinding + +// LINT.IfChange +class SimpleBuildNumberPreference : + PreferenceMetadata, PreferenceSummaryProvider, PreferenceBinding { + + override val key: String + get() = "os_build_number" + + override val title: Int + get() = R.string.build_number + + override fun isIndexable(context: Context) = false + + override fun getSummary(context: Context): CharSequence? { + val isRtl = context.resources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL + return BidiFormatter.getInstance(isRtl).unicodeWrap(Build.DISPLAY) + } + + override fun bind(preference: Preference, metadata: PreferenceMetadata) { + super.bind(preference, metadata) + preference.isSelectable = false + preference.isCopyingEnabled = true + } +} +// LINT.ThenChange(SimpleBuildNumberPreferenceController.java) diff --git a/src/com/android/settings/deviceinfo/firmwareversion/SimpleBuildNumberPreferenceController.java b/src/com/android/settings/deviceinfo/firmwareversion/SimpleBuildNumberPreferenceController.java index 53f5ff9aa9d..11988f7002e 100644 --- a/src/com/android/settings/deviceinfo/firmwareversion/SimpleBuildNumberPreferenceController.java +++ b/src/com/android/settings/deviceinfo/firmwareversion/SimpleBuildNumberPreferenceController.java @@ -22,6 +22,7 @@ import android.text.BidiFormatter; import com.android.settings.core.BasePreferenceController; +// LINT.IfChange public class SimpleBuildNumberPreferenceController extends BasePreferenceController { public SimpleBuildNumberPreferenceController(Context context, @@ -39,3 +40,4 @@ public class SimpleBuildNumberPreferenceController extends BasePreferenceControl return BidiFormatter.getInstance().unicodeWrap(Build.DISPLAY); } } +// LINT.ThenChange(SimpleBuildNumberPreference.kt) diff --git a/src/com/android/settings/language/OnDeviceRecognitionPreferenceController.java b/src/com/android/settings/language/OnDeviceRecognitionPreferenceController.java index cd9f266cf90..d0dec6430d7 100644 --- a/src/com/android/settings/language/OnDeviceRecognitionPreferenceController.java +++ b/src/com/android/settings/language/OnDeviceRecognitionPreferenceController.java @@ -20,7 +20,6 @@ import android.app.Dialog; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.pm.UserInfo; import android.os.UserHandle; import android.os.UserManager; import android.text.TextUtils; @@ -80,10 +79,7 @@ public class OnDeviceRecognitionPreferenceController extends BasePreferenceContr } private void show(Preference preference) { - final List userHandles = new ArrayList<>(); - for (UserInfo userInfo : UserManager.get(mContext).getUsers()) { - userHandles.add(userInfo.getUserHandle()); - } + final List userHandles = UserManager.get(mContext).getEnabledProfiles(); // Only a single profile is installed. Proceed with its settings. if (userHandles.size() == 1) { diff --git a/src/com/android/settings/network/MobileNetworkListFragment.kt b/src/com/android/settings/network/MobileNetworkListFragment.kt index d110779b36d..3118088c6c3 100644 --- a/src/com/android/settings/network/MobileNetworkListFragment.kt +++ b/src/com/android/settings/network/MobileNetworkListFragment.kt @@ -89,7 +89,7 @@ class MobileNetworkListFragment : DashboardFragment() { private val simRepositoryFactory: (Context) -> SimRepository = ::SimRepository ) : BaseSearchIndexProvider(R.xml.network_provider_sims_list) { public override fun isPageSearchEnabled(context: Context): Boolean = - simRepositoryFactory(context).showMobileNetworkPage() + simRepositoryFactory(context).canEnterMobileNetworkPage() } } } diff --git a/src/com/android/settings/network/MobileNetworkSummaryController.kt b/src/com/android/settings/network/MobileNetworkSummaryController.kt index 5980bbd7d05..8cf9bec283e 100644 --- a/src/com/android/settings/network/MobileNetworkSummaryController.kt +++ b/src/com/android/settings/network/MobileNetworkSummaryController.kt @@ -57,7 +57,7 @@ constructor( private var isAirplaneModeOn = false override fun getAvailabilityStatus() = - if (SimRepository(mContext).showMobileNetworkPage()) AVAILABLE + if (SimRepository(mContext).showMobileNetworkPageEntrance()) AVAILABLE else CONDITIONALLY_UNAVAILABLE override fun displayPreference(screen: PreferenceScreen) { diff --git a/src/com/android/settings/network/SimOnboardingActivity.kt b/src/com/android/settings/network/SimOnboardingActivity.kt index 25afb661e8b..7fab9c9848e 100644 --- a/src/com/android/settings/network/SimOnboardingActivity.kt +++ b/src/com/android/settings/network/SimOnboardingActivity.kt @@ -53,8 +53,8 @@ import androidx.compose.ui.text.style.TextAlign import androidx.lifecycle.LifecycleRegistry import com.android.settings.R import com.android.settings.SidecarFragment +import com.android.settings.network.telephony.SimRepository import com.android.settings.network.telephony.SubscriptionActionDialogActivity -import com.android.settings.network.telephony.SubscriptionRepository import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity import com.android.settings.network.telephony.requireSubscriptionManager import com.android.settings.spa.SpaActivity.Companion.startSpaActivity @@ -578,6 +578,10 @@ class SimOnboardingActivity : SpaBaseDialogActivity() { subId: Int, isNewTask: Boolean = false, ) { + if (!SimRepository(context).canEnterMobileNetworkPage()) { + Log.i(TAG, "Unable to start SimOnboardingActivity due to missing permissions") + return + } val intent = Intent(context, SimOnboardingActivity::class.java).apply { putExtra(SUB_ID, subId) if(isNewTask) { diff --git a/src/com/android/settings/network/TopLevelNetworkEntryPreferenceController.kt b/src/com/android/settings/network/TopLevelNetworkEntryPreferenceController.kt index 1722f6ae6b9..11698a6699c 100644 --- a/src/com/android/settings/network/TopLevelNetworkEntryPreferenceController.kt +++ b/src/com/android/settings/network/TopLevelNetworkEntryPreferenceController.kt @@ -48,7 +48,7 @@ constructor( override fun getSummary(): CharSequence { val summaryResId = - if (simRepository.showMobileNetworkPage()) { + if (simRepository.showMobileNetworkPageEntrance()) { R.string.network_dashboard_summary_mobile } else { R.string.network_dashboard_summary_no_mobile diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt b/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt index 83e3a31ff3f..f850e1d9b4b 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt +++ b/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt @@ -21,7 +21,6 @@ import android.provider.Settings import android.telephony.SubscriptionInfo import com.android.settings.R import com.android.settings.datausage.BillingCyclePreferenceController.Companion.BillingCycleSearchItem -import com.android.settings.network.SubscriptionUtil import com.android.settings.network.telephony.CarrierSettingsVersionPreferenceController.Companion.CarrierSettingsVersionSearchItem import com.android.settings.network.telephony.DataUsagePreferenceController.Companion.DataUsageSearchItem import com.android.settings.network.telephony.MmsMessagePreferenceController.Companion.MmsMessageSearchItem @@ -36,7 +35,6 @@ import com.android.settings.spa.search.SpaSearchRepository.Companion.createSearc import com.android.settings.spa.search.SpaSearchRepository.Companion.searchIndexProviderOf import com.android.settingslib.search.SearchIndexableData import com.android.settingslib.search.SearchIndexableRaw -import com.android.settingslib.spaprivileged.framework.common.userManager import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalBoolean class MobileNetworkSettingsSearchIndex( @@ -109,12 +107,8 @@ class MobileNetworkSettingsSearchIndex( companion object { /** suppress full page if user is not admin */ @JvmStatic - fun isMobileNetworkSettingsSearchable(context: Context): Boolean { - val isAirplaneMode by context.settingsGlobalBoolean(Settings.Global.AIRPLANE_MODE_ON) - return SubscriptionUtil.isSimHardwareVisible(context) && - !isAirplaneMode && - context.userManager.isAdminUser - } + fun isMobileNetworkSettingsSearchable(context: Context): Boolean = + SimRepository(context).canEnterMobileNetworkPage() fun createSearchItems(context: Context): List = listOf( diff --git a/src/com/android/settings/network/telephony/SimRepository.kt b/src/com/android/settings/network/telephony/SimRepository.kt index ed3c8aa303c..baff5cbe2a3 100644 --- a/src/com/android/settings/network/telephony/SimRepository.kt +++ b/src/com/android/settings/network/telephony/SimRepository.kt @@ -18,13 +18,24 @@ package com.android.settings.network.telephony import android.content.Context import android.content.pm.PackageManager +import android.os.UserManager +import android.provider.Settings import com.android.settingslib.spaprivileged.framework.common.userManager +import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalBoolean -class SimRepository(context: Context) { +class SimRepository(private val context: Context) { private val packageManager = context.packageManager private val userManager = context.userManager - /** Gets whether we show mobile network settings page to the current user. */ - fun showMobileNetworkPage(): Boolean = + /** Gets whether show mobile network settings page entrance to the current user. */ + fun showMobileNetworkPageEntrance(): Boolean = packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) && userManager.isAdminUser + + /** Gets whether current user can enter mobile network settings page. */ + fun canEnterMobileNetworkPage(): Boolean { + val isAirplaneMode by context.settingsGlobalBoolean(Settings.Global.AIRPLANE_MODE_ON) + return showMobileNetworkPageEntrance() && + !isAirplaneMode && + !userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS) + } } diff --git a/src/com/android/settings/notification/app/NotificationSoundPreference.java b/src/com/android/settings/notification/app/NotificationSoundPreference.java index b55f9bd7ce8..4084ffe685f 100644 --- a/src/com/android/settings/notification/app/NotificationSoundPreference.java +++ b/src/com/android/settings/notification/app/NotificationSoundPreference.java @@ -24,9 +24,8 @@ import android.media.RingtoneManager; import android.net.Uri; import android.os.AsyncTask; import android.util.AttributeSet; - import android.util.Log; -import com.android.settings.R; + import com.android.settings.RingtonePreference; public class NotificationSoundPreference extends RingtonePreference { @@ -49,6 +48,20 @@ public class NotificationSoundPreference extends RingtonePreference { updateRingtoneName(mRingtone); } + protected String generateRingtoneTitle(Uri uri) { + if (uri == null) { + return getContext().getString(com.android.internal.R.string.ringtone_silent); + } else if (RingtoneManager.isDefault(uri)) { + return getContext().getString(com.android.settings.R.string.notification_sound_default); + } else if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(uri.getScheme())) { + return getContext().getString( + com.android.settings.R.string.notification_unknown_sound_title); + } else { + return Ringtone.getTitle(getContext(), uri, false /* followSettingsUri */, + true /* allowRemote */); + } + } + @Override public boolean onActivityResult(int requestCode, int resultCode, Intent data) { if (data != null) { @@ -69,16 +82,7 @@ public class NotificationSoundPreference extends RingtonePreference { AsyncTask ringtoneNameTask = new AsyncTask() { @Override protected CharSequence doInBackground(Object... params) { - if (uri == null) { - return getContext().getString(com.android.internal.R.string.ringtone_silent); - } else if (RingtoneManager.isDefault(uri)) { - return getContext().getString(R.string.notification_sound_default); - } else if(ContentResolver.SCHEME_ANDROID_RESOURCE.equals(uri.getScheme())) { - return getContext().getString(R.string.notification_unknown_sound_title); - } else { - return Ringtone.getTitle(getContext(), uri, false /* followSettingsUri */, - true /* allowRemote */); - } + return generateRingtoneTitle(uri); } @Override diff --git a/src/com/android/settings/notification/zen/ZenModeBackend.java b/src/com/android/settings/notification/zen/ZenModeBackend.java index de641c5f38b..c85b06bef38 100644 --- a/src/com/android/settings/notification/zen/ZenModeBackend.java +++ b/src/com/android/settings/notification/zen/ZenModeBackend.java @@ -458,7 +458,7 @@ public class ZenModeBackend { } private static List getDefaultRuleIds() { - return ZenModeConfig.DEFAULT_RULE_IDS; + return ZenModeConfig.getDefaultRuleIds(); } NotificationManager.Policy toNotificationPolicy(ZenPolicy policy) { diff --git a/src/com/android/settings/sim/receivers/SimSlotChangeHandler.java b/src/com/android/settings/sim/receivers/SimSlotChangeHandler.java index 6e28a0d5a96..f808924fa41 100644 --- a/src/com/android/settings/sim/receivers/SimSlotChangeHandler.java +++ b/src/com/android/settings/sim/receivers/SimSlotChangeHandler.java @@ -206,7 +206,12 @@ public class SimSlotChangeHandler { if (hasActiveEsimSubscription()) { if (mTelMgr.isMultiSimSupported() == TelephonyManager.MULTISIM_ALLOWED) { Log.i(TAG, "Enabled profile exists. DSDS condition satisfied."); - startDsdsDialogActivity(); + if (Flags.isDualSimOnboardingEnabled()) { + // enable dsds by sim onboarding flow + handleRemovableSimInsertWhenDsds(removableSlotInfo); + } else { + startDsdsDialogActivity(); + } } else { Log.i(TAG, "Enabled profile exists. DSDS condition not satisfied."); startChooseSimActivity(true); diff --git a/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt b/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt index d736fe5224a..f60ba81fc4c 100644 --- a/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt +++ b/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt @@ -212,7 +212,7 @@ open class NetworkCellularGroupProvider : SettingsPageProvider, SearchablePage { const val fileName = "NetworkCellularGroupProvider" private fun isPageSearchable(context: Context) = - Flags.isDualSimOnboardingEnabled() && SimRepository(context).showMobileNetworkPage() + Flags.isDualSimOnboardingEnabled() && SimRepository(context).canEnterMobileNetworkPage() } } diff --git a/src/com/android/settings/widget/TickButtonPreference.java b/src/com/android/settings/widget/TickButtonPreference.java index 4778f8c8ee0..6bd17dea1b3 100644 --- a/src/com/android/settings/widget/TickButtonPreference.java +++ b/src/com/android/settings/widget/TickButtonPreference.java @@ -21,6 +21,8 @@ import android.util.AttributeSet; import android.view.View; import android.widget.ImageView; +import androidx.annotation.Nullable; +import androidx.annotation.NonNull; import androidx.preference.PreferenceViewHolder; import com.android.settings.R; @@ -35,6 +37,10 @@ public class TickButtonPreference extends TwoTargetPreference { super(context); } + public TickButtonPreference(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + @Override public void onBindViewHolder(PreferenceViewHolder holder) { super.onBindViewHolder(holder); diff --git a/tests/robotests/Android.bp b/tests/robotests/Android.bp index 3fd430610d3..60a68033c95 100644 --- a/tests/robotests/Android.bp +++ b/tests/robotests/Android.bp @@ -69,6 +69,9 @@ android_robolectric_test { "com_android_server_accessibility_flags_lib", "flag-junit", "flag-junit-base", + "kotlin-test", + "mockito-robolectric-prebuilt", // mockito deps order matters! + "mockito-kotlin2", "notification_flags_lib", "platform-test-annotations", "testables", diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingRetryDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingErrorDialogFragmentTest.java similarity index 91% rename from tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingRetryDialogFragmentTest.java rename to tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingErrorDialogFragmentTest.java index 4b0524b848a..fc38cd2817d 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingRetryDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingErrorDialogFragmentTest.java @@ -51,14 +51,14 @@ import org.robolectric.shadows.androidx.fragment.FragmentController; ShadowAlertDialogCompat.class, ShadowBluetoothAdapter.class, }) -public class AudioSharingRetryDialogFragmentTest { +public class AudioSharingErrorDialogFragmentTest { @Rule public final MockitoRule mocks = MockitoJUnit.rule(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private Fragment mParent; - private AudioSharingRetryDialogFragment mFragment; + private AudioSharingErrorDialogFragment mFragment; @Before public void setUp() { @@ -70,7 +70,7 @@ public class AudioSharingRetryDialogFragmentTest { BluetoothStatusCodes.FEATURE_SUPPORTED); shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( BluetoothStatusCodes.FEATURE_SUPPORTED); - mFragment = new AudioSharingRetryDialogFragment(); + mFragment = new AudioSharingErrorDialogFragment(); mParent = new Fragment(); FragmentController.setupFragment( mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null); @@ -91,7 +91,7 @@ public class AudioSharingRetryDialogFragmentTest { @Test public void onCreateDialog_flagOff_dialogNotExist() { mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); - AudioSharingRetryDialogFragment.show(mParent); + AudioSharingErrorDialogFragment.show(mParent); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog).isNull(); @@ -100,7 +100,7 @@ public class AudioSharingRetryDialogFragmentTest { @Test public void onCreateDialog_unattachedFragment_dialogNotExist() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); - AudioSharingRetryDialogFragment.show(new Fragment()); + AudioSharingErrorDialogFragment.show(new Fragment()); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog).isNull(); @@ -109,7 +109,7 @@ public class AudioSharingRetryDialogFragmentTest { @Test public void onCreateDialog_flagOn_showDialog() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); - AudioSharingRetryDialogFragment.show(mParent); + AudioSharingErrorDialogFragment.show(mParent); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog).isNotNull(); @@ -119,7 +119,7 @@ public class AudioSharingRetryDialogFragmentTest { @Test public void onCreateDialog_clickOk_dialogDismiss() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); - AudioSharingRetryDialogFragment.show(mParent); + AudioSharingErrorDialogFragment.show(mParent); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog).isNotNull(); diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingLoadingStateDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingProgressDialogFragmentTest.java similarity index 86% rename from tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingLoadingStateDialogFragmentTest.java rename to tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingProgressDialogFragmentTest.java index ff15f5269d4..b08e5c5e96b 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingLoadingStateDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingProgressDialogFragmentTest.java @@ -52,7 +52,7 @@ import org.robolectric.shadows.androidx.fragment.FragmentController; ShadowAlertDialogCompat.class, ShadowBluetoothAdapter.class, }) -public class AudioSharingLoadingStateDialogFragmentTest { +public class AudioSharingProgressDialogFragmentTest { @Rule public final MockitoRule mocks = MockitoJUnit.rule(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -60,7 +60,7 @@ public class AudioSharingLoadingStateDialogFragmentTest { private static final String TEST_MESSAGE2 = "message2"; private Fragment mParent; - private AudioSharingLoadingStateDialogFragment mFragment; + private AudioSharingProgressDialogFragment mFragment; @Before public void setUp() { @@ -72,7 +72,7 @@ public class AudioSharingLoadingStateDialogFragmentTest { BluetoothStatusCodes.FEATURE_SUPPORTED); shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( BluetoothStatusCodes.FEATURE_SUPPORTED); - mFragment = new AudioSharingLoadingStateDialogFragment(); + mFragment = new AudioSharingProgressDialogFragment(); mParent = new Fragment(); FragmentController.setupFragment( mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null); @@ -93,7 +93,7 @@ public class AudioSharingLoadingStateDialogFragmentTest { @Test public void onCreateDialog_flagOff_dialogNotExist() { mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); - AudioSharingLoadingStateDialogFragment.show(mParent, TEST_MESSAGE1); + AudioSharingProgressDialogFragment.show(mParent, TEST_MESSAGE1); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog).isNull(); @@ -102,7 +102,7 @@ public class AudioSharingLoadingStateDialogFragmentTest { @Test public void onCreateDialog_unattachedFragment_dialogNotExist() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); - AudioSharingLoadingStateDialogFragment.show(new Fragment(), TEST_MESSAGE1); + AudioSharingProgressDialogFragment.show(new Fragment(), TEST_MESSAGE1); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog).isNull(); @@ -111,7 +111,7 @@ public class AudioSharingLoadingStateDialogFragmentTest { @Test public void onCreateDialog_flagOn_showDialog() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); - AudioSharingLoadingStateDialogFragment.show(mParent, TEST_MESSAGE1); + AudioSharingProgressDialogFragment.show(mParent, TEST_MESSAGE1); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog).isNotNull(); @@ -124,13 +124,13 @@ public class AudioSharingLoadingStateDialogFragmentTest { @Test public void dismissDialog_succeed() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); - AudioSharingLoadingStateDialogFragment.show(mParent, TEST_MESSAGE1); + AudioSharingProgressDialogFragment.show(mParent, TEST_MESSAGE1); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog).isNotNull(); assertThat(dialog.isShowing()).isTrue(); - AudioSharingLoadingStateDialogFragment.dismiss(mParent); + AudioSharingProgressDialogFragment.dismiss(mParent); shadowMainLooper().idle(); assertThat(dialog.isShowing()).isFalse(); } @@ -138,13 +138,13 @@ public class AudioSharingLoadingStateDialogFragmentTest { @Test public void showDialog_sameMessage_keepExistingDialog() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); - AudioSharingLoadingStateDialogFragment.show(mParent, TEST_MESSAGE1); + AudioSharingProgressDialogFragment.show(mParent, TEST_MESSAGE1); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog).isNotNull(); assertThat(dialog.isShowing()).isTrue(); - AudioSharingLoadingStateDialogFragment.show(mParent, TEST_MESSAGE1); + AudioSharingProgressDialogFragment.show(mParent, TEST_MESSAGE1); shadowMainLooper().idle(); assertThat(dialog.isShowing()).isTrue(); } @@ -152,7 +152,7 @@ public class AudioSharingLoadingStateDialogFragmentTest { @Test public void showDialog_newMessage_keepAndUpdateDialog() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); - AudioSharingLoadingStateDialogFragment.show(mParent, TEST_MESSAGE1); + AudioSharingProgressDialogFragment.show(mParent, TEST_MESSAGE1); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog).isNotNull(); @@ -161,7 +161,7 @@ public class AudioSharingLoadingStateDialogFragmentTest { assertThat(view).isNotNull(); assertThat(view.getText().toString()).isEqualTo(TEST_MESSAGE1); - AudioSharingLoadingStateDialogFragment.show(mParent, TEST_MESSAGE2); + AudioSharingProgressDialogFragment.show(mParent, TEST_MESSAGE2); shadowMainLooper().idle(); assertThat(dialog.isShowing()).isTrue(); assertThat(view.getText().toString()).isEqualTo(TEST_MESSAGE2); diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java index eb2083ebe7b..58696dccbea 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java @@ -146,6 +146,7 @@ public class AudioSharingSwitchBarControllerTest { @Mock private LocalBluetoothLeBroadcastAssistant mAssistant; @Mock private VolumeControlProfile mVolumeControl; @Mock private BluetoothLeBroadcastMetadata mMetadata; + @Mock private BluetoothLeBroadcastReceiveState mState; @Mock private CompoundButton mBtnView; @Mock private CachedBluetoothDevice mCachedDevice1; @Mock private CachedBluetoothDevice mCachedDevice2; @@ -499,7 +500,7 @@ public class AudioSharingSwitchBarControllerTest { verify(mBroadcast).startPrivateBroadcast(); List childFragments = mParentFragment.getChildFragmentManager().getFragments(); - // No loading state dialog. + // No progress dialog. assertThat(childFragments).isEmpty(); mController.mBroadcastCallback.onPlaybackStarted(0, 0); @@ -519,10 +520,9 @@ public class AudioSharingSwitchBarControllerTest { mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true); when(mBtnView.isEnabled()).thenReturn(true); when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1)); - BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class); - when(state.getBroadcastId()).thenReturn(1); + when(mState.getBroadcastId()).thenReturn(1); when(mBroadcast.getLatestBroadcastId()).thenReturn(1); - when(mAssistant.getAllSources(mDevice2)).thenReturn(ImmutableList.of(state)); + when(mAssistant.getAllSources(mDevice2)).thenReturn(ImmutableList.of(mState)); when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata); doNothing().when(mBroadcast).startPrivateBroadcast(); mController.onCheckedChanged(mBtnView, /* isChecked= */ true); @@ -531,7 +531,7 @@ public class AudioSharingSwitchBarControllerTest { verify(mBroadcast).startPrivateBroadcast(); List childFragments = mParentFragment.getChildFragmentManager().getFragments(); assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly( - AudioSharingLoadingStateDialogFragment.class.getName()); + AudioSharingProgressDialogFragment.class.getName()); mController.mBroadcastCallback.onPlaybackStarted(0, 0); shadowOf(Looper.getMainLooper()).idle(); @@ -549,27 +549,92 @@ public class AudioSharingSwitchBarControllerTest { } @Test - public void onPlaybackStarted_showJoinAudioSharingDialog() { + public void onPlaybackStarted_singleActiveDevice_showJoinAudioSharingDialog() { FeatureFlagUtils.setEnabled( mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true); when(mBtnView.isEnabled()).thenReturn(true); - when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1)); + when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2)); when(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of()); doNothing().when(mBroadcast).startPrivateBroadcast(); - when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata); mController.onCheckedChanged(mBtnView, /* isChecked= */ true); shadowOf(Looper.getMainLooper()).idle(); verify(mBroadcast).startPrivateBroadcast(); List childFragments = mParentFragment.getChildFragmentManager().getFragments(); assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly( - AudioSharingLoadingStateDialogFragment.class.getName()); - AudioSharingLoadingStateDialogFragment loadingFragment = - (AudioSharingLoadingStateDialogFragment) Iterables.getOnlyElement(childFragments); + AudioSharingProgressDialogFragment.class.getName()); + + when(mBroadcast.isEnabled(null)).thenReturn(true); + when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata); + mController.mBroadcastCallback.onPlaybackStarted(0, 0); + shadowOf(Looper.getMainLooper()).idle(); + + verify(mFeatureFactory.metricsFeatureProvider) + .action(any(Context.class), eq(SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING)); + + when(mState.getBisSyncState()).thenReturn(ImmutableList.of(1L)); + mController.mBroadcastAssistantCallback.onReceiveStateChanged(mDevice2, /* sourceId= */ 1, + mState); + shadowOf(Looper.getMainLooper()).idle(); + + childFragments = mParentFragment.getChildFragmentManager().getFragments(); + assertThat(childFragments) + .comparingElementsUsing(CLAZZNAME_EQUALS) + .containsExactly(AudioSharingDialogFragment.class.getName()); + + Pair[] eventData = new Pair[0]; + for (Fragment fragment : childFragments) { + if (fragment instanceof AudioSharingDialogFragment) { + eventData = ((AudioSharingDialogFragment) fragment).getEventData(); + break; + } + } + assertThat(eventData) + .asList() + .containsExactly( + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_SOURCE_PAGE_ID.ordinal(), + SettingsEnums.AUDIO_SHARING_SETTINGS), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_PAGE_ID.ordinal(), + SettingsEnums.DIALOG_AUDIO_SHARING_ADD_DEVICE), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_USER_TRIGGERED.ordinal(), 0), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_DEVICE_COUNT_IN_SHARING + .ordinal(), + 1), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT + .ordinal(), + 0)); + + childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss()); + } + + @Test + public void onPlaybackStarted_oneActiveOnConnected_showJoinAudioSharingDialog() { + FeatureFlagUtils.setEnabled( + mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true); + when(mBtnView.isEnabled()).thenReturn(true); + when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1)); + when(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of()); + doNothing().when(mBroadcast).startPrivateBroadcast(); + mController.onCheckedChanged(mBtnView, /* isChecked= */ true); + shadowOf(Looper.getMainLooper()).idle(); + + verify(mBroadcast).startPrivateBroadcast(); + List childFragments = mParentFragment.getChildFragmentManager().getFragments(); + assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly( + AudioSharingProgressDialogFragment.class.getName()); + AudioSharingProgressDialogFragment progressFragment = + (AudioSharingProgressDialogFragment) Iterables.getOnlyElement(childFragments); // TODO: use string res once finalized String expectedMessage = "Starting audio stream..."; - checkLoadingStateDialogMessage(loadingFragment, expectedMessage); + checkProgressDialogMessage(progressFragment, expectedMessage); + when(mBroadcast.isEnabled(null)).thenReturn(true); + when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata); mController.mBroadcastCallback.onPlaybackStarted(0, 0); shadowOf(Looper.getMainLooper()).idle(); @@ -577,13 +642,13 @@ public class AudioSharingSwitchBarControllerTest { .action(any(Context.class), eq(SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING)); // TODO: use string res once finalized expectedMessage = "Sharing with " + TEST_DEVICE_NAME2 + "..."; - checkLoadingStateDialogMessage(loadingFragment, expectedMessage); + checkProgressDialogMessage(progressFragment, expectedMessage); childFragments = mParentFragment.getChildFragmentManager().getFragments(); assertThat(childFragments) .comparingElementsUsing(CLAZZNAME_EQUALS) .containsExactly(AudioSharingDialogFragment.class.getName(), - AudioSharingLoadingStateDialogFragment.class.getName()); + AudioSharingProgressDialogFragment.class.getName()); Pair[] eventData = new Pair[0]; for (Fragment fragment : childFragments) { @@ -616,18 +681,19 @@ public class AudioSharingSwitchBarControllerTest { } @Test - public void onPlaybackStarted_clickShareBtnOnDialog_addSource() { + public void onPlaybackStarted_oneActiveOnConnected_clickShareBtnOnDialog_addSource() { FeatureFlagUtils.setEnabled( mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true); when(mBtnView.isEnabled()).thenReturn(true); when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1)); when(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of()); - when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata); doNothing().when(mBroadcast).startPrivateBroadcast(); mController.onCheckedChanged(mBtnView, /* isChecked= */ true); shadowOf(Looper.getMainLooper()).idle(); verify(mBroadcast).startPrivateBroadcast(); + when(mBroadcast.isEnabled(null)).thenReturn(true); + when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata); mController.mBroadcastCallback.onPlaybackStarted(0, 0); shadowOf(Looper.getMainLooper()).idle(); @@ -642,32 +708,33 @@ public class AudioSharingSwitchBarControllerTest { verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false); assertThat(dialog.isShowing()).isFalse(); - // Loading state dialog shows sharing state for the user chosen sink. + // Progress dialog shows sharing progress for the user chosen sink. List childFragments = mParentFragment.getChildFragmentManager().getFragments(); assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly( - AudioSharingLoadingStateDialogFragment.class.getName()); - AudioSharingLoadingStateDialogFragment loadingFragment = - (AudioSharingLoadingStateDialogFragment) Iterables.getOnlyElement(childFragments); + AudioSharingProgressDialogFragment.class.getName()); + AudioSharingProgressDialogFragment progressFragment = + (AudioSharingProgressDialogFragment) Iterables.getOnlyElement(childFragments); // TODO: use string res once finalized String expectedMessage = "Sharing with " + TEST_DEVICE_NAME1 + "..."; - checkLoadingStateDialogMessage(loadingFragment, expectedMessage); + checkProgressDialogMessage(progressFragment, expectedMessage); childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss()); } @Test - public void onPlaybackStarted_clickCancelBtnOnDialog_doNothing() { + public void onPlaybackStarted_oneActiveOnConnected_clickCancelBtnOnDialog_doNothing() { FeatureFlagUtils.setEnabled( mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true); when(mBtnView.isEnabled()).thenReturn(true); when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1)); when(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of()); - when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata); doNothing().when(mBroadcast).startPrivateBroadcast(); mController.onCheckedChanged(mBtnView, /* isChecked= */ true); shadowOf(Looper.getMainLooper()).idle(); verify(mBroadcast).startPrivateBroadcast(); + when(mBroadcast.isEnabled(null)).thenReturn(true); + when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata); mController.mBroadcastCallback.onPlaybackStarted(0, 0); shadowOf(Looper.getMainLooper()).idle(); @@ -682,15 +749,15 @@ public class AudioSharingSwitchBarControllerTest { verify(mAssistant, never()).addSource(mDevice1, mMetadata, /* isGroupOp= */ false); assertThat(dialog.isShowing()).isFalse(); - // Loading state dialog shows sharing state for the auto add active sink. + // Progress dialog shows sharing progress for the auto add active sink. List childFragments = mParentFragment.getChildFragmentManager().getFragments(); assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly( - AudioSharingLoadingStateDialogFragment.class.getName()); - AudioSharingLoadingStateDialogFragment loadingFragment = - (AudioSharingLoadingStateDialogFragment) Iterables.getOnlyElement(childFragments); + AudioSharingProgressDialogFragment.class.getName()); + AudioSharingProgressDialogFragment progressFragment = + (AudioSharingProgressDialogFragment) Iterables.getOnlyElement(childFragments); // TODO: use string res once finalized String expectedMessage = "Sharing with " + TEST_DEVICE_NAME2 + "..."; - checkLoadingStateDialogMessage(loadingFragment, expectedMessage); + checkProgressDialogMessage(progressFragment, expectedMessage); childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss()); } @@ -754,28 +821,63 @@ public class AudioSharingSwitchBarControllerTest { } @Test - public void testAssistantCallbacks_onSourceAddFailed_logAction() { + public void testAssistantCallbacks_onSourceAddFailed_twoDevices_showErrorAndLogAction() { + FeatureFlagUtils.setEnabled( + mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true); + when(mBtnView.isEnabled()).thenReturn(true); + when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1)); + when(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of()); + doNothing().when(mBroadcast).startPrivateBroadcast(); + mController.onCheckedChanged(mBtnView, /* isChecked= */ true); + shadowOf(Looper.getMainLooper()).idle(); + + verify(mBroadcast).startPrivateBroadcast(); + + when(mBroadcast.isEnabled(null)).thenReturn(true); + when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata); + mController.mBroadcastCallback.onPlaybackStarted(0, 0); + shadowOf(Looper.getMainLooper()).idle(); + + verify(mAssistant).addSource(mDevice2, mMetadata, /* isGroupOp= */ false); + + AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); + assertThat(dialog).isNotNull(); + View btnView = dialog.findViewById(R.id.positive_btn); + assertThat(btnView).isNotNull(); + btnView.performClick(); + shadowMainLooper().idle(); + + verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false); + assertThat(dialog.isShowing()).isFalse(); + mController.mBroadcastAssistantCallback.onSourceAddFailed( mDevice1, mMetadata, /* reason= */ 1); + shadowMainLooper().idle(); + + // Progress dialog shows sharing progress for the user chosen sink. + List childFragments = mParentFragment.getChildFragmentManager().getFragments(); + assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly( + AudioSharingErrorDialogFragment.class.getName()); verify(mFeatureFactory.metricsFeatureProvider) .action( mContext, SettingsEnums.ACTION_AUDIO_SHARING_JOIN_FAILED, SettingsEnums.AUDIO_SHARING_SETTINGS); + + childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss()); } @Test - public void testAssistantCallbacks_onReceiveStateChanged_dismissLoadingDialog() { - AudioSharingLoadingStateDialogFragment.show(mParentFragment, TEST_DEVICE_NAME1); + public void testAssistantCallbacks_onReceiveStateChanged_dismissProgressDialog() { + AudioSharingProgressDialogFragment.show(mParentFragment, TEST_DEVICE_NAME1); shadowOf(Looper.getMainLooper()).idle(); List childFragments = mParentFragment.getChildFragmentManager().getFragments(); assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly( - AudioSharingLoadingStateDialogFragment.class.getName()); + AudioSharingProgressDialogFragment.class.getName()); - BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class); - when(state.getBisSyncState()).thenReturn(ImmutableList.of(1L)); + when(mState.getBisSyncState()).thenReturn(ImmutableList.of(1L)); mController.mBroadcastAssistantCallback.onReceiveStateChanged(mDevice1, /* sourceId= */ 1, - state); + mState); shadowOf(Looper.getMainLooper()).idle(); childFragments = mParentFragment.getChildFragmentManager().getFragments(); assertThat(childFragments).isEmpty(); @@ -783,11 +885,9 @@ public class AudioSharingSwitchBarControllerTest { @Test public void testAssistantCallbacks_doNothing() { - BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class); - // Do nothing mController.mBroadcastAssistantCallback.onReceiveStateChanged( - mDevice1, /* sourceId= */ 1, state); + mDevice1, /* sourceId= */ 1, mState); mController.mBroadcastAssistantCallback.onSearchStarted(/* reason= */ 1); mController.mBroadcastAssistantCallback.onSearchStartFailed(/* reason= */ 1); mController.mBroadcastAssistantCallback.onSearchStopped(/* reason= */ 1); @@ -923,13 +1023,13 @@ public class AudioSharingSwitchBarControllerTest { List childFragments = parentFragment.getChildFragmentManager().getFragments(); // Skip audio sharing dialog. assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly( - AudioSharingLoadingStateDialogFragment.class.getName()); - // The loading state dialog shows sharing state for the auto add second sink. - AudioSharingLoadingStateDialogFragment loadingFragment = - (AudioSharingLoadingStateDialogFragment) Iterables.getOnlyElement(childFragments); + AudioSharingProgressDialogFragment.class.getName()); + // Progress dialog shows sharing progress for the auto add second sink. + AudioSharingProgressDialogFragment progressFragment = + (AudioSharingProgressDialogFragment) Iterables.getOnlyElement(childFragments); // TODO: use string res once finalized String expectedMessage = "Sharing with " + TEST_DEVICE_NAME1 + "..."; - checkLoadingStateDialogMessage(loadingFragment, expectedMessage); + checkProgressDialogMessage(progressFragment, expectedMessage); childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss()); } @@ -944,7 +1044,7 @@ public class AudioSharingSwitchBarControllerTest { verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false); List childFragments = mParentFragment.getChildFragmentManager().getFragments(); assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly( - AudioSharingLoadingStateDialogFragment.class.getName()); + AudioSharingProgressDialogFragment.class.getName()); } private Fragment setUpFragmentWithStartSharingIntent() { @@ -964,12 +1064,12 @@ public class AudioSharingSwitchBarControllerTest { return fragment; } - private void checkLoadingStateDialogMessage( - @NonNull AudioSharingLoadingStateDialogFragment fragment, + private void checkProgressDialogMessage( + @NonNull AudioSharingProgressDialogFragment fragment, @NonNull String expectedMessage) { - TextView loadingMessage = fragment.getDialog() == null ? null + TextView progressMessage = fragment.getDialog() == null ? null : fragment.getDialog().findViewById(R.id.message); - assertThat(loadingMessage).isNotNull(); - assertThat(loadingMessage.getText().toString()).isEqualTo(expectedMessage); + assertThat(progressMessage).isNotNull(); + assertThat(progressMessage.getText().toString()).isEqualTo(expectedMessage); } } diff --git a/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceControllerTest.java index 87fdb222c42..5d7e82e98eb 100644 --- a/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceControllerTest.java @@ -38,6 +38,7 @@ import org.robolectric.RuntimeEnvironment; import java.util.Arrays; +// LINT.IfChange @RunWith(RobolectricTestRunner.class) public class BasebandVersionPreferenceControllerTest { @Mock @@ -68,3 +69,4 @@ public class BasebandVersionPreferenceControllerTest { assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); } } +// LINT.ThenChange(BasebandVersionPreferenceTest.kt) diff --git a/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceTest.kt b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceTest.kt new file mode 100644 index 00000000000..27220380712 --- /dev/null +++ b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceTest.kt @@ -0,0 +1,60 @@ +/* + * 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.deviceinfo.firmwareversion + +import android.content.Context +import android.content.ContextWrapper +import android.sysprop.TelephonyProperties +import android.telephony.TelephonyManager +import androidx.test.core.app.ApplicationProvider +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.robolectric.RobolectricTestRunner + +// LINT.IfChange +@RunWith(RobolectricTestRunner::class) +class BasebandVersionPreferenceTest { + private lateinit var telephonyManager: TelephonyManager + + private val context: Context = + object : ContextWrapper(ApplicationProvider.getApplicationContext()) { + override fun getSystemService(name: String): Any? = + when { + name == getSystemServiceName(TelephonyManager::class.java) -> telephonyManager + else -> super.getSystemService(name) + } + } + + private val basebandVersionPreference = BasebandVersionPreference() + + @Test + fun isAvailable_wifiOnly_unavailable() { + telephonyManager = mock { on { isDataCapable } doReturn false } + assertThat(basebandVersionPreference.isAvailable(context)).isFalse() + } + + @Test + fun isAvailable_hasMobile_available() { + TelephonyProperties.baseband_version(listOf("test")) + telephonyManager = mock { on { isDataCapable } doReturn true } + assertThat(basebandVersionPreference.isAvailable(context)).isTrue() + } +} +// LINT.ThenChange(BasebandVersionPreferenceControllerTest.java) diff --git a/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/KernelVersionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/KernelVersionPreferenceControllerTest.java index 9ee0bb32d99..145940608a9 100644 --- a/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/KernelVersionPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/KernelVersionPreferenceControllerTest.java @@ -29,6 +29,7 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +// LINT.IfChange @RunWith(RobolectricTestRunner.class) public class KernelVersionPreferenceControllerTest { @@ -49,3 +50,4 @@ public class KernelVersionPreferenceControllerTest { DeviceInfoUtils.getFormattedKernelVersion(mContext)); } } +// LINT.ThenChange(KernelVersionPreferenceTest.kt) diff --git a/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/KernelVersionPreferenceTest.kt b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/KernelVersionPreferenceTest.kt new file mode 100644 index 00000000000..162fc15eca5 --- /dev/null +++ b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/KernelVersionPreferenceTest.kt @@ -0,0 +1,40 @@ +/* + * 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.deviceinfo.firmwareversion + +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import com.android.settingslib.DeviceInfoUtils +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +// LINT.IfChange +@RunWith(RobolectricTestRunner::class) +class KernelVersionPreferenceTest { + private val context: Context = ApplicationProvider.getApplicationContext() + + private val kernelVersionPreference = KernelVersionPreference() + + @Test + fun getSummary() { + assertThat(kernelVersionPreference.getSummary(context)) + .isEqualTo(DeviceInfoUtils.getFormattedKernelVersion(context)) + } +} +// LINT.ThenChange(KernelVersionPreferenceControllerTest.java) diff --git a/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/SimpleBuildNumberPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/SimpleBuildNumberPreferenceControllerTest.java index 3f050db6ab4..4b7dfc5f749 100644 --- a/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/SimpleBuildNumberPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/SimpleBuildNumberPreferenceControllerTest.java @@ -29,6 +29,7 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +// LINT.IfChange @RunWith(RobolectricTestRunner.class) public class SimpleBuildNumberPreferenceControllerTest { @@ -48,3 +49,4 @@ public class SimpleBuildNumberPreferenceControllerTest { assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_UNSEARCHABLE); } } +// LINT.ThenChange(SimpleBuildNumberPreferenceTest.kt) diff --git a/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/SimpleBuildNumberPreferenceTest.kt b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/SimpleBuildNumberPreferenceTest.kt new file mode 100644 index 00000000000..9c9dd710b14 --- /dev/null +++ b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/SimpleBuildNumberPreferenceTest.kt @@ -0,0 +1,55 @@ +/* + * 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.deviceinfo.firmwareversion + +import android.content.Context +import android.os.Build +import android.text.BidiFormatter +import androidx.test.core.app.ApplicationProvider +import com.google.common.truth.Truth.assertThat +import java.util.Locale +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +// LINT.IfChange +@RunWith(RobolectricTestRunner::class) +class SimpleBuildNumberPreferenceTest { + private val context: Context = ApplicationProvider.getApplicationContext() + + private val simpleBuildNumberPreference = SimpleBuildNumberPreference() + + @Test + fun isIndexable() { + assertThat(simpleBuildNumberPreference.isIndexable(context)).isFalse() + } + + @Test + fun getSummary_ltr() { + context.resources.configuration.setLayoutDirection(Locale.ENGLISH) + assertThat(simpleBuildNumberPreference.getSummary(context)) + .isEqualTo(BidiFormatter.getInstance(false).unicodeWrap(Build.DISPLAY)) + } + + @Test + fun getSummary_rtl() { + context.resources.configuration.setLayoutDirection(Locale("ar")) + assertThat(simpleBuildNumberPreference.getSummary(context)) + .isEqualTo(BidiFormatter.getInstance(true).unicodeWrap(Build.DISPLAY)) + } +} +// LINT.ThenChange(SimpleBuildNumberPreferenceControllerTest.java) diff --git a/tests/robotests/src/com/android/settings/notification/zen/ZenModeBackendTest.java b/tests/robotests/src/com/android/settings/notification/zen/ZenModeBackendTest.java index 32bf9af3010..63da3c41ea4 100644 --- a/tests/robotests/src/com/android/settings/notification/zen/ZenModeBackendTest.java +++ b/tests/robotests/src/com/android/settings/notification/zen/ZenModeBackendTest.java @@ -23,14 +23,18 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.AutomaticZenRule; +import android.app.Flags; import android.app.NotificationManager; import android.app.NotificationManager.Policy; import android.content.Context; import android.database.Cursor; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.service.notification.ZenModeConfig; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -53,12 +57,15 @@ public class ZenModeBackendTest { private NotificationManager mNotificationManager; private static final String GENERIC_RULE_NAME = "test"; - private static final String DEFAULT_ID_1 = ZenModeConfig.EVENTS_DEFAULT_RULE_ID; + private static final String DEFAULT_ID_1 = ZenModeConfig.EVENTS_OBSOLETE_RULE_ID; private static final String DEFAULT_ID_2 = ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID; private Context mContext; private ZenModeBackend mBackend; + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Before public void setup() { MockitoAnnotations.initMocks(this); @@ -161,6 +168,10 @@ public class ZenModeBackendTest { } @Test + // With MODES_UI the Events rule is not default and is sorted differently. Most likely this + // whole test class should be disabled since ZenModeBackend should never be used with + // MODES_UI. However the other tests pass, so... + @DisableFlags(Flags.FLAG_MODES_UI) public void updateState_checkRuleOrderingDescending_withDefaultRules() { final int NUM_RULES = 4; @@ -168,8 +179,8 @@ public class ZenModeBackendTest { true); Arrays.sort(rules, ZenModeBackend.RULE_COMPARATOR); - assertEquals(rules[0].getKey(), DEFAULT_ID_1); - assertEquals(rules[1].getKey(), DEFAULT_ID_2); + assertEquals(DEFAULT_ID_1, rules[0].getKey()); + assertEquals(DEFAULT_ID_2, rules[1].getKey()); // NON-DEFAULT RULES check ordering, most recent at the bottom/end for (int i = 0; i < NUM_RULES; i++) { assertEquals(GENERIC_RULE_NAME + (NUM_RULES - 1 - i), rules[i + 2].getKey()); diff --git a/tests/spa_unit/src/com/android/settings/network/MobileNetworkListFragmentTest.kt b/tests/spa_unit/src/com/android/settings/network/MobileNetworkListFragmentTest.kt index 4bb5f2f803f..226f14f1dd2 100644 --- a/tests/spa_unit/src/com/android/settings/network/MobileNetworkListFragmentTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/MobileNetworkListFragmentTest.kt @@ -36,7 +36,7 @@ class MobileNetworkListFragmentTest { @Test fun isPageSearchEnabled_showMobileNetworkPage_returnTrue() { - mockSimRepository.stub { on { showMobileNetworkPage() } doReturn true } + mockSimRepository.stub { on { canEnterMobileNetworkPage() } doReturn true } val isEnabled = SearchIndexProvider { mockSimRepository }.isPageSearchEnabled(context) @@ -45,7 +45,7 @@ class MobileNetworkListFragmentTest { @Test fun isPageSearchEnabled_hideMobileNetworkPage_returnFalse() { - mockSimRepository.stub { on { showMobileNetworkPage() } doReturn false } + mockSimRepository.stub { on { canEnterMobileNetworkPage() } doReturn false } val isEnabled = SearchIndexProvider { mockSimRepository }.isPageSearchEnabled(context) diff --git a/tests/spa_unit/src/com/android/settings/network/TopLevelNetworkEntryPreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/network/TopLevelNetworkEntryPreferenceControllerTest.kt index 27c960282dc..78d2941ae89 100644 --- a/tests/spa_unit/src/com/android/settings/network/TopLevelNetworkEntryPreferenceControllerTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/TopLevelNetworkEntryPreferenceControllerTest.kt @@ -71,7 +71,7 @@ class TopLevelNetworkEntryPreferenceControllerTest { @Test fun getSummary_hasMobile_shouldReturnMobileSummary() { - mockSimRepository.stub { on { showMobileNetworkPage() } doReturn true } + mockSimRepository.stub { on { showMobileNetworkPageEntrance() } doReturn true } val summary = controller.summary @@ -84,7 +84,7 @@ class TopLevelNetworkEntryPreferenceControllerTest { @Test fun getSummary_noMobile_shouldReturnNoMobileSummary() { - mockSimRepository.stub { on { showMobileNetworkPage() } doReturn false } + mockSimRepository.stub { on { showMobileNetworkPageEntrance() } doReturn false } val summary = controller.summary diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/SimRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/SimRepositoryTest.kt index bbcac086a10..e80cc2f8704 100644 --- a/tests/spa_unit/src/com/android/settings/network/telephony/SimRepositoryTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/telephony/SimRepositoryTest.kt @@ -37,50 +37,76 @@ class SimRepositoryTest { private val mockPackageManager = mock() - private val context: Context = spy(ApplicationProvider.getApplicationContext()) { - on { userManager } doReturn mockUserManager - on { packageManager } doReturn mockPackageManager - } + private val context: Context = + spy(ApplicationProvider.getApplicationContext()) { + on { userManager } doReturn mockUserManager + on { packageManager } doReturn mockPackageManager + } private val repository = SimRepository(context) @Test - fun showMobileNetworkPage_adminUserAndHasTelephony_returnTrue() { - mockUserManager.stub { - on { isAdminUser } doReturn true - } + fun showMobileNetworkPageEntrance_adminUserAndHasTelephony_returnTrue() { + mockUserManager.stub { on { isAdminUser } doReturn true } mockPackageManager.stub { on { hasSystemFeature(PackageManager.FEATURE_TELEPHONY) } doReturn true } - val showMobileNetworkPage = repository.showMobileNetworkPage() + val showMobileNetworkPage = repository.showMobileNetworkPageEntrance() assertThat(showMobileNetworkPage).isTrue() } @Test - fun showMobileNetworkPage_notAdminUser_returnFalse() { + fun showMobileNetworkPageEntrance_notAdminUser_returnFalse() { + mockUserManager.stub { on { isAdminUser } doReturn false } + mockPackageManager.stub { + on { hasSystemFeature(PackageManager.FEATURE_TELEPHONY) } doReturn true + } + + val showMobileNetworkPage = repository.showMobileNetworkPageEntrance() + + assertThat(showMobileNetworkPage).isFalse() + } + + @Test + fun showMobileNetworkPageEntrance_noTelephony_returnFalse() { + mockUserManager.stub { on { isAdminUser } doReturn true } + mockPackageManager.stub { + on { hasSystemFeature(PackageManager.FEATURE_TELEPHONY) } doReturn false + } + + val showMobileNetworkPage = repository.showMobileNetworkPageEntrance() + + assertThat(showMobileNetworkPage).isFalse() + } + + @Test + fun canEnterMobileNetworkPage_allowConfigMobileNetwork_returnTrue() { mockUserManager.stub { - on { isAdminUser } doReturn false + on { isAdminUser } doReturn true + on { hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS) } doReturn false } mockPackageManager.stub { on { hasSystemFeature(PackageManager.FEATURE_TELEPHONY) } doReturn true } - val showMobileNetworkPage = repository.showMobileNetworkPage() + val showMobileNetworkPage = repository.canEnterMobileNetworkPage() - assertThat(showMobileNetworkPage).isFalse() + assertThat(showMobileNetworkPage).isTrue() } - @Test fun showMobileNetworkPage_noTelephony_returnFalse() { + @Test + fun canEnterMobileNetworkPage_disallowConfigMobileNetwork_returnFalse() { mockUserManager.stub { on { isAdminUser } doReturn true + on { hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS) } doReturn true } mockPackageManager.stub { - on { hasSystemFeature(PackageManager.FEATURE_TELEPHONY) } doReturn false + on { hasSystemFeature(PackageManager.FEATURE_TELEPHONY) } doReturn true } - val showMobileNetworkPage = repository.showMobileNetworkPage() + val showMobileNetworkPage = repository.canEnterMobileNetworkPage() assertThat(showMobileNetworkPage).isFalse() }