diff --git a/res/values/strings.xml b/res/values/strings.xml index 075056db64e..50556de6e0b 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -7930,16 +7930,28 @@ Select activation type - + Time - + Ex. \"9:30 – 5:00 PM\" - + Calendar - + Ex. \"Personal calendar\" + + ON + + + %1$s%2$s + + + Tap to set up + + + Paused + Limit interruptions diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreference.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreference.java index 0bb6b607041..bfccdc4c672 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreference.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreference.java @@ -95,14 +95,14 @@ public class AudioSharingNamePreference extends ValidatedEditTextPreference { } private void configureInvisibleStateForQrCodeIcon(ImageButton shareButton, View divider) { - divider.setVisibility(View.INVISIBLE); - shareButton.setVisibility(View.INVISIBLE); + divider.setVisibility(View.GONE); + shareButton.setVisibility(View.GONE); shareButton.setOnClickListener(null); } private void launchAudioSharingQrCodeFragment() { new SubSettingLauncher(getContext()) - .setTitleText(getContext().getString(R.string.audio_streams_qr_code_page_title)) + .setTitleRes(R.string.audio_streams_qr_code_page_title) .setDestination(AudioStreamsQrCodeFragment.class.getName()) .setSourceMetricsCategory(SettingsEnums.AUDIO_SHARING_SETTINGS) .launch(); diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreferenceController.java index 24b8f20cf51..894ba487014 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreferenceController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreferenceController.java @@ -26,6 +26,7 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import androidx.lifecycle.DefaultLifecycleObserver; import androidx.lifecycle.LifecycleOwner; import androidx.preference.Preference; @@ -56,7 +57,8 @@ public class AudioSharingNamePreferenceController extends BasePreferenceControll private static final boolean DEBUG = BluetoothUtils.D; private static final String PREF_KEY = "audio_sharing_stream_name"; - private final BluetoothLeBroadcast.Callback mBroadcastCallback = + @VisibleForTesting + final BluetoothLeBroadcast.Callback mBroadcastCallback = new BluetoothLeBroadcast.Callback() { @Override public void onBroadcastMetadataChanged( diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPasswordPreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPasswordPreferenceController.java index 258cf3be843..14930e11a6a 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPasswordPreferenceController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPasswordPreferenceController.java @@ -19,12 +19,19 @@ package com.android.settings.connecteddevice.audiosharing; import static com.android.settings.connecteddevice.audiosharing.AudioSharingUtils.isBroadcasting; import android.app.settings.SettingsEnums; +import android.content.ContentResolver; import android.content.Context; import android.content.SharedPreferences; +import android.database.ContentObserver; +import android.os.Handler; +import android.os.Looper; +import android.provider.Settings; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.lifecycle.DefaultLifecycleObserver; +import androidx.lifecycle.LifecycleOwner; import androidx.preference.PreferenceScreen; import com.android.settings.R; @@ -41,15 +48,19 @@ import java.nio.charset.StandardCharsets; public class AudioSharingPasswordPreferenceController extends BasePreferenceController implements ValidatedEditTextPreference.Validator, - AudioSharingPasswordPreference.OnDialogEventListener { - + AudioSharingPasswordPreference.OnDialogEventListener, + DefaultLifecycleObserver { private static final String TAG = "AudioSharingPasswordPreferenceController"; private static final String PREF_KEY = "audio_sharing_stream_password"; private static final String SHARED_PREF_NAME = "audio_sharing_settings"; private static final String SHARED_PREF_KEY = "default_password"; + @Nullable private final ContentResolver mContentResolver; + @Nullable private final SharedPreferences mSharedPref; @Nullable private final LocalBluetoothManager mBtManager; @Nullable private final LocalBluetoothLeBroadcast mBroadcast; @Nullable private AudioSharingPasswordPreference mPreference; + private final ContentObserver mSettingsObserver; + private final SharedPreferences.OnSharedPreferenceChangeListener mSharedPrefChangeListener; private final AudioSharingPasswordValidator mAudioSharingPasswordValidator; private final MetricsFeatureProvider mMetricsFeatureProvider; @@ -61,9 +72,44 @@ public class AudioSharingPasswordPreferenceController extends BasePreferenceCont ? mBtManager.getProfileManager().getLeAudioBroadcastProfile() : null; mAudioSharingPasswordValidator = new AudioSharingPasswordValidator(); + mContentResolver = context.getContentResolver(); + mSettingsObserver = new PasswordSettingsObserver(); + mSharedPref = context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE); + mSharedPrefChangeListener = new PasswordSharedPrefChangeListener(); mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(); } + @Override + public void onStart(@NonNull LifecycleOwner owner) { + if (!isAvailable()) { + Log.d(TAG, "Feature is not available."); + return; + } + if (mContentResolver != null) { + mContentResolver.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE), + false, + mSettingsObserver); + } + if (mSharedPref != null) { + mSharedPref.registerOnSharedPreferenceChangeListener(mSharedPrefChangeListener); + } + } + + @Override + public void onStop(@NonNull LifecycleOwner owner) { + if (!isAvailable()) { + Log.d(TAG, "Feature is not available."); + return; + } + if (mContentResolver != null) { + mContentResolver.unregisterContentObserver(mSettingsObserver); + } + if (mSharedPref != null) { + mSharedPref.unregisterOnSharedPreferenceChangeListener(mSharedPrefChangeListener); + } + } + @Override public int getAvailabilityStatus() { return AudioSharingUtils.isFeatureEnabled() ? AVAILABLE : UNSUPPORTED_ON_DEVICE; @@ -125,7 +171,6 @@ public class AudioSharingPasswordPreferenceController extends BasePreferenceCont persistDefaultPassword(mContext, password); mBroadcast.setBroadcastCode( isPublicBroadcast ? new byte[0] : password.getBytes()); - updatePreference(); mMetricsFeatureProvider.action( mContext, SettingsEnums.ACTION_AUDIO_STREAM_PASSWORD_UPDATED, @@ -164,32 +209,52 @@ public class AudioSharingPasswordPreferenceController extends BasePreferenceCont }); } - private static void persistDefaultPassword(Context context, String defaultPassword) { + private class PasswordSettingsObserver extends ContentObserver { + PasswordSettingsObserver() { + super(new Handler(Looper.getMainLooper())); + } + + @Override + public void onChange(boolean selfChange) { + Log.d(TAG, "onChange, broadcast password has been changed"); + updatePreference(); + } + } + + private class PasswordSharedPrefChangeListener + implements SharedPreferences.OnSharedPreferenceChangeListener { + @Override + public void onSharedPreferenceChanged( + SharedPreferences sharedPreferences, @Nullable String key) { + if (!SHARED_PREF_KEY.equals(key)) { + return; + } + Log.d(TAG, "onSharedPreferenceChanged, default password has been changed"); + updatePreference(); + } + } + + private void persistDefaultPassword(Context context, String defaultPassword) { if (getDefaultPassword(context).equals(defaultPassword)) { return; } - - SharedPreferences sharedPref = - context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE); - if (sharedPref == null) { + if (mSharedPref == null) { Log.w(TAG, "persistDefaultPassword(): sharedPref is empty!"); return; } - SharedPreferences.Editor editor = sharedPref.edit(); + SharedPreferences.Editor editor = mSharedPref.edit(); editor.putString(SHARED_PREF_KEY, defaultPassword); editor.apply(); } - private static String getDefaultPassword(Context context) { - SharedPreferences sharedPref = - context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE); - if (sharedPref == null) { + private String getDefaultPassword(Context context) { + if (mSharedPref == null) { Log.w(TAG, "getDefaultPassword(): sharedPref is empty!"); return ""; } - String value = sharedPref.getString(SHARED_PREF_KEY, ""); + String value = mSharedPref.getString(SHARED_PREF_KEY, ""); if (value != null && value.isEmpty()) { Log.w(TAG, "getDefaultPassword(): default password is empty!"); } diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AddSourceWaitForResponseState.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AddSourceWaitForResponseState.java index 24a28dd0602..7be01a20235 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AddSourceWaitForResponseState.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AddSourceWaitForResponseState.java @@ -36,7 +36,8 @@ class AddSourceWaitForResponseState extends AudioStreamStateHandler { @Nullable private static AddSourceWaitForResponseState sInstance = null; - private AddSourceWaitForResponseState() {} + @VisibleForTesting + AddSourceWaitForResponseState() {} static AddSourceWaitForResponseState getInstance() { if (sInstance == null) { diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandler.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandler.java index b0c5b6baebf..4bb84751b36 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandler.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandler.java @@ -35,10 +35,10 @@ class AudioStreamStateHandler { private static final boolean DEBUG = BluetoothUtils.D; @VisibleForTesting static final int EMPTY_STRING_RES = 0; - final AudioStreamsRepository mAudioStreamsRepository = AudioStreamsRepository.getInstance(); final Handler mHandler = new Handler(Looper.getMainLooper()); final MetricsFeatureProvider mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(); + AudioStreamsRepository mAudioStreamsRepository = AudioStreamsRepository.getInstance(); AudioStreamStateHandler() {} @@ -112,4 +112,9 @@ class AudioStreamStateHandler { AudioStreamsProgressCategoryController.AudioStreamState getStateEnum() { return AudioStreamsProgressCategoryController.AudioStreamState.UNKNOWN; } + + @VisibleForTesting + void setAudioStreamsRepositoryForTesting(AudioStreamsRepository repository) { + mAudioStreamsRepository = repository; + } } diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceSummaryUpdater.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceSummaryUpdater.java index ab22b0702bc..47ee440694f 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceSummaryUpdater.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceSummaryUpdater.java @@ -16,24 +16,22 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams; +import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.text.TextUtils; -import android.util.Log; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.settings.R; import com.android.settings.bluetooth.Utils; import com.android.settingslib.bluetooth.BluetoothCallback; -import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.utils.ThreadUtils; public class AudioStreamsActiveDeviceSummaryUpdater implements BluetoothCallback { - private static final String TAG = "AudioStreamsActiveDeviceSummaryUpdater"; - private static final boolean DEBUG = BluetoothUtils.D; private final LocalBluetoothManager mBluetoothManager; private Context mContext; @Nullable private String mSummary; @@ -47,17 +45,20 @@ public class AudioStreamsActiveDeviceSummaryUpdater implements BluetoothCallback } @Override - public void onActiveDeviceChanged( - @Nullable CachedBluetoothDevice activeDevice, int bluetoothProfile) { - if (DEBUG) { - Log.d( - TAG, - "onActiveDeviceChanged() with activeDevice : " - + (activeDevice == null ? "null" : activeDevice.getAddress()) - + " on profile : " - + bluetoothProfile); + public void onBluetoothStateChanged(@AdapterState int bluetoothState) { + if (bluetoothState == BluetoothAdapter.STATE_OFF) { + notifyChangeIfNeeded(); } - if (bluetoothProfile == BluetoothProfile.LE_AUDIO) { + } + + @Override + public void onProfileConnectionStateChanged( + @NonNull CachedBluetoothDevice cachedDevice, + @ConnectionState int state, + int bluetoothProfile) { + if (bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT + && (state == BluetoothAdapter.STATE_CONNECTED + || state == BluetoothAdapter.STATE_DISCONNECTED)) { notifyChangeIfNeeded(); } } diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryController.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryController.java index 3174ace8520..0107c6ee49b 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryController.java @@ -16,12 +16,12 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams; +import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.util.Log; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.lifecycle.LifecycleOwner; import com.android.settings.bluetooth.Utils; @@ -44,9 +44,13 @@ public class AudioStreamsCategoryController extends AudioSharingBasePreferenceCo private final BluetoothCallback mBluetoothCallback = new BluetoothCallback() { @Override - public void onActiveDeviceChanged( - @Nullable CachedBluetoothDevice activeDevice, int bluetoothProfile) { - if (bluetoothProfile == BluetoothProfile.LE_AUDIO) { + public void onProfileConnectionStateChanged( + @NonNull CachedBluetoothDevice cachedDevice, + @ConnectionState int state, + int bluetoothProfile) { + if (bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT + && (state == BluetoothAdapter.STATE_CONNECTED + || state == BluetoothAdapter.STATE_DISCONNECTED)) { updateVisibility(); } } diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java index 775186a859e..6e335a0971c 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java @@ -71,7 +71,8 @@ public class AudioStreamsHelper { * * @param source The LE broadcast metadata representing the audio source. */ - void addSource(BluetoothLeBroadcastMetadata source) { + @VisibleForTesting + public void addSource(BluetoothLeBroadcastMetadata source) { if (mLeBroadcastAssistant == null) { Log.w(TAG, "addSource(): LeBroadcastAssistant is null!"); return; @@ -97,7 +98,8 @@ public class AudioStreamsHelper { } /** Removes sources from LE broadcasts associated for all active sinks based on broadcast Id. */ - void removeSource(int broadcastId) { + @VisibleForTesting + public void removeSource(int broadcastId) { if (mLeBroadcastAssistant == null) { Log.w(TAG, "removeSource(): LeBroadcastAssistant is null!"); return; diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallback.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallback.java index cb3a0daac2f..3370d8dbfd5 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallback.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallback.java @@ -19,7 +19,6 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeBroadcastMetadata; import android.bluetooth.BluetoothLeBroadcastReceiveState; -import android.util.Log; public class AudioStreamsProgressCategoryCallback extends AudioStreamsBroadcastAssistantCallback { private static final String TAG = "AudioStreamsProgressCategoryCallback"; @@ -53,10 +52,6 @@ public class AudioStreamsProgressCategoryCallback extends AudioStreamsBroadcastA @Override public void onSearchStarted(int reason) { super.onSearchStarted(reason); - if (mCategoryController == null) { - Log.w(TAG, "onSearchStarted() : mCategoryController is null!"); - return; - } mCategoryController.setScanning(true); } @@ -69,10 +64,6 @@ public class AudioStreamsProgressCategoryCallback extends AudioStreamsBroadcastA @Override public void onSearchStopped(int reason) { super.onSearchStopped(reason); - if (mCategoryController == null) { - Log.w(TAG, "onSearchStopped() : mCategoryController is null!"); - return; - } mCategoryController.setScanning(false); } @@ -86,10 +77,6 @@ public class AudioStreamsProgressCategoryCallback extends AudioStreamsBroadcastA @Override public void onSourceFound(BluetoothLeBroadcastMetadata source) { super.onSourceFound(source); - if (mCategoryController == null) { - Log.w(TAG, "onSourceFound() : mCategoryController is null!"); - return; - } mCategoryController.handleSourceFound(source); } diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java index 890879e817c..9bbf135285c 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java @@ -20,6 +20,7 @@ import static java.util.Collections.emptyList; import android.app.AlertDialog; import android.app.settings.SettingsEnums; +import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothLeBroadcastMetadata; import android.bluetooth.BluetoothLeBroadcastReceiveState; import android.bluetooth.BluetoothProfile; @@ -27,10 +28,12 @@ import android.content.Context; import android.util.Log; import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; import androidx.lifecycle.DefaultLifecycleObserver; import androidx.lifecycle.LifecycleOwner; import androidx.preference.PreferenceScreen; +import com.android.internal.annotations.VisibleForTesting; import com.android.settings.R; import com.android.settings.bluetooth.Utils; import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment; @@ -55,13 +58,35 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro implements DefaultLifecycleObserver { private static final String TAG = "AudioStreamsProgressCategoryController"; private static final boolean DEBUG = BluetoothUtils.D; - private static final int UNSET_BROADCAST_ID = -1; - private final BluetoothCallback mBluetoothCallback = + @VisibleForTesting static final int UNSET_BROADCAST_ID = -1; + + @VisibleForTesting + final BluetoothCallback mBluetoothCallback = new BluetoothCallback() { @Override - public void onActiveDeviceChanged( - @Nullable CachedBluetoothDevice activeDevice, int bluetoothProfile) { - if (bluetoothProfile == BluetoothProfile.LE_AUDIO) { + public void onBluetoothStateChanged(@AdapterState int bluetoothState) { + Log.d(TAG, "onBluetoothStateChanged() with bluetoothState : " + bluetoothState); + if (bluetoothState == BluetoothAdapter.STATE_OFF) { + mExecutor.execute(() -> init()); + } + } + + @Override + public void onProfileConnectionStateChanged( + @NonNull CachedBluetoothDevice cachedDevice, + @ConnectionState int state, + int bluetoothProfile) { + Log.d( + TAG, + "onProfileConnectionStateChanged() with cachedDevice : " + + cachedDevice.getAddress() + + " with state : " + + state + + " on profile : " + + bluetoothProfile); + if (bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT + && (state == BluetoothAdapter.STATE_CONNECTED + || state == BluetoothAdapter.STATE_DISCONNECTED)) { mExecutor.execute(() -> init()); } } @@ -92,7 +117,7 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro SOURCE_ADDED, } - private final Executor mExecutor; + @VisibleForTesting Executor mExecutor; private final AudioStreamsProgressCategoryCallback mBroadcastAssistantCallback; private final AudioStreamsHelper mAudioStreamsHelper; private final MediaControlHelper mMediaControlHelper; @@ -103,7 +128,7 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro private @Nullable BluetoothLeBroadcastMetadata mSourceFromQrCode; private SourceOriginForLogging mSourceFromQrCodeOriginForLogging; @Nullable private AudioStreamsProgressCategoryPreference mCategoryPreference; - @Nullable private AudioStreamsDashboardFragment mFragment; + @Nullable private Fragment mFragment; public AudioStreamsProgressCategoryController(Context context, String preferenceKey) { super(context, preferenceKey); @@ -142,12 +167,12 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro mExecutor.execute(this::stopScanning); } - void setFragment(AudioStreamsDashboardFragment fragment) { + void setFragment(Fragment fragment) { mFragment = fragment; } @Nullable - AudioStreamsDashboardFragment getFragment() { + Fragment getFragment() { return mFragment; } @@ -546,7 +571,8 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro return preference; } - private void moveToState(AudioStreamPreference preference, AudioStreamState state) { + @VisibleForTesting + void moveToState(AudioStreamPreference preference, AudioStreamState state) { AudioStreamStateHandler stateHandler = switch (state) { case SYNCED -> SyncedState.getInstance(); diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsScanQrCodeController.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsScanQrCodeController.java index 5f50be72790..d0d82fbc678 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsScanQrCodeController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsScanQrCodeController.java @@ -16,6 +16,7 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams; +import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.util.Log; @@ -47,9 +48,13 @@ public class AudioStreamsScanQrCodeController extends BasePreferenceController final BluetoothCallback mBluetoothCallback = new BluetoothCallback() { @Override - public void onActiveDeviceChanged( - @Nullable CachedBluetoothDevice activeDevice, int bluetoothProfile) { - if (bluetoothProfile == BluetoothProfile.LE_AUDIO) { + public void onProfileConnectionStateChanged( + @NonNull CachedBluetoothDevice cachedDevice, + @ConnectionState int state, + int bluetoothProfile) { + if (bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT + && (state == BluetoothAdapter.STATE_CONNECTED + || state == BluetoothAdapter.STATE_DISCONNECTED)) { updateVisibility(); } } diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/SourceAddedState.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/SourceAddedState.java index ee84429663a..88393ab1b67 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/SourceAddedState.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/SourceAddedState.java @@ -25,6 +25,7 @@ import androidx.preference.Preference; import com.android.settings.R; import com.android.settings.core.SubSettingLauncher; +import com.android.settings.dashboard.DashboardFragment; class SourceAddedState extends AudioStreamStateHandler { @VisibleForTesting @@ -32,7 +33,8 @@ class SourceAddedState extends AudioStreamStateHandler { @Nullable private static SourceAddedState sInstance = null; - private SourceAddedState() {} + @VisibleForTesting + SourceAddedState() {} static SourceAddedState getInstance() { if (sInstance == null) { @@ -80,13 +82,13 @@ class SourceAddedState extends AudioStreamStateHandler { AudioStreamDetailsFragment.BROADCAST_ID_ARG, p.getAudioStreamBroadcastId()); new SubSettingLauncher(p.getContext()) - .setTitleText( - p.getContext().getString(R.string.audio_streams_detail_page_title)) + .setTitleRes(R.string.audio_streams_detail_page_title) .setDestination(AudioStreamDetailsFragment.class.getName()) .setSourceMetricsCategory( - controller.getFragment() == null + !(controller.getFragment() instanceof DashboardFragment) ? SettingsEnums.PAGE_UNKNOWN - : controller.getFragment().getMetricsCategory()) + : ((DashboardFragment) controller.getFragment()) + .getMetricsCategory()) .setArguments(broadcast) .launch(); return true; diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/WaitForSyncState.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/WaitForSyncState.java index 55f61fdd0e2..9689b263a47 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/WaitForSyncState.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/WaitForSyncState.java @@ -39,7 +39,8 @@ class WaitForSyncState extends AudioStreamStateHandler { @Nullable private static WaitForSyncState sInstance = null; - private WaitForSyncState() {} + @VisibleForTesting + WaitForSyncState() {} static WaitForSyncState getInstance() { if (sInstance == null) { @@ -114,7 +115,8 @@ class WaitForSyncState extends AudioStreamStateHandler { SettingsEnums.DIALOG_AUDIO_STREAM_MAIN_WAIT_FOR_SYNC_TIMEOUT); } - private void launchQrCodeScanFragment(Context context, Fragment fragment) { + @VisibleForTesting + void launchQrCodeScanFragment(Context context, Fragment fragment) { new SubSettingLauncher(context) .setTitleRes(R.string.audio_streams_main_page_scan_qr_code_title) .setDestination(AudioStreamsQrCodeScanFragment.class.getName()) diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java index 7b5cd9f9e00..b5d50994ed7 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java @@ -99,7 +99,8 @@ public class BatteryDiffData { return mScreenOnTime; } - List getAppDiffEntryList() { + /** Gets the {@link BatteryDiffEntry} list for apps. */ + public List getAppDiffEntryList() { return mAppEntries; } @@ -298,8 +299,7 @@ public class BatteryDiffData { * Sets total consume power, and adjusts the percentages to ensure the total round percentage * could be 100%, and then sorts entries based on the sorting key. */ - @VisibleForTesting - static void processAndSortEntries(final List batteryDiffEntries) { + public static void processAndSortEntries(final List batteryDiffEntries) { if (batteryDiffEntries.isEmpty()) { return; } diff --git a/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt b/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt index a5d4ba832ab..b5cdedae96c 100644 --- a/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt +++ b/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt @@ -21,14 +21,16 @@ import android.telephony.AccessNetworkConstants import android.telephony.CarrierConfigManager import android.telephony.CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL import android.telephony.SubscriptionManager -import android.telephony.TelephonyManager import android.telephony.ims.ImsMmTelManager.WiFiCallingMode import android.telephony.ims.feature.MmTelFeature import android.telephony.ims.stub.ImsRegistrationImplBase +import androidx.lifecycle.LifecycleOwner import com.android.settings.network.telephony.ims.ImsMmTelRepository import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl import com.android.settings.network.telephony.ims.imsFeatureProvisionedFlow import com.android.settings.network.telephony.subscriptionsChangedFlow +import com.android.settings.network.telephony.telephonyManager +import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -38,13 +40,19 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext -class WifiCallingRepository( +interface IWifiCallingRepository { + /** TODO: Move this to UI layer, when UI layer migrated to Kotlin. */ + fun collectIsWifiCallingReadyFlow(lifecycleOwner: LifecycleOwner, action: (Boolean) -> Unit) +} + +class WifiCallingRepository +@JvmOverloads +constructor( private val context: Context, private val subId: Int, private val imsMmTelRepository: ImsMmTelRepository = ImsMmTelRepositoryImpl(context, subId) -) { - private val telephonyManager = context.getSystemService(TelephonyManager::class.java)!! - .createForSubscriptionId(subId) +) : IWifiCallingRepository { + private val telephonyManager = context.telephonyManager(subId) private val carrierConfigManager = context.getSystemService(CarrierConfigManager::class.java)!! @@ -59,6 +67,14 @@ class WifiCallingRepository( .getConfigForSubId(subId, KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL) .getBoolean(KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL) + /** TODO: Move this to UI layer, when UI layer migrated to Kotlin. */ + override fun collectIsWifiCallingReadyFlow( + lifecycleOwner: LifecycleOwner, + action: (Boolean) -> Unit, + ) { + wifiCallingReadyFlow().collectLatestWithLifecycle(lifecycleOwner, action = action) + } + @OptIn(ExperimentalCoroutinesApi::class) fun wifiCallingReadyFlow(): Flow { if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false) diff --git a/src/com/android/settings/notification/modes/IconUtil.java b/src/com/android/settings/notification/modes/IconUtil.java index c6ecaa0a56d..1e653bf03fe 100644 --- a/src/com/android/settings/notification/modes/IconUtil.java +++ b/src/com/android/settings/notification/modes/IconUtil.java @@ -24,6 +24,7 @@ import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.shapes.OvalShape; +import androidx.annotation.AttrRes; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; @@ -32,10 +33,18 @@ import com.android.settingslib.Utils; class IconUtil { - static Drawable applyTint(@NonNull Context context, @NonNull Drawable icon) { + static Drawable applyNormalTint(@NonNull Context context, @NonNull Drawable icon) { + return applyTint(context, icon, android.R.attr.colorControlNormal); + } + + static Drawable applyAccentTint(@NonNull Context context, @NonNull Drawable icon) { + return applyTint(context, icon, android.R.attr.colorAccent); + } + + private static Drawable applyTint(@NonNull Context context, @NonNull Drawable icon, + @AttrRes int colorAttr) { icon = icon.mutate(); - icon.setTintList( - Utils.getColorAttr(context, android.R.attr.colorControlNormal)); + icon.setTintList(Utils.getColorAttr(context, colorAttr)); return icon; } diff --git a/src/com/android/settings/notification/modes/ZenModeButtonPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeButtonPreferenceController.java index 79da24da57a..4a99b33749c 100644 --- a/src/com/android/settings/notification/modes/ZenModeButtonPreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModeButtonPreferenceController.java @@ -27,7 +27,7 @@ import com.android.settingslib.notification.modes.ZenMode; import com.android.settingslib.notification.modes.ZenModesBackend; import com.android.settingslib.widget.LayoutPreference; -public class ZenModeButtonPreferenceController extends AbstractZenModePreferenceController { +class ZenModeButtonPreferenceController extends AbstractZenModePreferenceController { private Button mZenButton; diff --git a/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceController.java index b42e5b8eed3..b0d395296e9 100644 --- a/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceController.java @@ -26,7 +26,7 @@ import androidx.preference.TwoStatePreference; import com.android.settingslib.notification.modes.ZenMode; import com.android.settingslib.notification.modes.ZenModesBackend; -public class ZenModeDisplayEffectPreferenceController extends AbstractZenModePreferenceController +class ZenModeDisplayEffectPreferenceController extends AbstractZenModePreferenceController implements Preference.OnPreferenceChangeListener { public ZenModeDisplayEffectPreferenceController(Context context, String key, @@ -37,24 +37,20 @@ public class ZenModeDisplayEffectPreferenceController extends AbstractZenModePre @Override public void updateState(Preference preference, @NonNull ZenMode zenMode) { TwoStatePreference pref = (TwoStatePreference) preference; - ZenDeviceEffects effects = zenMode.getRule().getDeviceEffects(); - if (effects == null) { - pref.setChecked(false); - } else { - switch (getPreferenceKey()) { - case "effect_greyscale": - pref.setChecked(effects.shouldDisplayGrayscale()); - break; - case "effect_aod": - pref.setChecked(effects.shouldSuppressAmbientDisplay()); - break; - case "effect_wallpaper": - pref.setChecked(effects.shouldDimWallpaper()); - break; - case "effect_dark_theme": - pref.setChecked(effects.shouldUseNightMode()); - break; - } + ZenDeviceEffects effects = zenMode.getDeviceEffects(); + switch (getPreferenceKey()) { + case "effect_greyscale": + pref.setChecked(effects.shouldDisplayGrayscale()); + break; + case "effect_aod": + pref.setChecked(effects.shouldSuppressAmbientDisplay()); + break; + case "effect_wallpaper": + pref.setChecked(effects.shouldDimWallpaper()); + break; + case "effect_dark_theme": + pref.setChecked(effects.shouldUseNightMode()); + break; } } diff --git a/src/com/android/settings/notification/modes/ZenModeHeaderController.java b/src/com/android/settings/notification/modes/ZenModeHeaderController.java index 857d07949aa..1845ee8f190 100644 --- a/src/com/android/settings/notification/modes/ZenModeHeaderController.java +++ b/src/com/android/settings/notification/modes/ZenModeHeaderController.java @@ -66,7 +66,7 @@ class ZenModeHeaderController extends AbstractZenModePreferenceController { FutureUtil.whenDone( zenMode.getIcon(mContext, ZenIconLoader.getInstance()), - icon -> mHeaderController.setIcon(IconUtil.applyTint(mContext, icon)) + icon -> mHeaderController.setIcon(IconUtil.applyNormalTint(mContext, icon)) .done(/* rebindActions= */ false), mContext.getMainExecutor()); } diff --git a/src/com/android/settings/notification/modes/ZenModeIconPickerIconPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeIconPickerIconPreferenceController.java index d891e66c2f7..d1d53af9ddf 100644 --- a/src/com/android/settings/notification/modes/ZenModeIconPickerIconPreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModeIconPickerIconPreferenceController.java @@ -55,7 +55,7 @@ class ZenModeIconPickerIconPreferenceController extends AbstractZenModePreferenc FutureUtil.whenDone( zenMode.getIcon(mContext, ZenIconLoader.getInstance()), - icon -> mHeaderController.setIcon(IconUtil.applyTint(mContext, icon)) + icon -> mHeaderController.setIcon(IconUtil.applyNormalTint(mContext, icon)) .done(/* rebindActions= */ false), mContext.getMainExecutor()); } diff --git a/src/com/android/settings/notification/modes/ZenModesListItemPreference.java b/src/com/android/settings/notification/modes/ZenModesListItemPreference.java index 0b04ac6c087..1bc6e55acce 100644 --- a/src/com/android/settings/notification/modes/ZenModesListItemPreference.java +++ b/src/com/android/settings/notification/modes/ZenModesListItemPreference.java @@ -16,18 +16,31 @@ package com.android.settings.notification.modes; import android.content.Context; +import android.widget.TextView; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import androidx.preference.PreferenceViewHolder; + +import com.android.settings.R; import com.android.settingslib.RestrictedPreference; +import com.android.settingslib.Utils; import com.android.settingslib.notification.modes.ZenIconLoader; import com.android.settingslib.notification.modes.ZenMode; +import com.google.common.base.Strings; + /** * Preference representing a single mode item on the modes aggregator page. Clicking on this * preference leads to an individual mode's configuration page. */ class ZenModesListItemPreference extends RestrictedPreference { - final Context mContext; - ZenMode mZenMode; + + private final Context mContext; + private ZenMode mZenMode; + + private TextView mTitleView; + private TextView mSummaryView; ZenModesListItemPreference(Context context, ZenMode zenMode) { super(context); @@ -36,6 +49,18 @@ class ZenModesListItemPreference extends RestrictedPreference { setKey(zenMode.getId()); } + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + if (holder.findViewById(android.R.id.title) instanceof TextView titleView) { + mTitleView = titleView; + } + if (holder.findViewById(android.R.id.summary) instanceof TextView summaryView) { + mSummaryView = summaryView; + } + updateTextColor(mZenMode); + } + @Override public void onClick() { ZenSubSettingLauncher.forMode(mContext, mZenMode.getId()).launch(); @@ -43,13 +68,47 @@ class ZenModesListItemPreference extends RestrictedPreference { public void setZenMode(ZenMode zenMode) { mZenMode = zenMode; - setTitle(mZenMode.getRule().getName()); - setSummary(mZenMode.getRule().getTriggerDescription()); - setIconSize(ICON_SIZE_SMALL); + setTitle(mZenMode.getName()); + CharSequence statusText = switch (mZenMode.getStatus()) { + case ENABLED_AND_ACTIVE -> + Strings.isNullOrEmpty(mZenMode.getTriggerDescription()) + ? mContext.getString(R.string.zen_mode_active_text) + : mContext.getString( + R.string.zen_mode_format_status_and_trigger, + mContext.getString(R.string.zen_mode_active_text), + mZenMode.getRule().getTriggerDescription()); + case ENABLED -> mZenMode.getRule().getTriggerDescription(); + case DISABLED_BY_USER -> mContext.getString(R.string.zen_mode_disabled_by_user); + case DISABLED_BY_OTHER -> mContext.getString(R.string.zen_mode_disabled_tap_to_set_up); + }; + setSummary(statusText); + setIconSize(ICON_SIZE_SMALL); FutureUtil.whenDone( mZenMode.getIcon(mContext, ZenIconLoader.getInstance()), - icon -> setIcon(IconUtil.applyTint(mContext, icon)), + icon -> setIcon( + zenMode.isActive() + ? IconUtil.applyAccentTint(mContext, icon) + : IconUtil.applyNormalTint(mContext, icon)), mContext.getMainExecutor()); + + updateTextColor(zenMode); + } + + private void updateTextColor(@Nullable ZenMode zenMode) { + boolean isActive = zenMode != null && zenMode.isActive(); + if (mTitleView != null) { + mTitleView.setTextColor(Utils.getColorAttr(mContext, + isActive ? android.R.attr.colorAccent : android.R.attr.textColorPrimary)); + } + if (mSummaryView != null) { + mSummaryView.setTextColor(Utils.getColorAttr(mContext, + isActive ? android.R.attr.colorAccent : android.R.attr.textColorSecondary)); + } + } + + @VisibleForTesting(otherwise = VisibleForTesting.NONE) + ZenMode getZenMode() { + return mZenMode; } } diff --git a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java index 82537d48392..e5581d34b62 100644 --- a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java +++ b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java @@ -33,7 +33,6 @@ import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; import android.telephony.ims.ImsManager; import android.telephony.ims.ImsMmTelManager; -import android.telephony.ims.ProvisioningManager; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; @@ -42,12 +41,14 @@ import android.view.ViewGroup; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; +import androidx.lifecycle.LifecycleOwner; import androidx.preference.Preference; import androidx.preference.Preference.OnPreferenceClickListener; import androidx.preference.PreferenceScreen; -import com.android.ims.ImsConfig; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.Phone; import com.android.internal.telephony.flags.Flags; @@ -57,8 +58,12 @@ import com.android.settings.SettingsPreferenceFragment; import com.android.settings.Utils; import com.android.settings.core.SubSettingLauncher; import com.android.settings.network.ims.WifiCallingQueryImsState; +import com.android.settings.network.telephony.wificalling.IWifiCallingRepository; +import com.android.settings.network.telephony.wificalling.WifiCallingRepository; import com.android.settings.widget.SettingsMainSwitchPreference; +import kotlin.Unit; + import java.util.List; /** @@ -103,7 +108,6 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; private ImsMmTelManager mImsMmTelManager; - private ProvisioningManager mProvisioningManager; private TelephonyManager mTelephonyManager; private PhoneTelephonyCallback mTelephonyCallback; @@ -188,19 +192,6 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment return true; }; - private final ProvisioningManager.Callback mProvisioningCallback = - new ProvisioningManager.Callback() { - @Override - public void onProvisioningIntChanged(int item, int value) { - if (item == ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED - || item == ImsConfig.ConfigConstants.VLT_SETTING_ENABLED) { - // The provisioning policy might have changed. Update the body to make sure - // this change takes effect if needed. - updateBody(); - } - } - }; - @VisibleForTesting void showAlert(Intent intent) { final Context context = getActivity(); @@ -263,14 +254,6 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment return new WifiCallingQueryImsState(getContext(), subId); } - @VisibleForTesting - ProvisioningManager getImsProvisioningManager() { - if (!SubscriptionManager.isValidSubscriptionId(mSubId)) { - return null; - } - return ProvisioningManager.createForSubscriptionId(mSubId); - } - @VisibleForTesting ImsMmTelManager getImsMmTelManager() { if (!SubscriptionManager.isValidSubscriptionId(mSubId)) { @@ -294,7 +277,6 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment FRAGMENT_BUNDLE_SUBID, SubscriptionManager.INVALID_SUBSCRIPTION_ID); } - mProvisioningManager = getImsProvisioningManager(); mImsMmTelManager = getImsMmTelManager(); mSwitchBar = (SettingsMainSwitchPreference) findPreference(SWITCH_BAR); @@ -336,19 +318,33 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment return view; } + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + getWifiCallingRepository().collectIsWifiCallingReadyFlow( + getLifecycleOwner(), (isWifiWifiCallingReadyFlow) -> { + if (!isWifiWifiCallingReadyFlow) { + // This screen is not allowed to be shown due to provisioning policy and + // should therefore be closed. + finish(); + } + return Unit.INSTANCE; + }); + } + @VisibleForTesting - boolean isWfcProvisionedOnDevice() { - return queryImsState(mSubId).isWifiCallingProvisioned(); + @NonNull + IWifiCallingRepository getWifiCallingRepository() { + return new WifiCallingRepository(requireContext(), mSubId); + } + + @VisibleForTesting + @NonNull + LifecycleOwner getLifecycleOwner() { + return getViewLifecycleOwner(); } private void updateBody() { - if (!isWfcProvisionedOnDevice()) { - // This screen is not allowed to be shown due to provisioning policy and should - // therefore be closed. - finish(); - return; - } - final CarrierConfigManager configManager = (CarrierConfigManager) getSystemService(Context.CARRIER_CONFIG_SERVICE); boolean isWifiOnlySupported = true; @@ -448,8 +444,6 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment if (intent.getBooleanExtra(Phone.EXTRA_KEY_ALERT_SHOW, false)) { showAlert(intent); } - // Register callback for provisioning changes. - registerProvisioningChangedCallback(); } @Override @@ -462,8 +456,6 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment mSwitchBar.removeOnSwitchChangeListener(this); } context.unregisterReceiver(mIntentReceiver); - // Remove callback for provisioning changes. - unregisterProvisioningChangedCallback(); } /** @@ -699,27 +691,6 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment return SubscriptionManager.getResourcesForSubId(getContext(), mSubId); } - @VisibleForTesting - void registerProvisioningChangedCallback() { - if (mProvisioningManager == null) { - return; - } - try { - mProvisioningManager.registerProvisioningChangedCallback(getContext().getMainExecutor(), - mProvisioningCallback); - } catch (Exception ex) { - Log.w(TAG, "onResume: Unable to register callback for provisioning changes."); - } - } - - @VisibleForTesting - void unregisterProvisioningChangedCallback() { - if (mProvisioningManager == null) { - return; - } - mProvisioningManager.unregisterProvisioningChangedCallback(mProvisioningCallback); - } - /** * Determine whether to override roaming Wi-Fi calling preference when device is connected to * non-terrestrial network. diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreferenceControllerTest.java new file mode 100644 index 00000000000..618e02129e6 --- /dev/null +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreferenceControllerTest.java @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.connecteddevice.audiosharing; + +import static com.android.settings.core.BasePreferenceController.AVAILABLE; +import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.settings.SettingsEnums; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothLeBroadcast; +import android.bluetooth.BluetoothLeBroadcastMetadata; +import android.bluetooth.BluetoothStatusCodes; +import android.content.Context; +import android.platform.test.flag.junit.SetFlagsRule; + +import androidx.lifecycle.LifecycleOwner; +import androidx.preference.PreferenceScreen; +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.bluetooth.Utils; +import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; +import com.android.settings.testutils.shadow.ShadowBluetoothUtils; +import com.android.settingslib.bluetooth.BluetoothEventManager; +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast; +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; +import com.android.settingslib.bluetooth.VolumeControlProfile; +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.flags.Flags; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.shadow.api.Shadow; +import org.robolectric.shadows.ShadowLooper; + +import java.util.concurrent.Executor; + +@RunWith(RobolectricTestRunner.class) +@Config( + shadows = { + ShadowBluetoothAdapter.class, + ShadowBluetoothUtils.class, + }) +public class AudioSharingNamePreferenceControllerTest { + private static final String PREF_KEY = "audio_sharing_stream_name"; + private static final String BROADCAST_NAME = "broadcast_name"; + private static final CharSequence UPDATED_NAME = "updated_name"; + + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Spy Context mContext = ApplicationProvider.getApplicationContext(); + @Mock private LocalBluetoothLeBroadcast mBroadcast; + @Mock private LocalBluetoothLeBroadcastAssistant mAssistant; + @Mock private VolumeControlProfile mVolumeControl; + @Mock private LocalBluetoothManager mLocalBtManager; + @Mock private BluetoothEventManager mEventManager; + @Mock private LocalBluetoothProfileManager mProfileManager; + @Mock private PreferenceScreen mScreen; + private AudioSharingNamePreferenceController mController; + private ShadowBluetoothAdapter mShadowBluetoothAdapter; + private Lifecycle mLifecycle; + private LifecycleOwner mLifecycleOwner; + private AudioSharingNamePreference mPreference; + private FakeFeatureFactory mFeatureFactory; + + @Before + public void setUp() { + mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); + mShadowBluetoothAdapter.setEnabled(true); + mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager; + mLocalBtManager = Utils.getLocalBtManager(mContext); + when(mLocalBtManager.getEventManager()).thenReturn(mEventManager); + when(mLocalBtManager.getProfileManager()).thenReturn(mProfileManager); + when(mProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast); + when(mProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant); + when(mProfileManager.getVolumeControlProfile()).thenReturn(mVolumeControl); + when(mBroadcast.isProfileReady()).thenReturn(true); + when(mAssistant.isProfileReady()).thenReturn(true); + when(mVolumeControl.isProfileReady()).thenReturn(true); + when(mBroadcast.isProfileReady()).thenReturn(true); + mFeatureFactory = FakeFeatureFactory.setupForTest(); + mLifecycleOwner = () -> mLifecycle; + mLifecycle = new Lifecycle(mLifecycleOwner); + mController = new AudioSharingNamePreferenceController(mContext, PREF_KEY); + mPreference = spy(new AudioSharingNamePreference(mContext)); + when(mScreen.findPreference(PREF_KEY)).thenReturn(mPreference); + } + + @Test + public void getAvailabilityStatus_flagOn_available() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); + } + + @Test + public void getAvailabilityStatus_flagOff_unsupported() { + mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE); + } + + @Test + public void onStart_flagOff_doNothing() { + mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + mController.onStart(mLifecycleOwner); + verify(mBroadcast, never()) + .registerServiceCallBack( + any(Executor.class), any(BluetoothLeBroadcast.Callback.class)); + } + + @Test + public void onStart_flagOn_registerCallbacks() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + mController.onStart(mLifecycleOwner); + verify(mBroadcast) + .registerServiceCallBack( + any(Executor.class), any(BluetoothLeBroadcast.Callback.class)); + } + + @Test + public void onStart_flagOn_serviceNotReady_registerCallbacks() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + when(mBroadcast.isProfileReady()).thenReturn(false); + mController.onStart(mLifecycleOwner); + verify(mProfileManager) + .addServiceListener(any(LocalBluetoothProfileManager.ServiceListener.class)); + } + + @Test + public void onServiceConnected_removeCallbacks() { + mController.onServiceConnected(); + verify(mProfileManager) + .removeServiceListener(any(LocalBluetoothProfileManager.ServiceListener.class)); + } + + @Test + public void onStop_flagOff_doNothing() { + mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + mController.onStart(mLifecycleOwner); + mController.onStop(mLifecycleOwner); + verify(mBroadcast, never()) + .unregisterServiceCallBack(any(BluetoothLeBroadcast.Callback.class)); + } + + @Test + public void onStop_flagOn_unregisterCallbacks() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + mController.onStart(mLifecycleOwner); + mController.onStop(mLifecycleOwner); + verify(mBroadcast).unregisterServiceCallBack(any(BluetoothLeBroadcast.Callback.class)); + } + + @Test + public void displayPreference_updateName_showIcon() { + when(mBroadcast.getBroadcastName()).thenReturn(BROADCAST_NAME); + when(mBroadcast.isEnabled(any())).thenReturn(true); + mController.displayPreference(mScreen); + ShadowLooper.idleMainLooper(); + + assertThat(mPreference.getText()).isEqualTo(BROADCAST_NAME); + assertThat(mPreference.getSummary()).isEqualTo(BROADCAST_NAME); + verify(mPreference).setValidator(any()); + verify(mPreference).setShowQrCodeIcon(true); + } + + @Test + public void displayPreference_updateName_hideIcon() { + when(mBroadcast.getBroadcastName()).thenReturn(BROADCAST_NAME); + when(mBroadcast.isEnabled(any())).thenReturn(false); + mController.displayPreference(mScreen); + ShadowLooper.idleMainLooper(); + + assertThat(mPreference.getText()).isEqualTo(BROADCAST_NAME); + assertThat(mPreference.getSummary()).isEqualTo(BROADCAST_NAME); + verify(mPreference).setValidator(any()); + verify(mPreference).setShowQrCodeIcon(false); + } + + @Test + public void onPreferenceChange_noChange_doNothing() { + when(mPreference.getSummary()).thenReturn(BROADCAST_NAME); + mController.displayPreference(mScreen); + boolean changed = mController.onPreferenceChange(mPreference, BROADCAST_NAME); + ShadowLooper.idleMainLooper(); + + verify(mBroadcast, never()).setBroadcastName(anyString()); + verify(mBroadcast, never()).setProgramInfo(anyString()); + verify(mBroadcast, never()).updateBroadcast(); + verify(mFeatureFactory.metricsFeatureProvider, never()).action(any(), anyInt(), anyInt()); + + assertThat(changed).isFalse(); + } + + @Test + public void onPreferenceChange_changed_updateName_broadcasting() { + when(mPreference.getSummary()).thenReturn(BROADCAST_NAME); + when(mBroadcast.isEnabled(any())).thenReturn(true); + mController.displayPreference(mScreen); + boolean changed = mController.onPreferenceChange(mPreference, UPDATED_NAME); + ShadowLooper.idleMainLooper(); + + verify(mBroadcast).setBroadcastName(UPDATED_NAME.toString()); + verify(mBroadcast).setProgramInfo(UPDATED_NAME.toString()); + verify(mBroadcast).updateBroadcast(); + verify(mFeatureFactory.metricsFeatureProvider) + .action(mContext, SettingsEnums.ACTION_AUDIO_STREAM_NAME_UPDATED, 1); + assertThat(changed).isTrue(); + } + + @Test + public void onPreferenceChange_changed_updateName_notBroadcasting() { + when(mPreference.getSummary()).thenReturn(BROADCAST_NAME); + when(mBroadcast.isEnabled(any())).thenReturn(false); + mController.displayPreference(mScreen); + boolean changed = mController.onPreferenceChange(mPreference, UPDATED_NAME); + ShadowLooper.idleMainLooper(); + + verify(mBroadcast).setBroadcastName(UPDATED_NAME.toString()); + verify(mBroadcast).setProgramInfo(UPDATED_NAME.toString()); + verify(mBroadcast, never()).updateBroadcast(); + verify(mFeatureFactory.metricsFeatureProvider) + .action(mContext, SettingsEnums.ACTION_AUDIO_STREAM_NAME_UPDATED, 0); + assertThat(changed).isTrue(); + } + + @Test + public void unrelatedCallbacks_doNotUpdateIcon() { + mController.displayPreference(mScreen); + mController.mBroadcastCallback.onBroadcastStartFailed(/* reason= */ 0); + mController.mBroadcastCallback.onBroadcastStarted(/* reason= */ 0, /* broadcastId= */ 0); + mController.mBroadcastCallback.onBroadcastStopFailed(/* reason= */ 0); + mController.mBroadcastCallback.onBroadcastUpdateFailed( + /* reason= */ 0, /* broadcastId= */ 0); + mController.mBroadcastCallback.onBroadcastUpdated(/* reason= */ 0, /* broadcastId= */ 0); + mController.mBroadcastCallback.onPlaybackStarted(/* reason= */ 0, /* broadcastId= */ 0); + mController.mBroadcastCallback.onPlaybackStopped(/* reason= */ 0, /* broadcastId= */ 0); + + ShadowLooper.idleMainLooper(); + // Should be called once in displayPreference, but not called after callbacks + verify(mPreference).setShowQrCodeIcon(anyBoolean()); + } + + @Test + public void broadcastOnCallback_updateIcon() { + mController.displayPreference(mScreen); + mController.mBroadcastCallback.onBroadcastMetadataChanged( + /* broadcastId= */ 0, mock(BluetoothLeBroadcastMetadata.class)); + + ShadowLooper.idleMainLooper(); + + // Should be called twice, in displayPreference and also after callback + verify(mPreference, times(2)).setShowQrCodeIcon(anyBoolean()); + } + + @Test + public void broadcastStopCallback_updateIcon() { + mController.displayPreference(mScreen); + mController.mBroadcastCallback.onBroadcastStopped(/* reason= */ 0, /* broadcastId= */ 0); + + ShadowLooper.idleMainLooper(); + + // Should be called twice, in displayPreference and also after callback + verify(mPreference, times(2)).setShowQrCodeIcon(anyBoolean()); + } + + @Test + public void idTextValid_emptyString() { + boolean valid = mController.isTextValid(""); + + assertThat(valid).isFalse(); + } + + @Test + public void idTextValid_validName() { + boolean valid = mController.isTextValid("valid name"); + + assertThat(valid).isTrue(); + } +} diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreferenceTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreferenceTest.java new file mode 100644 index 00000000000..13e2a9d4636 --- /dev/null +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNamePreferenceTest.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.connecteddevice.audiosharing; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.settings.SettingsEnums; +import android.content.Context; +import android.content.Intent; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageButton; +import android.widget.LinearLayout; + +import androidx.preference.PreferenceViewHolder; +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.R; +import com.android.settings.SettingsActivity; +import com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsQrCodeFragment; +import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class AudioSharingNamePreferenceTest { + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + private Context mContext; + private AudioSharingNamePreference mPreference; + + @Before + public void setup() { + mContext = ApplicationProvider.getApplicationContext(); + mPreference = spy(new AudioSharingNamePreference(mContext, null)); + } + + @Test + public void initialize_correctLayout() { + assertThat(mPreference.getLayoutResource()) + .isEqualTo( + com.android.settingslib.widget.preference.twotarget.R.layout + .preference_two_target); + assertThat(mPreference.getWidgetLayoutResource()) + .isEqualTo(R.layout.preference_widget_qrcode); + } + + @Test + public void onBindViewHolder_correctLayout_noQrCodeButton() { + LayoutInflater inflater = LayoutInflater.from(mContext); + View view = inflater.inflate(mPreference.getLayoutResource(), null); + LinearLayout widgetView = view.findViewById(android.R.id.widget_frame); + assertThat(widgetView).isNotNull(); + inflater.inflate(mPreference.getWidgetLayoutResource(), widgetView, true); + + var holder = PreferenceViewHolder.createInstanceForTests(view); + mPreference.setShowQrCodeIcon(false); + mPreference.onBindViewHolder(holder); + + ImageButton shareButton = (ImageButton) holder.findViewById(R.id.button_icon); + View divider = + holder.findViewById( + com.android.settingslib.widget.preference.twotarget.R.id + .two_target_divider); + + assertThat(shareButton).isNotNull(); + assertThat(shareButton.getVisibility()).isEqualTo(View.GONE); + assertThat(shareButton.hasOnClickListeners()).isFalse(); + assertThat(divider).isNotNull(); + assertThat(divider.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void onBindViewHolder_correctLayout_showQrCodeButton() { + LayoutInflater inflater = LayoutInflater.from(mContext); + View view = inflater.inflate(mPreference.getLayoutResource(), null); + LinearLayout widgetView = view.findViewById(android.R.id.widget_frame); + assertThat(widgetView).isNotNull(); + inflater.inflate(mPreference.getWidgetLayoutResource(), widgetView, true); + + var holder = PreferenceViewHolder.createInstanceForTests(view); + mPreference.setShowQrCodeIcon(true); + mPreference.onBindViewHolder(holder); + + ImageButton shareButton = (ImageButton) holder.findViewById(R.id.button_icon); + View divider = + holder.findViewById( + com.android.settingslib.widget.preference.twotarget.R.id + .two_target_divider); + + assertThat(shareButton).isNotNull(); + assertThat(shareButton.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(shareButton.getDrawable()).isNotNull(); + assertThat(shareButton.hasOnClickListeners()).isTrue(); + assertThat(divider).isNotNull(); + assertThat(divider.getVisibility()).isEqualTo(View.VISIBLE); + + // mContext is not an Activity context, calling startActivity() from outside of an Activity + // context requires the FLAG_ACTIVITY_NEW_TASK flag, create a mock to avoid this + // AndroidRuntimeException. + Context activityContext = mock(Context.class); + when(mPreference.getContext()).thenReturn(activityContext); + shareButton.callOnClick(); + + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(activityContext).startActivity(argumentCaptor.capture()); + + Intent intent = argumentCaptor.getValue(); + assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)) + .isEqualTo(AudioStreamsQrCodeFragment.class.getName()); + assertThat(intent.getIntExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, 0)) + .isEqualTo(R.string.audio_streams_qr_code_page_title); + assertThat(intent.getIntExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, 0)) + .isEqualTo(SettingsEnums.AUDIO_SHARING_SETTINGS); + } +} diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNameTextValidatorTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNameTextValidatorTest.java new file mode 100644 index 00000000000..ada6117b34a --- /dev/null +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingNameTextValidatorTest.java @@ -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.connecteddevice.audiosharing; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class AudioSharingNameTextValidatorTest { + private AudioSharingNameTextValidator mValidator; + + @Before + public void setUp() { + mValidator = new AudioSharingNameTextValidator(); + } + + @Test + public void testValidNames() { + assertThat(mValidator.isTextValid("ValidName")).isTrue(); + assertThat(mValidator.isTextValid("12345678")).isTrue(); + assertThat(mValidator.isTextValid("Name_With_Underscores")).isTrue(); + assertThat(mValidator.isTextValid("ÄÖÜß")).isTrue(); + assertThat(mValidator.isTextValid("ThisNameIsExactly32Characters!")).isTrue(); + } + + @Test + public void testInvalidNames() { + assertThat(mValidator.isTextValid(null)).isFalse(); + assertThat(mValidator.isTextValid("")).isFalse(); + assertThat(mValidator.isTextValid("abc")).isFalse(); + assertThat(mValidator.isTextValid("ThisNameIsWayTooLongForAnAudioSharingName")).isFalse(); + assertThat(mValidator.isTextValid("Invalid\uDC00")).isFalse(); + } +} diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPasswordPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPasswordPreferenceControllerTest.java new file mode 100644 index 00000000000..5bfb9663e11 --- /dev/null +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPasswordPreferenceControllerTest.java @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.connecteddevice.audiosharing; + +import static com.android.settings.core.BasePreferenceController.AVAILABLE; +import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.settings.SettingsEnums; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothStatusCodes; +import android.content.ContentResolver; +import android.content.Context; +import android.content.SharedPreferences; +import android.database.ContentObserver; +import android.platform.test.flag.junit.SetFlagsRule; + +import androidx.lifecycle.LifecycleOwner; +import androidx.preference.PreferenceScreen; +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.R; +import com.android.settings.bluetooth.Utils; +import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; +import com.android.settings.testutils.shadow.ShadowBluetoothUtils; +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast; +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.flags.Flags; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.shadow.api.Shadow; +import org.robolectric.shadows.ShadowLooper; + +import java.nio.charset.StandardCharsets; + +@RunWith(RobolectricTestRunner.class) +@Config( + shadows = { + ShadowBluetoothAdapter.class, + ShadowBluetoothUtils.class, + }) +public class AudioSharingPasswordPreferenceControllerTest { + private static final String PREF_KEY = "audio_sharing_stream_password"; + private static final String SHARED_PREF_KEY = "default_password"; + private static final String BROADCAST_PASSWORD = "password"; + private static final String EDITTEXT_PASSWORD = "edittext_password"; + private static final String HIDDEN_PASSWORD = "********"; + + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Spy Context mContext = ApplicationProvider.getApplicationContext(); + @Mock private LocalBluetoothLeBroadcast mBroadcast; + @Mock private LocalBluetoothManager mLocalBtManager; + @Mock private LocalBluetoothProfileManager mProfileManager; + @Mock private SharedPreferences mSharedPreferences; + @Mock private SharedPreferences.Editor mEditor; + @Mock private ContentResolver mContentResolver; + @Mock private PreferenceScreen mScreen; + private AudioSharingPasswordPreferenceController mController; + private ShadowBluetoothAdapter mShadowBluetoothAdapter; + private Lifecycle mLifecycle; + private LifecycleOwner mLifecycleOwner; + private AudioSharingPasswordPreference mPreference; + private FakeFeatureFactory mFeatureFactory; + + @Before + public void setUp() { + mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); + mShadowBluetoothAdapter.setEnabled(true); + mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + mLocalBtManager = Utils.getLocalBtManager(mContext); + when(mLocalBtManager.getProfileManager()).thenReturn(mProfileManager); + when(mProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast); + mFeatureFactory = FakeFeatureFactory.setupForTest(); + when(mContext.getContentResolver()).thenReturn(mContentResolver); + when(mContext.getSharedPreferences(anyString(), anyInt())).thenReturn(mSharedPreferences); + when(mSharedPreferences.edit()).thenReturn(mEditor); + when(mEditor.putString(anyString(), anyString())).thenReturn(mEditor); + mLifecycleOwner = () -> mLifecycle; + mLifecycle = new Lifecycle(mLifecycleOwner); + mController = new AudioSharingPasswordPreferenceController(mContext, PREF_KEY); + mPreference = spy(new AudioSharingPasswordPreference(mContext)); + when(mScreen.findPreference(PREF_KEY)).thenReturn(mPreference); + } + + @Test + public void getAvailabilityStatus_flagOn_available() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); + } + + @Test + public void getAvailabilityStatus_flagOff_unsupported() { + mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE); + } + + @Test + public void onStart_flagOff_doNothing() { + mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + mController.onStart(mLifecycleOwner); + verify(mContentResolver, never()).registerContentObserver(any(), anyBoolean(), any()); + verify(mSharedPreferences, never()).registerOnSharedPreferenceChangeListener(any()); + } + + @Test + public void onStart_flagOn_registerCallbacks() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + mController.onStart(mLifecycleOwner); + verify(mContentResolver).registerContentObserver(any(), anyBoolean(), any()); + verify(mSharedPreferences).registerOnSharedPreferenceChangeListener(any()); + } + + @Test + public void onStop_flagOff_doNothing() { + mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + mController.onStop(mLifecycleOwner); + verify(mContentResolver, never()).unregisterContentObserver(any()); + verify(mSharedPreferences, never()).unregisterOnSharedPreferenceChangeListener(any()); + } + + @Test + public void onStop_flagOn_registerCallbacks() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + mController.onStop(mLifecycleOwner); + verify(mContentResolver).unregisterContentObserver(any()); + verify(mSharedPreferences).unregisterOnSharedPreferenceChangeListener(any()); + } + + @Test + public void displayPreference_setupPreference_noPassword() { + when(mSharedPreferences.getString(anyString(), anyString())).thenReturn(EDITTEXT_PASSWORD); + when(mBroadcast.getBroadcastCode()).thenReturn(new byte[] {}); + + mController.displayPreference(mScreen); + ShadowLooper.idleMainLooper(); + + assertThat(mPreference.isPassword()).isTrue(); + assertThat(mPreference.getDialogLayoutResource()) + .isEqualTo(R.layout.audio_sharing_password_dialog); + assertThat(mPreference.getText()).isEqualTo(EDITTEXT_PASSWORD); + assertThat(mPreference.getSummary()) + .isEqualTo(mContext.getString(R.string.audio_streams_no_password_summary)); + verify(mPreference).setValidator(any()); + verify(mPreference).setOnDialogEventListener(any()); + } + + @Test + public void contentObserver_updatePreferenceOnChange() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + when(mBroadcast.getBroadcastCode()) + .thenReturn(BROADCAST_PASSWORD.getBytes(StandardCharsets.UTF_8)); + mController.onStart(mLifecycleOwner); + mController.displayPreference(mScreen); + ShadowLooper.idleMainLooper(); + + ArgumentCaptor observerCaptor = + ArgumentCaptor.forClass(ContentObserver.class); + verify(mContentResolver) + .registerContentObserver(any(), anyBoolean(), observerCaptor.capture()); + + var observer = observerCaptor.getValue(); + assertThat(observer).isNotNull(); + observer.onChange(true); + verify(mPreference).setText(anyString()); + verify(mPreference).setSummary(anyString()); + } + + @Test + public void sharedPrefChangeListener_updatePreferenceOnChange() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + when(mBroadcast.getBroadcastCode()) + .thenReturn(BROADCAST_PASSWORD.getBytes(StandardCharsets.UTF_8)); + mController.onStart(mLifecycleOwner); + mController.displayPreference(mScreen); + ShadowLooper.idleMainLooper(); + + ArgumentCaptor captor = + ArgumentCaptor.forClass(SharedPreferences.OnSharedPreferenceChangeListener.class); + verify(mSharedPreferences).registerOnSharedPreferenceChangeListener(captor.capture()); + + var observer = captor.getValue(); + assertThat(captor).isNotNull(); + observer.onSharedPreferenceChanged(mSharedPreferences, SHARED_PREF_KEY); + verify(mPreference).setText(anyString()); + verify(mPreference).setSummary(anyString()); + } + + @Test + public void displayPreference_setupPreference_hasPassword() { + when(mBroadcast.getBroadcastCode()) + .thenReturn(BROADCAST_PASSWORD.getBytes(StandardCharsets.UTF_8)); + mController.displayPreference(mScreen); + ShadowLooper.idleMainLooper(); + + assertThat(mPreference.isPassword()).isTrue(); + assertThat(mPreference.getDialogLayoutResource()) + .isEqualTo(R.layout.audio_sharing_password_dialog); + assertThat(mPreference.getText()).isEqualTo(BROADCAST_PASSWORD); + assertThat(mPreference.getSummary()).isEqualTo(HIDDEN_PASSWORD); + verify(mPreference).setValidator(any()); + verify(mPreference).setOnDialogEventListener(any()); + } + + @Test + public void onBindDialogView_updatePreference_isBroadcasting_noPassword() { + when(mBroadcast.getBroadcastCode()).thenReturn(new byte[] {}); + when(mBroadcast.isEnabled(any())).thenReturn(true); + mController.displayPreference(mScreen); + mController.onBindDialogView(); + ShadowLooper.idleMainLooper(); + + verify(mPreference).setEditable(false); + verify(mPreference).setChecked(true); + } + + @Test + public void onBindDialogView_updatePreference_isNotBroadcasting_hasPassword() { + when(mBroadcast.getBroadcastCode()) + .thenReturn(BROADCAST_PASSWORD.getBytes(StandardCharsets.UTF_8)); + mController.displayPreference(mScreen); + mController.onBindDialogView(); + ShadowLooper.idleMainLooper(); + + verify(mPreference).setEditable(true); + verify(mPreference).setChecked(false); + } + + @Test + public void onPreferenceDataChanged_isBroadcasting_doNothing() { + when(mBroadcast.isEnabled(any())).thenReturn(true); + mController.displayPreference(mScreen); + mController.onPreferenceDataChanged(BROADCAST_PASSWORD, /* isPublicBroadcast= */ false); + ShadowLooper.idleMainLooper(); + + verify(mBroadcast, never()).setBroadcastCode(any()); + verify(mFeatureFactory.metricsFeatureProvider, never()).action(any(), anyInt(), anyInt()); + } + + @Test + public void onPreferenceDataChanged_noChange_doNothing() { + when(mSharedPreferences.getString(anyString(), anyString())).thenReturn(EDITTEXT_PASSWORD); + when(mBroadcast.getBroadcastCode()).thenReturn(new byte[] {}); + mController.displayPreference(mScreen); + mController.onPreferenceDataChanged(EDITTEXT_PASSWORD, /* isPublicBroadcast= */ true); + ShadowLooper.idleMainLooper(); + + verify(mBroadcast, never()).setBroadcastCode(any()); + verify(mFeatureFactory.metricsFeatureProvider, never()).action(any(), anyInt(), anyInt()); + } + + @Test + public void onPreferenceDataChanged_updateToNonPublicBroadcast() { + when(mSharedPreferences.getString(anyString(), anyString())).thenReturn(EDITTEXT_PASSWORD); + when(mBroadcast.getBroadcastCode()).thenReturn(new byte[] {}); + mController.displayPreference(mScreen); + mController.onPreferenceDataChanged(BROADCAST_PASSWORD, /* isPublicBroadcast= */ false); + ShadowLooper.idleMainLooper(); + + verify(mBroadcast).setBroadcastCode(BROADCAST_PASSWORD.getBytes(StandardCharsets.UTF_8)); + verify(mEditor).putString(anyString(), eq(BROADCAST_PASSWORD)); + verify(mFeatureFactory.metricsFeatureProvider) + .action(mContext, SettingsEnums.ACTION_AUDIO_STREAM_PASSWORD_UPDATED, 0); + } + + @Test + public void onPreferenceDataChanged_updateToPublicBroadcast() { + when(mSharedPreferences.getString(anyString(), anyString())).thenReturn(EDITTEXT_PASSWORD); + when(mBroadcast.getBroadcastCode()) + .thenReturn(BROADCAST_PASSWORD.getBytes(StandardCharsets.UTF_8)); + mController.displayPreference(mScreen); + mController.onPreferenceDataChanged(EDITTEXT_PASSWORD, /* isPublicBroadcast= */ true); + ShadowLooper.idleMainLooper(); + + verify(mBroadcast).setBroadcastCode("".getBytes(StandardCharsets.UTF_8)); + verify(mEditor, never()).putString(anyString(), eq(EDITTEXT_PASSWORD)); + verify(mFeatureFactory.metricsFeatureProvider) + .action(mContext, SettingsEnums.ACTION_AUDIO_STREAM_PASSWORD_UPDATED, 1); + } + + @Test + public void idTextValid_emptyString() { + boolean valid = mController.isTextValid(""); + + assertThat(valid).isFalse(); + } + + @Test + public void idTextValid_validPassword() { + boolean valid = mController.isTextValid(BROADCAST_PASSWORD); + + assertThat(valid).isTrue(); + } +} diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPasswordPreferenceTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPasswordPreferenceTest.java new file mode 100644 index 00000000000..0b87e8ca25d --- /dev/null +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPasswordPreferenceTest.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.connecteddevice.audiosharing; + +import static android.view.View.GONE; +import static android.view.View.VISIBLE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.content.DialogInterface; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.CheckBox; +import android.widget.EditText; + +import androidx.appcompat.app.AlertDialog; +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.R; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class AudioSharingPasswordPreferenceTest { + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + private static final String EDIT_TEXT_CONTENT = "text"; + private Context mContext; + private AudioSharingPasswordPreference mPreference; + + @Before + public void setup() { + mContext = ApplicationProvider.getApplicationContext(); + mPreference = new AudioSharingPasswordPreference(mContext, null); + } + + @Test + public void onBindDialogView_correctLayout() { + View view = + LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null); + mPreference.onBindDialogView(view); + + var editText = view.findViewById(android.R.id.edit); + var checkBox = view.findViewById(R.id.audio_sharing_stream_password_checkbox); + var dialogMessage = view.findViewById(android.R.id.message); + + assertThat(editText).isNotNull(); + assertThat(checkBox).isNotNull(); + assertThat(dialogMessage).isNotNull(); + } + + @Test + public void setEditable_true() { + View view = + LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null); + mPreference.onBindDialogView(view); + + var editText = view.findViewById(android.R.id.edit); + var checkBox = view.findViewById(R.id.audio_sharing_stream_password_checkbox); + var dialogMessage = view.findViewById(android.R.id.message); + + mPreference.setEditable(true); + + assertThat(editText).isNotNull(); + assertThat(editText.isEnabled()).isTrue(); + assertThat(editText.getAlpha()).isEqualTo(1.0f); + assertThat(checkBox).isNotNull(); + assertThat(checkBox.isEnabled()).isTrue(); + assertThat(dialogMessage).isNotNull(); + assertThat(dialogMessage.getVisibility()).isEqualTo(GONE); + } + + @Test + public void setEditable_false() { + View view = + LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null); + mPreference.onBindDialogView(view); + + var editText = view.findViewById(android.R.id.edit); + var checkBox = view.findViewById(R.id.audio_sharing_stream_password_checkbox); + var dialogMessage = view.findViewById(android.R.id.message); + + mPreference.setEditable(false); + + assertThat(editText).isNotNull(); + assertThat(editText.isEnabled()).isFalse(); + assertThat(editText.getAlpha()).isLessThan(1.0f); + assertThat(checkBox).isNotNull(); + assertThat(checkBox.isEnabled()).isFalse(); + assertThat(dialogMessage).isNotNull(); + assertThat(dialogMessage.getVisibility()).isEqualTo(VISIBLE); + } + + @Test + public void setChecked_true() { + View view = + LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null); + mPreference.onBindDialogView(view); + + CheckBox checkBox = view.findViewById(R.id.audio_sharing_stream_password_checkbox); + + mPreference.setChecked(true); + + assertThat(checkBox).isNotNull(); + assertThat(checkBox.isChecked()).isTrue(); + } + + @Test + public void setChecked_false() { + View view = + LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null); + mPreference.onBindDialogView(view); + + CheckBox checkBox = view.findViewById(R.id.audio_sharing_stream_password_checkbox); + + mPreference.setChecked(false); + + assertThat(checkBox).isNotNull(); + assertThat(checkBox.isChecked()).isFalse(); + } + + @Test + public void onDialogEventListener_onClick_positiveButton() { + AudioSharingPasswordPreference.OnDialogEventListener listener = + mock(AudioSharingPasswordPreference.OnDialogEventListener.class); + mPreference.setOnDialogEventListener(listener); + View view = + LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null); + mPreference.onBindDialogView(view); + + EditText editText = view.findViewById(android.R.id.edit); + assertThat(editText).isNotNull(); + editText.setText(EDIT_TEXT_CONTENT); + + mPreference.onClick(mock(DialogInterface.class), DialogInterface.BUTTON_POSITIVE); + + verify(listener).onBindDialogView(); + verify(listener).onPreferenceDataChanged(eq(EDIT_TEXT_CONTENT), anyBoolean()); + } + + @Test + public void onDialogEventListener_onClick_negativeButton_doNothing() { + AudioSharingPasswordPreference.OnDialogEventListener listener = + mock(AudioSharingPasswordPreference.OnDialogEventListener.class); + mPreference.setOnDialogEventListener(listener); + View view = + LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null); + mPreference.onBindDialogView(view); + + EditText editText = view.findViewById(android.R.id.edit); + assertThat(editText).isNotNull(); + editText.setText(EDIT_TEXT_CONTENT); + + mPreference.onClick(mock(DialogInterface.class), DialogInterface.BUTTON_NEGATIVE); + + verify(listener).onBindDialogView(); + verify(listener, never()).onPreferenceDataChanged(anyString(), anyBoolean()); + } + + @Test + public void onPrepareDialogBuilder_editable_doNothing() { + View view = + LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null); + mPreference.onBindDialogView(view); + mPreference.setEditable(true); + + var dialogBuilder = mock(AlertDialog.Builder.class); + mPreference.onPrepareDialogBuilder( + dialogBuilder, mock(DialogInterface.OnClickListener.class)); + + verify(dialogBuilder, never()).setPositiveButton(any(), any()); + } + + @Test + public void onPrepareDialogBuilder_notEditable_disableButton() { + View view = + LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null); + mPreference.onBindDialogView(view); + mPreference.setEditable(false); + + var dialogBuilder = mock(AlertDialog.Builder.class); + mPreference.onPrepareDialogBuilder( + dialogBuilder, mock(DialogInterface.OnClickListener.class)); + + verify(dialogBuilder).setPositiveButton(any(), any()); + } +} diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPasswordValidatorTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPasswordValidatorTest.java new file mode 100644 index 00000000000..5c96fe1b5bf --- /dev/null +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPasswordValidatorTest.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.connecteddevice.audiosharing; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class AudioSharingPasswordValidatorTest { + private AudioSharingPasswordValidator mValidator; + + @Before + public void setUp() { + mValidator = new AudioSharingPasswordValidator(); + } + + @Test + public void testValidPasswords() { + assertThat(mValidator.isTextValid("1234")).isTrue(); + assertThat(mValidator.isTextValid("Password")).isTrue(); + assertThat(mValidator.isTextValid("SecurePass123!")).isTrue(); + assertThat(mValidator.isTextValid("ÄÖÜß")).isTrue(); + assertThat(mValidator.isTextValid("1234567890abcdef")).isTrue(); + } + + @Test + public void testInvalidPasswords() { + assertThat(mValidator.isTextValid(null)).isFalse(); + assertThat(mValidator.isTextValid("")).isFalse(); + assertThat(mValidator.isTextValid("abc")).isFalse(); + assertThat(mValidator.isTextValid("ThisIsAVeryLongPasswordThatExceedsSixteenOctets")) + .isFalse(); + assertThat(mValidator.isTextValid("Invalid\uDC00")).isFalse(); + } +} diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AddSourceBadCodeStateTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AddSourceBadCodeStateTest.java index 2fddff52358..aba300e08bc 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AddSourceBadCodeStateTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AddSourceBadCodeStateTest.java @@ -20,18 +20,40 @@ import static com.android.settings.connecteddevice.audiosharing.audiostreams.Add import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.settings.SettingsEnums; +import android.content.Context; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.testutils.FakeFeatureFactory; + import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; @RunWith(RobolectricTestRunner.class) public class AddSourceBadCodeStateTest { + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + private final Context mContext = ApplicationProvider.getApplicationContext(); + @Mock private AudioStreamPreference mPreference; + @Mock private AudioStreamsProgressCategoryController mController; + @Mock private AudioStreamsHelper mHelper; + private FakeFeatureFactory mFeatureFactory; private AddSourceBadCodeState mInstance; @Before public void setUp() { - mInstance = AddSourceBadCodeState.getInstance(); + mFeatureFactory = FakeFeatureFactory.setupForTest(); + mInstance = new AddSourceBadCodeState(); } @Test @@ -55,4 +77,19 @@ public class AddSourceBadCodeStateTest { AudioStreamsProgressCategoryController.AudioStreamState .ADD_SOURCE_BAD_CODE); } + + @Test + public void testPerformAction() { + when(mPreference.getContext()).thenReturn(mContext); + when(mPreference.getSourceOriginForLogging()) + .thenReturn(SourceOriginForLogging.QR_CODE_SCAN_SETTINGS); + + mInstance.performAction(mPreference, mController, mHelper); + + verify(mFeatureFactory.metricsFeatureProvider) + .action( + eq(mContext), + eq(SettingsEnums.ACTION_AUDIO_STREAM_JOIN_FAILED_BAD_CODE), + eq(SourceOriginForLogging.QR_CODE_SCAN_SETTINGS.ordinal())); + } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AddSourceFailedStateTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AddSourceFailedStateTest.java index d8b1fcf9401..1bc9f9148f5 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AddSourceFailedStateTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AddSourceFailedStateTest.java @@ -20,18 +20,40 @@ import static com.android.settings.connecteddevice.audiosharing.audiostreams.Add import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.settings.SettingsEnums; +import android.content.Context; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.testutils.FakeFeatureFactory; + import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; @RunWith(RobolectricTestRunner.class) public class AddSourceFailedStateTest { + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + private final Context mContext = ApplicationProvider.getApplicationContext(); + @Mock private AudioStreamPreference mPreference; + @Mock private AudioStreamsProgressCategoryController mController; + @Mock private AudioStreamsHelper mHelper; + private FakeFeatureFactory mFeatureFactory; private AddSourceFailedState mInstance; @Before public void setUp() { - mInstance = AddSourceFailedState.getInstance(); + mFeatureFactory = FakeFeatureFactory.setupForTest(); + mInstance = new AddSourceFailedState(); } @Test @@ -54,4 +76,19 @@ public class AddSourceFailedStateTest { .isEqualTo( AudioStreamsProgressCategoryController.AudioStreamState.ADD_SOURCE_FAILED); } + + @Test + public void testPerformAction() { + when(mPreference.getContext()).thenReturn(mContext); + when(mPreference.getSourceOriginForLogging()) + .thenReturn(SourceOriginForLogging.QR_CODE_SCAN_SETTINGS); + + mInstance.performAction(mPreference, mController, mHelper); + + verify(mFeatureFactory.metricsFeatureProvider) + .action( + eq(mContext), + eq(SettingsEnums.ACTION_AUDIO_STREAM_JOIN_FAILED_OTHER), + eq(SourceOriginForLogging.QR_CODE_SCAN_SETTINGS.ordinal())); + } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AddSourceWaitForResponseStateTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AddSourceWaitForResponseStateTest.java index 6e5342bb3fe..950ad38a64a 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AddSourceWaitForResponseStateTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AddSourceWaitForResponseStateTest.java @@ -22,11 +22,21 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.settings.SettingsEnums; import android.bluetooth.BluetoothLeBroadcastMetadata; +import android.content.Context; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.R; +import com.android.settings.testutils.FakeFeatureFactory; import org.junit.Before; import org.junit.Rule; @@ -36,23 +46,36 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowAlertDialog; import org.robolectric.shadows.ShadowLooper; import java.util.concurrent.TimeUnit; @RunWith(RobolectricTestRunner.class) +@Config( + shadows = { + ShadowAlertDialog.class, + }) public class AddSourceWaitForResponseStateTest { - private static final int BROADCAST_ID = 1; @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + private static final int BROADCAST_ID = 1; + private final Context mContext = spy(ApplicationProvider.getApplicationContext()); @Mock private AudioStreamPreference mMockPreference; @Mock private AudioStreamsProgressCategoryController mMockController; @Mock private AudioStreamsHelper mMockHelper; @Mock private BluetoothLeBroadcastMetadata mMockMetadata; + @Mock private AudioStreamsRepository mMockRepository; + private FakeFeatureFactory mFeatureFactory; private AddSourceWaitForResponseState mInstance; @Before public void setUp() { - mInstance = AddSourceWaitForResponseState.getInstance(); + mFeatureFactory = FakeFeatureFactory.setupForTest(); + mInstance = new AddSourceWaitForResponseState(); + when(mMockPreference.getContext()).thenReturn(mContext); + when(mMockPreference.getSourceOriginForLogging()) + .thenReturn(SourceOriginForLogging.QR_CODE_SCAN_SETTINGS); } @Test @@ -93,11 +116,18 @@ public class AddSourceWaitForResponseStateTest { public void testPerformAction_metadataIsNotNull_addSource() { when(mMockPreference.getAudioStreamMetadata()).thenReturn(mMockMetadata); when(mMockPreference.getSourceOriginForLogging()) - .thenReturn(SourceOriginForLogging.UNKNOWN); + .thenReturn(SourceOriginForLogging.QR_CODE_SCAN_SETTINGS); + mInstance.setAudioStreamsRepositoryForTesting(mMockRepository); mInstance.performAction(mMockPreference, mMockController, mMockHelper); verify(mMockHelper).addSource(mMockMetadata); + verify(mFeatureFactory.metricsFeatureProvider) + .action( + eq(mContext), + eq(SettingsEnums.ACTION_AUDIO_STREAM_JOIN), + eq(SourceOriginForLogging.QR_CODE_SCAN_SETTINGS.ordinal())); + verify(mMockRepository).cacheMetadata(mMockMetadata); verify(mMockController, never()).handleSourceFailedToConnect(anyInt()); } @@ -108,12 +138,28 @@ public class AddSourceWaitForResponseStateTest { when(mMockPreference.getAudioStreamState()).thenReturn(mInstance.getStateEnum()); when(mMockPreference.getAudioStreamBroadcastId()).thenReturn(BROADCAST_ID); when(mMockPreference.getSourceOriginForLogging()) - .thenReturn(SourceOriginForLogging.UNKNOWN); + .thenReturn(SourceOriginForLogging.QR_CODE_SCAN_SETTINGS); + when(mMockController.getFragment()).thenReturn(mock(AudioStreamsDashboardFragment.class)); + mInstance.setAudioStreamsRepositoryForTesting(mMockRepository); mInstance.performAction(mMockPreference, mMockController, mMockHelper); ShadowLooper.idleMainLooper(ADD_SOURCE_WAIT_FOR_RESPONSE_TIMEOUT_MILLIS, TimeUnit.SECONDS); verify(mMockHelper).addSource(mMockMetadata); verify(mMockController).handleSourceFailedToConnect(BROADCAST_ID); + verify(mFeatureFactory.metricsFeatureProvider) + .action( + eq(mContext), + eq(SettingsEnums.ACTION_AUDIO_STREAM_JOIN), + eq(SourceOriginForLogging.QR_CODE_SCAN_SETTINGS.ordinal())); + verify(mMockRepository).cacheMetadata(mMockMetadata); + verify(mFeatureFactory.metricsFeatureProvider) + .action( + eq(mContext), + eq(SettingsEnums.ACTION_AUDIO_STREAM_JOIN_FAILED_TIMEOUT), + eq(SourceOriginForLogging.QR_CODE_SCAN_SETTINGS.ordinal())); + verify(mContext).getString(R.string.audio_streams_dialog_stream_is_not_available); + verify(mContext).getString(R.string.audio_streams_is_not_playing); + verify(mContext).getString(R.string.audio_streams_dialog_close); } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandlerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandlerTest.java new file mode 100644 index 00000000000..adc77a183f6 --- /dev/null +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandlerTest.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.connecteddevice.audiosharing.audiostreams; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; + +import androidx.preference.Preference; +import androidx.test.core.app.ApplicationProvider; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class AudioStreamStateHandlerTest { + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + private static final int SUMMARY_RES = 1; + private static final String SUMMARY = "summary"; + private final Context mContext = spy(ApplicationProvider.getApplicationContext()); + @Mock private AudioStreamsProgressCategoryController mController; + @Mock private AudioStreamsHelper mHelper; + @Mock private AudioStreamPreference mPreference; + private AudioStreamStateHandler mHandler; + + @Before + public void setUp() { + mHandler = spy(new AudioStreamStateHandler()); + } + + @Test + public void testHandleStateChange_noChange_doNothing() { + when(mHandler.getStateEnum()) + .thenReturn( + AudioStreamsProgressCategoryController.AudioStreamState + .ADD_SOURCE_BAD_CODE); + when(mPreference.getAudioStreamState()) + .thenReturn( + AudioStreamsProgressCategoryController.AudioStreamState + .ADD_SOURCE_BAD_CODE); + + mHandler.handleStateChange(mPreference, mController, mHelper); + + verify(mPreference, never()).setAudioStreamState(any()); + verify(mHandler, never()).performAction(any(), any(), any()); + verify(mPreference, never()).setIsConnected(anyBoolean(), anyString(), any()); + } + + @Test + public void testHandleStateChange_setNewState() { + when(mHandler.getStateEnum()) + .thenReturn(AudioStreamsProgressCategoryController.AudioStreamState.SOURCE_ADDED); + when(mPreference.getAudioStreamState()) + .thenReturn( + AudioStreamsProgressCategoryController.AudioStreamState + .ADD_SOURCE_BAD_CODE); + + mHandler.handleStateChange(mPreference, mController, mHelper); + + verify(mPreference) + .setAudioStreamState( + AudioStreamsProgressCategoryController.AudioStreamState.SOURCE_ADDED); + verify(mHandler).performAction(any(), any(), any()); + verify(mPreference).setIsConnected(eq(true), eq(""), eq(null)); + } + + @Test + public void testHandleStateChange_setNewState_newSummary_newListener() { + Preference.OnPreferenceClickListener listener = + mock(Preference.OnPreferenceClickListener.class); + when(mHandler.getStateEnum()) + .thenReturn( + AudioStreamsProgressCategoryController.AudioStreamState + .ADD_SOURCE_BAD_CODE); + when(mHandler.getSummary()).thenReturn(SUMMARY_RES); + when(mHandler.getOnClickListener(any())).thenReturn(listener); + when(mPreference.getAudioStreamState()) + .thenReturn( + AudioStreamsProgressCategoryController.AudioStreamState.ADD_SOURCE_FAILED); + when(mPreference.getContext()).thenReturn(mContext); + doReturn(SUMMARY).when(mContext).getString(anyInt()); + + mHandler.handleStateChange(mPreference, mController, mHelper); + + verify(mPreference) + .setAudioStreamState( + AudioStreamsProgressCategoryController.AudioStreamState + .ADD_SOURCE_BAD_CODE); + verify(mHandler).performAction(any(), any(), any()); + verify(mPreference).setIsConnected(eq(false), eq(SUMMARY), eq(listener)); + } + + @Test + public void testGetSummary() { + int res = mHandler.getSummary(); + assertThat(res).isEqualTo(AudioStreamStateHandler.EMPTY_STRING_RES); + } + + @Test + public void testGetOnClickListener() { + Preference.OnPreferenceClickListener listener = mHandler.getOnClickListener(mController); + assertThat(listener).isNull(); + } + + @Test + public void testGetStateEnum() { + var state = mHandler.getStateEnum(); + assertThat(state) + .isEqualTo(AudioStreamsProgressCategoryController.AudioStreamState.UNKNOWN); + } +} diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceSummaryUpdaterTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceSummaryUpdaterTest.java index 4403528e2fb..d6b99a1d3e3 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceSummaryUpdaterTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceSummaryUpdaterTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; +import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothProfile; import android.content.Context; @@ -76,25 +77,46 @@ public class AudioStreamsActiveDeviceSummaryUpdaterTest { } @Test - public void onActiveDeviceChanged_notLeProfile_doNothing() { - mUpdater.onActiveDeviceChanged(mCachedBluetoothDevice, 0); + public void unregister_doNothing() { + mUpdater.register(false); assertThat(mUpdatedSummary).isNull(); } @Test - public void onActiveDeviceChanged_leProfile_summaryUpdated() { + public void onProfileConnectionStateChanged_notLeAssistProfile_doNothing() { + mUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice, 0, 0); + + assertThat(mUpdatedSummary).isNull(); + } + + @Test + public void onProfileConnectionStateChanged_leAssistantProfile_summaryUpdated() { ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected( mCachedBluetoothDevice); when(mCachedBluetoothDevice.getName()).thenReturn(DEVICE_NAME); - mUpdater.onActiveDeviceChanged(mCachedBluetoothDevice, BluetoothProfile.LE_AUDIO); + mUpdater.onProfileConnectionStateChanged( + mCachedBluetoothDevice, + BluetoothAdapter.STATE_CONNECTED, + BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); assertThat(mUpdatedSummary).isEqualTo(DEVICE_NAME); } @Test - public void onActiveDeviceChanged_leProfile_noDevice_summaryUpdated() { - mUpdater.onActiveDeviceChanged(mCachedBluetoothDevice, BluetoothProfile.LE_AUDIO); + public void onActiveDeviceChanged_leAssistantProfile_noDevice_summaryUpdated() { + mUpdater.onProfileConnectionStateChanged( + mCachedBluetoothDevice, + BluetoothAdapter.STATE_CONNECTED, + BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); + + assertThat(mUpdatedSummary) + .isEqualTo(mContext.getString(R.string.audio_streams_dialog_no_le_device_title)); + } + + @Test + public void onBluetoothStateOff_summaryUpdated() { + mUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_OFF); assertThat(mUpdatedSummary) .isEqualTo(mContext.getString(R.string.audio_streams_dialog_no_le_device_title)); diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryControllerTest.java index e4b6903e800..0e003097a3f 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryControllerTest.java @@ -23,11 +23,13 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothStatusCodes; import android.content.Context; import android.os.Looper; @@ -42,6 +44,7 @@ import com.android.settings.bluetooth.Utils; import com.android.settings.connecteddevice.audiosharing.audiostreams.testshadows.ShadowAudioStreamsHelper; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settings.testutils.shadow.ShadowBluetoothUtils; +import com.android.settingslib.bluetooth.BluetoothCallback; import com.android.settingslib.bluetooth.BluetoothEventManager; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast; @@ -57,6 +60,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -116,7 +120,7 @@ public class AudioStreamsCategoryControllerTest { when(mBroadcast.isProfileReady()).thenReturn(true); when(mAssistant.isProfileReady()).thenReturn(true); when(mVolumeControl.isProfileReady()).thenReturn(true); - mController = new AudioStreamsCategoryController(mContext, KEY); + mController = spy(new AudioStreamsCategoryController(mContext, KEY)); mPreference = new Preference(mContext); when(mScreen.findPreference(KEY)).thenReturn(mPreference); mController.displayPreference(mScreen); @@ -228,4 +232,21 @@ public class AudioStreamsCategoryControllerTest { shadowOf(Looper.getMainLooper()).idle(); assertThat(mPreference.isVisible()).isTrue(); } + + @Test + public void onProfileConnectionStateChanged_updateVisibility() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING); + ArgumentCaptor argumentCaptor = + ArgumentCaptor.forClass(BluetoothCallback.class); + mController.onStart(mLifecycleOwner); + verify(mBluetoothEventManager).registerCallback(argumentCaptor.capture()); + + BluetoothCallback callback = argumentCaptor.getValue(); + callback.onProfileConnectionStateChanged( + mCachedBluetoothDevice, + BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT, + BluetoothAdapter.STATE_DISCONNECTED); + + verify(mController).updateVisibility(); + } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDialogFragmentTest.java new file mode 100644 index 00000000000..e83dade16e9 --- /dev/null +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDialogFragmentTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.connecteddevice.audiosharing.audiostreams; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.app.settings.SettingsEnums; +import android.content.Context; +import android.os.Bundle; + +import androidx.test.core.app.ApplicationProvider; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowAlertDialog; +import org.robolectric.shadows.ShadowLooper; +import org.robolectric.shadows.androidx.fragment.FragmentController; + +@RunWith(RobolectricTestRunner.class) +@Config( + shadows = { + ShadowAlertDialog.class, + }) +public class AudioStreamsDialogFragmentTest { + private final Context mContext = ApplicationProvider.getApplicationContext(); + private AudioStreamsDialogFragment.DialogBuilder mDialogBuilder; + private AudioStreamsDialogFragment mFragment; + + @Before + public void setUp() { + mDialogBuilder = spy(new AudioStreamsDialogFragment.DialogBuilder(mContext)); + mFragment = new AudioStreamsDialogFragment(mDialogBuilder, SettingsEnums.PAGE_UNKNOWN); + } + + @After + public void tearDown() { + ShadowAlertDialog.reset(); + } + + @Test + public void testGetMetricsCategory() { + int dialogId = mFragment.getMetricsCategory(); + + assertThat(dialogId).isEqualTo(SettingsEnums.PAGE_UNKNOWN); + } + + @Test + public void testOnCreateDialog() { + mFragment.onCreateDialog(Bundle.EMPTY); + + verify(mDialogBuilder).build(); + } + + @Test + public void testShowDialog() { + FragmentController.setupFragment(mFragment); + AudioStreamsDialogFragment.show(mFragment, mDialogBuilder, SettingsEnums.PAGE_UNKNOWN); + ShadowLooper.idleMainLooper(); + + var dialog = ShadowAlertDialog.getLatestAlertDialog(); + assertThat(dialog).isNotNull(); + assertThat(dialog.isShowing()).isTrue(); + } +} diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallbackTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallbackTest.java new file mode 100644 index 00000000000..164c2f093e8 --- /dev/null +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallbackTest.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.connecteddevice.audiosharing.audiostreams; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothLeBroadcastMetadata; +import android.bluetooth.BluetoothLeBroadcastReceiveState; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.RobolectricTestRunner; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(RobolectricTestRunner.class) +public class AudioStreamsProgressCategoryCallbackTest { + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Mock private AudioStreamsProgressCategoryController mController; + @Mock private BluetoothDevice mDevice; + @Mock private BluetoothLeBroadcastReceiveState mState; + @Mock private BluetoothLeBroadcastMetadata mMetadata; + private AudioStreamsProgressCategoryCallback mCallback; + + @Before + public void setUp() { + mCallback = new AudioStreamsProgressCategoryCallback(mController); + } + + @Test + public void testOnReceiveStateChanged_connected() { + List bisSyncState = new ArrayList<>(); + bisSyncState.add(1L); + when(mState.getBisSyncState()).thenReturn(bisSyncState); + mCallback.onReceiveStateChanged(mDevice, /* sourceId= */ 0, mState); + + verify(mController).handleSourceConnected(any()); + } + + @Test + public void testOnReceiveStateChanged_badCode() { + when(mState.getPaSyncState()) + .thenReturn(BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED); + when(mState.getBigEncryptionState()) + .thenReturn(BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_BAD_CODE); + mCallback.onReceiveStateChanged(mDevice, /* sourceId= */ 0, mState); + + verify(mController).handleSourceConnectBadCode(any()); + } + + @Test + public void testOnSearchStartFailed() { + mCallback.onSearchStartFailed(/* reason= */ 0); + + verify(mController).showToast(anyString()); + verify(mController).setScanning(anyBoolean()); + } + + @Test + public void testOnSearchStarted() { + mCallback.onSearchStarted(/* reason= */ 0); + + verify(mController).setScanning(anyBoolean()); + } + + @Test + public void testOnSearchStopFailed() { + mCallback.onSearchStopFailed(/* reason= */ 0); + + verify(mController).showToast(anyString()); + } + + @Test + public void testOnSearchStopped() { + mCallback.onSearchStopped(/* reason= */ 0); + + verify(mController).setScanning(anyBoolean()); + } + + @Test + public void testOnSourceAddFailed() { + when(mMetadata.getBroadcastId()).thenReturn(1); + mCallback.onSourceAddFailed(mDevice, mMetadata, /* reason= */ 0); + + verify(mController).handleSourceFailedToConnect(1); + } + + @Test + public void testOnSourceFound() { + mCallback.onSourceFound(mMetadata); + + verify(mController).handleSourceFound(mMetadata); + } + + @Test + public void testOnSourceLost() { + mCallback.onSourceLost(/* broadcastId= */ 1); + + verify(mController).handleSourceLost(1); + } + + @Test + public void testOnSourceRemoveFailed() { + mCallback.onSourceRemoveFailed(mDevice, /* sourceId= */ 0, /* reason= */ 0); + + verify(mController).showToast(anyString()); + } + + @Test + public void testOnSourceRemoved() { + mCallback.onSourceRemoved(mDevice, /* sourceId= */ 0, /* reason= */ 0); + + verify(mController).handleSourceRemoved(); + } +} diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryControllerTest.java new file mode 100644 index 00000000000..d43ec81ddaf --- /dev/null +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryControllerTest.java @@ -0,0 +1,671 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.connecteddevice.audiosharing.audiostreams; + +import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.AudioStreamState.ADD_SOURCE_BAD_CODE; +import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.AudioStreamState.ADD_SOURCE_FAILED; +import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.AudioStreamState.ADD_SOURCE_WAIT_FOR_RESPONSE; +import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.AudioStreamState.SOURCE_ADDED; +import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.AudioStreamState.SYNCED; +import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.AudioStreamState.WAIT_FOR_SYNC; +import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.UNSET_BROADCAST_ID; +import static com.android.settings.core.BasePreferenceController.AVAILABLE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.robolectric.Shadows.shadowOf; + +import static java.util.Collections.emptyList; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothLeAudioContentMetadata; +import android.bluetooth.BluetoothLeBroadcastMetadata; +import android.bluetooth.BluetoothLeBroadcastReceiveState; +import android.bluetooth.BluetoothProfile; +import android.content.Context; +import android.os.Looper; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; + +import androidx.fragment.app.Fragment; +import androidx.lifecycle.LifecycleOwner; +import androidx.preference.PreferenceScreen; +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.R; +import com.android.settings.connecteddevice.audiosharing.audiostreams.testshadows.ShadowAudioStreamsHelper; +import com.android.settings.testutils.shadow.ShadowBluetoothUtils; +import com.android.settings.testutils.shadow.ShadowThreadUtils; +import com.android.settingslib.bluetooth.BluetoothEventManager; +import com.android.settingslib.bluetooth.BluetoothLeBroadcastMetadataExt; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.core.lifecycle.Lifecycle; + +import com.google.common.collect.ImmutableList; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowAlertDialog; +import org.robolectric.shadows.androidx.fragment.FragmentController; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(RobolectricTestRunner.class) +@Config( + shadows = { + ShadowBluetoothUtils.class, + ShadowAudioStreamsHelper.class, + ShadowThreadUtils.class, + ShadowAlertDialog.class, + }) +public class AudioStreamsProgressCategoryControllerTest { + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + private static final String VALID_METADATA = + "BLUETOOTH:UUID:184F;BN:VGVzdA==;AT:1;AD:00A1A1A1A1A1;BI:1E240;BC:VGVzdENvZGU=;" + + "MD:BgNwVGVzdA==;AS:1;PI:A0;NS:1;BS:3;NB:2;SM:BQNUZXN0BARlbmc=;;"; + private static final String KEY = "audio_streams_nearby_category"; + private static final int QR_CODE_BROADCAST_ID = 1; + private static final int ALREADY_CONNECTED_BROADCAST_ID = 2; + private static final int NEWLY_FOUND_BROADCAST_ID = 3; + private static final String BROADCAST_NAME_1 = "name_1"; + private static final String BROADCAST_NAME_2 = "name_2"; + private static final byte[] BROADCAST_CODE = new byte[] {1}; + private final Context mContext = ApplicationProvider.getApplicationContext(); + @Mock private LocalBluetoothManager mLocalBtManager; + @Mock private BluetoothEventManager mBluetoothEventManager; + @Mock private PreferenceScreen mScreen; + @Mock private AudioStreamsHelper mAudioStreamsHelper; + @Mock private LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant; + @Mock private BluetoothLeBroadcastMetadata mMetadata; + @Mock private CachedBluetoothDevice mDevice; + @Mock private AudioStreamsProgressCategoryPreference mPreference; + private Lifecycle mLifecycle; + private LifecycleOwner mLifecycleOwner; + private Fragment mFragment; + private TestController mController; + + @Before + public void setUp() { + ShadowAudioStreamsHelper.setUseMock(mAudioStreamsHelper); + when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(mLeBroadcastAssistant); + when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(emptyList()); + + ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager; + when(mLocalBtManager.getEventManager()).thenReturn(mBluetoothEventManager); + when(mLeBroadcastAssistant.isSearchInProgress()).thenReturn(false); + + when(mScreen.findPreference(anyString())).thenReturn(mPreference); + + mLifecycleOwner = () -> mLifecycle; + mLifecycle = new Lifecycle(mLifecycleOwner); + + mFragment = new Fragment(); + mController = spy(new TestController(mContext, KEY)); + } + + @After + public void tearDown() { + ShadowBluetoothUtils.reset(); + ShadowAudioStreamsHelper.reset(); + } + + @Test + public void testGetAvailabilityStatus() { + int status = mController.getAvailabilityStatus(); + + assertThat(status).isEqualTo(AVAILABLE); + } + + @Test + public void testDisplayPreference() { + mController.displayPreference(mScreen); + + verify(mPreference).setVisible(true); + } + + @Test + public void testSetScanning() { + mController.displayPreference(mScreen); + mController.setScanning(true); + + verify(mPreference).setProgress(true); + } + + @Test + public void testOnStart_initNoDevice_showDialog() { + when(mLeBroadcastAssistant.isSearchInProgress()).thenReturn(true); + + FragmentController.setupFragment(mFragment); + mController.setFragment(mFragment); + mController.displayPreference(mScreen); + mController.onStart(mLifecycleOwner); + shadowOf(Looper.getMainLooper()).idle(); + + // Called twice, once in displayPreference, the other in init() + verify(mPreference, times(2)).setVisible(anyBoolean()); + verify(mPreference).removeAudioStreamPreferences(); + verify(mLeBroadcastAssistant).stopSearchingForSources(); + verify(mLeBroadcastAssistant).unregisterServiceCallBack(any()); + + var dialog = ShadowAlertDialog.getLatestAlertDialog(); + assertThat(dialog).isNotNull(); + assertThat(dialog.isShowing()).isTrue(); + + TextView title = dialog.findViewById(R.id.dialog_title); + assertThat(title).isNotNull(); + assertThat(title.getText()) + .isEqualTo(mContext.getString(R.string.audio_streams_dialog_no_le_device_title)); + TextView subtitle1 = dialog.findViewById(R.id.dialog_subtitle); + assertThat(subtitle1).isNotNull(); + assertThat(subtitle1.getVisibility()).isEqualTo(View.GONE); + TextView subtitle2 = dialog.findViewById(R.id.dialog_subtitle_2); + assertThat(subtitle2).isNotNull(); + assertThat(subtitle2.getText()) + .isEqualTo(mContext.getString(R.string.audio_streams_dialog_no_le_device_subtitle)); + View leftButton = dialog.findViewById(R.id.left_button); + assertThat(leftButton).isNotNull(); + assertThat(leftButton.getVisibility()).isEqualTo(View.VISIBLE); + Button rightButton = dialog.findViewById(R.id.right_button); + assertThat(rightButton).isNotNull(); + assertThat(rightButton.getText()) + .isEqualTo(mContext.getString(R.string.audio_streams_dialog_no_le_device_button)); + assertThat(rightButton.hasOnClickListeners()).isTrue(); + + dialog.cancel(); + } + + @Test + public void testBluetoothOff_triggerRunnable() { + mController.mBluetoothCallback.onBluetoothStateChanged(BluetoothAdapter.STATE_OFF); + + verify(mController.mExecutor).execute(any()); + } + + @Test + public void testDeviceConnectionStateChanged_triggerRunnable() { + mController.mBluetoothCallback.onProfileConnectionStateChanged( + mDevice, + BluetoothAdapter.STATE_DISCONNECTED, + BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); + + verify(mController.mExecutor).execute(any()); + } + + @Test + public void testOnStart_initHasDevice_noPreference() { + // Setup a device + ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(mDevice); + + mController.onStart(mLifecycleOwner); + shadowOf(Looper.getMainLooper()).idle(); + + verify(mLeBroadcastAssistant).registerServiceCallBack(any(), any()); + verify(mLeBroadcastAssistant).startSearchingForSources(any()); + + var dialog = ShadowAlertDialog.getLatestAlertDialog(); + assertThat(dialog).isNull(); + + verify(mController, never()).moveToState(any(), any()); + } + + @Test + public void testOnStart_handleSourceFromQrCode() { + // Setup a device + ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(mDevice); + + // Setup a source from qr code + mController.setSourceFromQrCode(mMetadata, SourceOriginForLogging.UNKNOWN); + when(mMetadata.getBroadcastId()).thenReturn(QR_CODE_BROADCAST_ID); + + // Handle the source from qr code in onStart + mController.displayPreference(mScreen); + mController.onStart(mLifecycleOwner); + shadowOf(Looper.getMainLooper()).idle(); + + // Verify the connected source is created and moved to WAIT_FOR_SYNC + ArgumentCaptor preference = + ArgumentCaptor.forClass(AudioStreamPreference.class); + ArgumentCaptor state = + ArgumentCaptor.forClass( + AudioStreamsProgressCategoryController.AudioStreamState.class); + + verify(mController).moveToState(preference.capture(), state.capture()); + assertThat(preference.getValue()).isNotNull(); + assertThat(preference.getValue().getAudioStreamBroadcastId()) + .isEqualTo(QR_CODE_BROADCAST_ID); + assertThat(state.getValue()).isEqualTo(WAIT_FOR_SYNC); + } + + @Test + public void testOnStart_handleSourceAlreadyConnected() { + // Setup a device + ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(mDevice); + + // Setup a connected source + BluetoothLeBroadcastReceiveState connected = + createConnectedMock(ALREADY_CONNECTED_BROADCAST_ID); + List list = new ArrayList<>(); + list.add(connected); + when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(list); + + // Handle already connected source in onStart + mController.displayPreference(mScreen); + mController.onStart(mLifecycleOwner); + shadowOf(Looper.getMainLooper()).idle(); + + ArgumentCaptor preference = + ArgumentCaptor.forClass(AudioStreamPreference.class); + ArgumentCaptor state = + ArgumentCaptor.forClass( + AudioStreamsProgressCategoryController.AudioStreamState.class); + + // Verify the connected source is created and moved to SOURCE_ADDED + verify(mController).moveToState(preference.capture(), state.capture()); + assertThat(preference.getValue()).isNotNull(); + assertThat(preference.getValue().getAudioStreamBroadcastId()) + .isEqualTo(ALREADY_CONNECTED_BROADCAST_ID); + assertThat(state.getValue()).isEqualTo(SOURCE_ADDED); + } + + @Test + public void testOnStart_sourceFromQrCodeNoId_sourceAlreadyConnected_sameName_updateId() { + // Setup a device + ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(mDevice); + + // Setup source from qr code with unset id and BROADCAST_NAME_1. Creating a real metadata + // for properly update its id. + var metadata = + BluetoothLeBroadcastMetadataExt.INSTANCE.convertToBroadcastMetadata(VALID_METADATA); + assertThat(metadata).isNotNull(); + var metadataWithNoIdAndSameName = + new BluetoothLeBroadcastMetadata.Builder(metadata) + .setBroadcastId(UNSET_BROADCAST_ID) + .setBroadcastName(BROADCAST_NAME_1) + .build(); + mController.setSourceFromQrCode( + metadataWithNoIdAndSameName, SourceOriginForLogging.UNKNOWN); + + // Setup a connected source with name BROADCAST_NAME_1 and id + BluetoothLeBroadcastReceiveState connected = + createConnectedMock(ALREADY_CONNECTED_BROADCAST_ID); + var data = mock(BluetoothLeAudioContentMetadata.class); + when(connected.getSubgroupMetadata()).thenReturn(ImmutableList.of(data)); + when(data.getProgramInfo()).thenReturn(BROADCAST_NAME_1); + when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(ImmutableList.of(connected)); + + // Handle both source from qr code and already connected source in onStart + mController.displayPreference(mScreen); + mController.onStart(mLifecycleOwner); + shadowOf(Looper.getMainLooper()).idle(); + + // Verify two preferences created, one moved to state WAIT_FOR_SYNC, one to SOURCE_ADDED. + // Both has ALREADY_CONNECTED_BROADCAST_ID as the UNSET_ID is updated to match. + ArgumentCaptor preference = + ArgumentCaptor.forClass(AudioStreamPreference.class); + ArgumentCaptor state = + ArgumentCaptor.forClass( + AudioStreamsProgressCategoryController.AudioStreamState.class); + verify(mController, times(2)).moveToState(preference.capture(), state.capture()); + + List preferences = preference.getAllValues(); + assertThat(preferences.size()).isEqualTo(2); + List states = state.getAllValues(); + assertThat(states.size()).isEqualTo(2); + + // The preference contains source from qr code + assertThat(preferences.get(0).getAudioStreamBroadcastId()) + .isEqualTo(ALREADY_CONNECTED_BROADCAST_ID); + assertThat(states.get(0)).isEqualTo(WAIT_FOR_SYNC); + + // The preference contains already connected source + assertThat(preferences.get(1).getAudioStreamBroadcastId()) + .isEqualTo(ALREADY_CONNECTED_BROADCAST_ID); + assertThat(states.get(1)).isEqualTo(SOURCE_ADDED); + } + + @Test + public void testHandleSourceFound_addNew() { + // Setup a device + ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(mDevice); + + when(mMetadata.getBroadcastId()).thenReturn(NEWLY_FOUND_BROADCAST_ID); + // A new source is found + mController.handleSourceFound(mMetadata); + + // Verify a preference is created with state SYNCED. + ArgumentCaptor preference = + ArgumentCaptor.forClass(AudioStreamPreference.class); + ArgumentCaptor state = + ArgumentCaptor.forClass( + AudioStreamsProgressCategoryController.AudioStreamState.class); + + verify(mController).moveToState(preference.capture(), state.capture()); + assertThat(preference.getValue()).isNotNull(); + assertThat(preference.getValue().getAudioStreamBroadcastId()) + .isEqualTo(NEWLY_FOUND_BROADCAST_ID); + assertThat(state.getValue()).isEqualTo(SYNCED); + } + + @Test + public void testHandleSourceFound_sameIdWithSourceFromQrCode_updateMetadataAndState() { + // Setup a device + ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(mDevice); + + // Setup source from qr code with QR_CODE_BROADCAST_ID, BROADCAST_NAME_1 and BROADCAST_CODE. + var metadata = + BluetoothLeBroadcastMetadataExt.INSTANCE.convertToBroadcastMetadata(VALID_METADATA); + assertThat(metadata).isNotNull(); + var metadataFromQrCode = + new BluetoothLeBroadcastMetadata.Builder(metadata) + .setBroadcastId(QR_CODE_BROADCAST_ID) + .setBroadcastName(BROADCAST_NAME_1) + .setBroadcastCode(BROADCAST_CODE) + .build(); + mController.setSourceFromQrCode(metadataFromQrCode, SourceOriginForLogging.UNKNOWN); + + // Handle the source from qr code in onStart + mController.displayPreference(mScreen); + mController.onStart(mLifecycleOwner); + shadowOf(Looper.getMainLooper()).idle(); + + // A new source is found + mController.handleSourceFound( + new BluetoothLeBroadcastMetadata.Builder(metadata) + .setBroadcastId(QR_CODE_BROADCAST_ID) + .setBroadcastName(BROADCAST_NAME_2) + .build()); + shadowOf(Looper.getMainLooper()).idle(); + + ArgumentCaptor preference = + ArgumentCaptor.forClass(AudioStreamPreference.class); + ArgumentCaptor state = + ArgumentCaptor.forClass( + AudioStreamsProgressCategoryController.AudioStreamState.class); + + verify(mController, times(2)).moveToState(preference.capture(), state.capture()); + List preferences = preference.getAllValues(); + List states = state.getAllValues(); + + // Verify the qr code source is created with WAIT_FOR_SYNC, broadcast name got updated to + // BROADCAST_NAME_2 + var sourceFromQrCode = preferences.get(0); + assertThat(sourceFromQrCode.getAudioStreamBroadcastId()).isEqualTo(QR_CODE_BROADCAST_ID); + assertThat(sourceFromQrCode.getAudioStreamMetadata()).isNotNull(); + assertThat(sourceFromQrCode.getAudioStreamMetadata().getBroadcastName()) + .isEqualTo(BROADCAST_NAME_2); + assertThat(sourceFromQrCode.getAudioStreamMetadata().getBroadcastCode()) + .isEqualTo(BROADCAST_CODE); + assertThat(states.get(0)).isEqualTo(WAIT_FOR_SYNC); + + // Verify the newly found source is created, broadcast code is retrieved from the source + // from qr code, and state updated to ADD_SOURCE_WAIT_FOR_RESPONSE + var newlyFoundSource = preferences.get(1); + assertThat(newlyFoundSource.getAudioStreamBroadcastId()).isEqualTo(QR_CODE_BROADCAST_ID); + assertThat(newlyFoundSource.getAudioStreamMetadata()).isNotNull(); + assertThat(newlyFoundSource.getAudioStreamMetadata().getBroadcastName()) + .isEqualTo(BROADCAST_NAME_2); + assertThat(newlyFoundSource.getAudioStreamMetadata().getBroadcastCode()) + .isEqualTo(BROADCAST_CODE); + assertThat(states.get(1)).isEqualTo(ADD_SOURCE_WAIT_FOR_RESPONSE); + } + + @Test + public void testHandleSourceFound_sameIdWithOtherState_doNothing() { + // Setup a device + ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(mDevice); + + // Setup source already connected + BluetoothLeBroadcastReceiveState connected = + createConnectedMock(ALREADY_CONNECTED_BROADCAST_ID); + when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(ImmutableList.of(connected)); + + // Handle source already connected in onStart + mController.displayPreference(mScreen); + mController.onStart(mLifecycleOwner); + shadowOf(Looper.getMainLooper()).idle(); + + // A new source found + when(mMetadata.getBroadcastId()).thenReturn(ALREADY_CONNECTED_BROADCAST_ID); + mController.handleSourceFound(mMetadata); + shadowOf(Looper.getMainLooper()).idle(); + + // Verify only the connected source has created a preference, and its state remains as + // SOURCE_ADDED + ArgumentCaptor preference = + ArgumentCaptor.forClass(AudioStreamPreference.class); + ArgumentCaptor state = + ArgumentCaptor.forClass( + AudioStreamsProgressCategoryController.AudioStreamState.class); + + verify(mController).moveToState(preference.capture(), state.capture()); + assertThat(preference.getValue()).isNotNull(); + assertThat(preference.getValue().getAudioStreamBroadcastId()) + .isEqualTo(ALREADY_CONNECTED_BROADCAST_ID); + assertThat(preference.getValue().getAudioStreamState()).isEqualTo(SOURCE_ADDED); + } + + @Test + public void testHandleSourceLost_removed() { + // Setup a device + ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(mDevice); + + // Setup mPreference so it's not null + mController.displayPreference(mScreen); + + // A new source found + when(mMetadata.getBroadcastId()).thenReturn(NEWLY_FOUND_BROADCAST_ID); + mController.handleSourceFound(mMetadata); + shadowOf(Looper.getMainLooper()).idle(); + + // A new source found is lost + mController.handleSourceLost(NEWLY_FOUND_BROADCAST_ID); + shadowOf(Looper.getMainLooper()).idle(); + + ArgumentCaptor preferenceToAdd = + ArgumentCaptor.forClass(AudioStreamPreference.class); + ArgumentCaptor preferenceToRemove = + ArgumentCaptor.forClass(AudioStreamPreference.class); + ArgumentCaptor state = + ArgumentCaptor.forClass( + AudioStreamsProgressCategoryController.AudioStreamState.class); + + // Verify a new preference is created with state SYNCED. + verify(mController).moveToState(preferenceToAdd.capture(), state.capture()); + assertThat(preferenceToAdd.getValue()).isNotNull(); + assertThat(preferenceToAdd.getValue().getAudioStreamBroadcastId()) + .isEqualTo(NEWLY_FOUND_BROADCAST_ID); + assertThat(state.getValue()).isEqualTo(SYNCED); + + // Verify the preference with NEWLY_FOUND_BROADCAST_ID is removed. + verify(mPreference).removePreference(preferenceToRemove.capture()); + assertThat(preferenceToRemove.getValue().getAudioStreamBroadcastId()) + .isEqualTo(NEWLY_FOUND_BROADCAST_ID); + } + + @Test + public void testHandleSourceRemoved_removed() { + // Setup a device + ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(mDevice); + + // Setup already connected source + BluetoothLeBroadcastReceiveState connected = + createConnectedMock(ALREADY_CONNECTED_BROADCAST_ID); + when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(ImmutableList.of(connected)); + + // Handle connected source in onStart + mController.displayPreference(mScreen); + mController.onStart(mLifecycleOwner); + shadowOf(Looper.getMainLooper()).idle(); + + // The connect source is no longer connected + when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(emptyList()); + mController.handleSourceRemoved(); + shadowOf(Looper.getMainLooper()).idle(); + + ArgumentCaptor preferenceToAdd = + ArgumentCaptor.forClass(AudioStreamPreference.class); + ArgumentCaptor preferenceToRemove = + ArgumentCaptor.forClass(AudioStreamPreference.class); + ArgumentCaptor state = + ArgumentCaptor.forClass( + AudioStreamsProgressCategoryController.AudioStreamState.class); + + // Verify a new preference is created with state SOURCE_ADDED. + verify(mController).moveToState(preferenceToAdd.capture(), state.capture()); + assertThat(preferenceToAdd.getValue()).isNotNull(); + assertThat(preferenceToAdd.getValue().getAudioStreamBroadcastId()) + .isEqualTo(ALREADY_CONNECTED_BROADCAST_ID); + assertThat(state.getValue()).isEqualTo(SOURCE_ADDED); + + // Verify the preference with ALREADY_CONNECTED_BROADCAST_ID is removed. + verify(mPreference).removePreference(preferenceToRemove.capture()); + assertThat(preferenceToRemove.getValue().getAudioStreamBroadcastId()) + .isEqualTo(ALREADY_CONNECTED_BROADCAST_ID); + } + + @Test + public void testHandleSourceRemoved_updateState() { + // Setup a device + ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(mDevice); + + // Setup a connected source + BluetoothLeBroadcastReceiveState connected = + createConnectedMock(ALREADY_CONNECTED_BROADCAST_ID); + when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(ImmutableList.of(connected)); + + // Handle connected source in onStart + mController.displayPreference(mScreen); + mController.onStart(mLifecycleOwner); + shadowOf(Looper.getMainLooper()).idle(); + + // The connected source is identified as having a bad code + BluetoothLeBroadcastReceiveState badCode = mock(BluetoothLeBroadcastReceiveState.class); + when(badCode.getBroadcastId()).thenReturn(ALREADY_CONNECTED_BROADCAST_ID); + when(badCode.getPaSyncState()) + .thenReturn(BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED); + when(badCode.getBigEncryptionState()) + .thenReturn(BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_BAD_CODE); + mController.handleSourceConnectBadCode(badCode); + shadowOf(Looper.getMainLooper()).idle(); + + ArgumentCaptor preference = + ArgumentCaptor.forClass(AudioStreamPreference.class); + ArgumentCaptor state = + ArgumentCaptor.forClass( + AudioStreamsProgressCategoryController.AudioStreamState.class); + + verify(mController, times(2)).moveToState(preference.capture(), state.capture()); + List preferences = preference.getAllValues(); + assertThat(preferences.size()).isEqualTo(2); + List states = state.getAllValues(); + assertThat(states.size()).isEqualTo(2); + + // Verify the connected source is created state SOURCE_ADDED + assertThat(preferences.get(0).getAudioStreamBroadcastId()) + .isEqualTo(ALREADY_CONNECTED_BROADCAST_ID); + assertThat(states.get(0)).isEqualTo(SOURCE_ADDED); + + // Verify the connected source is updated to state ADD_SOURCE_BAD_CODE + assertThat(preferences.get(1).getAudioStreamBroadcastId()) + .isEqualTo(ALREADY_CONNECTED_BROADCAST_ID); + assertThat(states.get(1)).isEqualTo(ADD_SOURCE_BAD_CODE); + } + + @Test + public void testHandleSourceFailedToConnect_updateState() { + // Setup a device + ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(mDevice); + + // Setup mPreference so it's not null + mController.displayPreference(mScreen); + + // A new source found + when(mMetadata.getBroadcastId()).thenReturn(NEWLY_FOUND_BROADCAST_ID); + mController.handleSourceFound(mMetadata); + shadowOf(Looper.getMainLooper()).idle(); + + // The new found source is identified as failed to connect + mController.handleSourceFailedToConnect(NEWLY_FOUND_BROADCAST_ID); + shadowOf(Looper.getMainLooper()).idle(); + + ArgumentCaptor preference = + ArgumentCaptor.forClass(AudioStreamPreference.class); + ArgumentCaptor state = + ArgumentCaptor.forClass( + AudioStreamsProgressCategoryController.AudioStreamState.class); + + verify(mController, times(2)).moveToState(preference.capture(), state.capture()); + List preferences = preference.getAllValues(); + assertThat(preferences.size()).isEqualTo(2); + List states = state.getAllValues(); + assertThat(states.size()).isEqualTo(2); + + // Verify one preference is created with SYNCED + assertThat(preferences.get(0).getAudioStreamBroadcastId()) + .isEqualTo(NEWLY_FOUND_BROADCAST_ID); + assertThat(states.get(0)).isEqualTo(SYNCED); + + // Verify the preference is updated to state ADD_SOURCE_FAILED + assertThat(preferences.get(1).getAudioStreamBroadcastId()) + .isEqualTo(NEWLY_FOUND_BROADCAST_ID); + assertThat(states.get(1)).isEqualTo(ADD_SOURCE_FAILED); + } + + private static BluetoothLeBroadcastReceiveState createConnectedMock(int id) { + var connected = mock(BluetoothLeBroadcastReceiveState.class); + List bisSyncState = new ArrayList<>(); + bisSyncState.add(1L); + when(connected.getBroadcastId()).thenReturn(id); + when(connected.getBisSyncState()).thenReturn(bisSyncState); + return connected; + } + + static class TestController extends AudioStreamsProgressCategoryController { + TestController(Context context, String preferenceKey) { + super(context, preferenceKey); + mExecutor = spy(mContext.getMainExecutor()); + } + + @Override + void moveToState(AudioStreamPreference preference, AudioStreamState state) { + preference.setAudioStreamState(state); + // Do nothing else to avoid side effect from AudioStreamStateHandler#performAction + } + } +} diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsScanQrCodeControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsScanQrCodeControllerTest.java index 4990f26ac22..a83cbf03379 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsScanQrCodeControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsScanQrCodeControllerTest.java @@ -16,29 +16,38 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams; +import static android.app.settings.SettingsEnums.AUDIO_STREAM_MAIN; + +import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsScanQrCodeController.REQUEST_SCAN_BT_BROADCAST_QR_CODE; import static com.android.settings.core.BasePreferenceController.AVAILABLE; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothProfile; import android.content.Context; +import android.content.Intent; import androidx.lifecycle.LifecycleOwner; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import androidx.test.core.app.ApplicationProvider; +import com.android.settings.R; +import com.android.settings.SettingsActivity; import com.android.settings.connecteddevice.audiosharing.audiostreams.testshadows.ShadowAudioStreamsHelper; import com.android.settings.testutils.shadow.ShadowBluetoothUtils; import com.android.settingslib.bluetooth.BluetoothEventManager; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.lifecycle.Lifecycle; import org.junit.After; @@ -46,6 +55,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -139,17 +149,46 @@ public class AudioStreamsScanQrCodeControllerTest { public void onPreferenceClick_hasFragment_launchSubSetting() { mController.displayPreference(mScreen); mController.setFragment(mFragment); + when(mFragment.getMetricsCategory()).thenReturn(AUDIO_STREAM_MAIN); var listener = mPreference.getOnPreferenceClickListener(); assertThat(listener).isNotNull(); + + // mContext is not an Activity context, calling startActivity() from outside of an Activity + // context requires the FLAG_ACTIVITY_NEW_TASK flag, create a mock to avoid this + // AndroidRuntimeException. + Context activityContext = mock(Context.class); + when(mPreference.getContext()).thenReturn(activityContext); + when(mPreference.getKey()).thenReturn(AudioStreamsScanQrCodeController.KEY); + var clicked = listener.onPreferenceClick(mPreference); + + ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); + ArgumentCaptor requestCodeCaptor = ArgumentCaptor.forClass(Integer.class); + verify(mFragment) + .startActivityForResult(intentCaptor.capture(), requestCodeCaptor.capture()); + + Intent intent = intentCaptor.getValue(); + assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)) + .isEqualTo(AudioStreamsQrCodeScanFragment.class.getName()); + assertThat(intent.getIntExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, 0)) + .isEqualTo(R.string.audio_streams_main_page_scan_qr_code_title); + assertThat(intent.getIntExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, 0)) + .isEqualTo(AUDIO_STREAM_MAIN); + + int requestCode = requestCodeCaptor.getValue(); + assertThat(requestCode).isEqualTo(REQUEST_SCAN_BT_BROADCAST_QR_CODE); + assertThat(clicked).isTrue(); } @Test public void updateVisibility_noConnected_invisible() { mController.displayPreference(mScreen); - mController.mBluetoothCallback.onActiveDeviceChanged(mDevice, BluetoothProfile.LE_AUDIO); + mController.mBluetoothCallback.onProfileConnectionStateChanged( + mDevice, + BluetoothAdapter.STATE_DISCONNECTED, + BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); assertThat(mPreference.isVisible()).isFalse(); } @@ -158,7 +197,10 @@ public class AudioStreamsScanQrCodeControllerTest { public void updateVisibility_hasConnected_visible() { mController.displayPreference(mScreen); ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(mDevice); - mController.mBluetoothCallback.onActiveDeviceChanged(mDevice, BluetoothProfile.LE_AUDIO); + mController.mBluetoothCallback.onProfileConnectionStateChanged( + mDevice, + BluetoothAdapter.STATE_CONNECTED, + BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); assertThat(mPreference.isVisible()).isTrue(); } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/SourceAddedStateTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/SourceAddedStateTest.java index 0f0bafe217a..082735a31fc 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/SourceAddedStateTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/SourceAddedStateTest.java @@ -16,27 +16,71 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams; +import static android.app.settings.SettingsEnums.AUDIO_STREAM_MAIN; + import static com.android.settings.connecteddevice.audiosharing.audiostreams.SourceAddedState.AUDIO_STREAM_SOURCE_ADDED_STATE_SUMMARY; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.settings.SettingsEnums; +import android.bluetooth.BluetoothLeBroadcastMetadata; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; + +import androidx.fragment.app.FragmentActivity; +import androidx.preference.Preference; +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.R; +import com.android.settings.SettingsActivity; +import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.testutils.shadow.ShadowFragment; +import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; @RunWith(RobolectricTestRunner.class) +@Config( + shadows = { + ShadowFragment.class, + }) public class SourceAddedStateTest { - @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + private static final int BROADCAST_ID = 1; + private static final String BROADCAST_TITLE = "title"; + private final Context mContext = ApplicationProvider.getApplicationContext(); + @Mock private AudioStreamPreference mPreference; + @Mock private AudioStreamsProgressCategoryController mController; + @Mock private AudioStreamsHelper mHelper; + @Mock private AudioStreamsRepository mRepository; + @Mock private AudioStreamsDashboardFragment mFragment; + @Mock private FragmentActivity mActivity; + private FakeFeatureFactory mFeatureFactory; private SourceAddedState mInstance; @Before public void setUp() { - mInstance = SourceAddedState.getInstance(); + when(mFragment.getActivity()).thenReturn(mActivity); + mFeatureFactory = FakeFeatureFactory.setupForTest(); + mInstance = new SourceAddedState(); + when(mPreference.getAudioStreamBroadcastId()).thenReturn(BROADCAST_ID); + when(mPreference.getTitle()).thenReturn(BROADCAST_TITLE); } @Test @@ -58,4 +102,59 @@ public class SourceAddedStateTest { assertThat(stateEnum) .isEqualTo(AudioStreamsProgressCategoryController.AudioStreamState.SOURCE_ADDED); } + + @Test + public void testPerformAction() { + mInstance.setAudioStreamsRepositoryForTesting(mRepository); + BluetoothLeBroadcastMetadata mockMetadata = mock(BluetoothLeBroadcastMetadata.class); + when(mRepository.getCachedMetadata(anyInt())).thenReturn(mockMetadata); + when(mPreference.getContext()).thenReturn(mContext); + when(mPreference.getSourceOriginForLogging()) + .thenReturn(SourceOriginForLogging.QR_CODE_SCAN_SETTINGS); + + mInstance.performAction(mPreference, mController, mHelper); + + verify(mRepository).saveMetadata(eq(mContext), eq(mockMetadata)); + verify(mFeatureFactory.metricsFeatureProvider) + .action( + eq(mContext), + eq(SettingsEnums.ACTION_AUDIO_STREAM_JOIN_SUCCEED), + eq(SourceOriginForLogging.QR_CODE_SCAN_SETTINGS.ordinal())); + verify(mHelper).startMediaService(eq(mContext), eq(BROADCAST_ID), eq(BROADCAST_TITLE)); + } + + @Test + public void testGetOnClickListener_startSubSettings() { + when(mController.getFragment()).thenReturn(mFragment); + when(mFragment.getMetricsCategory()).thenReturn(AUDIO_STREAM_MAIN); + + Preference.OnPreferenceClickListener listener = mInstance.getOnClickListener(mController); + assertThat(listener).isNotNull(); + + // mContext is not an Activity context, calling startActivity() from outside of an Activity + // context requires the FLAG_ACTIVITY_NEW_TASK flag, create a mock to avoid this + // AndroidRuntimeException. + Context activityContext = mock(Context.class); + when(mPreference.getContext()).thenReturn(activityContext); + + listener.onPreferenceClick(mPreference); + + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(activityContext).startActivity(argumentCaptor.capture()); + + Intent intent = argumentCaptor.getValue(); + assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)) + .isEqualTo(AudioStreamDetailsFragment.class.getName()); + assertThat(intent.getIntExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, 0)) + .isEqualTo(R.string.audio_streams_detail_page_title); + assertThat(intent.getIntExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, 0)) + .isEqualTo(AUDIO_STREAM_MAIN); + + Bundle bundle = intent.getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS); + assertThat(bundle).isNotNull(); + assertThat(bundle.getString(AudioStreamDetailsFragment.BROADCAST_NAME_ARG)) + .isEqualTo(BROADCAST_TITLE); + assertThat(bundle.getInt(AudioStreamDetailsFragment.BROADCAST_ID_ARG)) + .isEqualTo(BROADCAST_ID); + } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/SyncedStateTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/SyncedStateTest.java index e9eab5066ac..2b19e2058b3 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/SyncedStateTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/SyncedStateTest.java @@ -20,7 +20,7 @@ import static com.android.settings.connecteddevice.audiosharing.audiostreams.Aud import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.never; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.robolectric.shadows.ShadowLooper.shadowMainLooper; @@ -28,10 +28,16 @@ import static org.robolectric.shadows.ShadowLooper.shadowMainLooper; import android.app.AlertDialog; import android.bluetooth.BluetoothLeBroadcastMetadata; import android.content.Context; +import android.content.DialogInterface; +import android.widget.Button; +import android.widget.TextView; import androidx.preference.Preference; import androidx.test.core.app.ApplicationProvider; +import com.android.settings.R; +import com.android.settingslib.bluetooth.BluetoothLeBroadcastMetadataExt; + import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -42,7 +48,9 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; +import org.robolectric.shadow.api.Shadow; import org.robolectric.shadows.ShadowAlertDialog; +import org.robolectric.shadows.ShadowLooper; @RunWith(RobolectricTestRunner.class) @Config( @@ -51,6 +59,10 @@ import org.robolectric.shadows.ShadowAlertDialog; }) public class SyncedStateTest { @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + private static final String ENCRYPTED_METADATA = + "BLUETOOTH:UUID:184F;BN:VGVzdA==;AT:1;AD:00A1A1A1A1A1;BI:1E240;BC:VGVzdENvZGU=;" + + "MD:BgNwVGVzdA==;AS:1;PI:A0;NS:1;BS:3;NB:2;SM:BQNUZXN0BARlbmc=;;"; + private static final String BROADCAST_TITLE = "title"; @Mock private AudioStreamsProgressCategoryController mMockController; @Mock private AudioStreamPreference mMockPreference; @Mock private BluetoothLeBroadcastMetadata mMockMetadata; @@ -105,18 +117,47 @@ public class SyncedStateTest { @Test public void testGetOnClickListener_isEncrypted_passwordDialogShowing() { + when(mMockPreference.getAudioStreamMetadata()) + .thenReturn( + BluetoothLeBroadcastMetadataExt.INSTANCE.convertToBroadcastMetadata( + ENCRYPTED_METADATA)); + when(mMockPreference.getContext()).thenReturn(mMockContext); + when(mMockPreference.getTitle()).thenReturn(BROADCAST_TITLE); + Preference.OnPreferenceClickListener listener = mInstance.getOnClickListener(mMockController); - when(mMockPreference.getAudioStreamMetadata()).thenReturn(mMockMetadata); - when(mMockPreference.getContext()).thenReturn(mMockContext); - when(mMockMetadata.isEncrypted()).thenReturn(true); + assertThat(listener).isNotNull(); listener.onPreferenceClick(mMockPreference); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialog.getLatestAlertDialog(); + assertThat(dialog).isNotNull(); assertThat(dialog.isShowing()).isTrue(); - verify(mMockController, never()).handleSourceAddRequest(mMockPreference, mMockMetadata); + + Button neutralButton = dialog.getButton(DialogInterface.BUTTON_NEUTRAL); + assertThat(neutralButton).isNotNull(); + assertThat(neutralButton.getText().toString()) + .isEqualTo(mMockContext.getString(android.R.string.cancel)); + + Button positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE); + assertThat(positiveButton).isNotNull(); + assertThat(positiveButton.getText().toString()) + .isEqualTo( + mMockContext.getString(R.string.bluetooth_connect_access_dialog_positive)); + + positiveButton.callOnClick(); + ShadowLooper.idleMainLooper(); + verify(mMockController).handleSourceAddRequest(any(), any()); + + ShadowAlertDialog shadowDialog = Shadow.extract(dialog); + TextView title = shadowDialog.getView().findViewById(R.id.broadcast_name_text); + assertThat(title).isNotNull(); + assertThat(title.getText().toString()).isEqualTo(BROADCAST_TITLE); + assertThat(shadowDialog.getTitle().toString()) + .isEqualTo(mMockContext.getString(R.string.find_broadcast_password_dialog_title)); + + dialog.cancel(); } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/WaitForSyncStateTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/WaitForSyncStateTest.java index 3eb07a46f74..d97bf8fe58e 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/WaitForSyncStateTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/WaitForSyncStateTest.java @@ -16,22 +16,39 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams; +import static android.app.settings.SettingsEnums.DIALOG_AUDIO_STREAM_MAIN_WAIT_FOR_SYNC_TIMEOUT; + +import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsScanQrCodeController.REQUEST_SCAN_BT_BROADCAST_QR_CODE; import static com.android.settings.connecteddevice.audiosharing.audiostreams.WaitForSyncState.AUDIO_STREAM_WAIT_FOR_SYNC_STATE_SUMMARY; import static com.android.settings.connecteddevice.audiosharing.audiostreams.WaitForSyncState.WAIT_FOR_SYNC_TIMEOUT_MILLIS; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.settings.SettingsEnums; import android.bluetooth.BluetoothLeBroadcastMetadata; +import android.content.Context; +import android.content.Intent; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.R; +import com.android.settings.SettingsActivity; +import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -43,15 +60,18 @@ import java.util.concurrent.TimeUnit; @RunWith(RobolectricTestRunner.class) public class WaitForSyncStateTest { @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + private final Context mContext = spy(ApplicationProvider.getApplicationContext()); @Mock private AudioStreamPreference mMockPreference; @Mock private AudioStreamsProgressCategoryController mMockController; @Mock private AudioStreamsHelper mMockHelper; @Mock private BluetoothLeBroadcastMetadata mMockMetadata; + private FakeFeatureFactory mFeatureFactory; private WaitForSyncState mInstance; @Before public void setUp() { - mInstance = WaitForSyncState.getInstance(); + mFeatureFactory = FakeFeatureFactory.setupForTest(); + mInstance = new WaitForSyncState(); } @Test @@ -93,12 +113,49 @@ public class WaitForSyncStateTest { .thenReturn(AudioStreamsProgressCategoryController.AudioStreamState.WAIT_FOR_SYNC); when(mMockPreference.getAudioStreamBroadcastId()).thenReturn(1); when(mMockPreference.getAudioStreamMetadata()).thenReturn(mMockMetadata); + when(mMockPreference.getContext()).thenReturn(mContext); when(mMockPreference.getSourceOriginForLogging()) - .thenReturn(SourceOriginForLogging.UNKNOWN); + .thenReturn(SourceOriginForLogging.BROADCAST_SEARCH); + when(mMockController.getFragment()).thenReturn(mock(AudioStreamsDashboardFragment.class)); mInstance.performAction(mMockPreference, mMockController, mMockHelper); ShadowLooper.idleMainLooper(WAIT_FOR_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); verify(mMockController).handleSourceLost(1); + verify(mFeatureFactory.metricsFeatureProvider) + .action( + eq(mContext), + eq(SettingsEnums.ACTION_AUDIO_STREAM_JOIN_FAILED_WAIT_FOR_SYNC_TIMEOUT), + eq(SourceOriginForLogging.BROADCAST_SEARCH.ordinal())); + verify(mContext).getString(R.string.audio_streams_dialog_stream_is_not_available); + verify(mContext).getString(R.string.audio_streams_is_not_playing); + verify(mContext).getString(R.string.audio_streams_dialog_close); + verify(mContext).getString(R.string.audio_streams_dialog_retry); + } + + @Test + public void testLaunchQrCodeScanFragment() { + // mContext is not an Activity context, calling startActivity() from outside of an Activity + // context requires the FLAG_ACTIVITY_NEW_TASK flag, create a mock to avoid this + // AndroidRuntimeException. + Context activityContext = mock(Context.class); + AudioStreamsDashboardFragment fragment = mock(AudioStreamsDashboardFragment.class); + mInstance.launchQrCodeScanFragment(activityContext, fragment); + + ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); + ArgumentCaptor requestCodeCaptor = ArgumentCaptor.forClass(Integer.class); + verify(fragment) + .startActivityForResult(intentCaptor.capture(), requestCodeCaptor.capture()); + + Intent intent = intentCaptor.getValue(); + assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)) + .isEqualTo(AudioStreamsQrCodeScanFragment.class.getName()); + assertThat(intent.getIntExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, 0)) + .isEqualTo(R.string.audio_streams_main_page_scan_qr_code_title); + assertThat(intent.getIntExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, 0)) + .isEqualTo(DIALOG_AUDIO_STREAM_MAIN_WAIT_FOR_SYNC_TIMEOUT); + + int requestCode = requestCodeCaptor.getValue(); + assertThat(requestCode).isEqualTo(REQUEST_SCAN_BT_BROADCAST_QR_CODE); } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowAudioStreamsHelper.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowAudioStreamsHelper.java index 3a0a6c4b757..13c19cae872 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowAudioStreamsHelper.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowAudioStreamsHelper.java @@ -32,7 +32,7 @@ import org.robolectric.annotation.Resetter; import java.util.List; import java.util.Optional; -@Implements(value = AudioStreamsHelper.class, callThroughByDefault = false) +@Implements(value = AudioStreamsHelper.class, callThroughByDefault = true) public class ShadowAudioStreamsHelper { private static AudioStreamsHelper sMockHelper; @Nullable private static CachedBluetoothDevice sCachedBluetoothDevice; diff --git a/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java index 0aef136d5f6..ff253222ac2 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java @@ -25,10 +25,8 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.Context; -import android.net.Uri; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.service.notification.ZenPolicy; @@ -71,12 +69,9 @@ public final class InterruptionFilterPreferenceControllerTest { @Test public void testUpdateState_all() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_ALL) - .setZenPolicy(new ZenPolicy.Builder().allowAlarms(true).build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setInterruptionFilter(INTERRUPTION_FILTER_ALL) + .build(); mController.updateZenMode(preference, zenMode); verify(preference).setChecked(false); @@ -85,12 +80,9 @@ public final class InterruptionFilterPreferenceControllerTest { @Test public void testOnPreferenceChange_fromAll() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_ALL) - .setZenPolicy(new ZenPolicy.Builder().allowAlarms(false).build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setInterruptionFilter(INTERRUPTION_FILTER_ALL) + .build(); mController.updateZenMode(preference, zenMode); @@ -107,12 +99,10 @@ public final class InterruptionFilterPreferenceControllerTest { @Test public void testUpdateState_priority() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowAlarms(true).build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + .setZenPolicy(new ZenPolicy.Builder().allowAlarms(true).build()) + .build(); mController.updateZenMode(preference, zenMode); verify(preference).setChecked(true); @@ -121,12 +111,10 @@ public final class InterruptionFilterPreferenceControllerTest { @Test public void testOnPreferenceChange_fromPriority() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowAlarms(false).build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + .setZenPolicy(new ZenPolicy.Builder().allowAlarms(false).build()) + .build(); mController.updateZenMode(preference, zenMode); diff --git a/tests/robotests/src/com/android/settings/notification/modes/TestModeBuilder.java b/tests/robotests/src/com/android/settings/notification/modes/TestModeBuilder.java new file mode 100644 index 00000000000..fa12b30590c --- /dev/null +++ b/tests/robotests/src/com/android/settings/notification/modes/TestModeBuilder.java @@ -0,0 +1,149 @@ +/* + * 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.notification.modes; + +import android.app.AutomaticZenRule; +import android.app.NotificationManager; +import android.net.Uri; +import android.service.notification.Condition; +import android.service.notification.ZenDeviceEffects; +import android.service.notification.ZenModeConfig; +import android.service.notification.ZenPolicy; + +import androidx.annotation.Nullable; + +import com.android.settingslib.notification.modes.ZenMode; + +import java.util.Random; + +class TestModeBuilder { + + private String mId; + private AutomaticZenRule mRule; + private ZenModeConfig.ZenRule mConfigZenRule; + + public static final ZenMode EXAMPLE = new TestModeBuilder().build(); + + TestModeBuilder() { + // Reasonable defaults + int id = new Random().nextInt(1000); + mId = "rule_" + id; + mRule = new AutomaticZenRule.Builder("Test Rule #" + id, Uri.parse("rule://" + id)) + .setPackage("some_package") + .setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY) + .setZenPolicy(new ZenPolicy.Builder().disallowAllSounds().build()) + .build(); + mConfigZenRule = new ZenModeConfig.ZenRule(); + mConfigZenRule.enabled = true; + mConfigZenRule.pkg = "some_package"; + } + + TestModeBuilder setId(String id) { + mId = id; + return this; + } + + TestModeBuilder setAzr(AutomaticZenRule rule) { + mRule = rule; + mConfigZenRule.pkg = rule.getPackageName(); + mConfigZenRule.conditionId = rule.getConditionId(); + mConfigZenRule.enabled = rule.isEnabled(); + return this; + } + + TestModeBuilder setConfigZenRule(ZenModeConfig.ZenRule configZenRule) { + mConfigZenRule = configZenRule; + return this; + } + + public TestModeBuilder setName(String name) { + mRule.setName(name); + mConfigZenRule.name = name; + return this; + } + + public TestModeBuilder setPackage(String pkg) { + mRule.setPackageName(pkg); + mConfigZenRule.pkg = pkg; + return this; + } + + TestModeBuilder setConditionId(Uri conditionId) { + mRule.setConditionId(conditionId); + mConfigZenRule.conditionId = conditionId; + return this; + } + + TestModeBuilder setType(@AutomaticZenRule.Type int type) { + mRule.setType(type); + mConfigZenRule.type = type; + return this; + } + + TestModeBuilder setInterruptionFilter( + @NotificationManager.InterruptionFilter int interruptionFilter) { + mRule.setInterruptionFilter(interruptionFilter); + mConfigZenRule.zenMode = NotificationManager.zenModeFromInterruptionFilter( + interruptionFilter, NotificationManager.INTERRUPTION_FILTER_PRIORITY); + return this; + } + + TestModeBuilder setZenPolicy(@Nullable ZenPolicy policy) { + mRule.setZenPolicy(policy); + mConfigZenRule.zenPolicy = policy; + return this; + } + + TestModeBuilder setDeviceEffects(@Nullable ZenDeviceEffects deviceEffects) { + mRule.setDeviceEffects(deviceEffects); + mConfigZenRule.zenDeviceEffects = deviceEffects; + return this; + } + + public TestModeBuilder setEnabled(boolean enabled) { + mRule.setEnabled(enabled); + mConfigZenRule.enabled = enabled; + return this; + } + + TestModeBuilder setManualInvocationAllowed(boolean allowed) { + mRule.setManualInvocationAllowed(allowed); + mConfigZenRule.allowManualInvocation = allowed; + return this; + } + + public TestModeBuilder setTriggerDescription(@Nullable String triggerDescription) { + mRule.setTriggerDescription(triggerDescription); + mConfigZenRule.triggerDescription = triggerDescription; + return this; + } + + TestModeBuilder setActive(boolean active) { + if (active) { + mConfigZenRule.enabled = true; + mConfigZenRule.condition = new Condition(mRule.getConditionId(), "...", + Condition.STATE_TRUE); + } else { + mConfigZenRule.condition = null; + } + return this; + } + + ZenMode build() { + return new ZenMode(mId, mRule, mConfigZenRule); + } +} diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java index 066a215e61f..83f8de0e70c 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java @@ -28,12 +28,10 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; -import android.net.Uri; import android.os.Bundle; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; @@ -103,14 +101,13 @@ public final class ZenModeAppsLinkPreferenceControllerTest { } private ZenMode createPriorityChannelsZenMode() { - return new ZenMode("id", new AutomaticZenRule.Builder("Bedtime", - Uri.parse("bed")) - .setType(AutomaticZenRule.TYPE_BEDTIME) + return new TestModeBuilder() + .setId("id") .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) .setZenPolicy(new ZenPolicy.Builder() .allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY) .build()) - .build(), true); + .build(); } @Test diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsPreferenceControllerTest.java index d89e187c7da..c96dbb6566f 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsPreferenceControllerTest.java @@ -26,10 +26,8 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.Context; -import android.net.Uri; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.service.notification.ZenPolicy; @@ -107,13 +105,12 @@ public final class ZenModeAppsPreferenceControllerTest { @Test public void testUpdateState_None() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setZenPolicy(new ZenPolicy.Builder() - .allowChannels(ZenPolicy.CHANNEL_POLICY_NONE) - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder() + .allowChannels(ZenPolicy.CHANNEL_POLICY_NONE) + .build()) + .build(); + mNoneController.updateZenMode(preference, zenMode); verify(preference).setChecked(true); @@ -122,13 +119,12 @@ public final class ZenModeAppsPreferenceControllerTest { @Test public void testUpdateState_None_Unchecked() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setZenPolicy(new ZenPolicy.Builder() - .allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY) - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder() + .allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY) + .build()) + .build(); + mNoneController.updateZenMode(preference, zenMode); verify(preference).setChecked(false); @@ -137,13 +133,12 @@ public final class ZenModeAppsPreferenceControllerTest { @Test public void testUpdateState_Priority() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setZenPolicy(new ZenPolicy.Builder() - .allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY) - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder() + .allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY) + .build()) + .build(); + mPriorityController.updateZenMode(preference, zenMode); verify(preference).setChecked(true); @@ -152,13 +147,12 @@ public final class ZenModeAppsPreferenceControllerTest { @Test public void testUpdateState_Priority_Unchecked() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setZenPolicy(new ZenPolicy.Builder() - .allowChannels(ZenPolicy.CHANNEL_POLICY_NONE) - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder() + .allowChannels(ZenPolicy.CHANNEL_POLICY_NONE) + .build()) + .build(); + mPriorityController.updateZenMode(preference, zenMode); verify(preference).setChecked(false); @@ -166,21 +160,19 @@ public final class ZenModeAppsPreferenceControllerTest { @Test public void testPreferenceClick_passesCorrectCheckedState_None() { - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setZenPolicy(new ZenPolicy.Builder() - .allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY) - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder() + .allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY) + .build()) + .build(); mNoneController.updateZenMode(mNonePref, zenMode); mPriorityController.updateZenMode(mPriorityPref, zenMode); - assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE)) - .isChecked()); - assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY)) - .isChecked()); + assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE)) + .isChecked()).isFalse(); + assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY)) + .isChecked()).isTrue(); // Click on NONE mPrefCategory.findPreference(KEY_NONE).performClick(); @@ -192,30 +184,31 @@ public final class ZenModeAppsPreferenceControllerTest { // See AbstractZenModePreferenceController. assertThat(captor.getValue().getRule().getInterruptionFilter()) .isEqualTo(INTERRUPTION_FILTER_PRIORITY); - // NONE is now checked; others are unchecked. + + // After screen is refreshed, NONE is now checked; others are unchecked. + mNoneController.updateZenMode(mNonePref, captor.getValue()); + mPriorityController.updateZenMode(mPriorityPref, captor.getValue()); assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE)) - .isChecked()); - assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY)) - .isChecked()); + .isChecked()).isTrue(); + assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY)) + .isChecked()).isFalse(); } @Test public void testPreferenceClick_passesCorrectCheckedState_Priority() { - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setZenPolicy(new ZenPolicy.Builder() - .allowChannels(ZenPolicy.CHANNEL_POLICY_NONE) - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder() + .allowChannels(ZenPolicy.CHANNEL_POLICY_NONE) + .build()) + .build(); mNoneController.updateZenMode(mNonePref, zenMode); mPriorityController.updateZenMode(mPriorityPref, zenMode); assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE)) - .isChecked()); - assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY)) - .isChecked()); + .isChecked()).isTrue(); + assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY)) + .isChecked()).isFalse(); // Click on PRIORITY mPrefCategory.findPreference(KEY_PRIORITY).performClick(); @@ -225,11 +218,13 @@ public final class ZenModeAppsPreferenceControllerTest { // Checks the policy value for PRIORITY is propagated to the backend. assertThat(captor.getValue().getRule().getInterruptionFilter()) .isEqualTo(INTERRUPTION_FILTER_PRIORITY); - // PRIORITY is now checked; others are unchecked. - assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY)) - .isChecked()); - assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE)) - .isChecked()); - } + // After screen is refreshed, PRIORITY is now checked; others are unchecked. + mNoneController.updateZenMode(mNonePref, captor.getValue()); + mPriorityController.updateZenMode(mPriorityPref, captor.getValue()); + assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY)) + .isChecked()).isTrue(); + assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE)) + .isChecked()).isFalse(); + } } diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeButtonPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeButtonPreferenceControllerTest.java index 98099aa1a20..625f2311d21 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeButtonPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeButtonPreferenceControllerTest.java @@ -16,8 +16,6 @@ package com.android.settings.notification.modes; -import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; - import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyInt; @@ -25,13 +23,10 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.Context; -import android.net.Uri; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; -import android.service.notification.ZenPolicy; import android.widget.Button; import com.android.settingslib.notification.modes.ZenMode; @@ -73,43 +68,34 @@ public final class ZenModeButtonPreferenceControllerTest { @Test public void isAvailable_notIfAppOptsOut() { - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build()) + ZenMode zenMode = new TestModeBuilder() .setManualInvocationAllowed(false) - .setEnabled(true) - .build(), false); + .build(); mController.setZenMode(zenMode); assertThat(mController.isAvailable()).isFalse(); } @Test public void isAvailable_notIfModeDisabled() { - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build()) - .setManualInvocationAllowed(true) - .setEnabled(false) - .build(), false); + ZenMode zenMode = new TestModeBuilder() + .setManualInvocationAllowed(true) + .setEnabled(false) + .build(); + mController.setZenMode(zenMode); + assertThat(mController.isAvailable()).isFalse(); } @Test public void isAvailable_appOptedIn_modeEnabled() { - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build()) - .setManualInvocationAllowed(true) - .setEnabled(true) - .build(), false); + ZenMode zenMode = new TestModeBuilder() + .setManualInvocationAllowed(true) + .setEnabled(true) + .build(); + mController.setZenMode(zenMode); + assertThat(mController.isAvailable()).isTrue(); } @@ -118,15 +104,13 @@ public final class ZenModeButtonPreferenceControllerTest { Button button = new Button(mContext); LayoutPreference pref = mock(LayoutPreference.class); when(pref.findViewById(anyInt())).thenReturn(button); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build()) - .setManualInvocationAllowed(true) - .setEnabled(true) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setManualInvocationAllowed(true) + .setActive(true) + .build(); + mController.updateZenMode(pref, zenMode); + assertThat(button.getText().toString()).contains("off"); assertThat(button.hasOnClickListeners()).isTrue(); } @@ -136,15 +120,13 @@ public final class ZenModeButtonPreferenceControllerTest { Button button = new Button(mContext); LayoutPreference pref = mock(LayoutPreference.class); when(pref.findViewById(anyInt())).thenReturn(button); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build()) - .setManualInvocationAllowed(true) - .setEnabled(true) - .build(), false); + ZenMode zenMode = new TestModeBuilder() + .setManualInvocationAllowed(true) + .setActive(false) + .build(); + mController.updateZenMode(pref, zenMode); + assertThat(button.getText().toString()).contains("on"); assertThat(button.hasOnClickListeners()).isTrue(); } @@ -154,14 +136,11 @@ public final class ZenModeButtonPreferenceControllerTest { Button button = new Button(mContext); LayoutPreference pref = mock(LayoutPreference.class); when(pref.findViewById(anyInt())).thenReturn(button); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build()) - .setManualInvocationAllowed(true) - .setEnabled(true) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setManualInvocationAllowed(true) + .setActive(true) + .build(); + mController.updateZenMode(pref, zenMode); button.callOnClick(); @@ -173,14 +152,11 @@ public final class ZenModeButtonPreferenceControllerTest { Button button = new Button(mContext); LayoutPreference pref = mock(LayoutPreference.class); when(pref.findViewById(anyInt())).thenReturn(button); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build()) - .setManualInvocationAllowed(true) - .setEnabled(true) - .build(), false); + ZenMode zenMode = new TestModeBuilder() + .setManualInvocationAllowed(true) + .setActive(false) + .build(); + mController.updateZenMode(pref, zenMode); button.callOnClick(); diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeCallsLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeCallsLinkPreferenceControllerTest.java index c8e654d5af8..058b2d7d566 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeCallsLinkPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeCallsLinkPreferenceControllerTest.java @@ -16,23 +16,17 @@ package com.android.settings.notification.modes; -import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; - import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.Context; -import android.net.Uri; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; -import android.service.notification.ZenPolicy; import androidx.preference.Preference; -import com.android.settingslib.notification.modes.ZenMode; import com.android.settingslib.notification.modes.ZenModesBackend; import org.junit.Before; @@ -70,13 +64,7 @@ public final class ZenModeCallsLinkPreferenceControllerTest { @EnableFlags(Flags.FLAG_MODES_UI) public void testHasSummary() { Preference pref = mock(Preference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build()) - .build(), true); - mController.updateZenMode(pref, zenMode); + mController.updateZenMode(pref, TestModeBuilder.EXAMPLE); verify(pref).setSummary(any()); } } \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceControllerTest.java index 1052c0d831e..a735cd928f3 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceControllerTest.java @@ -16,21 +16,16 @@ package com.android.settings.notification.modes; -import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; - import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.Context; -import android.net.Uri; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.service.notification.ZenDeviceEffects; -import android.service.notification.ZenPolicy; import androidx.preference.TwoStatePreference; @@ -67,15 +62,11 @@ public final class ZenModeDisplayEffectPreferenceControllerTest { @Test public void testUpdateState_grayscale() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowAlarms(true).build()) - .setDeviceEffects(new ZenDeviceEffects.Builder() - .setShouldDisplayGrayscale(true) - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setDeviceEffects(new ZenDeviceEffects.Builder() + .setShouldDisplayGrayscale(true) + .build()) + .build(); ZenModeDisplayEffectPreferenceController controller = new ZenModeDisplayEffectPreferenceController( @@ -89,15 +80,11 @@ public final class ZenModeDisplayEffectPreferenceControllerTest { @Test public void testOnPreferenceChange_grayscale() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowAlarms(false).build()) - .setDeviceEffects(new ZenDeviceEffects.Builder() - .setShouldDisplayGrayscale(true) - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setDeviceEffects(new ZenDeviceEffects.Builder() + .setShouldDisplayGrayscale(true) + .build()) + .build(); ZenModeDisplayEffectPreferenceController controller = new ZenModeDisplayEffectPreferenceController(mContext, "effect_greyscale", mBackend); @@ -108,22 +95,18 @@ public final class ZenModeDisplayEffectPreferenceControllerTest { ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class); verify(mBackend).updateMode(captor.capture()); - assertThat(captor.getValue().getRule().getDeviceEffects().shouldDisplayGrayscale()) + assertThat(captor.getValue().getDeviceEffects().shouldDisplayGrayscale()) .isFalse(); } @Test public void testUpdateState_aod() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowMedia(true).build()) - .setDeviceEffects(new ZenDeviceEffects.Builder() - .setShouldSuppressAmbientDisplay(true) - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setDeviceEffects(new ZenDeviceEffects.Builder() + .setShouldSuppressAmbientDisplay(true) + .build()) + .build(); ZenModeDisplayEffectPreferenceController controller = new ZenModeDisplayEffectPreferenceController(mContext, "effect_aod", mBackend); @@ -136,15 +119,11 @@ public final class ZenModeDisplayEffectPreferenceControllerTest { @Test public void testOnPreferenceChange_aod() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowMedia(false).build()) - .setDeviceEffects(new ZenDeviceEffects.Builder() - .setShouldSuppressAmbientDisplay(true) - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setDeviceEffects(new ZenDeviceEffects.Builder() + .setShouldSuppressAmbientDisplay(true) + .build()) + .build(); ZenModeDisplayEffectPreferenceController controller = new ZenModeDisplayEffectPreferenceController(mContext, "effect_aod", mBackend); @@ -155,22 +134,18 @@ public final class ZenModeDisplayEffectPreferenceControllerTest { ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class); verify(mBackend).updateMode(captor.capture()); - assertThat(captor.getValue().getRule().getDeviceEffects().shouldSuppressAmbientDisplay()) + assertThat(captor.getValue().getDeviceEffects().shouldSuppressAmbientDisplay()) .isFalse(); } @Test public void testUpdateState_wallpaper() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowSystem(true).build()) - .setDeviceEffects(new ZenDeviceEffects.Builder() - .setShouldDimWallpaper(true) - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setDeviceEffects(new ZenDeviceEffects.Builder() + .setShouldDimWallpaper(true) + .build()) + .build(); ZenModeDisplayEffectPreferenceController controller = new ZenModeDisplayEffectPreferenceController( @@ -184,15 +159,11 @@ public final class ZenModeDisplayEffectPreferenceControllerTest { @Test public void testOnPreferenceChange_wallpaper() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowSystem(false).build()) - .setDeviceEffects(new ZenDeviceEffects.Builder() - .setShouldDimWallpaper(true) - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setDeviceEffects(new ZenDeviceEffects.Builder() + .setShouldDimWallpaper(true) + .build()) + .build(); ZenModeDisplayEffectPreferenceController controller = new ZenModeDisplayEffectPreferenceController( @@ -204,21 +175,17 @@ public final class ZenModeDisplayEffectPreferenceControllerTest { ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class); verify(mBackend).updateMode(captor.capture()); - assertThat(captor.getValue().getRule().getDeviceEffects().shouldDimWallpaper()).isFalse(); + assertThat(captor.getValue().getDeviceEffects().shouldDimWallpaper()).isFalse(); } @Test public void testUpdateState_darkTheme() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowReminders(true).build()) - .setDeviceEffects(new ZenDeviceEffects.Builder() - .setShouldUseNightMode(true) - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setDeviceEffects(new ZenDeviceEffects.Builder() + .setShouldUseNightMode(true) + .build()) + .build(); ZenModeDisplayEffectPreferenceController controller = new ZenModeDisplayEffectPreferenceController(mContext, "effect_dark_theme", @@ -232,15 +199,11 @@ public final class ZenModeDisplayEffectPreferenceControllerTest { @Test public void testOnPreferenceChange_darkTheme() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowReminders(false).build()) - .setDeviceEffects(new ZenDeviceEffects.Builder() - .setShouldUseNightMode(true) - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setDeviceEffects(new ZenDeviceEffects.Builder() + .setShouldUseNightMode(true) + .build()) + .build(); ZenModeDisplayEffectPreferenceController controller = new ZenModeDisplayEffectPreferenceController(mContext, "effect_dark_theme", @@ -252,6 +215,6 @@ public final class ZenModeDisplayEffectPreferenceControllerTest { ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class); verify(mBackend).updateMode(captor.capture()); - assertThat(captor.getValue().getRule().getDeviceEffects().shouldUseNightMode()).isFalse(); + assertThat(captor.getValue().getDeviceEffects().shouldUseNightMode()).isFalse(); } } \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceControllerTest.java index c78883b0b68..3ccfb9f10dd 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceControllerTest.java @@ -16,23 +16,17 @@ package com.android.settings.notification.modes; -import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; - import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.Context; -import android.net.Uri; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; -import android.service.notification.ZenPolicy; import androidx.preference.Preference; -import com.android.settingslib.notification.modes.ZenMode; import com.android.settingslib.notification.modes.ZenModesBackend; import org.junit.Before; @@ -70,13 +64,7 @@ public final class ZenModeDisplayLinkPreferenceControllerTest { @EnableFlags(Flags.FLAG_MODES_UI) public void testHasSummary() { Preference pref = mock(Preference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build()) - .build(), true); - mController.updateZenMode(pref, zenMode); + mController.updateZenMode(pref, TestModeBuilder.EXAMPLE); verify(pref).setSummary(any()); } } \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeExitAtAlarmPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeExitAtAlarmPreferenceControllerTest.java index f95fc85ec5f..03c75fb12c1 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeExitAtAlarmPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeExitAtAlarmPreferenceControllerTest.java @@ -21,7 +21,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import android.app.AutomaticZenRule; import android.content.Context; import android.service.notification.ZenModeConfig; @@ -68,10 +67,9 @@ public class ZenModeExitAtAlarmPreferenceControllerTest { scheduleInfo.endHour = 2; scheduleInfo.exitAtAlarm = false; - ZenMode mode = new ZenMode("id", - new AutomaticZenRule.Builder("name", - ZenModeConfig.toScheduleConditionId(scheduleInfo)).build(), - true); // is active + ZenMode mode = new TestModeBuilder() + .setConditionId(ZenModeConfig.toScheduleConditionId(scheduleInfo)) + .build(); // need to call updateZenMode for the first call mPrefController.updateZenMode(preference, mode); @@ -97,10 +95,9 @@ public class ZenModeExitAtAlarmPreferenceControllerTest { scheduleInfo.endHour = 2; scheduleInfo.exitAtAlarm = true; - ZenMode mode = new ZenMode("id", - new AutomaticZenRule.Builder("name", - ZenModeConfig.toScheduleConditionId(scheduleInfo)).build(), - true); // is active + ZenMode mode = new TestModeBuilder() + .setConditionId(ZenModeConfig.toScheduleConditionId(scheduleInfo)) + .build(); mPrefController.updateZenMode(preference, mode); // turn off exit at alarm diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeIconPickerListPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeIconPickerListPreferenceControllerTest.java index 31324604bd0..5db7e925eef 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeIconPickerListPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeIconPickerListPreferenceControllerTest.java @@ -23,9 +23,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.AutomaticZenRule; import android.content.Context; -import android.net.Uri; import androidx.annotation.NonNull; import androidx.preference.PreferenceScreen; @@ -49,10 +47,7 @@ import org.robolectric.RuntimeEnvironment; @RunWith(RobolectricTestRunner.class) public class ZenModeIconPickerListPreferenceControllerTest { - private static final ZenMode ZEN_MODE = new ZenMode( - "mode_id", - new AutomaticZenRule.Builder("mode name", Uri.parse("mode")).build(), - /* isActive= */ false); + private static final ZenMode ZEN_MODE = TestModeBuilder.EXAMPLE; private ZenModesBackend mBackend; private ZenModeIconPickerListPreferenceController mController; diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeMessagesLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeMessagesLinkPreferenceControllerTest.java index 6d70ec542b7..288359aeb57 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeMessagesLinkPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeMessagesLinkPreferenceControllerTest.java @@ -16,23 +16,17 @@ package com.android.settings.notification.modes; -import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; - import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.Context; -import android.net.Uri; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; -import android.service.notification.ZenPolicy; import androidx.preference.Preference; -import com.android.settingslib.notification.modes.ZenMode; import com.android.settingslib.notification.modes.ZenModesBackend; import org.junit.Before; @@ -70,13 +64,7 @@ public final class ZenModeMessagesLinkPreferenceControllerTest { @EnableFlags(Flags.FLAG_MODES_UI) public void testHasSummary() { Preference pref = mock(Preference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build()) - .build(), true); - mController.updateZenMode(pref, zenMode); + mController.updateZenMode(pref, TestModeBuilder.EXAMPLE); verify(pref).setSummary(any()); } } \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceControllerTest.java index bf5171a8726..ee7340bd526 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceControllerTest.java @@ -16,23 +16,17 @@ package com.android.settings.notification.modes; -import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; - import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.Context; -import android.net.Uri; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; -import android.service.notification.ZenPolicy; import androidx.preference.Preference; -import com.android.settingslib.notification.modes.ZenMode; import com.android.settingslib.notification.modes.ZenModesBackend; import org.junit.Before; @@ -70,13 +64,7 @@ public final class ZenModeNotifVisLinkPreferenceControllerTest { @EnableFlags(Flags.FLAG_MODES_UI) public void testHasSummary() { Preference pref = mock(Preference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build()) - .build(), true); - mController.updateZenMode(pref, zenMode); + mController.updateZenMode(pref, TestModeBuilder.EXAMPLE); verify(pref).setSummary(any()); } } \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeNotifVisPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeNotifVisPreferenceControllerTest.java index 137806771da..b23d946058f 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeNotifVisPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeNotifVisPreferenceControllerTest.java @@ -16,7 +16,6 @@ package com.android.settings.notification.modes; -import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; import static android.service.notification.ZenPolicy.STATE_ALLOW; import static android.service.notification.ZenPolicy.STATE_DISALLOW; import static android.service.notification.ZenPolicy.VISUAL_EFFECT_LIGHTS; @@ -32,11 +31,9 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.Context; import android.content.res.Resources; -import android.net.Uri; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.service.notification.ZenPolicy; @@ -99,15 +96,12 @@ public final class ZenModeNotifVisPreferenceControllerTest { @Test public void updateState_notChecked() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder() - .allowAlarms(true) - .showAllVisualEffects() - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder() + .allowAlarms(true) + .showAllVisualEffects() + .build()) + .build(); mController.updateZenMode(preference, zenMode); @@ -118,15 +112,12 @@ public final class ZenModeNotifVisPreferenceControllerTest { @Test public void updateState_checked() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder() - .allowAlarms(true) - .showVisualEffect(VISUAL_EFFECT_PEEK, false) - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder() + .allowAlarms(true) + .showVisualEffect(VISUAL_EFFECT_PEEK, false) + .build()) + .build(); mController.updateZenMode(preference, zenMode); @@ -141,16 +132,13 @@ public final class ZenModeNotifVisPreferenceControllerTest { "zen_effect_status", VISUAL_EFFECT_STATUS_BAR, new int[]{VISUAL_EFFECT_NOTIFICATION_LIST}, mBackend); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder() - .allowAlarms(true) - .showVisualEffect(VISUAL_EFFECT_NOTIFICATION_LIST, false) - .showVisualEffect(VISUAL_EFFECT_STATUS_BAR, true) - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder() + .allowAlarms(true) + .showVisualEffect(VISUAL_EFFECT_NOTIFICATION_LIST, false) + .showVisualEffect(VISUAL_EFFECT_STATUS_BAR, true) + .build()) + .build(); mController.updateZenMode(preference, zenMode); @@ -171,15 +159,12 @@ public final class ZenModeNotifVisPreferenceControllerTest { "zen_effect_status", VISUAL_EFFECT_STATUS_BAR, new int[]{VISUAL_EFFECT_NOTIFICATION_LIST}, mBackend); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder() - .allowAlarms(true) - .showAllVisualEffects() - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder() + .allowAlarms(true) + .showAllVisualEffects() + .build()) + .build(); mController.updateZenMode(preference, zenMode); @@ -191,15 +176,12 @@ public final class ZenModeNotifVisPreferenceControllerTest { @Test public void onPreferenceChanged_checkedFalse() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder() - .allowAlarms(true) - .hideAllVisualEffects() - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder() + .allowAlarms(true) + .hideAllVisualEffects() + .build()) + .build(); mController.updateZenMode(preference, zenMode); @@ -216,15 +198,12 @@ public final class ZenModeNotifVisPreferenceControllerTest { @Test public void onPreferenceChanged_checkedTrue() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder() - .allowAlarms(true) - .showAllVisualEffects() - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder() + .allowAlarms(true) + .showAllVisualEffects() + .build()) + .build(); mController.updateZenMode(preference, zenMode); diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceControllerTest.java index f272b80d427..c4d03fe3360 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceControllerTest.java @@ -16,23 +16,17 @@ package com.android.settings.notification.modes; -import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; - import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.Context; -import android.net.Uri; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; -import android.service.notification.ZenPolicy; import androidx.preference.Preference; -import com.android.settingslib.notification.modes.ZenMode; import com.android.settingslib.notification.modes.ZenModesBackend; import org.junit.Before; @@ -71,13 +65,7 @@ public final class ZenModeOtherLinkPreferenceControllerTest { @EnableFlags(Flags.FLAG_MODES_UI) public void testHasSummary() { Preference pref = mock(Preference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build()) - .build(), true); - mController.updateZenMode(pref, zenMode); + mController.updateZenMode(pref, TestModeBuilder.EXAMPLE); verify(pref).setSummary(any()); } } \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherPreferenceControllerTest.java index 66ac7fa1a41..c69a8a0ebd6 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeOtherPreferenceControllerTest.java @@ -16,7 +16,6 @@ package com.android.settings.notification.modes; -import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; import static android.service.notification.ZenPolicy.STATE_ALLOW; import static android.service.notification.ZenPolicy.STATE_UNSET; @@ -25,10 +24,8 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.Context; -import android.net.Uri; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.service.notification.ZenPolicy; @@ -68,12 +65,9 @@ public final class ZenModeOtherPreferenceControllerTest { @Test public void testUpdateState_alarms() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowAlarms(true).build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder().allowAlarms(true).build()) + .build(); ZenModeOtherPreferenceController controller = new ZenModeOtherPreferenceController(mContext, "modes_category_alarm", mBackend); @@ -86,12 +80,9 @@ public final class ZenModeOtherPreferenceControllerTest { @Test public void testOnPreferenceChange_alarms() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowAlarms(false).build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder().allowAlarms(false).build()) + .build(); ZenModeOtherPreferenceController controller = new ZenModeOtherPreferenceController(mContext, "modes_category_alarm", mBackend); @@ -111,12 +102,9 @@ public final class ZenModeOtherPreferenceControllerTest { @Test public void testUpdateState_media() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowMedia(true).build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder().allowMedia(true).build()) + .build(); ZenModeOtherPreferenceController controller = new ZenModeOtherPreferenceController(mContext, "modes_category_media", mBackend); @@ -129,12 +117,9 @@ public final class ZenModeOtherPreferenceControllerTest { @Test public void testOnPreferenceChange_media() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowMedia(false).build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder().allowMedia(false).build()) + .build(); ZenModeOtherPreferenceController controller = new ZenModeOtherPreferenceController(mContext, "modes_category_media", mBackend); @@ -154,12 +139,9 @@ public final class ZenModeOtherPreferenceControllerTest { @Test public void testUpdateState_system() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowSystem(true).build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder().allowSystem(true).build()) + .build(); ZenModeOtherPreferenceController controller = new ZenModeOtherPreferenceController(mContext, "modes_category_system", mBackend); @@ -172,12 +154,9 @@ public final class ZenModeOtherPreferenceControllerTest { @Test public void testOnPreferenceChange_system() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowSystem(false).build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder().allowSystem(false).build()) + .build(); ZenModeOtherPreferenceController controller = new ZenModeOtherPreferenceController(mContext, "modes_category_system", mBackend); @@ -197,12 +176,9 @@ public final class ZenModeOtherPreferenceControllerTest { @Test public void testUpdateState_reminders() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowReminders(true).build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder().allowReminders(true).build()) + .build(); ZenModeOtherPreferenceController controller = new ZenModeOtherPreferenceController(mContext, "modes_category_reminders", @@ -216,12 +192,9 @@ public final class ZenModeOtherPreferenceControllerTest { @Test public void testOnPreferenceChange_reminders() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowReminders(false).build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder().allowReminders(false).build()) + .build(); ZenModeOtherPreferenceController controller = new ZenModeOtherPreferenceController(mContext, "modes_category_reminders", @@ -242,12 +215,9 @@ public final class ZenModeOtherPreferenceControllerTest { @Test public void testUpdateState_events() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowEvents(true).build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder().allowEvents(true).build()) + .build(); ZenModeOtherPreferenceController controller = new ZenModeOtherPreferenceController(mContext, "modes_category_events", mBackend); @@ -260,12 +230,9 @@ public final class ZenModeOtherPreferenceControllerTest { @Test public void testOnPreferenceChange_events() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowEvents(false).build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder().allowEvents(false).build()) + .build(); ZenModeOtherPreferenceController controller = new ZenModeOtherPreferenceController(mContext, "modes_category_events", mBackend); diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceControllerTest.java index ceb8dbc7e88..6591b724c58 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceControllerTest.java @@ -16,23 +16,17 @@ package com.android.settings.notification.modes; -import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; - import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.Context; -import android.net.Uri; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; -import android.service.notification.ZenPolicy; import androidx.preference.Preference; -import com.android.settingslib.notification.modes.ZenMode; import com.android.settingslib.notification.modes.ZenModesBackend; import org.junit.Before; @@ -72,13 +66,7 @@ public final class ZenModePeopleLinkPreferenceControllerTest { @EnableFlags(Flags.FLAG_MODES_UI) public void testHasSummary() { Preference pref = mock(Preference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build()) - .build(), true); - mController.updateZenMode(pref, zenMode); + mController.updateZenMode(pref, TestModeBuilder.EXAMPLE); verify(pref).setSummary(any()); } } \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceControllerTest.java index 8317a143ead..04df27ec229 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceControllerTest.java @@ -16,7 +16,6 @@ package com.android.settings.notification.modes; -import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE; import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT; import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_NONE; @@ -40,11 +39,9 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.Context; import android.database.Cursor; -import android.net.Uri; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.service.notification.ZenPolicy; @@ -441,20 +438,17 @@ public final class ZenModePrioritySendersPreferenceControllerTest { @Test public void testPreferenceClick_passesCorrectCheckedState_startingUnchecked_messages() { - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder() - .disallowAllSounds() - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder() + .disallowAllSounds() + .build()) + .build(); mMessagesController.displayPreference(mPreferenceScreen); mMessagesController.updateZenMode(mMessagesPrefCategory, zenMode); assertThat(((SelectorWithWidgetPreference) mMessagesPrefCategory.findPreference(KEY_NONE)) - .isChecked()); + .isChecked()).isTrue(); mMessagesPrefCategory.findPreference(KEY_STARRED).performClick(); @@ -466,14 +460,11 @@ public final class ZenModePrioritySendersPreferenceControllerTest { @Test public void testPreferenceClick_passesCorrectCheckedState_startingChecked_messages() { - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder() - .allowAllSounds() - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder() + .allowAllSounds() + .build()) + .build(); mMessagesController.displayPreference(mPreferenceScreen); mMessagesController.updateZenMode(mMessagesPrefCategory, zenMode); @@ -492,14 +483,11 @@ public final class ZenModePrioritySendersPreferenceControllerTest { @Test public void testPreferenceClick_passesCorrectCheckedState_startingUnchecked_calls() { - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder() - .disallowAllSounds() - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder() + .disallowAllSounds() + .build()) + .build(); mCallsController.displayPreference(mPreferenceScreen); mCallsController.updateZenMode(mCallsPrefCategory, zenMode); @@ -517,14 +505,11 @@ public final class ZenModePrioritySendersPreferenceControllerTest { @Test public void testPreferenceClick_passesCorrectCheckedState_startingChecked_calls() { - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder() - .disallowAllSounds() - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder() + .disallowAllSounds() + .build()) + .build(); mCallsController.displayPreference(mPreferenceScreen); mCallsController.updateZenMode(mCallsPrefCategory, zenMode); diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeRepeatCallersPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeRepeatCallersPreferenceControllerTest.java index c86b57bbeba..c1b99e5ee19 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeRepeatCallersPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeRepeatCallersPreferenceControllerTest.java @@ -16,7 +16,6 @@ package com.android.settings.notification.modes; -import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE; import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED; import static android.service.notification.ZenPolicy.STATE_DISALLOW; @@ -27,10 +26,8 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.Context; -import android.net.Uri; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.service.notification.ZenPolicy; @@ -70,14 +67,11 @@ public final class ZenModeRepeatCallersPreferenceControllerTest { @Test public void testUpdateState_allCalls() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder() - .allowCalls(PEOPLE_TYPE_ANYONE) - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder() + .allowCalls(PEOPLE_TYPE_ANYONE) + .build()) + .build(); ZenModeRepeatCallersPreferenceController controller = new ZenModeRepeatCallersPreferenceController(mContext, "repeat", mBackend, 1); @@ -91,15 +85,12 @@ public final class ZenModeRepeatCallersPreferenceControllerTest { @Test public void testUpdateState_someCalls() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder() - .allowCalls(PEOPLE_TYPE_STARRED) - .allowRepeatCallers(true) - .build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder() + .allowCalls(PEOPLE_TYPE_STARRED) + .allowRepeatCallers(true) + .build()) + .build(); ZenModeRepeatCallersPreferenceController controller = new ZenModeRepeatCallersPreferenceController(mContext, "repeat", mBackend, 1); @@ -113,12 +104,9 @@ public final class ZenModeRepeatCallersPreferenceControllerTest { @Test public void testOnPreferenceChange() { TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(true).build()) - .build(), true); + ZenMode zenMode = new TestModeBuilder() + .setZenPolicy(new ZenPolicy.Builder().allowRepeatCallers(true).build()) + .build(); ZenModeRepeatCallersPreferenceController controller = new ZenModeRepeatCallersPreferenceController(mContext, "repeat", mBackend, 1); @@ -136,4 +124,4 @@ public final class ZenModeRepeatCallersPreferenceControllerTest { assertThat(captor.getValue().getPolicy().getPriorityCallSenders()) .isEqualTo(STATE_UNSET); } -} \ No newline at end of file +} diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetCalendarPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetCalendarPreferenceControllerTest.java index 9e1d94af09e..cc6a49791cd 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetCalendarPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetCalendarPreferenceControllerTest.java @@ -28,10 +28,8 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.Context; -import android.net.Uri; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.service.notification.SystemZenRules; @@ -88,11 +86,9 @@ public class ZenModeSetCalendarPreferenceControllerTest { @Test @EnableFlags({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI}) public void updateEventMode_updatesConditionAndTriggerDescription() { - ZenMode mode = new ZenMode("id", - new AutomaticZenRule.Builder("name", Uri.parse("condition")) - .setPackage(SystemZenRules.PACKAGE_ANDROID) - .build(), - true); // is active + ZenMode mode = new TestModeBuilder() + .setPackage(SystemZenRules.PACKAGE_ANDROID) + .build(); // Explicitly update preference controller with mode info first, which will also call // updateState() @@ -118,10 +114,9 @@ public class ZenModeSetCalendarPreferenceControllerTest { eventInfo.calName = "Definitely A Calendar"; eventInfo.reply = REPLY_YES; - ZenMode mode = new ZenMode("id", - new AutomaticZenRule.Builder("name", - ZenModeConfig.toEventConditionId(eventInfo)).build(), - true); // is active + ZenMode mode = new TestModeBuilder() + .setConditionId(ZenModeConfig.toEventConditionId(eventInfo)) + .build(); mPrefController.updateZenMode(mPrefCategory, mode); // We should see mCalendar, mReply have their values set diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetSchedulePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetSchedulePreferenceControllerTest.java index 519bb433ffe..7dbc80262de 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetSchedulePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetSchedulePreferenceControllerTest.java @@ -23,10 +23,8 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.Context; -import android.net.Uri; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.service.notification.SystemZenRules; @@ -83,11 +81,9 @@ public class ZenModeSetSchedulePreferenceControllerTest { @Test @EnableFlags({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI}) public void updateScheduleRule_updatesConditionAndTriggerDescription() { - ZenMode mode = new ZenMode("id", - new AutomaticZenRule.Builder("name", Uri.parse("condition")) - .setPackage(SystemZenRules.PACKAGE_ANDROID) - .build(), - true); // is active + ZenMode mode = new TestModeBuilder() + .setPackage(SystemZenRules.PACKAGE_ANDROID) + .build(); ZenModeConfig.ScheduleInfo scheduleInfo = new ZenModeConfig.ScheduleInfo(); scheduleInfo.days = new int[] { Calendar.MONDAY }; diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceControllerTest.java index 7782a9b2cf2..31959e5d699 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceControllerTest.java @@ -37,7 +37,6 @@ import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.service.notification.SystemZenRules; import android.service.notification.ZenModeConfig; -import android.service.notification.ZenPolicy; import androidx.preference.PreferenceCategory; import androidx.test.core.app.ApplicationProvider; @@ -102,26 +101,13 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest { assertThat(mPrefController.isAvailable()).isFalse(); // should be available for other modes - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowAlarms(true).build()) - .setEnabled(false) - .build(), false); - mPrefController.updateZenMode(mPrefCategory, zenMode); + mPrefController.updateZenMode(mPrefCategory, TestModeBuilder.EXAMPLE); assertThat(mPrefController.isAvailable()).isTrue(); } @Test public void testUpdateState() { - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowAlarms(true).build()) - .setEnabled(false) - .build(), false); + ZenMode zenMode = new TestModeBuilder().setEnabled(false).build(); // Update preference controller with a zen mode that is not enabled mPrefController.updateZenMode(mPrefCategory, zenMode); @@ -135,13 +121,7 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest { @Test public void testOnPreferenceChange() { - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowAlarms(true).build()) - .setEnabled(false) - .build(), false); + ZenMode zenMode = new TestModeBuilder().setEnabled(false).build(); // start with disabled rule mPrefController.updateZenMode(mPrefCategory, zenMode); @@ -160,13 +140,12 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest { ZenModeConfig.EventInfo eventInfo = new ZenModeConfig.EventInfo(); eventInfo.calendarId = 1L; eventInfo.calName = "My events"; - ZenMode mode = new ZenMode("id", new AutomaticZenRule.Builder("name", - ZenModeConfig.toEventConditionId(eventInfo)) + ZenMode mode = new TestModeBuilder() .setPackage(SystemZenRules.PACKAGE_ANDROID) + .setConditionId(ZenModeConfig.toEventConditionId(eventInfo)) .setType(TYPE_SCHEDULE_CALENDAR) .setTriggerDescription("My events") - .build(), - true); // is active + .build(); mPrefController.updateZenMode(mPrefCategory, mode); assertThat(mPreference.getTitle()).isNotNull(); @@ -188,13 +167,12 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest { scheduleInfo.days = new int[] { Calendar.MONDAY, Calendar.TUESDAY, Calendar.THURSDAY }; scheduleInfo.startHour = 1; scheduleInfo.endHour = 15; - ZenMode mode = new ZenMode("id", new AutomaticZenRule.Builder("name", - ZenModeConfig.toScheduleConditionId(scheduleInfo)) + ZenMode mode = new TestModeBuilder() + .setConditionId(ZenModeConfig.toScheduleConditionId(scheduleInfo)) .setPackage(SystemZenRules.PACKAGE_ANDROID) .setType(TYPE_SCHEDULE_TIME) .setTriggerDescription("some schedule") - .build(), - true); // is active + .build(); mPrefController.updateZenMode(mPrefCategory, mode); assertThat(mPreference.getTitle()).isNotNull(); @@ -212,13 +190,12 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest { @Test public void testRuleLink_manual() { - ZenMode mode = new ZenMode("id", new AutomaticZenRule.Builder("name", - ZenModeConfig.toCustomManualConditionId()) + ZenMode mode = new TestModeBuilder() + .setConditionId(ZenModeConfig.toCustomManualConditionId()) .setPackage(SystemZenRules.PACKAGE_ANDROID) .setType(TYPE_OTHER) .setTriggerDescription("Will not be shown") - .build(), - true); // is active + .build(); mPrefController.updateZenMode(mPrefCategory, mode); assertThat(mPreference.getTitle()).isNotNull(); @@ -234,13 +211,12 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest { @Test public void onScheduleChosen_updatesMode() { - ZenMode originalMode = new ZenMode("id", - new AutomaticZenRule.Builder("name", ZenModeConfig.toCustomManualConditionId()) - .setPackage(SystemZenRules.PACKAGE_ANDROID) - .setType(TYPE_OTHER) - .setTriggerDescription("") - .build(), - false); + ZenMode originalMode = new TestModeBuilder() + .setConditionId(ZenModeConfig.toCustomManualConditionId()) + .setPackage(SystemZenRules.PACKAGE_ANDROID) + .setType(TYPE_OTHER) + .setTriggerDescription("") + .build(); mPrefController.updateZenMode(mPrefCategory, originalMode); ZenModeConfig.ScheduleInfo scheduleInfo = new ZenModeConfig.ScheduleInfo(); diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModesListItemPreferenceTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModesListItemPreferenceTest.java new file mode 100644 index 00000000000..495a24c8c34 --- /dev/null +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModesListItemPreferenceTest.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.notification.modes; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.service.notification.ZenModeConfig; + +import com.android.settingslib.notification.modes.ZenMode; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.shadows.ShadowLooper; + +@RunWith(RobolectricTestRunner.class) +public class ZenModesListItemPreferenceTest { + + private Context mContext; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application; + } + + @Test + public void constructor_setsMode() { + ZenModesListItemPreference preference = new ZenModesListItemPreference(mContext, + TestModeBuilder.EXAMPLE); + + assertThat(preference.getKey()).isEqualTo(TestModeBuilder.EXAMPLE.getId()); + assertThat(preference.getZenMode()).isEqualTo(TestModeBuilder.EXAMPLE); + } + + @Test + public void setZenMode_modeEnabled() { + ZenMode mode = new TestModeBuilder() + .setName("Enabled mode") + .setTriggerDescription("When the thrush knocks") + .setEnabled(true) + .build(); + + ZenModesListItemPreference preference = new ZenModesListItemPreference(mContext, mode); + ShadowLooper.idleMainLooper(); // To load icon. + + assertThat(preference.getTitle()).isEqualTo("Enabled mode"); + assertThat(preference.getSummary()).isEqualTo("When the thrush knocks"); + assertThat(preference.getIcon()).isNotNull(); + } + + @Test + public void setZenMode_modeActive() { + ZenMode mode = new TestModeBuilder() + .setName("Active mode") + .setTriggerDescription("When Birnam forest comes to Dunsinane") + .setEnabled(true) + .setActive(true) + .build(); + + ZenModesListItemPreference preference = new ZenModesListItemPreference(mContext, mode); + ShadowLooper.idleMainLooper(); + + assertThat(preference.getTitle()).isEqualTo("Active mode"); + assertThat(preference.getSummary()).isEqualTo("ON • When Birnam forest comes to Dunsinane"); + assertThat(preference.getIcon()).isNotNull(); + } + + @Test + public void setZenMode_modeDisabledByApp() { + ZenModeConfig.ZenRule configRule = new ZenModeConfig.ZenRule(); + configRule.enabled = false; + configRule.disabledOrigin = ZenModeConfig.UPDATE_ORIGIN_APP; + ZenMode mode = new TestModeBuilder() + .setName("Mode disabled by app") + .setTriggerDescription("When the cat's away") + .setEnabled(false) + .setConfigZenRule(configRule) + .build(); + + ZenModesListItemPreference preference = new ZenModesListItemPreference(mContext, mode); + ShadowLooper.idleMainLooper(); + + assertThat(preference.getTitle()).isEqualTo("Mode disabled by app"); + assertThat(preference.getSummary()).isEqualTo("Tap to set up"); + assertThat(preference.getIcon()).isNotNull(); + } + + @Test + public void setZenMode_modeDisabledByUser() { + ZenModeConfig.ZenRule configRule = new ZenModeConfig.ZenRule(); + configRule.enabled = false; + configRule.disabledOrigin = ZenModeConfig.UPDATE_ORIGIN_USER; + ZenMode mode = new TestModeBuilder() + .setName("Mode disabled by user") + .setTriggerDescription("When the Levee Breaks") + .setEnabled(false) + .setConfigZenRule(configRule) + .build(); + + ZenModesListItemPreference preference = new ZenModesListItemPreference(mContext, mode); + ShadowLooper.idleMainLooper(); + + assertThat(preference.getTitle()).isEqualTo("Mode disabled by user"); + assertThat(preference.getSummary()).isEqualTo("Paused"); + assertThat(preference.getIcon()).isNotNull(); + } +} diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModesListPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModesListPreferenceControllerTest.java index 04d5f0ae22c..f2624acd9e9 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModesListPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModesListPreferenceControllerTest.java @@ -60,14 +60,15 @@ import java.util.List; public class ZenModesListPreferenceControllerTest { private static final String TEST_MODE_ID = "test_mode"; private static final String TEST_MODE_NAME = "Test Mode"; - private static final ZenMode TEST_MODE = new ZenMode( - TEST_MODE_ID, - new AutomaticZenRule.Builder(TEST_MODE_NAME, Uri.parse("test_uri")) + + private static final ZenMode TEST_MODE = new TestModeBuilder() + .setId(TEST_MODE_ID) + .setAzr(new AutomaticZenRule.Builder(TEST_MODE_NAME, Uri.parse("test_uri")) .setType(AutomaticZenRule.TYPE_BEDTIME) .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build()) - .build(), - false); + .build()) + .build(); private static final ZenMode TEST_MANUAL_MODE = ZenMode.manualDndMode( new AutomaticZenRule.Builder("Do Not Disturb", Uri.EMPTY) @@ -112,14 +113,9 @@ public class ZenModesListPreferenceControllerTest { assertThat(mPreference.getPreferenceCount()).isEqualTo(5); List itemPreferences = getModeListItems(mPreference); - assertThat(itemPreferences.stream().map(pref -> pref.mZenMode).toList()) + assertThat(itemPreferences.stream().map(ZenModesListItemPreference::getZenMode).toList()) .containsExactlyElementsIn(modes) .inOrder(); - - for (int i = 0; i < modes.size(); i++) { - assertThat(((ZenModesListItemPreference) (mPreference.getPreference(i))).mZenMode) - .isEqualTo(modes.get(i)); - } } @Test @@ -140,7 +136,7 @@ public class ZenModesListPreferenceControllerTest { mPrefController.updateState(mPreference); List newPreferences = getModeListItems(mPreference); - assertThat(newPreferences.stream().map(pref -> pref.mZenMode).toList()) + assertThat(newPreferences.stream().map(ZenModesListItemPreference::getZenMode).toList()) .containsExactlyElementsIn(updatedModes) .inOrder(); @@ -196,7 +192,7 @@ public class ZenModesListPreferenceControllerTest { assertThat(newData).hasSize(1); SearchIndexableRaw newItem = newData.get(0); - assertThat(newItem.key).isEqualTo(ZenMode.MANUAL_DND_MODE_ID); + assertThat(newItem.key).isEqualTo(TEST_MANUAL_MODE.getId()); assertThat(newItem.title).isEqualTo("Do Not Disturb"); // set above } @@ -211,7 +207,7 @@ public class ZenModesListPreferenceControllerTest { // Should keep the order presented by getModes() SearchIndexableRaw item0 = data.get(0); - assertThat(item0.key).isEqualTo(ZenMode.MANUAL_DND_MODE_ID); + assertThat(item0.key).isEqualTo(TEST_MANUAL_MODE.getId()); assertThat(item0.title).isEqualTo("Do Not Disturb"); // set above SearchIndexableRaw item1 = data.get(1); @@ -220,13 +216,7 @@ public class ZenModesListPreferenceControllerTest { } private static ZenMode newMode(String id) { - return new ZenMode( - id, - new AutomaticZenRule.Builder("Mode " + id, Uri.parse("test_uri")) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build()) - .build(), - false); + return new TestModeBuilder().setId(id).setName("Mode " + id).build(); } /** diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java index 2822e003ee7..62b5ee02ee6 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java @@ -16,7 +16,6 @@ package com.android.settings.notification.modes; -import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE; import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE; import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS; @@ -25,9 +24,7 @@ import static android.service.notification.ZenPolicy.VISUAL_EFFECT_LIGHTS; import static com.google.common.truth.Truth.assertThat; -import android.app.AutomaticZenRule; import android.content.Context; -import android.net.Uri; import android.service.notification.ZenDeviceEffects; import android.service.notification.ZenPolicy; @@ -59,50 +56,38 @@ public class ZenModesSummaryHelperTest { @Test public void getPeopleSummary_noOne() { - AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed")) - .setType(AutomaticZenRule.TYPE_BEDTIME) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + ZenMode zenMode = new TestModeBuilder() .setZenPolicy(new ZenPolicy.Builder().disallowAllSounds().build()) .build(); - ZenMode zenMode = new ZenMode("id", rule, true); assertThat(mSummaryHelper.getPeopleSummary(zenMode)).isEqualTo("No one can interrupt"); } @Test public void getPeopleSummary_some() { - AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed")) - .setType(AutomaticZenRule.TYPE_BEDTIME) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + ZenMode zenMode = new TestModeBuilder() .setZenPolicy(new ZenPolicy.Builder().allowCalls(PEOPLE_TYPE_CONTACTS).build()) .build(); - ZenMode zenMode = new ZenMode("id", rule, true); assertThat(mSummaryHelper.getPeopleSummary(zenMode)).isEqualTo("Some people can interrupt"); } @Test public void getPeopleSummary_all() { - AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed")) - .setType(AutomaticZenRule.TYPE_BEDTIME) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + ZenMode zenMode = new TestModeBuilder() .setZenPolicy(new ZenPolicy.Builder().allowCalls(PEOPLE_TYPE_ANYONE). allowConversations(CONVERSATION_SENDERS_ANYONE) .allowMessages(PEOPLE_TYPE_ANYONE).build()) .build(); - ZenMode zenMode = new ZenMode("id", rule, true); assertThat(mSummaryHelper.getPeopleSummary(zenMode)).isEqualTo("All people can interrupt"); } @Test public void getOtherSoundCategoriesSummary_single() { - AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed")) - .setType(AutomaticZenRule.TYPE_BEDTIME) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + ZenMode zenMode = new TestModeBuilder() .setZenPolicy(new ZenPolicy.Builder().allowAlarms(true).build()) .build(); - ZenMode zenMode = new ZenMode("id", rule, true); assertThat(mSummaryHelper.getOtherSoundCategoriesSummary(zenMode)).isEqualTo( "Alarms can interrupt"); @@ -110,12 +95,9 @@ public class ZenModesSummaryHelperTest { @Test public void getOtherSoundCategoriesSummary_duo() { - AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed")) - .setType(AutomaticZenRule.TYPE_BEDTIME) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + ZenMode zenMode = new TestModeBuilder() .setZenPolicy(new ZenPolicy.Builder().allowAlarms(true).allowMedia(true).build()) .build(); - ZenMode zenMode = new ZenMode("id", rule, true); assertThat(mSummaryHelper.getOtherSoundCategoriesSummary(zenMode)).isEqualTo( "Alarms and media can interrupt"); @@ -123,16 +105,13 @@ public class ZenModesSummaryHelperTest { @Test public void getOtherSoundCategoriesSummary_trio() { - AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed")) - .setType(AutomaticZenRule.TYPE_BEDTIME) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + ZenMode zenMode = new TestModeBuilder() .setZenPolicy(new ZenPolicy.Builder() .allowAlarms(true) .allowMedia(true) .allowSystem(true) .build()) .build(); - ZenMode zenMode = new ZenMode("id", rule, true); assertThat(mSummaryHelper.getOtherSoundCategoriesSummary(zenMode)).isEqualTo( "Alarms, media, and touch sounds can interrupt"); @@ -140,9 +119,7 @@ public class ZenModesSummaryHelperTest { @Test public void getOtherSoundCategoriesSummary_quad() { - AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed")) - .setType(AutomaticZenRule.TYPE_BEDTIME) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + ZenMode zenMode = new TestModeBuilder() .setZenPolicy(new ZenPolicy.Builder() .allowAlarms(true) .allowMedia(true) @@ -150,7 +127,6 @@ public class ZenModesSummaryHelperTest { .allowReminders(true) .build()) .build(); - ZenMode zenMode = new ZenMode("id", rule, true); assertThat(mSummaryHelper.getOtherSoundCategoriesSummary(zenMode)).isEqualTo( "Alarms, media, and 2 more can interrupt"); @@ -158,9 +134,7 @@ public class ZenModesSummaryHelperTest { @Test public void getOtherSoundCategoriesSummary_all() { - AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed")) - .setType(AutomaticZenRule.TYPE_BEDTIME) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + ZenMode zenMode = new TestModeBuilder() .setZenPolicy(new ZenPolicy.Builder() .allowAlarms(true) .allowMedia(true) @@ -169,7 +143,6 @@ public class ZenModesSummaryHelperTest { .allowEvents(true) .build()) .build(); - ZenMode zenMode = new ZenMode("id", rule, true); assertThat(mSummaryHelper.getOtherSoundCategoriesSummary(zenMode)).isEqualTo( "Alarms, media, and 3 more can interrupt"); @@ -177,61 +150,52 @@ public class ZenModesSummaryHelperTest { @Test public void getBlockedEffectsSummary_none() { - AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed")) - .setType(AutomaticZenRule.TYPE_BEDTIME) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + ZenMode zenMode = new TestModeBuilder() .setZenPolicy(new ZenPolicy.Builder() .showAllVisualEffects() .allowAlarms(true) .build()) .build(); - ZenMode zenMode = new ZenMode("id", rule, true); + assertThat(mSummaryHelper.getBlockedEffectsSummary(zenMode)) .isEqualTo("Notifications shown"); } @Test public void getBlockedEffectsSummary_some() { - AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed")) - .setType(AutomaticZenRule.TYPE_BEDTIME) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + ZenMode zenMode = new TestModeBuilder() .setZenPolicy(new ZenPolicy.Builder() .allowAlarms(true) .showAllVisualEffects() .showVisualEffect(VISUAL_EFFECT_AMBIENT, false) .build()) .build(); - ZenMode zenMode = new ZenMode("id", rule, true); + assertThat(mSummaryHelper.getBlockedEffectsSummary(zenMode)) .isEqualTo("Notifications partially hidden"); } @Test public void getBlockedEffectsSummary_all() { - AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed")) - .setType(AutomaticZenRule.TYPE_BEDTIME) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + ZenMode zenMode = new TestModeBuilder() .setZenPolicy(new ZenPolicy.Builder() .allowAlarms(true) .hideAllVisualEffects() .build()) .build(); - ZenMode zenMode = new ZenMode("id", rule, true); + assertThat(mSummaryHelper.getBlockedEffectsSummary(zenMode)) .isEqualTo("Notifications hidden"); } @Test public void getDisplayEffectsSummary_single_notifVis() { - AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed")) - .setType(AutomaticZenRule.TYPE_BEDTIME) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + ZenMode zenMode = new TestModeBuilder() .setZenPolicy(new ZenPolicy.Builder() .showAllVisualEffects() .showVisualEffect(VISUAL_EFFECT_AMBIENT, false) .build()) .build(); - ZenMode zenMode = new ZenMode("id", rule, true); assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo( "Notifications partially hidden"); @@ -239,15 +203,12 @@ public class ZenModesSummaryHelperTest { @Test public void getDisplayEffectsSummary_single_notifVis_unusedEffect() { - AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed")) - .setType(AutomaticZenRule.TYPE_BEDTIME) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + ZenMode zenMode = new TestModeBuilder() .setZenPolicy(new ZenPolicy.Builder() .showAllVisualEffects() .showVisualEffect(VISUAL_EFFECT_LIGHTS, false) .build()) .build(); - ZenMode zenMode = new ZenMode("id", rule, true); assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo( "Notifications shown"); @@ -255,15 +216,12 @@ public class ZenModesSummaryHelperTest { @Test public void getDisplayEffectsSummary_single_displayEffect() { - AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed")) - .setType(AutomaticZenRule.TYPE_BEDTIME) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + ZenMode zenMode = new TestModeBuilder() .setZenPolicy(new ZenPolicy.Builder().showAllVisualEffects().build()) .setDeviceEffects(new ZenDeviceEffects.Builder() .setShouldDimWallpaper(true) .build()) .build(); - ZenMode zenMode = new ZenMode("id", rule, true); assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo( "Dim the wallpaper"); @@ -271,16 +229,13 @@ public class ZenModesSummaryHelperTest { @Test public void getDisplayEffectsSummary_duo() { - AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed")) - .setType(AutomaticZenRule.TYPE_BEDTIME) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + ZenMode zenMode = new TestModeBuilder() .setZenPolicy(new ZenPolicy.Builder().showAllVisualEffects().build()) .setDeviceEffects(new ZenDeviceEffects.Builder() .setShouldDimWallpaper(true) .setShouldDisplayGrayscale(true) .build()) .build(); - ZenMode zenMode = new ZenMode("id", rule, true); assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo( "Grayscale and dim the wallpaper"); @@ -288,9 +243,7 @@ public class ZenModesSummaryHelperTest { @Test public void getDisplayEffectsSummary_trio() { - AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed")) - .setType(AutomaticZenRule.TYPE_BEDTIME) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + ZenMode zenMode = new TestModeBuilder() .setZenPolicy(new ZenPolicy.Builder() .hideAllVisualEffects() .allowAlarms(true) @@ -302,7 +255,6 @@ public class ZenModesSummaryHelperTest { .setShouldDimWallpaper(true) .build()) .build(); - ZenMode zenMode = new ZenMode("id", rule, true); assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo( "Notifications hidden, grayscale, and dim the wallpaper"); @@ -310,9 +262,7 @@ public class ZenModesSummaryHelperTest { @Test public void getDisplayEffectsSummary_quad() { - AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed")) - .setType(AutomaticZenRule.TYPE_BEDTIME) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + ZenMode zenMode = new TestModeBuilder() .setZenPolicy(new ZenPolicy.Builder() .showAllVisualEffects() .showVisualEffect(VISUAL_EFFECT_AMBIENT, false) @@ -326,7 +276,6 @@ public class ZenModesSummaryHelperTest { .setShouldUseNightMode(true) .build()) .build(); - ZenMode zenMode = new ZenMode("id", rule, true); assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo( "Notifications partially hidden, grayscale, and 2 more"); @@ -334,28 +283,22 @@ public class ZenModesSummaryHelperTest { @Test public void getAppsSummary_none() { - AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed")) - .setType(AutomaticZenRule.TYPE_BEDTIME) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + ZenMode zenMode = new TestModeBuilder() .setZenPolicy(new ZenPolicy.Builder() .allowChannels(ZenPolicy.CHANNEL_POLICY_NONE) .build()) .build(); - ZenMode zenMode = new ZenMode("id", rule, true); assertThat(mSummaryHelper.getAppsSummary(zenMode, new LinkedHashSet<>())).isEqualTo("None"); } @Test public void getAppsSummary_priorityAppsNoList() { - AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed")) - .setType(AutomaticZenRule.TYPE_BEDTIME) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + ZenMode zenMode = new TestModeBuilder() .setZenPolicy(new ZenPolicy.Builder() .allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY) .build()) .build(); - ZenMode zenMode = new ZenMode("id", rule, true); assertThat(mSummaryHelper.getAppsSummary(zenMode, null)).isEqualTo("Selected apps"); } @@ -397,19 +340,15 @@ public class ZenModesSummaryHelperTest { @Test public void getAppsSummary_priorityApps() { - AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed")) - .setType(AutomaticZenRule.TYPE_BEDTIME) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + ZenMode zenMode = new TestModeBuilder() .setZenPolicy(new ZenPolicy.Builder() .allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY) .build()) .build(); - ZenMode zenMode = new ZenMode("id", rule, true); Set apps = Set.of("My App", "SecondApp", "ThirdApp", "FourthApp", "FifthApp", "SixthApp"); assertThat(mSummaryHelper.getAppsSummary(zenMode, apps)).isEqualTo("FifthApp, FourthApp, " + "and 4 more can interrupt"); } - } diff --git a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java index 4497a0a62b9..4440bc94ca0 100644 --- a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java +++ b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java @@ -48,6 +48,8 @@ import android.telephony.TelephonyManager; import android.telephony.ims.ImsMmTelManager; import android.view.View; +import androidx.annotation.NonNull; +import androidx.lifecycle.LifecycleOwner; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; @@ -56,10 +58,14 @@ import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.network.ims.MockWifiCallingQueryImsState; import com.android.settings.network.ims.WifiCallingQueryImsState; +import com.android.settings.network.telephony.wificalling.IWifiCallingRepository; import com.android.settings.testutils.shadow.ShadowFragment; import com.android.settings.widget.SettingsMainSwitchBar; import com.android.settings.widget.SettingsMainSwitchPreference; +import kotlin.Unit; +import kotlin.jvm.functions.Function1; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -183,35 +189,24 @@ public class WifiCallingSettingsForSubTest { } @Test - public void onResume_provisioningAllowed_shouldNotFinish() { - // Call onResume while provisioning is allowed. - mFragment.onResume(); + public void onViewCreated_provisioningAllowed_shouldNotFinish() { + // Call onViewCreated while provisioning is allowed. + mFragment.onViewCreated(mView, null); // Verify that finish() is not called. verify(mFragment, never()).finish(); } @Test - public void onResume_provisioningDisallowed_shouldFinish() { - // Call onResume while provisioning is disallowed. - mQueryImsState.setIsProvisionedOnDevice(false); - mFragment.onResume(); + public void onViewCreated_provisioningDisallowed_shouldFinish() { + // Call onViewCreated while provisioning is disallowed. + mFragment.mIsWifiCallingReady = false; + mFragment.onViewCreated(mView, null); // Verify that finish() is called verify(mFragment).finish(); } - @Test - public void onResumeOnPause_provisioningCallbackRegistration() throws Exception { - // Verify that provisioning callback is registered after call to onResume(). - mFragment.onResume(); - verify(mFragment).registerProvisioningChangedCallback(); - - // Verify that provisioning callback is unregistered after call to onPause. - mFragment.onPause(); - verify(mFragment).unregisterProvisioningChangedCallback(); - } - @Test public void onResume_useWfcHomeModeConfigFalseAndEditable_shouldShowWfcRoaming() { // Call onResume to update the WFC roaming preference. @@ -377,6 +372,7 @@ public class WifiCallingSettingsForSubTest { protected class TestFragment extends WifiCallingSettingsForSub { private SettingsMainSwitchPreference mSwitchPref; + protected boolean mIsWifiCallingReady = true; protected void setSwitchBar(SettingsMainSwitchPreference switchPref) { mSwitchPref = switchPref; @@ -421,6 +417,25 @@ public class WifiCallingSettingsForSubTest { return mQueryImsState; } + @Override + @NonNull + IWifiCallingRepository getWifiCallingRepository() { + return new IWifiCallingRepository() { + @Override + public void collectIsWifiCallingReadyFlow( + @NonNull LifecycleOwner lifecycleOwner, + @NonNull Function1 action) { + action.invoke(mIsWifiCallingReady); + } + }; + } + + @NonNull + @Override + LifecycleOwner getLifecycleOwner() { + return this; + } + @Override void showAlert(Intent intent) { }