diff --git a/res/values/strings.xml b/res/values/strings.xml index fa0adac9fd9..f92fd2aca84 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -9214,6 +9214,16 @@ None All + + + {count, plural, offset:2 + =0 {No apps can interrupt} + =1 {{app_1} can interrupt} + =2 {{app_1} and {app_2} can interrupt} + =3 {{app_1}, {app_2}, and {app_3} can interrupt} + other {{app_1}, {app_2}, and # more can interrupt} + } + Allow apps to override diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceController.java index b623ba85b15..6ba7183e897 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceController.java @@ -72,7 +72,7 @@ public class AudioSharingCallAudioPreferenceController extends AudioSharingBaseP private static final String PREF_KEY = "calls_and_alarms"; @VisibleForTesting - protected enum ChangeCallAudioType { + enum ChangeCallAudioType { UNKNOWN, CONNECTED_EARLIER, CONNECTED_LATER @@ -90,7 +90,9 @@ public class AudioSharingCallAudioPreferenceController extends AudioSharingBaseP Map> mGroupedConnectedDevices = new HashMap<>(); private List mDeviceItemsInSharingSession = new ArrayList<>(); private final AtomicBoolean mCallbacksRegistered = new AtomicBoolean(false); - private final BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback = + + @VisibleForTesting + final BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback = new BluetoothLeBroadcastAssistant.Callback() { @Override public void onSearchStarted(int reason) {} @@ -276,7 +278,7 @@ public class AudioSharingCallAudioPreferenceController extends AudioSharingBaseP /** Test only: set callback registration status in tests. */ @VisibleForTesting - public void setCallbacksRegistered(boolean registered) { + void setCallbacksRegistered(boolean registered) { mCallbacksRegistered.set(registered); } @@ -385,7 +387,7 @@ public class AudioSharingCallAudioPreferenceController extends AudioSharingBaseP } @VisibleForTesting - protected void logCallAudioDeviceChange(int currentGroupId, CachedBluetoothDevice target) { + void logCallAudioDeviceChange(int currentGroupId, CachedBluetoothDevice target) { var unused = ThreadUtils.postOnBackgroundThread( () -> { diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCompatibilityPreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCompatibilityPreferenceController.java index 581ad62bb31..42a90387714 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCompatibilityPreferenceController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCompatibilityPreferenceController.java @@ -60,7 +60,7 @@ public class AudioSharingCompatibilityPreferenceController extends TogglePrefere private final AtomicBoolean mCallbacksRegistered = new AtomicBoolean(false); @VisibleForTesting - protected final BluetoothLeBroadcast.Callback mBroadcastCallback = + final BluetoothLeBroadcast.Callback mBroadcastCallback = new BluetoothLeBroadcast.Callback() { @Override public void onBroadcastStarted(int reason, int broadcastId) { @@ -219,7 +219,7 @@ public class AudioSharingCompatibilityPreferenceController extends TogglePrefere /** Test only: set callbacks registration state for test setup. */ @VisibleForTesting - public void setCallbacksRegistered(boolean registered) { + void setCallbacksRegistered(boolean registered) { mCallbacksRegistered.set(registered); } diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java index c7d740740e7..ad41e8a2997 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java @@ -103,7 +103,7 @@ public class AudioSharingDashboardFragment extends DashboardFragment /** Test only: set mock controllers for the {@link AudioSharingDashboardFragment} */ @VisibleForTesting - protected void setControllers( + void setControllers( AudioSharingDeviceVolumeGroupController volumeGroupController, AudioSharingCallAudioPreferenceController callAudioController, AudioSharingPlaySoundPreferenceController playSoundController, diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java index 51a8e117522..b932a7e28ef 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java @@ -87,7 +87,8 @@ public class AudioSharingDevicePreferenceController extends BasePreferenceContro @Nullable private AudioSharingDialogHandler mDialogHandler; private AtomicBoolean mIntentHandled = new AtomicBoolean(false); - private BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback = + @VisibleForTesting + BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback = new BluetoothLeBroadcastAssistant.Callback() { @Override public void onSearchStarted(int reason) {} @@ -368,23 +369,23 @@ public class AudioSharingDevicePreferenceController extends BasePreferenceContro } @VisibleForTesting - public void setBluetoothDeviceUpdater(@Nullable BluetoothDeviceUpdater bluetoothDeviceUpdater) { + void setBluetoothDeviceUpdater(@Nullable BluetoothDeviceUpdater bluetoothDeviceUpdater) { mBluetoothDeviceUpdater = bluetoothDeviceUpdater; } @VisibleForTesting - public void setDialogHandler(@Nullable AudioSharingDialogHandler dialogHandler) { + void setDialogHandler(@Nullable AudioSharingDialogHandler dialogHandler) { mDialogHandler = dialogHandler; } @VisibleForTesting - public void setHostFragment(@Nullable DashboardFragment fragment) { + void setHostFragment(@Nullable DashboardFragment fragment) { mFragment = fragment; } /** Test only: set intent handle state for test. */ @VisibleForTesting - public void setIntentHandled(boolean handled) { + void setIntentHandled(boolean handled) { mIntentHandled.set(handled); } diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupController.java index 4a067ace2f2..ee2ba7bb2e3 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupController.java @@ -79,12 +79,9 @@ public class AudioSharingDeviceVolumeGroupController extends AudioSharingBasePre private Map mValueMap = new HashMap(); private AtomicBoolean mCallbacksRegistered = new AtomicBoolean(false); - private BluetoothVolumeControl.Callback mVolumeControlCallback = + @VisibleForTesting + BluetoothVolumeControl.Callback mVolumeControlCallback = new BluetoothVolumeControl.Callback() { - @Override - public void onVolumeOffsetChanged( - @NonNull BluetoothDevice device, int volumeOffset) {} - @Override public void onDeviceVolumeChanged( @NonNull BluetoothDevice device, @@ -117,7 +114,8 @@ public class AudioSharingDeviceVolumeGroupController extends AudioSharingBasePre } }; - private BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback = + @VisibleForTesting + BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback = new BluetoothLeBroadcastAssistant.Callback() { @Override public void onSearchStarted(int reason) {} @@ -323,26 +321,26 @@ public class AudioSharingDeviceVolumeGroupController extends AudioSharingBasePre } @VisibleForTesting - public void setDeviceUpdater(@Nullable AudioSharingDeviceVolumeControlUpdater updater) { + void setDeviceUpdater(@Nullable AudioSharingDeviceVolumeControlUpdater updater) { mBluetoothDeviceUpdater = updater; } /** Test only: set callback registration status in tests. */ @VisibleForTesting - public void setCallbacksRegistered(boolean registered) { + void setCallbacksRegistered(boolean registered) { mCallbacksRegistered.set(registered); } /** Test only: set volume map in tests. */ @VisibleForTesting - public void setVolumeMap(@Nullable Map map) { + void setVolumeMap(@Nullable Map map) { mValueMap.clear(); mValueMap.putAll(map); } /** Test only: set value for private preferenceGroup in tests. */ @VisibleForTesting - public void setPreferenceGroup(@Nullable PreferenceGroup group) { + void setPreferenceGroup(@Nullable PreferenceGroup group) { mPreferenceGroup = group; mPreference = group; } diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFactory.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFactory.java index 165beae9be2..2ee286d4b0e 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFactory.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFactory.java @@ -345,4 +345,6 @@ public class AudioSharingDialogFactory { return dialog; } } + + private AudioSharingDialogFactory() {} } diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java index 3d111fd8e3b..e787be30e03 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java @@ -100,7 +100,8 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment { /** Test only: get the event data passed to the dialog. */ @VisibleForTesting - protected @NonNull Pair[] getEventData() { + @NonNull + Pair[] getEventData() { return sEventData; } diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java index 5458a9f259b..8d69cf6232c 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java @@ -28,6 +28,7 @@ import android.util.Pair; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import androidx.fragment.app.DialogFragment; import androidx.fragment.app.Fragment; @@ -61,7 +62,8 @@ public class AudioSharingDialogHandler { private final MetricsFeatureProvider mMetricsFeatureProvider; private List mTargetSinks = new ArrayList<>(); - private final BluetoothLeBroadcast.Callback mBroadcastCallback = + @VisibleForTesting + final BluetoothLeBroadcast.Callback mBroadcastCallback = new BluetoothLeBroadcast.Callback() { @Override public void onBroadcastStarted(int reason, int broadcastId) { diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java index 5f6d84a1929..dcd8a3be19a 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java @@ -145,9 +145,17 @@ public class AudioSharingDisconnectDialogFragment extends InstrumentedDialogFrag return sNewDevice; } + /** Test only: get the {@link DialogEventListener} passed to the dialog. */ + @VisibleForTesting + @Nullable + DialogEventListener getListener() { + return sListener; + } + /** Test only: get the event data passed to the dialog. */ @VisibleForTesting - protected @NonNull Pair[] getEventData() { + @NonNull + Pair[] getEventData() { return sEventData; } diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java index 7eebbcb2156..ec669bf3ac0 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java @@ -110,9 +110,17 @@ public class AudioSharingJoinDialogFragment extends InstrumentedDialogFragment { return sNewDevice; } + /** Test only: get the {@link DialogEventListener} passed to the dialog. */ + @VisibleForTesting + @Nullable + DialogEventListener getListener() { + return sListener; + } + /** Test only: get the event data passed to the dialog. */ @VisibleForTesting - protected @NonNull Pair[] getEventData() { + @NonNull + Pair[] getEventData() { return sEventData; } diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPlaySoundPreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPlaySoundPreferenceController.java index 46c7f9c63cc..11b195c47dd 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPlaySoundPreferenceController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPlaySoundPreferenceController.java @@ -105,7 +105,7 @@ public class AudioSharingPlaySoundPreferenceController } @VisibleForTesting - protected void setRingtone(Ringtone ringtone) { + void setRingtone(Ringtone ringtone) { mRingtone = ringtone; } diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPreferenceController.java index d27d3a20d72..02448892243 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPreferenceController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPreferenceController.java @@ -52,7 +52,7 @@ public class AudioSharingPreferenceController extends BasePreferenceController private final Executor mExecutor; @VisibleForTesting - protected final BluetoothLeBroadcast.Callback mBroadcastCallback = + final BluetoothLeBroadcast.Callback mBroadcastCallback = new BluetoothLeBroadcast.Callback() { @Override public void onBroadcastStarted(int reason, int broadcastId) { diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java index beac4b0fd33..59593ba340f 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java @@ -142,7 +142,8 @@ public class AudioSharingStopDialogFragment extends InstrumentedDialogFragment { /** Test only: get the event data passed to the dialog. */ @VisibleForTesting - protected @NonNull Pair[] getEventData() { + @NonNull + Pair[] getEventData() { return sEventData; } diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java index 5022579ce65..89d2c953a57 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java @@ -117,7 +117,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController }; @VisibleForTesting - protected final BluetoothLeBroadcast.Callback mBroadcastCallback = + final BluetoothLeBroadcast.Callback mBroadcastCallback = new BluetoothLeBroadcast.Callback() { @Override public void onBroadcastStarted(int reason, int broadcastId) { @@ -392,7 +392,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController /** Test only: set callback registration status in tests. */ @VisibleForTesting - public void setCallbacksRegistered(boolean registered) { + void setCallbacksRegistered(boolean registered) { mCallbacksRegistered.set(registered); } diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java index 8e6019c3b48..0661de7a5e7 100644 --- a/src/com/android/settings/notification/NotificationBackend.java +++ b/src/com/android/settings/notification/NotificationBackend.java @@ -357,6 +357,19 @@ public class NotificationBackend { } } + /** + * Returns all of a user's packages that have at least one channel that will bypass DND + */ + public List getPackagesBypassingDnd(int userId, + boolean includeConversationChannels) { + try { + return sINM.getPackagesBypassingDnd(userId, includeConversationChannels); + } catch (Exception e) { + Log.w(TAG, "Error calling NoMan", e); + return new ArrayList<>(); + } + } + public void updateChannel(String pkg, int uid, NotificationChannel channel) { try { sINM.updateNotificationChannelForPackage(pkg, uid, channel); diff --git a/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java index 42b58b1346e..581fff57a26 100644 --- a/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java @@ -20,23 +20,44 @@ import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_I import android.content.Context; import android.os.Bundle; +import android.util.ArraySet; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; +import androidx.core.text.BidiFormatter; +import androidx.fragment.app.Fragment; import androidx.preference.Preference; import com.android.settings.core.SubSettingLauncher; +import com.android.settings.notification.NotificationBackend; +import com.android.settingslib.applications.ApplicationsState; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * Preference with a link and summary about what apps can break through the mode */ -public class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceController { +class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceController { + + private static final String TAG = "ZenModeAppsLinkPreferenceController"; private final ZenModeSummaryHelper mSummaryHelper; + private ApplicationsState.Session mAppSession; + private NotificationBackend mNotificationBackend = new NotificationBackend(); + private ZenMode mZenMode; + private Preference mPreference; - public ZenModeAppsLinkPreferenceController(Context context, String key, - ZenModesBackend backend) { + ZenModeAppsLinkPreferenceController(Context context, String key, Fragment host, + ApplicationsState applicationsState, ZenModesBackend backend) { super(context, key, backend); mSummaryHelper = new ZenModeSummaryHelper(mContext, mBackend); + if (applicationsState != null && host != null) { + mAppSession = applicationsState.newSession(mAppSessionCallbacks, host.getLifecycle()); + } } @Override @@ -49,6 +70,84 @@ public class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferen .setSourceMetricsCategory(0) .setArguments(bundle) .toIntent()); - preference.setSummary(mSummaryHelper.getAppsSummary(zenMode)); + mZenMode = zenMode; + mPreference = preference; + triggerUpdateAppsBypassingDndSummaryText(); } + + private void triggerUpdateAppsBypassingDndSummaryText() { + if (mAppSession == null) { + return; + } + + ApplicationsState.AppFilter filter = android.multiuser.Flags.enablePrivateSpaceFeatures() + && android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace() + ? ApplicationsState.FILTER_ENABLED_NOT_QUIET + : ApplicationsState.FILTER_ALL_ENABLED; + // We initiate a rebuild in the background here. Once the rebuild is completed, + // the onRebuildComplete() callback will be invoked, which will trigger the summary text + // to be initialized. + mAppSession.rebuild(filter, ApplicationsState.ALPHA_COMPARATOR, false); + } + + private void updateAppsBypassingDndSummaryText(List apps) { + Set appNames = getAppsBypassingDnd(apps); + mPreference.setSummary(mSummaryHelper.getAppsSummary(mZenMode, appNames)); + } + + @VisibleForTesting + ArraySet getAppsBypassingDnd(@NonNull List apps) { + ArraySet appsBypassingDnd = new ArraySet<>(); + + Map pkgLabelMap = new HashMap(); + for (ApplicationsState.AppEntry entry : apps) { + if (entry.info != null) { + pkgLabelMap.put(entry.info.packageName, entry.label); + } + } + for (String pkg : mNotificationBackend.getPackagesBypassingDnd(mContext.getUserId(), + /* includeConversationChannels= */ false)) { + // Settings may hide some packages from the user, so if they're not present here + // we skip displaying them, even if they bypass dnd. + if (pkgLabelMap.get(pkg) == null) { + continue; + } + appsBypassingDnd.add(BidiFormatter.getInstance().unicodeWrap(pkgLabelMap.get(pkg))); + } + return appsBypassingDnd; + } + + @VisibleForTesting final ApplicationsState.Callbacks mAppSessionCallbacks = + new ApplicationsState.Callbacks() { + + @Override + public void onRunningStateChanged(boolean running) { } + + @Override + public void onPackageListChanged() { + triggerUpdateAppsBypassingDndSummaryText(); + } + + @Override + public void onRebuildComplete(ArrayList apps) { + updateAppsBypassingDndSummaryText(apps); + } + + @Override + public void onPackageIconChanged() { } + + @Override + public void onPackageSizeChanged(String packageName) { } + + @Override + public void onAllSizesComputed() { } + + @Override + public void onLauncherInfoChanged() { } + + @Override + public void onLoadEntriesCompleted() { + triggerUpdateAppsBypassingDndSummaryText(); + } + }; } diff --git a/src/com/android/settings/notification/modes/ZenModeFragment.java b/src/com/android/settings/notification/modes/ZenModeFragment.java index 87165b85d72..e4d81fe51d5 100644 --- a/src/com/android/settings/notification/modes/ZenModeFragment.java +++ b/src/com/android/settings/notification/modes/ZenModeFragment.java @@ -16,11 +16,13 @@ package com.android.settings.notification.modes; +import android.app.Application; import android.app.AutomaticZenRule; import android.app.settings.SettingsEnums; import android.content.Context; import com.android.settings.R; +import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.core.AbstractPreferenceController; import java.util.ArrayList; @@ -42,7 +44,9 @@ public class ZenModeFragment extends ZenModeFragmentBase { prefControllers.add(new ZenModePeopleLinkPreferenceController( context, "zen_mode_people", mBackend)); prefControllers.add(new ZenModeAppsLinkPreferenceController( - context, "zen_mode_apps", mBackend)); + context, "zen_mode_apps", this, + ApplicationsState.getInstance((Application) context.getApplicationContext()), + mBackend)); prefControllers.add(new ZenModeOtherLinkPreferenceController( context, "zen_other_settings", mBackend)); prefControllers.add(new ZenModeDisplayLinkPreferenceController( diff --git a/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java b/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java index b4075cde656..77f364cd701 100644 --- a/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java +++ b/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java @@ -43,13 +43,18 @@ import android.icu.text.MessageFormat; import android.service.notification.ZenDeviceEffects; import android.service.notification.ZenPolicy; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.android.settings.R; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Set; import java.util.function.Predicate; class ZenModeSummaryHelper { @@ -397,12 +402,13 @@ class ZenModeSummaryHelper { } /** - * Generates a summary to display under the top level "Apps" preference for a mode. + * Generates a summary to display under the top level "Apps" preference for a mode, based + * on the given mode and provided set of apps. */ - public String getAppsSummary(ZenMode zenMode) { - // TODO: b/308819928 - Set summary using priority app list if Selected Apps Chosen. + public @NonNull String getAppsSummary(@NonNull ZenMode zenMode, + @Nullable Set appsBypassing) { if (zenMode.getPolicy().getAllowedChannels() == ZenPolicy.CHANNEL_POLICY_PRIORITY) { - return mContext.getResources().getString(R.string.zen_mode_apps_priority_apps); + return formatAppsList(appsBypassing); } else if (zenMode.getPolicy().getAllowedChannels() == ZenPolicy.CHANNEL_POLICY_NONE) { return mContext.getResources().getString(R.string.zen_mode_apps_none_apps); } else if (zenMode.getPolicy().getAllowedChannels() == ZenMode.CHANNEL_POLICY_ALL) { @@ -410,4 +416,35 @@ class ZenModeSummaryHelper { } return ""; } + + /** + * Generates a formatted string declaring which apps can interrupt in the style of + * "App, App2, and 4 more can interrupt." + * Apps selected for explicit mention are selected in order from the provided set sorted + * alphabetically. + */ + public @NonNull String formatAppsList(@Nullable Set appsBypassingDnd) { + if (appsBypassingDnd == null) { + return mContext.getResources().getString(R.string.zen_mode_apps_priority_apps); + } + final int numAppsBypassingDnd = appsBypassingDnd.size(); + String[] appsBypassingDndArr = appsBypassingDnd.toArray(new String[numAppsBypassingDnd]); + // Sorts the provided apps alphabetically. + Arrays.sort(appsBypassingDndArr); + MessageFormat msgFormat = new MessageFormat( + mContext.getString(R.string.zen_mode_apps_subtext), + Locale.getDefault()); + Map args = new HashMap<>(); + args.put("count", numAppsBypassingDnd); + if (numAppsBypassingDnd >= 1) { + args.put("app_1", appsBypassingDndArr[0]); + if (numAppsBypassingDnd >= 2) { + args.put("app_2", appsBypassingDndArr[1]); + if (numAppsBypassingDnd == 3) { + args.put("app_3", appsBypassingDndArr[2]); + } + } + } + return msgFormat.format(args); + } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingBluetoothDeviceUpdaterTest.java index 23be20846fd..785b1dc9f9e 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingBluetoothDeviceUpdaterTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingBluetoothDeviceUpdaterTest.java @@ -58,6 +58,7 @@ import com.android.settingslib.flags.Flags; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -143,6 +144,12 @@ public class AudioSharingBluetoothDeviceUpdaterTest { mDeviceUpdater.setPrefContext(mContext); } + @After + public void tearDown() { + ShadowThreadUtils.reset(); + ShadowBluetoothUtils.reset(); + } + @Test public void onProfileConnectionStateChanged_leaDeviceConnected_flagOff_removesPref() { setupPreferenceMapWithDevice(); diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioDialogFragmentTest.java index 979f149b8a6..7f0c1c97858 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioDialogFragmentTest.java @@ -33,6 +33,7 @@ import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settingslib.flags.Flags; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -66,16 +67,16 @@ public class AudioSharingCallAudioDialogFragmentTest { private Fragment mParent; private AudioSharingCallAudioDialogFragment mFragment; - private ShadowBluetoothAdapter mShadowBluetoothAdapter; @Before public void setUp() { ShadowAlertDialogCompat.reset(); - mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); - mShadowBluetoothAdapter.setEnabled(true); - mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( + ShadowBluetoothAdapter shadowBluetoothAdapter = + Shadow.extract(BluetoothAdapter.getDefaultAdapter()); + shadowBluetoothAdapter.setEnabled(true); + shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( BluetoothStatusCodes.FEATURE_SUPPORTED); - mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( + shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( BluetoothStatusCodes.FEATURE_SUPPORTED); mFragment = new AudioSharingCallAudioDialogFragment(); mParent = new Fragment(); @@ -83,6 +84,11 @@ public class AudioSharingCallAudioDialogFragmentTest { mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null); } + @After + public void tearDown() { + ShadowAlertDialogCompat.reset(); + } + @Test public void getMetricsCategory_correctValue() { assertThat(mFragment.getMetricsCategory()) diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceControllerTest.java index af817d2987d..c72b5a5138f 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceControllerTest.java @@ -25,7 +25,7 @@ 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.times; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; @@ -35,6 +35,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothCsipSetCoordinator; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeBroadcastAssistant; +import android.bluetooth.BluetoothLeBroadcastMetadata; import android.bluetooth.BluetoothLeBroadcastReceiveState; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothStatusCodes; @@ -77,6 +78,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -130,6 +132,7 @@ public class AudioSharingCallAudioPreferenceControllerTest { @Mock private CachedBluetoothDevice mCachedDevice2; @Mock private CachedBluetoothDevice mCachedDevice3; @Mock private BluetoothLeBroadcastReceiveState mState; + @Mock private BluetoothLeBroadcastMetadata mSource; @Mock private ContentResolver mContentResolver; private AudioSharingCallAudioPreferenceController mController; @Spy private ContentObserver mContentObserver; @@ -142,6 +145,7 @@ public class AudioSharingCallAudioPreferenceControllerTest { @Before public void setUp() { + ShadowAlertDialogCompat.reset(); mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); mShadowBluetoothAdapter.setEnabled(true); mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( @@ -179,17 +183,24 @@ public class AudioSharingCallAudioPreferenceControllerTest { when(mScreen.findPreference(PREF_KEY)).thenReturn(mPreference); } + @After + public void tearDown() { + ShadowAlertDialogCompat.reset(); + ShadowThreadUtils.reset(); + ShadowBluetoothUtils.reset(); + } + @Test public void onStart_flagOff_doNothing() { mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mController.onStart(mLifecycleOwner); - verify(mBtEventManager, times(0)).registerCallback(mController); - verify(mContentResolver, times(0)) + verify(mBtEventManager, never()).registerCallback(mController); + verify(mContentResolver, never()) .registerContentObserver( Settings.Secure.getUriFor(SETTINGS_KEY_FALLBACK_DEVICE_GROUP_ID), false, mContentObserver); - verify(mAssistant, times(0)) + verify(mAssistant, never()) .registerServiceCallBack(any(), any(BluetoothLeBroadcastAssistant.Callback.class)); } @@ -212,9 +223,9 @@ public class AudioSharingCallAudioPreferenceControllerTest { mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mController.setCallbacksRegistered(true); mController.onStop(mLifecycleOwner); - verify(mBtEventManager, times(0)).unregisterCallback(mController); - verify(mContentResolver, times(0)).unregisterContentObserver(mContentObserver); - verify(mAssistant, times(0)) + verify(mBtEventManager, never()).unregisterCallback(mController); + verify(mContentResolver, never()).unregisterContentObserver(mContentObserver); + verify(mAssistant, never()) .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class)); } @@ -223,9 +234,9 @@ public class AudioSharingCallAudioPreferenceControllerTest { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mController.setCallbacksRegistered(false); mController.onStop(mLifecycleOwner); - verify(mBtEventManager, times(0)).unregisterCallback(mController); - verify(mContentResolver, times(0)).unregisterContentObserver(mContentObserver); - verify(mAssistant, times(0)) + verify(mBtEventManager, never()).unregisterCallback(mController); + verify(mContentResolver, never()).unregisterContentObserver(mContentObserver); + verify(mAssistant, never()) .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class)); } @@ -500,4 +511,80 @@ public class AudioSharingCallAudioPreferenceControllerTest { AudioSharingCallAudioPreferenceController.ChangeCallAudioType.UNKNOWN .ordinal()); } + + @Test + public void testBluetoothLeBroadcastAssistantCallbacks_updateSummary() { + when(mCachedDevice1.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID1); + when(mCachedDevice1.getDevice()).thenReturn(mDevice1); + when(mCachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME1); + when(mCacheManager.findDevice(mDevice1)).thenReturn(mCachedDevice1); + Settings.Secure.putInt( + mContentResolver, TEST_SETTINGS_KEY, BluetoothCsipSetCoordinator.GROUP_ID_INVALID); + when(mBroadcast.isEnabled(any())).thenReturn(true); + when(mAssistant.getDevicesMatchingConnectionStates( + new int[] {BluetoothProfile.STATE_CONNECTED})) + .thenReturn(ImmutableList.of()); + mController.displayPreference(mScreen); + shadowOf(Looper.getMainLooper()).idle(); + assertThat(mPreference.getSummary().toString()).isEmpty(); + + // onReceiveStateChanged will update summary + Settings.Secure.putInt(mContentResolver, TEST_SETTINGS_KEY, TEST_DEVICE_GROUP_ID1); + when(mAssistant.getDevicesMatchingConnectionStates( + new int[] {BluetoothProfile.STATE_CONNECTED})) + .thenReturn(ImmutableList.of(mDevice1)); + when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mState)); + mController.mBroadcastAssistantCallback.onReceiveStateChanged( + mDevice1, /* sourceId= */ 1, mState); + shadowOf(Looper.getMainLooper()).idle(); + assertThat(mPreference.getSummary().toString()) + .isEqualTo( + mContext.getString( + R.string.audio_sharing_call_audio_description, TEST_DEVICE_NAME1)); + } + + @Test + public void testBluetoothLeBroadcastAssistantCallbacks_doNothing() { + when(mCachedDevice1.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID1); + when(mCachedDevice1.getDevice()).thenReturn(mDevice1); + when(mCachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME1); + when(mCacheManager.findDevice(mDevice1)).thenReturn(mCachedDevice1); + Settings.Secure.putInt( + mContentResolver, TEST_SETTINGS_KEY, BluetoothCsipSetCoordinator.GROUP_ID_INVALID); + when(mBroadcast.isEnabled(any())).thenReturn(true); + when(mAssistant.getDevicesMatchingConnectionStates( + new int[] {BluetoothProfile.STATE_CONNECTED})) + .thenReturn(ImmutableList.of()); + mController.displayPreference(mScreen); + shadowOf(Looper.getMainLooper()).idle(); + assertThat(mPreference.getSummary().toString()).isEmpty(); + + Settings.Secure.putInt(mContentResolver, TEST_SETTINGS_KEY, TEST_DEVICE_GROUP_ID1); + when(mAssistant.getDevicesMatchingConnectionStates( + new int[] {BluetoothProfile.STATE_CONNECTED})) + .thenReturn(ImmutableList.of(mDevice1)); + when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mState)); + mController.mBroadcastAssistantCallback.onSearchStarted(/* reason= */ 1); + mController.mBroadcastAssistantCallback.onSearchStartFailed(/* reason= */ 1); + mController.mBroadcastAssistantCallback.onSearchStopped(/* reason= */ 1); + mController.mBroadcastAssistantCallback.onSearchStopFailed(/* reason= */ 1); + mController.mBroadcastAssistantCallback.onSourceAdded( + mDevice1, /* sourceId= */ 1, /* reason= */ 1); + mController.mBroadcastAssistantCallback.onSourceAddFailed( + mDevice1, mSource, /* reason= */ 1); + mController.mBroadcastAssistantCallback.onSourceRemoved( + mDevice1, /* sourceId= */ 1, /* reason= */ 1); + mController.mBroadcastAssistantCallback.onSourceRemoveFailed( + mDevice1, /* sourceId= */ 1, /* reason= */ 1); + mController.mBroadcastAssistantCallback.onSourceModified( + mDevice1, /* sourceId= */ 1, /* reason= */ 1); + mController.mBroadcastAssistantCallback.onSourceModifyFailed( + mDevice1, /* sourceId= */ 1, /* reason= */ 1); + mController.mBroadcastAssistantCallback.onSourceFound(mSource); + mController.mBroadcastAssistantCallback.onSourceLost(/* broadcastId= */ 1); + shadowOf(Looper.getMainLooper()).idle(); + + // Above callbacks won't update summary. + assertThat(mPreference.getSummary().toString()).isEmpty(); + } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCompatibilityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCompatibilityPreferenceControllerTest.java index 19221a65abe..74210cd0ea3 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCompatibilityPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCompatibilityPreferenceControllerTest.java @@ -61,6 +61,7 @@ import com.android.settingslib.bluetooth.VolumeControlProfile; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.flags.Flags; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -128,6 +129,12 @@ public class AudioSharingCompatibilityPreferenceControllerTest { when(mScreen.findPreference(PREF_KEY)).thenReturn(mPreference); } + @After + public void tearDown() { + ShadowThreadUtils.reset(); + ShadowBluetoothUtils.reset(); + } + @Test public void onStart_flagOn_registerCallback() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingConfirmDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingConfirmDialogFragmentTest.java index e5facc1d62d..32f666f05f4 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingConfirmDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingConfirmDialogFragmentTest.java @@ -61,7 +61,7 @@ public class AudioSharingConfirmDialogFragmentTest { @Before public void setUp() { - cleanUpDialogs(); + ShadowAlertDialogCompat.reset(); ShadowBluetoothAdapter shadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); shadowBluetoothAdapter.setEnabled(true); @@ -77,7 +77,7 @@ public class AudioSharingConfirmDialogFragmentTest { @After public void tearDown() { - cleanUpDialogs(); + ShadowAlertDialogCompat.reset(); } @Test @@ -118,12 +118,4 @@ public class AudioSharingConfirmDialogFragmentTest { shadowMainLooper().idle(); assertThat(dialog.isShowing()).isFalse(); } - - private void cleanUpDialogs() { - AlertDialog latestAlertDialog = ShadowAlertDialogCompat.getLatestAlertDialog(); - if (latestAlertDialog != null) { - latestAlertDialog.dismiss(); - ShadowAlertDialogCompat.reset(); - } - } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceControllerTest.java index 14bca0840d0..18f75ba2b85 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceControllerTest.java @@ -25,6 +25,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -35,6 +36,8 @@ import static org.robolectric.Shadows.shadowOf; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeBroadcastAssistant; +import android.bluetooth.BluetoothLeBroadcastMetadata; +import android.bluetooth.BluetoothLeBroadcastReceiveState; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothStatusCodes; import android.content.Context; @@ -74,6 +77,7 @@ import com.android.settingslib.flags.Flags; import com.google.common.collect.ImmutableList; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -85,6 +89,8 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.Executor; @RunWith(RobolectricTestRunner.class) @@ -113,6 +119,8 @@ public class AudioSharingDevicePreferenceControllerTest { @Mock private LocalBluetoothLeBroadcast mBroadcast; @Mock private LocalBluetoothLeBroadcastAssistant mAssistant; @Mock private VolumeControlProfile mVolumeControl; + @Mock private BluetoothLeBroadcastReceiveState mState; + @Mock private BluetoothLeBroadcastMetadata mSource; @Mock private PreferenceScreen mScreen; @Mock private AudioSharingDialogHandler mDialogHandler; @Mock private DashboardFragment mFragment; @@ -173,17 +181,22 @@ public class AudioSharingDevicePreferenceControllerTest { mController.setHostFragment(mFragment); } + @After + public void tearDown() { + ShadowBluetoothUtils.reset(); + } + @Test public void onStart_flagOff_doNothing() { mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mController.onStart(mLifecycleOwner); - verify(mEventManager, times(0)).registerCallback(any(BluetoothCallback.class)); - verify(mDialogHandler, times(0)).registerCallbacks(any(Executor.class)); - verify(mAssistant, times(0)) + verify(mEventManager, never()).registerCallback(any(BluetoothCallback.class)); + verify(mDialogHandler, never()).registerCallbacks(any(Executor.class)); + verify(mAssistant, never()) .registerServiceCallBack( any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class)); - verify(mBluetoothDeviceUpdater, times(0)).registerCallback(); - verify(mBluetoothDeviceUpdater, times(0)).refreshPreference(); + verify(mBluetoothDeviceUpdater, never()).registerCallback(); + verify(mBluetoothDeviceUpdater, never()).refreshPreference(); } @Test @@ -203,11 +216,11 @@ public class AudioSharingDevicePreferenceControllerTest { public void onStop_flagOff_doNothing() { mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mController.onStop(mLifecycleOwner); - verify(mEventManager, times(0)).unregisterCallback(any(BluetoothCallback.class)); - verify(mDialogHandler, times(0)).unregisterCallbacks(); - verify(mAssistant, times(0)) + verify(mEventManager, never()).unregisterCallback(any(BluetoothCallback.class)); + verify(mDialogHandler, never()).unregisterCallbacks(); + verify(mAssistant, never()) .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class)); - verify(mBluetoothDeviceUpdater, times(0)).unregisterCallback(); + verify(mBluetoothDeviceUpdater, never()).unregisterCallback(); } @Test @@ -227,7 +240,7 @@ public class AudioSharingDevicePreferenceControllerTest { mController.displayPreference(mScreen); assertThat(mPreferenceGroup.isVisible()).isFalse(); assertThat(mAudioSharingPreference.isVisible()).isFalse(); - verify(mBluetoothDeviceUpdater, times(0)).forceUpdate(); + verify(mBluetoothDeviceUpdater, never()).forceUpdate(); } @Test @@ -401,8 +414,8 @@ public class AudioSharingDevicePreferenceControllerTest { doReturn(intent).when(mActivity).getIntent(); mController.displayPreference(mScreen); - verify(mDeviceManager, times(0)).findDevice(any(BluetoothDevice.class)); - verify(mDialogHandler, times(0)) + verify(mDeviceManager, never()).findDevice(any(BluetoothDevice.class)); + verify(mDialogHandler, never()) .handleDeviceConnected(any(CachedBluetoothDevice.class), anyBoolean()); } @@ -418,8 +431,8 @@ public class AudioSharingDevicePreferenceControllerTest { when(mDevice.isConnected()).thenReturn(false); mController.displayPreference(mScreen); - verify(mDeviceManager, times(0)).findDevice(any(BluetoothDevice.class)); - verify(mDialogHandler, times(0)) + verify(mDeviceManager, never()).findDevice(any(BluetoothDevice.class)); + verify(mDialogHandler, never()) .handleDeviceConnected(any(CachedBluetoothDevice.class), anyBoolean()); } @@ -436,8 +449,8 @@ public class AudioSharingDevicePreferenceControllerTest { mController.setIntentHandled(true); mController.displayPreference(mScreen); - verify(mDeviceManager, times(0)).findDevice(any(BluetoothDevice.class)); - verify(mDialogHandler, times(0)) + verify(mDeviceManager, never()).findDevice(any(BluetoothDevice.class)); + verify(mDialogHandler, never()) .handleDeviceConnected(any(CachedBluetoothDevice.class), anyBoolean()); } @@ -484,4 +497,56 @@ public class AudioSharingDevicePreferenceControllerTest { verify(mDialogHandler).handleDeviceConnected(mCachedDevice, true); } + + @Test + public void testBluetoothLeBroadcastAssistantCallbacks_updateGroup() { + // onReceiveStateChanged with unconnected state will do nothing + when(mState.getBisSyncState()).thenReturn(new ArrayList<>()); + mController.mBroadcastAssistantCallback.onReceiveStateChanged( + mDevice, /* sourceId= */ 1, mState); + shadowOf(Looper.getMainLooper()).idle(); + verify(mBluetoothDeviceUpdater, never()).forceUpdate(); + verify(mDialogHandler, never()).closeOpeningDialogsForLeaDevice(mCachedDevice); + + // onReceiveStateChanged with connected state will update group preference and handle + // stale dialogs + List bisSyncState = new ArrayList<>(); + bisSyncState.add(1L); + when(mState.getBisSyncState()).thenReturn(bisSyncState); + mController.mBroadcastAssistantCallback.onReceiveStateChanged( + mDevice, /* sourceId= */ 1, mState); + shadowOf(Looper.getMainLooper()).idle(); + verify(mBluetoothDeviceUpdater).forceUpdate(); + verify(mDialogHandler).closeOpeningDialogsForLeaDevice(mCachedDevice); + + // onSourceRemoved will update group preference + mController.mBroadcastAssistantCallback.onSourceRemoved( + mDevice, /* sourceId= */ 1, /* reason= */ 1); + shadowOf(Looper.getMainLooper()).idle(); + verify(mBluetoothDeviceUpdater, times(2)).forceUpdate(); + } + + @Test + public void testBluetoothLeBroadcastAssistantCallbacks_doNothing() { + mController.mBroadcastAssistantCallback.onSearchStarted(/* reason= */ 1); + mController.mBroadcastAssistantCallback.onSearchStartFailed(/* reason= */ 1); + mController.mBroadcastAssistantCallback.onSearchStopped(/* reason= */ 1); + mController.mBroadcastAssistantCallback.onSearchStopFailed(/* reason= */ 1); + mController.mBroadcastAssistantCallback.onSourceAdded( + mDevice, /* sourceId= */ 1, /* reason= */ 1); + mController.mBroadcastAssistantCallback.onSourceAddFailed( + mDevice, mSource, /* reason= */ 1); + mController.mBroadcastAssistantCallback.onSourceRemoveFailed( + mDevice, /* sourceId= */ 1, /* reason= */ 1); + mController.mBroadcastAssistantCallback.onSourceModified( + mDevice, /* sourceId= */ 1, /* reason= */ 1); + mController.mBroadcastAssistantCallback.onSourceModifyFailed( + mDevice, /* sourceId= */ 1, /* reason= */ 1); + mController.mBroadcastAssistantCallback.onSourceFound(mSource); + mController.mBroadcastAssistantCallback.onSourceLost(/* broadcastId= */ 1); + shadowOf(Looper.getMainLooper()).idle(); + + // Above callbacks won't update group preference + verify(mBluetoothDeviceUpdater, never()).forceUpdate(); + } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeControlUpdaterTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeControlUpdaterTest.java index 7a21f02f676..e7cfb859e74 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeControlUpdaterTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeControlUpdaterTest.java @@ -22,6 +22,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doNothing; 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.verifyNoInteractions; @@ -41,6 +42,7 @@ import android.widget.SeekBar; import androidx.preference.Preference; import androidx.test.core.app.ApplicationProvider; +import com.android.settings.bluetooth.BluetoothDevicePreference; import com.android.settings.bluetooth.Utils; import com.android.settings.connecteddevice.DevicePreferenceCallback; import com.android.settings.testutils.FakeFeatureFactory; @@ -56,6 +58,7 @@ import com.android.settingslib.bluetooth.VolumeControlProfile; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -80,6 +83,9 @@ public class AudioSharingDeviceVolumeControlUpdaterTest { private static final String TEST_SETTINGS_KEY = "bluetooth_le_broadcast_fallback_active_group_id"; private static final int TEST_DEVICE_GROUP_ID = 1; + private static final int TEST_VOLUME_VALUE = 255; + private static final int TEST_MAX_STREAM_VALUE = 10; + private static final int TEST_MIN_STREAM_VALUE = 0; @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); @@ -131,6 +137,11 @@ public class AudioSharingDeviceVolumeControlUpdaterTest { mDeviceUpdater.setPrefContext(mContext); } + @After + public void tearDown() { + ShadowBluetoothUtils.reset(); + } + @Test public void onProfileConnectionStateChanged_leaDeviceConnected_noSharing_removesPref() { setupPreferenceMapWithDevice(); @@ -249,10 +260,11 @@ public class AudioSharingDeviceVolumeControlUpdaterTest { (AudioSharingDeviceVolumePreference) captor.getValue(); SeekBar seekBar = mock(SeekBar.class); - when(seekBar.getProgress()).thenReturn(255); + when(seekBar.getProgress()).thenReturn(TEST_VOLUME_VALUE); preference.onStopTrackingTouch(seekBar); - verify(mVolumeControl).setDeviceVolume(mBluetoothDevice, 255, true); + verify(mVolumeControl) + .setDeviceVolume(mBluetoothDevice, TEST_VOLUME_VALUE, /* isGroupOp= */ true); verifyNoInteractions(mAudioManager); verify(mFeatureFactory.metricsFeatureProvider) .action( @@ -273,14 +285,17 @@ public class AudioSharingDeviceVolumeControlUpdaterTest { Settings.Secure.putInt( mContext.getContentResolver(), TEST_SETTINGS_KEY, TEST_DEVICE_GROUP_ID); - when(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)).thenReturn(10); - when(mAudioManager.getStreamMinVolume(AudioManager.STREAM_MUSIC)).thenReturn(0); + when(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)) + .thenReturn(TEST_MAX_STREAM_VALUE); + when(mAudioManager.getStreamMinVolume(AudioManager.STREAM_MUSIC)) + .thenReturn(TEST_MIN_STREAM_VALUE); SeekBar seekBar = mock(SeekBar.class); - when(seekBar.getProgress()).thenReturn(255); + when(seekBar.getProgress()).thenReturn(TEST_VOLUME_VALUE); preference.onStopTrackingTouch(seekBar); verifyNoInteractions(mVolumeControl); - verify(mAudioManager).setStreamVolume(AudioManager.STREAM_MUSIC, 10, 0); + verify(mAudioManager) + .setStreamVolume(AudioManager.STREAM_MUSIC, TEST_MAX_STREAM_VALUE, /* flags= */ 0); verify(mFeatureFactory.metricsFeatureProvider) .action( mContext, @@ -288,6 +303,22 @@ public class AudioSharingDeviceVolumeControlUpdaterTest { /* isPrimary= */ true); } + @Test + public void testOnSeekBarChangeListener_doNothing() { + ArgumentCaptor captor = ArgumentCaptor.forClass(Preference.class); + setupPreferenceMapWithDevice(); + + verify(mDevicePreferenceCallback).onDeviceAdded(captor.capture()); + assertThat(captor.getValue() instanceof AudioSharingDeviceVolumePreference).isTrue(); + AudioSharingDeviceVolumePreference preference = + (AudioSharingDeviceVolumePreference) captor.getValue(); + SeekBar seekBar = mock(SeekBar.class); + preference.onProgressChanged(seekBar, TEST_VOLUME_VALUE, /* fromUser= */ false); + + verifyNoInteractions(mAudioManager); + verifyNoInteractions(mVolumeControl); + } + @Test public void getLogTag_returnsCorrectTag() { assertThat(mDeviceUpdater.getLogTag()).isEqualTo(TAG); @@ -298,6 +329,35 @@ public class AudioSharingDeviceVolumeControlUpdaterTest { assertThat(mDeviceUpdater.getPreferenceKey()).isEqualTo(PREF_KEY); } + @Test + public void addPreferenceWithSortType_doNothing() { + mDeviceUpdater.addPreference( + mCachedBluetoothDevice, BluetoothDevicePreference.SortType.TYPE_DEFAULT); + // Verify AudioSharingDeviceVolumeControlUpdater overrides BluetoothDeviceUpdater and won't + // trigger add preference. + verifyNoInteractions(mDevicePreferenceCallback); + } + + @Test + public void launchDeviceDetails_doNothing() { + Preference preference = mock(Preference.class); + mDeviceUpdater.launchDeviceDetails(preference); + // Verify AudioSharingDeviceVolumeControlUpdater overrides BluetoothDeviceUpdater and won't + // launch device details + verifyNoInteractions(preference); + } + + @Test + public void refreshPreference_doNothing() { + setupPreferenceMapWithDevice(); + verify(mDevicePreferenceCallback).onDeviceAdded(any(Preference.class)); + when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(ImmutableList.of()); + mDeviceUpdater.refreshPreference(); + // Verify AudioSharingDeviceVolumeControlUpdater overrides BluetoothDeviceUpdater and won't + // refresh preference map + verify(mDevicePreferenceCallback, never()).onDeviceRemoved(any(Preference.class)); + } + private void setupPreferenceMapWithDevice() { // Add device to preferenceMap when(mBroadcast.isEnabled(null)).thenReturn(true); diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupControllerTest.java index 7c8709cac18..f2f0a2f31f6 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceVolumeGroupControllerTest.java @@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -34,6 +35,8 @@ import static org.robolectric.Shadows.shadowOf; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeBroadcastAssistant; +import android.bluetooth.BluetoothLeBroadcastMetadata; +import android.bluetooth.BluetoothLeBroadcastReceiveState; import android.bluetooth.BluetoothStatusCodes; import android.bluetooth.BluetoothVolumeControl; import android.content.ContentResolver; @@ -69,6 +72,7 @@ import com.android.settingslib.flags.Flags; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -81,6 +85,8 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.Executor; @RunWith(RobolectricTestRunner.class) @@ -114,6 +120,8 @@ public class AudioSharingDeviceVolumeGroupControllerTest { @Mock private LocalBluetoothProfileManager mProfileManager; @Mock private LocalBluetoothLeBroadcast mBroadcast; @Mock private LocalBluetoothLeBroadcastAssistant mAssistant; + @Mock private BluetoothLeBroadcastReceiveState mState; + @Mock private BluetoothLeBroadcastMetadata mSource; @Mock private AudioSharingDeviceVolumeControlUpdater mDeviceUpdater; @Mock private VolumeControlProfile mVolumeControl; @Mock private PreferenceScreen mScreen; @@ -164,6 +172,7 @@ public class AudioSharingDeviceVolumeGroupControllerTest { doReturn(TEST_DEVICE_GROUP_ID1).when(mCachedDevice1).getGroupId(); doReturn(mDevice1).when(mCachedDevice1).getDevice(); doReturn(ImmutableSet.of()).when(mCachedDevice1).getMemberDevice(); + when(mCachedDeviceManager.findDevice(mDevice1)).thenReturn(mCachedDevice1); when(mPreference1.getCachedDevice()).thenReturn(mCachedDevice1); doReturn(TEST_DEVICE_NAME2).when(mCachedDevice2).getName(); doReturn(TEST_DEVICE_GROUP_ID2).when(mCachedDevice2).getGroupId(); @@ -181,17 +190,23 @@ public class AudioSharingDeviceVolumeGroupControllerTest { mContentObserver = mController.getSettingsObserver(); } + @After + public void tearDown() { + ShadowThreadUtils.reset(); + ShadowBluetoothUtils.reset(); + } + @Test public void onStart_flagOff_doNothing() { mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mController.onStart(mLifecycleOwner); - verify(mAssistant, times(0)) + verify(mAssistant, never()) .registerServiceCallBack( any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class)); - verify(mDeviceUpdater, times(0)).registerCallback(); - verify(mVolumeControl, times(0)) + verify(mDeviceUpdater, never()).registerCallback(); + verify(mVolumeControl, never()) .registerCallback(any(Executor.class), any(BluetoothVolumeControl.Callback.class)); - verify(mContentResolver, times(0)) + verify(mContentResolver, never()) .registerContentObserver( Settings.Secure.getUriFor(SETTINGS_KEY_FALLBACK_DEVICE_GROUP_ID), false, @@ -215,16 +230,33 @@ public class AudioSharingDeviceVolumeGroupControllerTest { mContentObserver); } + @Test + public void onAudioSharingProfilesConnected_flagOn_registerCallbacks() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + mController.onAudioSharingProfilesConnected(); + verify(mAssistant) + .registerServiceCallBack( + any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class)); + verify(mDeviceUpdater).registerCallback(); + verify(mVolumeControl) + .registerCallback(any(Executor.class), any(BluetoothVolumeControl.Callback.class)); + verify(mContentResolver) + .registerContentObserver( + Settings.Secure.getUriFor(SETTINGS_KEY_FALLBACK_DEVICE_GROUP_ID), + false, + mContentObserver); + } + @Test public void onStop_flagOff_doNothing() { mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mController.onStop(mLifecycleOwner); - verify(mAssistant, times(0)) + verify(mAssistant, never()) .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class)); - verify(mDeviceUpdater, times(0)).unregisterCallback(); - verify(mVolumeControl, times(0)) + verify(mDeviceUpdater, never()).unregisterCallback(); + verify(mVolumeControl, never()) .unregisterCallback(any(BluetoothVolumeControl.Callback.class)); - verify(mContentResolver, times(0)).unregisterContentObserver(mContentObserver); + verify(mContentResolver, never()).unregisterContentObserver(mContentObserver); } @Test @@ -232,12 +264,12 @@ public class AudioSharingDeviceVolumeGroupControllerTest { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mController.setCallbacksRegistered(false); mController.onStop(mLifecycleOwner); - verify(mAssistant, times(0)) + verify(mAssistant, never()) .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class)); - verify(mDeviceUpdater, times(0)).unregisterCallback(); - verify(mVolumeControl, times(0)) + verify(mDeviceUpdater, never()).unregisterCallback(); + verify(mVolumeControl, never()) .unregisterCallback(any(BluetoothVolumeControl.Callback.class)); - verify(mContentResolver, times(0)).unregisterContentObserver(mContentObserver); + verify(mContentResolver, never()).unregisterContentObserver(mContentObserver); } @Test @@ -257,7 +289,7 @@ public class AudioSharingDeviceVolumeGroupControllerTest { mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mController.displayPreference(mScreen); assertThat(mPreferenceGroup.isVisible()).isFalse(); - verify(mDeviceUpdater, times(0)).forceUpdate(); + verify(mDeviceUpdater, never()).forceUpdate(); } @Test @@ -324,7 +356,7 @@ public class AudioSharingDeviceVolumeGroupControllerTest { mPreferenceGroup.addPreference(mPreference1); mController.setPreferenceGroup(mPreferenceGroup); mController.onDeviceRemoved(mPreference1); - verify(mPreferenceGroup, times(0)).setVisible(false); + verify(mPreferenceGroup, never()).setVisible(false); assertThat(mPreferenceGroup.isVisible()).isTrue(); } @@ -344,7 +376,7 @@ public class AudioSharingDeviceVolumeGroupControllerTest { mController.updateVisibility(); shadowOf(Looper.getMainLooper()).idle(); - verify(mPreferenceGroup, times(0)).setVisible(anyBoolean()); + verify(mPreferenceGroup, never()).setVisible(anyBoolean()); } @Test @@ -411,4 +443,64 @@ public class AudioSharingDeviceVolumeGroupControllerTest { verify(mPreference1).setOrder(0); verify(mPreference2).setOrder(1); } + + @Test + public void onDeviceVolumeChanged_updatePreference() { + when(mPreference1.getProgress()).thenReturn(TEST_MAX_VOLUME_VALUE); + mController.setPreferenceGroup(mPreferenceGroup); + mController.onDeviceAdded(mPreference1); + shadowOf(Looper.getMainLooper()).idle(); + assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1); + + mController.mVolumeControlCallback.onDeviceVolumeChanged(mDevice1, TEST_VOLUME_VALUE); + shadowOf(Looper.getMainLooper()).idle(); + + verify(mPreference1).setProgress(TEST_VOLUME_VALUE); + } + + @Test + public void testBluetoothLeBroadcastAssistantCallbacks_updateGroup() { + when(mState.getBisSyncState()).thenReturn(new ArrayList<>()); + // onReceiveStateChanged with unconnected state will do nothing + mController.mBroadcastAssistantCallback.onReceiveStateChanged( + mDevice1, /* sourceId= */ 1, mState); + verify(mDeviceUpdater, never()).forceUpdate(); + + // onReceiveStateChanged with connected state will update group preference + List bisSyncState = new ArrayList<>(); + bisSyncState.add(1L); + when(mState.getBisSyncState()).thenReturn(bisSyncState); + mController.mBroadcastAssistantCallback.onReceiveStateChanged( + mDevice1, /* sourceId= */ 1, mState); + verify(mDeviceUpdater).forceUpdate(); + + // onSourceRemoved will update group preference + mController.mBroadcastAssistantCallback.onSourceRemoved( + mDevice1, /* sourceId= */ 1, /* reason= */ 1); + verify(mDeviceUpdater, times(2)).forceUpdate(); + } + + @Test + public void testBluetoothLeBroadcastAssistantCallbacks_doNothing() { + mController.mBroadcastAssistantCallback.onSearchStarted(/* reason= */ 1); + mController.mBroadcastAssistantCallback.onSearchStartFailed(/* reason= */ 1); + mController.mBroadcastAssistantCallback.onSearchStopped(/* reason= */ 1); + mController.mBroadcastAssistantCallback.onSearchStopFailed(/* reason= */ 1); + mController.mBroadcastAssistantCallback.onSourceAdded( + mDevice1, /* sourceId= */ 1, /* reason= */ 1); + mController.mBroadcastAssistantCallback.onSourceAddFailed( + mDevice1, mSource, /* reason= */ 1); + mController.mBroadcastAssistantCallback.onSourceRemoveFailed( + mDevice1, /* sourceId= */ 1, /* reason= */ 1); + mController.mBroadcastAssistantCallback.onSourceModified( + mDevice1, /* sourceId= */ 1, /* reason= */ 1); + mController.mBroadcastAssistantCallback.onSourceModifyFailed( + mDevice1, /* sourceId= */ 1, /* reason= */ 1); + mController.mBroadcastAssistantCallback.onSourceFound(mSource); + mController.mBroadcastAssistantCallback.onSourceLost(/* broadcastId= */ 1); + shadowOf(Looper.getMainLooper()).idle(); + + // Above callbacks won't update group preference + verify(mDeviceUpdater, never()).forceUpdate(); + } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragmentTest.java index c63a1a971a4..39709c14931 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragmentTest.java @@ -45,6 +45,7 @@ import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settingslib.flags.Flags; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -112,6 +113,11 @@ public class AudioSharingDialogFragmentTest { mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null); } + @After + public void tearDown() { + ShadowAlertDialogCompat.reset(); + } + @Test public void getMetricsCategory_correctValue() { assertThat(mFragment.getMetricsCategory()) diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandlerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandlerTest.java index 633bc06aa30..4c060d4356d 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandlerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandlerTest.java @@ -19,7 +19,11 @@ package com.android.settings.connecteddevice.audiosharing; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.times; +import static org.mockito.ArgumentMatchers.anyBoolean; +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.verify; import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; @@ -27,6 +31,8 @@ import static org.robolectric.Shadows.shadowOf; import android.app.settings.SettingsEnums; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothLeBroadcast; +import android.bluetooth.BluetoothLeBroadcastMetadata; import android.bluetooth.BluetoothLeBroadcastReceiveState; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothStatusCodes; @@ -38,7 +44,6 @@ import android.util.Pair; import androidx.fragment.app.DialogFragment; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; -import androidx.test.core.app.ApplicationProvider; import com.android.settings.bluetooth.Utils; import com.android.settings.testutils.FakeFeatureFactory; @@ -57,6 +62,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.truth.Correspondence; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -71,6 +77,7 @@ import org.robolectric.shadows.androidx.fragment.FragmentController; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executor; @RunWith(RobolectricTestRunner.class) @Config( @@ -82,6 +89,7 @@ public class AudioSharingDialogHandlerTest { @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private static final int TEST_SOURCE_ID = 1; private static final String TEST_DEVICE_NAME1 = "test1"; private static final String TEST_DEVICE_NAME2 = "test2"; private static final String TEST_DEVICE_NAME3 = "test3"; @@ -109,15 +117,22 @@ public class AudioSharingDialogHandlerTest { @Mock private BluetoothDevice mDevice3; @Mock private BluetoothDevice mDevice4; @Mock private LeAudioProfile mLeAudioProfile; - private Fragment mParentFragment; @Mock private BluetoothLeBroadcastReceiveState mState; + @Mock private BluetoothLeBroadcastMetadata mMetadata; + private Fragment mParentFragment; private Context mContext; private AudioSharingDialogHandler mHandler; private FakeFeatureFactory mFeatureFactory; @Before public void setup() { - mContext = ApplicationProvider.getApplicationContext(); + mParentFragment = new Fragment(); + FragmentController.setupFragment( + mParentFragment, + FragmentActivity.class, + 0 /* containerViewId */, + null /* bundle */); + mContext = mParentFragment.getContext(); ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager; mLocalBtManager = Utils.getLocalBtManager(mContext); ShadowBluetoothAdapter shadowBluetoothAdapter = @@ -135,6 +150,7 @@ public class AudioSharingDialogHandlerTest { List bisSyncState = new ArrayList<>(); bisSyncState.add(1L); when(mState.getBisSyncState()).thenReturn(bisSyncState); + when(mState.getSourceId()).thenReturn(TEST_SOURCE_ID); when(mLeAudioProfile.isEnabled(any())).thenReturn(true); when(mCachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME1); when(mCachedDevice1.getDevice()).thenReturn(mDevice1); @@ -158,15 +174,14 @@ public class AudioSharingDialogHandlerTest { when(mCacheManager.findDevice(mDevice2)).thenReturn(mCachedDevice2); when(mCacheManager.findDevice(mDevice3)).thenReturn(mCachedDevice3); when(mCacheManager.findDevice(mDevice4)).thenReturn(mCachedDevice4); - mParentFragment = new Fragment(); - FragmentController.setupFragment( - mParentFragment, - FragmentActivity.class, - 0 /* containerViewId */, - null /* bundle */); mHandler = new AudioSharingDialogHandler(mContext, mParentFragment); } + @After + public void tearDown() { + ShadowBluetoothUtils.reset(); + } + @Test public void handleUserTriggeredNonLeaDeviceConnected_noSharing_setActive() { setUpBroadcast(false); @@ -269,6 +284,10 @@ public class AudioSharingDialogHandlerTest { AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT .ordinal(), 2)); + AudioSharingJoinDialogFragment.DialogEventListener listener = fragment.getListener(); + assertThat(listener).isNotNull(); + listener.onShareClick(); + verify(mBroadcast).startPrivateBroadcast(); } @Test @@ -309,6 +328,10 @@ public class AudioSharingDialogHandlerTest { AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT .ordinal(), 1)); + AudioSharingJoinDialogFragment.DialogEventListener listener = fragment.getListener(); + assertThat(listener).isNotNull(); + listener.onShareClick(); + verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false); } @Test @@ -351,6 +374,11 @@ public class AudioSharingDialogHandlerTest { AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT .ordinal(), 1)); + AudioSharingDisconnectDialogFragment.DialogEventListener listener = fragment.getListener(); + assertThat(listener).isNotNull(); + listener.onItemClick(AudioSharingUtils.buildAudioSharingDeviceItem(mCachedDevice3)); + verify(mAssistant).removeSource(mDevice3, TEST_SOURCE_ID); + verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false); } @Test @@ -363,7 +391,7 @@ public class AudioSharingDialogHandlerTest { when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of()); mHandler.handleDeviceConnected(mCachedDevice2, /* userTriggered= */ false); shadowOf(Looper.getMainLooper()).idle(); - verify(mCachedDevice2, times(0)).setActive(); + verify(mCachedDevice2, never()).setActive(); } @Test @@ -415,7 +443,7 @@ public class AudioSharingDialogHandlerTest { when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of()); mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ false); shadowOf(Looper.getMainLooper()).idle(); - verify(mCachedDevice1, times(0)).setActive(); + verify(mCachedDevice1, never()).setActive(); } @Test @@ -455,6 +483,10 @@ public class AudioSharingDialogHandlerTest { AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT .ordinal(), 2)); + AudioSharingJoinDialogFragment.DialogEventListener listener = fragment.getListener(); + assertThat(listener).isNotNull(); + listener.onShareClick(); + verify(mBroadcast).startPrivateBroadcast(); } @Test @@ -495,6 +527,10 @@ public class AudioSharingDialogHandlerTest { AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT .ordinal(), 1)); + AudioSharingJoinDialogFragment.DialogEventListener listener = fragment.getListener(); + assertThat(listener).isNotNull(); + listener.onShareClick(); + verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false); } @Test @@ -536,6 +572,11 @@ public class AudioSharingDialogHandlerTest { AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT .ordinal(), 1)); + AudioSharingDisconnectDialogFragment.DialogEventListener listener = fragment.getListener(); + assertThat(listener).isNotNull(); + listener.onItemClick(AudioSharingUtils.buildAudioSharingDeviceItem(mCachedDevice3)); + verify(mAssistant).removeSource(mDevice3, TEST_SOURCE_ID); + verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false); } @Test @@ -588,7 +629,106 @@ public class AudioSharingDialogHandlerTest { SettingsEnums.DIALOG_STOP_AUDIO_SHARING); } + @Test + public void closeOpeningDialogsOtherThan() { + setUpBroadcast(true); + ImmutableList deviceList = ImmutableList.of(mDevice3); + when(mAssistant.getDevicesMatchingConnectionStates( + new int[] {BluetoothProfile.STATE_CONNECTED})) + .thenReturn(deviceList); + when(mAssistant.getAllSources(mDevice3)).thenReturn(ImmutableList.of(mState)); + mHandler.handleDeviceConnected(mCachedDevice2, /* userTriggered= */ true); + shadowOf(Looper.getMainLooper()).idle(); + List childFragments = mParentFragment.getChildFragmentManager().getFragments(); + assertThat(childFragments) + .comparingElementsUsing(TAG_EQUALS) + .containsExactly(AudioSharingStopDialogFragment.tag()); + + deviceList = ImmutableList.of(mDevice1, mDevice3); + when(mAssistant.getDevicesMatchingConnectionStates( + new int[] {BluetoothProfile.STATE_CONNECTED})) + .thenReturn(deviceList); + when(mAssistant.getAllSources(mDevice1)).thenReturn(ImmutableList.of()); + mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ false); + shadowOf(Looper.getMainLooper()).idle(); + childFragments = mParentFragment.getChildFragmentManager().getFragments(); + assertThat(childFragments) + .comparingElementsUsing(TAG_EQUALS) + .containsExactly(AudioSharingJoinDialogFragment.tag()); + + verify(mFeatureFactory.metricsFeatureProvider) + .action( + mContext, + SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_AUTO_DISMISS, + SettingsEnums.DIALOG_STOP_AUDIO_SHARING); + } + + @Test + public void registerCallbacks() { + Executor executor = mock(Executor.class); + mHandler.registerCallbacks(executor); + verify(mBroadcast) + .registerServiceCallBack(eq(executor), any(BluetoothLeBroadcast.Callback.class)); + } + + @Test + public void unregisterCallbacks() { + mHandler.unregisterCallbacks(); + verify(mBroadcast).unregisterServiceCallBack(any(BluetoothLeBroadcast.Callback.class)); + } + + @Test + public void onPlaybackStarted_addSource() { + setUpBroadcast(false); + ImmutableList deviceList = ImmutableList.of(mDevice1, mDevice3); + when(mAssistant.getDevicesMatchingConnectionStates( + new int[] {BluetoothProfile.STATE_CONNECTED})) + .thenReturn(deviceList); + when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of()); + mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ true); + shadowOf(Looper.getMainLooper()).idle(); + List childFragments = mParentFragment.getChildFragmentManager().getFragments(); + assertThat(childFragments) + .comparingElementsUsing(TAG_EQUALS) + .containsExactly(AudioSharingJoinDialogFragment.tag()); + AudioSharingJoinDialogFragment fragment = + (AudioSharingJoinDialogFragment) Iterables.getOnlyElement(childFragments); + AudioSharingJoinDialogFragment.DialogEventListener listener = fragment.getListener(); + assertThat(listener).isNotNull(); + listener.onShareClick(); + + setUpBroadcast(true); + mHandler.mBroadcastCallback.onPlaybackStarted(/* reason= */ 1, /* broadcastId= */ 1); + shadowOf(Looper.getMainLooper()).idle(); + + verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false); + verify(mAssistant).addSource(mDevice3, mMetadata, /* isGroupOp= */ false); + } + + @Test + public void testBluetoothLeBroadcastCallbacks_doNothing() { + mHandler.mBroadcastCallback.onBroadcastStarted(/* reason= */ 1, /* broadcastId= */ 1); + mHandler.mBroadcastCallback.onBroadcastStopped(/* reason= */ 1, /* broadcastId= */ 1); + mHandler.mBroadcastCallback.onBroadcastMetadataChanged(/* reason= */ 1, mMetadata); + mHandler.mBroadcastCallback.onBroadcastUpdated(/* reason= */ 1, /* broadcastId= */ 1); + mHandler.mBroadcastCallback.onPlaybackStarted(/* reason= */ 1, /* broadcastId= */ 1); + mHandler.mBroadcastCallback.onPlaybackStopped(/* reason= */ 1, /* broadcastId= */ 1); + mHandler.mBroadcastCallback.onBroadcastStartFailed(/* reason= */ 1); + mHandler.mBroadcastCallback.onBroadcastStopFailed(/* reason= */ 1); + mHandler.mBroadcastCallback.onBroadcastUpdateFailed(/* reason= */ 1, /* broadcastId= */ 1); + + verify(mAssistant, never()) + .addSource( + any(BluetoothDevice.class), + any(BluetoothLeBroadcastMetadata.class), + anyBoolean()); + verify(mAssistant, never()).removeSource(any(BluetoothDevice.class), anyInt()); + } + private void setUpBroadcast(boolean isBroadcasting) { when(mBroadcast.isEnabled(any())).thenReturn(isBroadcasting); + if (isBroadcasting) { + when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata); + } } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragmentTest.java index 481c78d2917..6b984af9939 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragmentTest.java @@ -47,6 +47,7 @@ import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.flags.Flags; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -104,11 +105,7 @@ public class AudioSharingDisconnectDialogFragmentTest { @Before public void setUp() { - AlertDialog latestAlertDialog = ShadowAlertDialogCompat.getLatestAlertDialog(); - if (latestAlertDialog != null) { - latestAlertDialog.dismiss(); - ShadowAlertDialogCompat.reset(); - } + ShadowAlertDialogCompat.reset(); ShadowBluetoothAdapter shadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); shadowBluetoothAdapter.setEnabled(true); @@ -131,6 +128,11 @@ public class AudioSharingDisconnectDialogFragmentTest { mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null); } + @After + public void tearDown() { + ShadowAlertDialogCompat.reset(); + } + @Test public void getMetricsCategory_correctValue() { assertThat(mFragment.getMetricsCategory()) diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragmentTest.java index c7b21ade886..13ea965d3c5 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragmentTest.java @@ -48,6 +48,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import com.android.settingslib.flags.Flags; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -104,11 +105,7 @@ public class AudioSharingJoinDialogFragmentTest { @Before public void setUp() { - AlertDialog latestAlertDialog = ShadowAlertDialogCompat.getLatestAlertDialog(); - if (latestAlertDialog != null) { - latestAlertDialog.dismiss(); - ShadowAlertDialogCompat.reset(); - } + ShadowAlertDialogCompat.reset(); ShadowBluetoothAdapter shadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); shadowBluetoothAdapter.setEnabled(true); @@ -129,6 +126,12 @@ public class AudioSharingJoinDialogFragmentTest { mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null); } + @After + public void tearDown() { + ShadowAlertDialogCompat.reset(); + ShadowBluetoothUtils.reset(); + } + @Test public void getMetricsCategory_notInSharing_correctValue() { when(mBroadcast.isEnabled(null)).thenReturn(false); diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPreferenceControllerTest.java index 046a4ce8f67..d9c883e5b1f 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingPreferenceControllerTest.java @@ -56,6 +56,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.flags.Flags; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -114,6 +115,12 @@ public class AudioSharingPreferenceControllerTest { when(mScreen.findPreference(PREF_KEY)).thenReturn(mPreference); } + @After + public void tearDown() { + ShadowBluetoothUtils.reset(); + ShadowThreadUtils.reset(); + } + @Test public void onStart_flagOn_registerCallback() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiverTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiverTest.java index d542b985bc2..deed229bbcb 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiverTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingReceiverTest.java @@ -50,6 +50,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import com.android.settingslib.flags.Flags; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -103,6 +104,11 @@ public class AudioSharingReceiverTest { mFeatureFactory = FakeFeatureFactory.setupForTest(); } + @After + public void tearDown() { + ShadowBluetoothUtils.reset(); + } + @Test public void broadcastReceiver_isRegistered() { List registeredReceivers = diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragmentTest.java index 7d46a18a4f2..b6babfb8016 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragmentTest.java @@ -48,6 +48,7 @@ import com.android.settingslib.flags.Flags; import com.google.common.collect.ImmutableList; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -101,11 +102,7 @@ public class AudioSharingStopDialogFragmentTest { @Before public void setUp() { - AlertDialog latestAlertDialog = ShadowAlertDialogCompat.getLatestAlertDialog(); - if (latestAlertDialog != null) { - latestAlertDialog.dismiss(); - ShadowAlertDialogCompat.reset(); - } + ShadowAlertDialogCompat.reset(); ShadowBluetoothAdapter shadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); shadowBluetoothAdapter.setEnabled(true); @@ -126,6 +123,11 @@ public class AudioSharingStopDialogFragmentTest { mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null); } + @After + public void tearDown() { + ShadowAlertDialogCompat.reset(); + } + @Test public void getMetricsCategory_correctValue() { assertThat(mFragment.getMetricsCategory()) diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java index 8f85feb89fd..45d99c7adde 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java @@ -22,11 +22,13 @@ import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_ 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.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; +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; @@ -36,6 +38,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeBroadcast; import android.bluetooth.BluetoothLeBroadcastAssistant; +import android.bluetooth.BluetoothLeBroadcastMetadata; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothStatusCodes; import android.content.BroadcastReceiver; @@ -75,6 +78,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.truth.Correspondence; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -181,7 +185,7 @@ public class AudioSharingSwitchBarControllerTest { doNothing() .when(mAssistant) .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class)); - mSwitchBar = new SettingsMainSwitchBar(mContext); + mSwitchBar = spy(new SettingsMainSwitchBar(mContext)); mSwitchBar.setDisabledByAdmin(mock(RestrictedLockUtils.EnforcedAdmin.class)); mOnAudioSharingStateChanged = false; mOnAudioSharingServiceConnected = false; @@ -207,6 +211,12 @@ public class AudioSharingSwitchBarControllerTest { mController.init(mParentFragment); } + @After + public void tearDown() { + ShadowBluetoothUtils.reset(); + ShadowThreadUtils.reset(); + } + @Test public void bluetoothOff_switchDisabled() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); @@ -258,15 +268,15 @@ public class AudioSharingSwitchBarControllerTest { public void onStart_flagOff_doNothing() { mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mController.onStart(mLifecycleOwner); - verify(mContext, times(0)) + verify(mContext, never()) .registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class), anyInt()); - verify(mBroadcast, times(0)) + verify(mBroadcast, never()) .registerServiceCallBack( any(Executor.class), any(BluetoothLeBroadcast.Callback.class)); - verify(mAssistant, times(0)) + verify(mAssistant, never()) .registerServiceCallBack( any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class)); - verify(mBtProfileManager, times(0)).addServiceListener(mController); + verify(mBtProfileManager, never()).addServiceListener(mController); } @Test @@ -279,10 +289,10 @@ public class AudioSharingSwitchBarControllerTest { verify(mContext) .registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class), anyInt()); - verify(mBroadcast, times(0)) + verify(mBroadcast, never()) .registerServiceCallBack( any(Executor.class), any(BluetoothLeBroadcast.Callback.class)); - verify(mAssistant, times(0)) + verify(mAssistant, never()) .registerServiceCallBack( any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class)); verify(mBtProfileManager).addServiceListener(mController); @@ -305,7 +315,7 @@ public class AudioSharingSwitchBarControllerTest { verify(mAssistant) .registerServiceCallBack( any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class)); - verify(mBtProfileManager, times(0)).addServiceListener(mController); + verify(mBtProfileManager, never()).addServiceListener(mController); assertThat(mSwitchBar.isChecked()).isTrue(); assertThat(mSwitchBar.isEnabled()).isTrue(); } @@ -314,12 +324,12 @@ public class AudioSharingSwitchBarControllerTest { public void onStop_flagOff_doNothing() { mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mController.onStop(mLifecycleOwner); - verify(mContext, times(0)).unregisterReceiver(any(BroadcastReceiver.class)); - verify(mBroadcast, times(0)) + verify(mContext, never()).unregisterReceiver(any(BroadcastReceiver.class)); + verify(mBroadcast, never()) .unregisterServiceCallBack(any(BluetoothLeBroadcast.Callback.class)); - verify(mAssistant, times(0)) + verify(mAssistant, never()) .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class)); - verify(mBtProfileManager, times(0)).removeServiceListener(mController); + verify(mBtProfileManager, never()).removeServiceListener(mController); } @Test @@ -331,9 +341,9 @@ public class AudioSharingSwitchBarControllerTest { verify(mContext).unregisterReceiver(any(BroadcastReceiver.class)); verify(mBtProfileManager).removeServiceListener(mController); - verify(mBroadcast, times(0)) + verify(mBroadcast, never()) .unregisterServiceCallBack(any(BluetoothLeBroadcast.Callback.class)); - verify(mAssistant, times(0)) + verify(mAssistant, never()) .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class)); } @@ -358,7 +368,7 @@ public class AudioSharingSwitchBarControllerTest { when(mBtnView.isEnabled()).thenReturn(true); when(mBroadcast.isEnabled(null)).thenReturn(true); mController.onCheckedChanged(mBtnView, /* isChecked= */ true); - verify(mBroadcast, times(0)).startPrivateBroadcast(); + verify(mBroadcast, never()).startPrivateBroadcast(); } @Test @@ -372,7 +382,7 @@ public class AudioSharingSwitchBarControllerTest { doNothing().when(mBroadcast).startPrivateBroadcast(); mController.onCheckedChanged(mBtnView, /* isChecked= */ true); assertThat(mSwitchBar.isChecked()).isFalse(); - verify(mBroadcast, times(0)).startPrivateBroadcast(); + verify(mBroadcast, never()).startPrivateBroadcast(); } @Test @@ -407,7 +417,7 @@ public class AudioSharingSwitchBarControllerTest { when(mBroadcast.isEnabled(null)).thenReturn(false); when(mBroadcast.getLatestBroadcastId()).thenReturn(1); mController.onCheckedChanged(mBtnView, /* isChecked= */ false); - verify(mBroadcast, times(0)).stopBroadcast(anyInt()); + verify(mBroadcast, never()).stopBroadcast(anyInt()); } @Test @@ -465,4 +475,46 @@ public class AudioSharingSwitchBarControllerTest { .ordinal(), 1)); } + + @Test + public void testBluetoothLeBroadcastCallbacks_updateSwitch() { + mOnAudioSharingStateChanged = false; + mSwitchBar.setChecked(false); + when(mBroadcast.isEnabled(any())).thenReturn(false); + mController.mBroadcastCallback.onBroadcastStartFailed(/* reason= */ 1); + shadowOf(Looper.getMainLooper()).idle(); + assertThat(mSwitchBar.isChecked()).isFalse(); + assertThat(mOnAudioSharingStateChanged).isFalse(); + + when(mBroadcast.isEnabled(any())).thenReturn(true); + mController.mBroadcastCallback.onBroadcastStarted(/* reason= */ 1, /* broadcastId= */ 1); + shadowOf(Looper.getMainLooper()).idle(); + assertThat(mSwitchBar.isChecked()).isTrue(); + assertThat(mOnAudioSharingStateChanged).isTrue(); + + mOnAudioSharingStateChanged = false; + mController.mBroadcastCallback.onBroadcastStopFailed(/* reason= */ 1); + shadowOf(Looper.getMainLooper()).idle(); + assertThat(mSwitchBar.isChecked()).isTrue(); + assertThat(mOnAudioSharingStateChanged).isFalse(); + + when(mBroadcast.isEnabled(any())).thenReturn(false); + mController.mBroadcastCallback.onBroadcastStopped(/* reason= */ 1, /* broadcastId= */ 1); + shadowOf(Looper.getMainLooper()).idle(); + assertThat(mSwitchBar.isChecked()).isFalse(); + assertThat(mOnAudioSharingStateChanged).isTrue(); + } + + @Test + public void testBluetoothLeBroadcastCallbacks_doNothing() { + BluetoothLeBroadcastMetadata metadata = mock(BluetoothLeBroadcastMetadata.class); + mController.mBroadcastCallback.onBroadcastMetadataChanged(/* reason= */ 1, metadata); + mController.mBroadcastCallback.onBroadcastUpdated(/* reason= */ 1, /* broadcastId= */ 1); + mController.mBroadcastCallback.onPlaybackStarted(/* reason= */ 1, /* broadcastId= */ 1); + mController.mBroadcastCallback.onPlaybackStopped(/* reason= */ 1, /* broadcastId= */ 1); + mController.mBroadcastCallback.onBroadcastUpdateFailed( + /* reason= */ 1, /* broadcastId= */ 1); + verify(mSwitchBar, never()).setChecked(anyBoolean()); + assertThat(mOnAudioSharingStateChanged).isFalse(); + } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/StreamSettingsCategoryControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/StreamSettingsCategoryControllerTest.java index 50dde0fcaab..ee92b3bc82f 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/StreamSettingsCategoryControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/StreamSettingsCategoryControllerTest.java @@ -54,6 +54,7 @@ import com.android.settingslib.bluetooth.VolumeControlProfile; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.flags.Flags; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -117,6 +118,11 @@ public class StreamSettingsCategoryControllerTest { when(mScreen.findPreference(KEY)).thenReturn(mPreference); } + @After + public void tearDown() { + ShadowBluetoothUtils.reset(); + } + @Test public void bluetoothOff_updateVisibility() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); 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 67e1f9f919a..c8b11855c72 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java @@ -16,28 +16,51 @@ package com.android.settings.notification.modes; +import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; + +import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID; + +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.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; import android.service.notification.ZenPolicy; +import androidx.fragment.app.Fragment; import androidx.preference.Preference; +import com.android.settings.SettingsActivity; +import com.android.settings.notification.NotificationBackend; +import com.android.settingslib.applications.ApplicationsState; +import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; +import com.android.settingslib.widget.SelectorWithWidgetPreference; + 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.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.util.ReflectionHelpers; + +import java.util.ArrayList; +import java.util.List; @RunWith(RobolectricTestRunner.class) @EnableFlags(Flags.FLAG_MODES_UI) @@ -47,7 +70,15 @@ public final class ZenModeAppsLinkPreferenceControllerTest { private Context mContext; @Mock - private ZenModesBackend mBackend; + private ZenModesBackend mZenModesBackend; + + @Mock + private NotificationBackend mNotificationBackend; + + @Mock + private ApplicationsState mApplicationsState; + @Mock + private ApplicationsState.Session mSession; @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -56,21 +87,109 @@ public final class ZenModeAppsLinkPreferenceControllerTest { public void setup() { MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; + when(mApplicationsState.newSession(any(), any())).thenReturn(mSession); mController = new ZenModeAppsLinkPreferenceController( - mContext, "controller_key", mBackend); + mContext, "controller_key", mock(Fragment.class), mApplicationsState, + mZenModesBackend); + ReflectionHelpers.setField(mController, "mNotificationBackend", mNotificationBackend); + } + + private ApplicationsState.AppEntry createAppEntry(String packageName, String label) { + ApplicationsState.AppEntry entry = mock(ApplicationsState.AppEntry.class); + entry.info = new ApplicationInfo(); + entry.info.packageName = packageName; + entry.label = label; + entry.info.uid = 0; + return entry; + } + + private ZenMode createPriorityChannelsZenMode() { + return new ZenMode("id", new AutomaticZenRule.Builder("Bedtime", + Uri.parse("bed")) + .setType(AutomaticZenRule.TYPE_BEDTIME) + .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + .setZenPolicy(new ZenPolicy.Builder() + .allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY) + .build()) + .build(), true); } @Test - @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) - .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build()) - .build(), true); - mController.updateZenMode(pref, zenMode); - verify(pref).setSummary(any()); + public void testIsAvailable() { + assertThat(mController.isAvailable()).isTrue(); } + @Test + public void testUpdateSetsIntent() { + // Creates the preference + SelectorWithWidgetPreference preference = mock(SelectorWithWidgetPreference.class); + // Create a zen mode that allows priority channels to breakthrough. + ZenMode zenMode = createPriorityChannelsZenMode(); + + // Capture the intent + ArgumentCaptor captor = ArgumentCaptor.forClass(Intent.class); + mController.updateState((Preference) preference, zenMode); + verify(preference).setIntent(captor.capture()); + Intent launcherIntent = captor.getValue(); + + assertThat(launcherIntent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)) + .isEqualTo("com.android.settings.notification.modes.ZenModeAppsFragment"); + assertThat(launcherIntent.getIntExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, + -1)).isEqualTo(0); + + Bundle bundle = launcherIntent.getBundleExtra( + SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS); + assertThat(bundle).isNotNull(); + assertThat(bundle.getString(MODE_ID)).isEqualTo("id"); + } + + @Test + public void testGetAppsBypassingDnd() { + ApplicationsState.AppEntry entry = createAppEntry("test", "testLabel"); + ApplicationsState.AppEntry entryConv = createAppEntry("test_conv", "test_convLabel"); + List appEntries = List.of(entry, entryConv); + + when(mNotificationBackend.getPackagesBypassingDnd(mContext.getUserId(), + false)).thenReturn(List.of("test")); + + assertThat(mController.getAppsBypassingDnd(appEntries)).containsExactly("testLabel"); + } + + @Test + public void testUpdateTriggersRebuild() { + // Creates the preference + SelectorWithWidgetPreference preference = mock(SelectorWithWidgetPreference.class); + // Create a zen mode that allows priority channels to breakthrough. + ZenMode zenMode = createPriorityChannelsZenMode(); + + // Create some applications. + ArrayList appEntries = + new ArrayList(); + appEntries.add(createAppEntry("test", "pkgLabel")); + + when(mNotificationBackend.getPackagesBypassingDnd( + mContext.getUserId(), false)) + .thenReturn(List.of("test")); + + // Updates the preference with the zen mode. We expect that this causes the app session + // to trigger a rebuild. + mController.updateZenMode((Preference) preference, zenMode); + verify(mSession).rebuild(any(), any(), eq(false)); + + // Manually triggers the callback that will happen on rebuild. + mController.mAppSessionCallbacks.onRebuildComplete(appEntries); + verify(preference).setSummary("pkgLabel can interrupt"); + } + + @Test + public void testOnPackageListChangedTriggersRebuild() { + mController.mAppSessionCallbacks.onPackageListChanged(); + verify(mSession).rebuild(any(), any(), eq(false)); + } + + @Test + public void testOnLoadEntriesCompletedTriggersRebuild() { + mController.mAppSessionCallbacks.onLoadEntriesCompleted(); + verify(mSession).rebuild(any(), any(), eq(false)); + } } 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 d8c8bf0bfb5..ef8290a4873 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java @@ -38,6 +38,9 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import java.util.LinkedHashSet; +import java.util.Set; + @RunWith(RobolectricTestRunner.class) public class ZenModesSummaryHelperTest { private Context mContext; @@ -339,7 +342,7 @@ public class ZenModesSummaryHelperTest { .build(); ZenMode zenMode = new ZenMode("id", rule, true); - assertThat(mSummaryHelper.getAppsSummary(zenMode)).isEqualTo("All"); + assertThat(mSummaryHelper.getAppsSummary(zenMode, new LinkedHashSet<>())).isEqualTo("All"); } @Test @@ -353,7 +356,56 @@ public class ZenModesSummaryHelperTest { .build(); ZenMode zenMode = new ZenMode("id", rule, true); - assertThat(mSummaryHelper.getAppsSummary(zenMode)).isEqualTo("None"); + 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) + .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"); + } + + @Test + public void getAppsSummary_formatAppsListEmpty() { + Set apps = new LinkedHashSet<>(); + assertThat(mSummaryHelper.formatAppsList(apps)).isEqualTo("No apps can interrupt"); + } + + @Test + public void getAppsSummary_formatAppsListSingle() { + Set apps = Set.of("My App"); + assertThat(mSummaryHelper.formatAppsList(apps)).isEqualTo("My App can interrupt"); + } + + @Test + public void getAppsSummary_formatAppsListTwo() { + Set apps = Set.of("My App", "SecondApp"); + assertThat(mSummaryHelper.formatAppsList(apps)).isEqualTo("My App and SecondApp " + + "can interrupt"); + } + + @Test + public void getAppsSummary_formatAppsListThree() { + Set apps = Set.of("My App", "SecondApp", "ThirdApp"); + assertThat(mSummaryHelper.formatAppsList(apps)).isEqualTo("My App, SecondApp, " + + "and ThirdApp can interrupt"); + } + + @Test + public void getAppsSummary_formatAppsListMany() { + Set apps = Set.of("My App", "SecondApp", "ThirdApp", "FourthApp", + "FifthApp", "SixthApp"); + // Note that apps are selected alphabetically. + assertThat(mSummaryHelper.formatAppsList(apps)).isEqualTo("FifthApp, FourthApp, " + + "and 4 more can interrupt"); } @Test @@ -366,7 +418,11 @@ public class ZenModesSummaryHelperTest { .build()) .build(); ZenMode zenMode = new ZenMode("id", rule, true); + Set apps = Set.of("My App", "SecondApp", "ThirdApp", "FourthApp", + "FifthApp", "SixthApp"); - assertThat(mSummaryHelper.getAppsSummary(zenMode)).isEqualTo("Selected apps"); + assertThat(mSummaryHelper.getAppsSummary(zenMode, apps)).isEqualTo("FifthApp, FourthApp, " + + "and 4 more can interrupt"); } + }