diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java index 6f7de8ca74e..3d111fd8e3b 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java @@ -20,9 +20,11 @@ import android.app.Dialog; import android.app.settings.SettingsEnums; import android.os.Bundle; import android.util.Log; +import android.util.Pair; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; @@ -48,13 +50,17 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment { * @param item The device item clicked. */ void onItemClick(AudioSharingDeviceItem item); + + /** Called when users click the cancel button in the dialog. */ + void onCancelClick(); } @Nullable private static DialogEventListener sListener; + private static Pair[] sEventData = new Pair[0]; @Override public int getMetricsCategory() { - return SettingsEnums.DIALOG_START_AUDIO_SHARING; + return SettingsEnums.DIALOG_AUDIO_SHARING_ADD_DEVICE; } /** @@ -63,14 +69,17 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment { * @param host The Fragment this dialog will be hosted. * @param deviceItems The connected device items eligible for audio sharing. * @param listener The callback to handle the user action on this dialog. + * @param eventData The eventData to log with for dialog onClick events. */ public static void show( @NonNull Fragment host, @NonNull List deviceItems, - @NonNull DialogEventListener listener) { + @NonNull DialogEventListener listener, + @NonNull Pair[] eventData) { if (!AudioSharingUtils.isFeatureEnabled()) return; final FragmentManager manager = host.getChildFragmentManager(); sListener = listener; + sEventData = eventData; AlertDialog dialog = AudioSharingDialogHelper.getDialogIfShowing(manager, TAG); if (dialog != null) { Log.d(TAG, "Dialog is showing, return."); @@ -84,7 +93,19 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment { dialogFrag.show(manager, TAG); } + /** Return the tag of {@link AudioSharingDialogFragment} dialog. */ + public static @NonNull String tag() { + return TAG; + } + + /** Test only: get the event data passed to the dialog. */ + @VisibleForTesting + protected @NonNull Pair[] getEventData() { + return sEventData; + } + @Override + @NonNull public Dialog onCreateDialog(Bundle savedInstanceState) { Bundle arguments = requireArguments(); List deviceItems = @@ -93,12 +114,17 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment { AudioSharingDialogFactory.newBuilder(getActivity()) .setTitleIcon(com.android.settingslib.R.drawable.ic_bt_le_audio_sharing) .setIsCustomBodyEnabled(true); + if (deviceItems == null) { + Log.d(TAG, "Create dialog error: null deviceItems"); + return builder.build(); + } if (deviceItems.isEmpty()) { builder.setTitle(R.string.audio_sharing_share_dialog_title) .setCustomImage(R.drawable.audio_sharing_guidance) .setCustomMessage(R.string.audio_sharing_dialog_connect_device_content) .setNegativeButton( - R.string.audio_sharing_close_button_label, (dig, which) -> dismiss()); + R.string.audio_sharing_close_button_label, + (dig, which) -> onCancelClick()); } else if (deviceItems.size() == 1) { AudioSharingDeviceItem deviceItem = Iterables.getOnlyElement(deviceItems); builder.setTitle( @@ -111,11 +137,16 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment { v -> { if (sListener != null) { sListener.onItemClick(deviceItem); + mMetricsFeatureProvider.action( + getContext(), + SettingsEnums + .ACTION_AUDIO_SHARING_DIALOG_POSITIVE_BTN_CLICKED, + sEventData); } dismiss(); }) .setCustomNegativeButton( - R.string.audio_sharing_no_thanks_button_label, v -> dismiss()); + R.string.audio_sharing_no_thanks_button_label, v -> onCancelClick()); } else { builder.setTitle(R.string.audio_sharing_share_with_more_dialog_title) .setCustomMessage(R.string.audio_sharing_dialog_share_more_content) @@ -130,8 +161,20 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment { dismiss(); }, AudioSharingDeviceAdapter.ActionType.SHARE)) - .setCustomNegativeButton(com.android.settings.R.string.cancel, v -> dismiss()); + .setCustomNegativeButton( + com.android.settings.R.string.cancel, v -> onCancelClick()); } return builder.build(); } + + private void onCancelClick() { + if (sListener != null) { + sListener.onCancelClick(); + mMetricsFeatureProvider.action( + getContext(), + SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_NEGATIVE_BTN_CLICKED, + sEventData); + } + dismiss(); + } } diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java index c329e82cc7a..5458a9f259b 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandler.java @@ -24,6 +24,7 @@ import android.bluetooth.BluetoothLeBroadcastMetadata; import android.bluetooth.BluetoothLeBroadcastReceiveState; import android.content.Context; import android.util.Log; +import android.util.Pair; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -33,15 +34,21 @@ import androidx.fragment.app.Fragment; import com.android.settings.bluetooth.Utils; import com.android.settings.core.SubSettingLauncher; import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; +import com.android.settingslib.utils.ThreadUtils; + +import com.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.Executor; public class AudioSharingDialogHandler { @@ -51,6 +58,7 @@ public class AudioSharingDialogHandler { @Nullable private final LocalBluetoothManager mLocalBtManager; @Nullable private final LocalBluetoothLeBroadcast mBroadcast; @Nullable private final LocalBluetoothLeBroadcastAssistant mAssistant; + private final MetricsFeatureProvider mMetricsFeatureProvider; private List mTargetSinks = new ArrayList<>(); private final BluetoothLeBroadcast.Callback mBroadcastCallback = @@ -119,9 +127,7 @@ public class AudioSharingDialogHandler { new SubSettingLauncher(mContext) .setDestination(AudioSharingDashboardFragment.class.getName()) .setSourceMetricsCategory( - (mHostFragment != null - && mHostFragment - instanceof DashboardFragment) + (mHostFragment instanceof DashboardFragment) ? ((DashboardFragment) mHostFragment) .getMetricsCategory() : SettingsEnums.PAGE_UNKNOWN) @@ -146,6 +152,7 @@ public class AudioSharingDialogHandler { mLocalBtManager != null ? mLocalBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile() : null; + mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(); } /** Register callbacks for dialog handler */ @@ -191,6 +198,18 @@ public class AudioSharingDialogHandler { List deviceItemsInSharingSession = AudioSharingUtils.buildOrderedConnectedLeadAudioSharingDeviceItem( mLocalBtManager, groupedDevices, /* filterByInSharing= */ true); + AudioSharingStopDialogFragment.DialogEventListener listener = + () -> { + cachedDevice.setActive(); + AudioSharingUtils.stopBroadcasting(mLocalBtManager); + }; + Pair[] eventData = + AudioSharingUtils.buildAudioSharingDialogEventData( + SettingsEnums.SETTINGS_CONNECTED_DEVICE_CATEGORY, + SettingsEnums.DIALOG_STOP_AUDIO_SHARING, + userTriggered, + deviceItemsInSharingSession.size(), + /* candidateDeviceCount= */ 0); postOnMainThread( () -> { closeOpeningDialogsOtherThan(AudioSharingStopDialogFragment.tag()); @@ -198,10 +217,8 @@ public class AudioSharingDialogHandler { mHostFragment, deviceItemsInSharingSession, cachedDevice, - () -> { - cachedDevice.setActive(); - AudioSharingUtils.stopBroadcasting(mLocalBtManager); - }); + listener, + eventData); }); } else { if (userTriggered) { @@ -252,6 +269,20 @@ public class AudioSharingDialogHandler { // Show audio sharing switch dialog when the third eligible (LE audio) remote device // connected during a sharing session. if (deviceItemsInSharingSession.size() >= 2) { + AudioSharingDisconnectDialogFragment.DialogEventListener listener = + (AudioSharingDeviceItem item) -> { + // Remove all sources from the device user clicked + removeSourceForGroup(item.getGroupId(), groupedDevices); + // Add current broadcast to the latest connected device + addSourceForGroup(groupId, groupedDevices); + }; + Pair[] eventData = + AudioSharingUtils.buildAudioSharingDialogEventData( + SettingsEnums.SETTINGS_CONNECTED_DEVICE_CATEGORY, + SettingsEnums.DIALOG_AUDIO_SHARING_SWITCH_DEVICE, + userTriggered, + deviceItemsInSharingSession.size(), + /* candidateDeviceCount= */ 1); postOnMainThread( () -> { closeOpeningDialogsOtherThan( @@ -260,16 +291,29 @@ public class AudioSharingDialogHandler { mHostFragment, deviceItemsInSharingSession, cachedDevice, - (AudioSharingDeviceItem item) -> { - // Remove all sources from the device user clicked - removeSourceForGroup(item.getGroupId(), groupedDevices); - // Add current broadcast to the latest connected device - addSourceForGroup(groupId, groupedDevices); - }); + listener, + eventData); }); } else { // Show audio sharing join dialog when the first or second eligible (LE audio) // remote device connected during a sharing session. + AudioSharingJoinDialogFragment.DialogEventListener listener = + new AudioSharingJoinDialogFragment.DialogEventListener() { + @Override + public void onShareClick() { + addSourceForGroup(groupId, groupedDevices); + } + + @Override + public void onCancelClick() {} + }; + Pair[] eventData = + AudioSharingUtils.buildAudioSharingDialogEventData( + SettingsEnums.SETTINGS_CONNECTED_DEVICE_CATEGORY, + SettingsEnums.DIALOG_AUDIO_SHARING_ADD_DEVICE, + userTriggered, + deviceItemsInSharingSession.size(), + /* candidateDeviceCount= */ 1); postOnMainThread( () -> { closeOpeningDialogsOtherThan(AudioSharingJoinDialogFragment.tag()); @@ -277,15 +321,8 @@ public class AudioSharingDialogHandler { mHostFragment, deviceItemsInSharingSession, cachedDevice, - new AudioSharingJoinDialogFragment.DialogEventListener() { - @Override - public void onShareClick() { - addSourceForGroup(groupId, groupedDevices); - } - - @Override - public void onCancelClick() {} - }); + listener, + eventData); }); } } else { @@ -302,39 +339,43 @@ public class AudioSharingDialogHandler { // Show audio sharing join dialog when the second eligible (LE audio) remote // device connect and no sharing session. if (deviceItems.size() == 1) { + AudioSharingJoinDialogFragment.DialogEventListener listener = + new AudioSharingJoinDialogFragment.DialogEventListener() { + @Override + public void onShareClick() { + mTargetSinks = new ArrayList<>(); + for (List devices : + groupedDevices.values()) { + for (CachedBluetoothDevice device : devices) { + mTargetSinks.add(device.getDevice()); + } + } + Log.d(TAG, "Start broadcast with sinks = " + mTargetSinks.size()); + if (mBroadcast != null) { + mBroadcast.startPrivateBroadcast(); + } + } + + @Override + public void onCancelClick() { + if (userTriggered) { + cachedDevice.setActive(); + } + } + }; + + Pair[] eventData = + AudioSharingUtils.buildAudioSharingDialogEventData( + SettingsEnums.SETTINGS_CONNECTED_DEVICE_CATEGORY, + SettingsEnums.DIALOG_START_AUDIO_SHARING, + userTriggered, + /* deviceCountInSharing= */ 0, + /* candidateDeviceCount= */ 2); postOnMainThread( () -> { closeOpeningDialogsOtherThan(AudioSharingJoinDialogFragment.tag()); AudioSharingJoinDialogFragment.show( - mHostFragment, - deviceItems, - cachedDevice, - new AudioSharingJoinDialogFragment.DialogEventListener() { - @Override - public void onShareClick() { - mTargetSinks = new ArrayList<>(); - for (List devices : - groupedDevices.values()) { - for (CachedBluetoothDevice device : devices) { - mTargetSinks.add(device.getDevice()); - } - } - Log.d( - TAG, - "Start broadcast with sinks: " - + mTargetSinks.size()); - if (mBroadcast != null) { - mBroadcast.startPrivateBroadcast(); - } - } - - @Override - public void onCancelClick() { - if (userTriggered) { - cachedDevice.setActive(); - } - } - }); + mHostFragment, deviceItems, cachedDevice, listener, eventData); }); } else if (userTriggered) { cachedDevice.setActive(); @@ -346,9 +387,12 @@ public class AudioSharingDialogHandler { if (mHostFragment == null) return; List fragments = mHostFragment.getChildFragmentManager().getFragments(); for (Fragment fragment : fragments) { - if (fragment instanceof DialogFragment && !fragment.getTag().equals(tag)) { + if (fragment instanceof DialogFragment + && fragment.getTag() != null + && !fragment.getTag().equals(tag)) { Log.d(TAG, "Remove staled opening dialog " + fragment.getTag()); ((DialogFragment) fragment).dismiss(); + logDialogDismissEvent(fragment); } } } @@ -365,6 +409,7 @@ public class AudioSharingDialogHandler { && AudioSharingUtils.getGroupId(device) == groupId) { Log.d(TAG, "Remove staled opening dialog for group " + groupId); ((DialogFragment) fragment).dismiss(); + logDialogDismissEvent(fragment); } } } @@ -382,6 +427,7 @@ public class AudioSharingDialogHandler { "Remove staled opening dialog for device " + cachedDevice.getDevice().getAnonymizedAddress()); ((DialogFragment) fragment).dismiss(); + logDialogDismissEvent(fragment); } } } @@ -409,9 +455,9 @@ public class AudioSharingDialogHandler { Log.d(TAG, "Fail to remove source for group " + groupId); return; } - groupedDevices.get(groupId).stream() + groupedDevices.getOrDefault(groupId, ImmutableList.of()).stream() .map(CachedBluetoothDevice::getDevice) - .filter(device -> device != null) + .filter(Objects::nonNull) .forEach( device -> { for (BluetoothLeBroadcastReceiveState source : @@ -431,9 +477,9 @@ public class AudioSharingDialogHandler { Log.d(TAG, "Fail to add source due to invalid group id, group = " + groupId); return; } - groupedDevices.get(groupId).stream() + groupedDevices.getOrDefault(groupId, ImmutableList.of()).stream() .map(CachedBluetoothDevice::getDevice) - .filter(device -> device != null) + .filter(Objects::nonNull) .forEach( device -> mAssistant.addSource( @@ -449,4 +495,29 @@ public class AudioSharingDialogHandler { private boolean isBroadcasting() { return mBroadcast != null && mBroadcast.isEnabled(null); } + + private void logDialogDismissEvent(Fragment fragment) { + var unused = + ThreadUtils.postOnBackgroundThread( + () -> { + int pageId = SettingsEnums.PAGE_UNKNOWN; + if (fragment instanceof AudioSharingJoinDialogFragment) { + pageId = + ((AudioSharingJoinDialogFragment) fragment) + .getMetricsCategory(); + } else if (fragment instanceof AudioSharingStopDialogFragment) { + pageId = + ((AudioSharingStopDialogFragment) fragment) + .getMetricsCategory(); + } else if (fragment instanceof AudioSharingDisconnectDialogFragment) { + pageId = + ((AudioSharingDisconnectDialogFragment) fragment) + .getMetricsCategory(); + } + mMetricsFeatureProvider.action( + mContext, + SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_AUTO_DISMISS, + pageId); + }); + } } diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java index e859693a17d..5f6d84a1929 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragment.java @@ -20,16 +20,20 @@ import android.app.Dialog; import android.app.settings.SettingsEnums; import android.os.Bundle; import android.util.Log; +import android.util.Pair; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import com.android.settings.R; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; +import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.utils.ThreadUtils; import java.util.List; import java.util.Locale; @@ -55,6 +59,7 @@ public class AudioSharingDisconnectDialogFragment extends InstrumentedDialogFrag @Nullable private static DialogEventListener sListener; @Nullable private static CachedBluetoothDevice sNewDevice; + private static Pair[] sEventData = new Pair[0]; @Override public int getMetricsCategory() { @@ -70,12 +75,14 @@ public class AudioSharingDisconnectDialogFragment extends InstrumentedDialogFrag * @param deviceItems The existing connected device items in audio sharing session. * @param newDevice The latest connected device triggered this dialog. * @param listener The callback to handle the user action on this dialog. + * @param eventData The eventData to log with for dialog onClick events. */ public static void show( @NonNull Fragment host, @NonNull List deviceItems, @NonNull CachedBluetoothDevice newDevice, - @NonNull DialogEventListener listener) { + @NonNull DialogEventListener listener, + @NonNull Pair[] eventData) { if (!AudioSharingUtils.isFeatureEnabled()) return; FragmentManager manager = host.getChildFragmentManager(); AlertDialog dialog = AudioSharingDialogHelper.getDialogIfShowing(manager, TAG); @@ -91,6 +98,7 @@ public class AudioSharingDisconnectDialogFragment extends InstrumentedDialogFrag newGroupId)); sListener = listener; sNewDevice = newDevice; + sEventData = eventData; return; } else { Log.d( @@ -101,10 +109,22 @@ public class AudioSharingDisconnectDialogFragment extends InstrumentedDialogFrag + "dismiss current dialog.", newGroupId)); dialog.dismiss(); + var unused = + ThreadUtils.postOnBackgroundThread( + () -> + FeatureFactory.getFeatureFactory() + .getMetricsFeatureProvider() + .action( + dialog.getContext(), + SettingsEnums + .ACTION_AUDIO_SHARING_DIALOG_AUTO_DISMISS, + SettingsEnums + .DIALOG_AUDIO_SHARING_SWITCH_DEVICE)); } } sListener = listener; sNewDevice = newDevice; + sEventData = eventData; Log.d(TAG, "Show up the dialog."); final Bundle bundle = new Bundle(); bundle.putParcelableList(BUNDLE_KEY_DEVICE_TO_DISCONNECT_ITEMS, deviceItems); @@ -125,28 +145,54 @@ public class AudioSharingDisconnectDialogFragment extends InstrumentedDialogFrag return sNewDevice; } + /** Test only: get the event data passed to the dialog. */ + @VisibleForTesting + protected @NonNull Pair[] getEventData() { + return sEventData; + } + @Override + @NonNull public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { Bundle arguments = requireArguments(); List deviceItems = arguments.getParcelable(BUNDLE_KEY_DEVICE_TO_DISCONNECT_ITEMS, List.class); - return AudioSharingDialogFactory.newBuilder(getActivity()) - .setTitle(R.string.audio_sharing_disconnect_dialog_title) - .setTitleIcon(com.android.settingslib.R.drawable.ic_bt_le_audio_sharing) - .setIsCustomBodyEnabled(true) - .setCustomMessage(R.string.audio_sharing_dialog_disconnect_content) - .setCustomDeviceActions( - new AudioSharingDeviceAdapter( - getContext(), - deviceItems, - (AudioSharingDeviceItem item) -> { - if (sListener != null) { - sListener.onItemClick(item); - } + AudioSharingDialogFactory.DialogBuilder builder = + AudioSharingDialogFactory.newBuilder(getActivity()) + .setTitle(R.string.audio_sharing_disconnect_dialog_title) + .setTitleIcon(com.android.settingslib.R.drawable.ic_bt_le_audio_sharing) + .setIsCustomBodyEnabled(true) + .setCustomMessage(R.string.audio_sharing_dialog_disconnect_content) + .setCustomNegativeButton( + com.android.settings.R.string.cancel, + v -> { + mMetricsFeatureProvider.action( + getContext(), + SettingsEnums + .ACTION_AUDIO_SHARING_DIALOG_NEGATIVE_BTN_CLICKED, + sEventData); dismiss(); - }, - AudioSharingDeviceAdapter.ActionType.REMOVE)) - .setCustomNegativeButton(com.android.settings.R.string.cancel, v -> dismiss()) - .build(); + }); + if (deviceItems == null) { + Log.d(TAG, "Create dialog error: null deviceItems"); + return builder.build(); + } + builder.setCustomDeviceActions( + new AudioSharingDeviceAdapter( + getContext(), + deviceItems, + (AudioSharingDeviceItem item) -> { + if (sListener != null) { + sListener.onItemClick(item); + mMetricsFeatureProvider.action( + getContext(), + SettingsEnums + .ACTION_AUDIO_SHARING_DIALOG_POSITIVE_BTN_CLICKED, + sEventData); + } + dismiss(); + }, + AudioSharingDeviceAdapter.ActionType.REMOVE)); + return builder.build(); } } diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java index 4982179ccfc..7eebbcb2156 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragment.java @@ -20,9 +20,11 @@ import android.app.Dialog; import android.app.settings.SettingsEnums; import android.os.Bundle; import android.util.Log; +import android.util.Pair; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; @@ -52,6 +54,7 @@ public class AudioSharingJoinDialogFragment extends InstrumentedDialogFragment { @Nullable private static DialogEventListener sListener; @Nullable private static CachedBluetoothDevice sNewDevice; + private static Pair[] sEventData = new Pair[0]; @Override public int getMetricsCategory() { @@ -69,16 +72,19 @@ public class AudioSharingJoinDialogFragment extends InstrumentedDialogFragment { * @param deviceItems The existing connected device items eligible for audio sharing. * @param newDevice The latest connected device triggered this dialog. * @param listener The callback to handle the user action on this dialog. + * @param eventData The eventData to log with for dialog onClick events. */ public static void show( @NonNull Fragment host, @NonNull List deviceItems, @NonNull CachedBluetoothDevice newDevice, - @NonNull DialogEventListener listener) { + @NonNull DialogEventListener listener, + @NonNull Pair[] eventData) { if (!AudioSharingUtils.isFeatureEnabled()) return; final FragmentManager manager = host.getChildFragmentManager(); sListener = listener; sNewDevice = newDevice; + sEventData = eventData; AlertDialog dialog = AudioSharingDialogHelper.getDialogIfShowing(manager, TAG); if (dialog != null) { Log.d(TAG, "Dialog is showing, update the content."); @@ -104,7 +110,14 @@ public class AudioSharingJoinDialogFragment extends InstrumentedDialogFragment { return sNewDevice; } + /** Test only: get the event data passed to the dialog. */ + @VisibleForTesting + protected @NonNull Pair[] getEventData() { + return sEventData; + } + @Override + @NonNull public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { Bundle arguments = requireArguments(); List deviceItems = @@ -121,6 +134,11 @@ public class AudioSharingJoinDialogFragment extends InstrumentedDialogFragment { v -> { if (sListener != null) { sListener.onShareClick(); + mMetricsFeatureProvider.action( + getContext(), + SettingsEnums + .ACTION_AUDIO_SHARING_DIALOG_POSITIVE_BTN_CLICKED, + sEventData); } dismiss(); }) @@ -129,11 +147,20 @@ public class AudioSharingJoinDialogFragment extends InstrumentedDialogFragment { v -> { if (sListener != null) { sListener.onCancelClick(); + mMetricsFeatureProvider.action( + getContext(), + SettingsEnums + .ACTION_AUDIO_SHARING_DIALOG_NEGATIVE_BTN_CLICKED, + sEventData); } dismiss(); }) .build(); - updateDialog(deviceItems, newDeviceName, dialog); + if (deviceItems == null) { + Log.d(TAG, "Fail to create dialog: null deviceItems"); + } else { + updateDialog(deviceItems, newDeviceName, dialog); + } dialog.show(); AudioSharingDialogHelper.updateMessageStyle(dialog); return dialog; diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java index affd54acf5e..beac4b0fd33 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragment.java @@ -20,16 +20,20 @@ import android.app.Dialog; import android.app.settings.SettingsEnums; import android.os.Bundle; import android.util.Log; +import android.util.Pair; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import com.android.settings.R; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; +import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.utils.ThreadUtils; import com.google.common.collect.Iterables; @@ -52,6 +56,7 @@ public class AudioSharingStopDialogFragment extends InstrumentedDialogFragment { @Nullable private static DialogEventListener sListener; @Nullable private static CachedBluetoothDevice sCachedDevice; + private static Pair[] sEventData = new Pair[0]; @Override public int getMetricsCategory() { @@ -67,12 +72,14 @@ public class AudioSharingStopDialogFragment extends InstrumentedDialogFragment { * @param deviceItems The existing connected device items in audio sharing session. * @param newDevice The latest connected device triggered this dialog. * @param listener The callback to handle the user action on this dialog. + * @param eventData The eventData to log with for dialog onClick events. */ public static void show( @NonNull Fragment host, @NonNull List deviceItems, @NonNull CachedBluetoothDevice newDevice, - @NonNull DialogEventListener listener) { + @NonNull DialogEventListener listener, + @NonNull Pair[] eventData) { if (!AudioSharingUtils.isFeatureEnabled()) return; final FragmentManager manager = host.getChildFragmentManager(); AlertDialog dialog = AudioSharingDialogHelper.getDialogIfShowing(manager, TAG); @@ -88,6 +95,7 @@ public class AudioSharingStopDialogFragment extends InstrumentedDialogFragment { newGroupId)); sListener = listener; sCachedDevice = newDevice; + sEventData = eventData; return; } else { Log.d( @@ -98,10 +106,21 @@ public class AudioSharingStopDialogFragment extends InstrumentedDialogFragment { + "dismiss current dialog.", newGroupId)); dialog.dismiss(); + var unused = + ThreadUtils.postOnBackgroundThread( + () -> + FeatureFactory.getFeatureFactory() + .getMetricsFeatureProvider() + .action( + dialog.getContext(), + SettingsEnums + .ACTION_AUDIO_SHARING_DIALOG_AUTO_DISMISS, + SettingsEnums.DIALOG_STOP_AUDIO_SHARING)); } } sListener = listener; sCachedDevice = newDevice; + sEventData = eventData; Log.d(TAG, "Show up the dialog."); final Bundle bundle = new Bundle(); bundle.putParcelableList(BUNDLE_KEY_DEVICE_TO_DISCONNECT_ITEMS, deviceItems); @@ -121,23 +140,34 @@ public class AudioSharingStopDialogFragment extends InstrumentedDialogFragment { return sCachedDevice; } + /** Test only: get the event data passed to the dialog. */ + @VisibleForTesting + protected @NonNull Pair[] getEventData() { + return sEventData; + } + @Override + @NonNull public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { Bundle arguments = requireArguments(); List deviceItems = arguments.getParcelable(BUNDLE_KEY_DEVICE_TO_DISCONNECT_ITEMS, List.class); String newDeviceName = arguments.getString(BUNDLE_KEY_NEW_DEVICE_NAME); - String customMessage = - deviceItems.size() == 1 - ? getString( - R.string.audio_sharing_stop_dialog_content, - Iterables.getOnlyElement(deviceItems).getName()) - : (deviceItems.size() == 2 - ? getString( - R.string.audio_sharing_stop_dialog_with_two_content, - deviceItems.get(0).getName(), - deviceItems.get(1).getName()) - : getString(R.string.audio_sharing_stop_dialog_with_more_content)); + String customMessage = ""; + if (deviceItems != null) { + customMessage = + deviceItems.size() == 1 + ? getString( + R.string.audio_sharing_stop_dialog_content, + Iterables.getOnlyElement(deviceItems).getName()) + : (deviceItems.size() == 2 + ? getString( + R.string.audio_sharing_stop_dialog_with_two_content, + deviceItems.get(0).getName(), + deviceItems.get(1).getName()) + : getString( + R.string.audio_sharing_stop_dialog_with_more_content)); + } AlertDialog dialog = AudioSharingDialogFactory.newBuilder(getActivity()) .setTitle( @@ -150,10 +180,21 @@ public class AudioSharingStopDialogFragment extends InstrumentedDialogFragment { (dlg, which) -> { if (sListener != null) { sListener.onStopSharingClick(); + mMetricsFeatureProvider.action( + getContext(), + SettingsEnums + .ACTION_AUDIO_SHARING_DIALOG_POSITIVE_BTN_CLICKED, + sEventData); } }) .setNegativeButton( - com.android.settings.R.string.cancel, (dlg, which) -> dismiss()) + com.android.settings.R.string.cancel, + (dlg, which) -> + mMetricsFeatureProvider.action( + getContext(), + SettingsEnums + .ACTION_AUDIO_SHARING_DIALOG_NEGATIVE_BTN_CLICKED, + sEventData)) .build(); dialog.show(); AudioSharingDialogHelper.updateMessageStyle(dialog); diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java index 475be85a8a3..5022579ce65 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java @@ -16,6 +16,7 @@ package com.android.settings.connecteddevice.audiosharing; +import android.app.settings.SettingsEnums; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeBroadcast; @@ -29,24 +30,27 @@ import android.content.Intent; import android.content.IntentFilter; import android.util.FeatureFlagUtils; import android.util.Log; +import android.util.Pair; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import androidx.fragment.app.Fragment; import androidx.lifecycle.DefaultLifecycleObserver; import androidx.lifecycle.LifecycleOwner; import com.android.settings.bluetooth.Utils; import com.android.settings.core.BasePreferenceController; -import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.overlay.FeatureFactory; import com.android.settings.widget.SettingsMainSwitchBar; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; +import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.utils.ThreadUtils; import com.google.common.collect.ImmutableList; @@ -56,6 +60,7 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; @@ -91,14 +96,15 @@ public class AudioSharingSwitchBarController extends BasePreferenceController @Nullable private final LocalBluetoothProfileManager mProfileManager; @Nullable private final LocalBluetoothLeBroadcast mBroadcast; @Nullable private final LocalBluetoothLeBroadcastAssistant mAssistant; - @Nullable private DashboardFragment mFragment; + @Nullable private Fragment mFragment; private final Executor mExecutor; + private final MetricsFeatureProvider mMetricsFeatureProvider; private final OnAudioSharingStateChangedListener mListener; private Map> mGroupedConnectedDevices = new HashMap<>(); private List mTargetActiveSinks = new ArrayList<>(); private List mDeviceItemsForSharing = new ArrayList<>(); @VisibleForTesting IntentFilter mIntentFilter; - private AtomicBoolean mCallbacksRegistered = new AtomicBoolean(false); + private final AtomicBoolean mCallbacksRegistered = new AtomicBoolean(false); @VisibleForTesting BroadcastReceiver mReceiver = @@ -110,7 +116,8 @@ public class AudioSharingSwitchBarController extends BasePreferenceController } }; - private final BluetoothLeBroadcast.Callback mBroadcastCallback = + @VisibleForTesting + protected final BluetoothLeBroadcast.Callback mBroadcastCallback = new BluetoothLeBroadcast.Callback() { @Override public void onBroadcastStarted(int reason, int broadcastId) { @@ -182,7 +189,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController public void onPlaybackStopped(int reason, int broadcastId) {} }; - private BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback = + private final BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback = new BluetoothLeBroadcastAssistant.Callback() { @Override public void onSearchStarted(int reason) {} @@ -251,9 +258,9 @@ public class AudioSharingSwitchBarController extends BasePreferenceController @Override public void onReceiveStateChanged( - BluetoothDevice sink, + @NonNull BluetoothDevice sink, int sourceId, - BluetoothLeBroadcastReceiveState state) {} + @NonNull BluetoothLeBroadcastReceiveState state) {} }; AudioSharingSwitchBarController( @@ -273,6 +280,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController ? null : mProfileManager.getLeAudioBroadcastAssistantProfile(); mExecutor = Executors.newSingleThreadExecutor(); + mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(); } @Override @@ -378,7 +386,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController * * @param fragment The fragment to host the {@link AudioSharingSwitchBarController} dialog. */ - public void init(DashboardFragment fragment) { + public void init(@NonNull Fragment fragment) { this.mFragment = fragment; } @@ -494,34 +502,58 @@ public class AudioSharingSwitchBarController extends BasePreferenceController } private void handleOnBroadcastReady() { - AudioSharingUtils.addSourceToTargetSinks(mTargetActiveSinks, mBtManager); - mTargetActiveSinks.clear(); + Pair[] eventData = + AudioSharingUtils.buildAudioSharingDialogEventData( + SettingsEnums.AUDIO_SHARING_SETTINGS, + SettingsEnums.DIALOG_AUDIO_SHARING_ADD_DEVICE, + /* userTriggered= */ false, + /* deviceCountInSharing= */ mTargetActiveSinks.isEmpty() ? 0 : 1, + /* candidateDeviceCount= */ mDeviceItemsForSharing.size()); + if (!mTargetActiveSinks.isEmpty()) { + Log.d(TAG, "handleOnBroadcastReady: automatically add source to active sinks."); + AudioSharingUtils.addSourceToTargetSinks(mTargetActiveSinks, mBtManager); + mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING); + mTargetActiveSinks.clear(); + } if (mFragment == null) { - Log.w(TAG, "Dialog fail to show due to null fragment."); + Log.d(TAG, "handleOnBroadcastReady: dialog fail to show due to null fragment."); mGroupedConnectedDevices.clear(); mDeviceItemsForSharing.clear(); return; } + showDialog(eventData); + } + + private void showDialog(Pair[] eventData) { + AudioSharingDialogFragment.DialogEventListener listener = + new AudioSharingDialogFragment.DialogEventListener() { + @Override + public void onItemClick(@NonNull AudioSharingDeviceItem item) { + AudioSharingUtils.addSourceToTargetSinks( + mGroupedConnectedDevices + .getOrDefault(item.getGroupId(), ImmutableList.of()) + .stream() + .map(CachedBluetoothDevice::getDevice) + .filter(Objects::nonNull) + .collect(Collectors.toList()), + mBtManager); + mGroupedConnectedDevices.clear(); + mDeviceItemsForSharing.clear(); + } + + @Override + public void onCancelClick() { + mGroupedConnectedDevices.clear(); + mDeviceItemsForSharing.clear(); + } + }; AudioSharingUtils.postOnMainThread( mContext, () -> { // Check nullability to pass NullAway check if (mFragment != null) { AudioSharingDialogFragment.show( - mFragment, - mDeviceItemsForSharing, - item -> { - AudioSharingUtils.addSourceToTargetSinks( - mGroupedConnectedDevices - .getOrDefault( - item.getGroupId(), ImmutableList.of()) - .stream() - .map(CachedBluetoothDevice::getDevice) - .collect(Collectors.toList()), - mBtManager); - mGroupedConnectedDevices.clear(); - mDeviceItemsForSharing.clear(); - }); + mFragment, mDeviceItemsForSharing, listener, eventData); } }); } diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java index f63717eb536..29f605c94a4 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java @@ -16,6 +16,12 @@ package com.android.settings.connecteddevice.audiosharing; +import static com.android.settings.connecteddevice.audiosharing.AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT; +import static com.android.settings.connecteddevice.audiosharing.AudioSharingUtils.MetricKey.METRIC_KEY_DEVICE_COUNT_IN_SHARING; +import static com.android.settings.connecteddevice.audiosharing.AudioSharingUtils.MetricKey.METRIC_KEY_PAGE_ID; +import static com.android.settings.connecteddevice.audiosharing.AudioSharingUtils.MetricKey.METRIC_KEY_SOURCE_PAGE_ID; +import static com.android.settings.connecteddevice.audiosharing.AudioSharingUtils.MetricKey.METRIC_KEY_USER_TRIGGERED; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothCsipSetCoordinator; import android.bluetooth.BluetoothDevice; @@ -25,6 +31,7 @@ import android.bluetooth.BluetoothStatusCodes; import android.content.Context; import android.provider.Settings; import android.util.Log; +import android.util.Pair; import android.widget.Toast; import androidx.annotation.NonNull; @@ -54,6 +61,14 @@ public class AudioSharingUtils { private static final String TAG = "AudioSharingUtils"; private static final boolean DEBUG = BluetoothUtils.D; + public enum MetricKey { + METRIC_KEY_SOURCE_PAGE_ID, + METRIC_KEY_PAGE_ID, + METRIC_KEY_USER_TRIGGERED, + METRIC_KEY_DEVICE_COUNT_IN_SHARING, + METRIC_KEY_CANDIDATE_DEVICE_COUNT + } + /** * Fetch {@link CachedBluetoothDevice}s connected to the broadcast assistant. The devices are * grouped by CSIP group id. @@ -121,7 +136,7 @@ public class AudioSharingUtils { boolean filterByInSharing) { List orderedDevices = new ArrayList<>(); for (List devices : groupedConnectedDevices.values()) { - @Nullable CachedBluetoothDevice leadDevice = getLeadDevice(devices); + CachedBluetoothDevice leadDevice = getLeadDevice(devices); if (leadDevice == null) { Log.d(TAG, "Skip due to no lead device"); continue; @@ -206,7 +221,7 @@ public class AudioSharingUtils { return buildOrderedConnectedLeadDevices( localBtManager, groupedConnectedDevices, filterByInSharing) .stream() - .map(device -> buildAudioSharingDeviceItem(device)) + .map(AudioSharingUtils::buildAudioSharingDeviceItem) .collect(Collectors.toList()); } @@ -315,8 +330,9 @@ public class AudioSharingUtils { manager.getProfileManager().getLeAudioBroadcastProfile(); if (broadcast == null) { Log.d(TAG, "Skip stop broadcasting due to broadcast profile is null"); + } else { + broadcast.stopBroadcast(broadcast.getLatestBroadcastId()); } - broadcast.stopBroadcast(broadcast.getLatestBroadcastId()); } /** @@ -378,9 +394,32 @@ public class AudioSharingUtils { return false; } VolumeControlProfile vc = profileManager.getVolumeControlProfile(); - if (vc == null || !vc.isProfileReady()) { - return false; - } - return true; + return vc != null && vc.isProfileReady(); + } + + /** + * Build audio sharing dialog log event data + * + * @param sourcePageId The source page id on which the dialog is shown. * + * @param pageId The page id of the dialog. + * @param userTriggered Indicates whether the dialog is triggered by user click. + * @param deviceCountInSharing The count of the devices joining the audio sharing. + * @param candidateDeviceCount The count of the eligible devices to join the audio sharing. + * @return The event data to be attached to the audio sharing action logs. + */ + @NonNull + public static Pair[] buildAudioSharingDialogEventData( + int sourcePageId, + int pageId, + boolean userTriggered, + int deviceCountInSharing, + int candidateDeviceCount) { + return new Pair[] { + Pair.create(METRIC_KEY_SOURCE_PAGE_ID.ordinal(), sourcePageId), + Pair.create(METRIC_KEY_PAGE_ID.ordinal(), pageId), + Pair.create(METRIC_KEY_USER_TRIGGERED.ordinal(), userTriggered ? 1 : 0), + Pair.create(METRIC_KEY_DEVICE_COUNT_IN_SHARING.ordinal(), deviceCountInSharing), + Pair.create(METRIC_KEY_CANDIDATE_DEVICE_COUNT.ordinal(), candidateDeviceCount) + }; } } 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 4336e771c96..c63a1a971a4 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragmentTest.java @@ -18,11 +18,17 @@ package com.android.settings.connecteddevice.audiosharing; 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.verify; import static org.robolectric.shadows.ShadowLooper.shadowMainLooper; +import android.app.settings.SettingsEnums; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothStatusCodes; +import android.content.Context; import android.platform.test.flag.junit.SetFlagsRule; +import android.util.Pair; import android.view.View; import android.widget.Button; import android.widget.ImageView; @@ -34,6 +40,7 @@ import androidx.fragment.app.FragmentActivity; import androidx.recyclerview.widget.RecyclerView; import com.android.settings.R; +import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settingslib.flags.Flags; @@ -72,30 +79,50 @@ public class AudioSharingDialogFragmentTest { new AudioSharingDeviceItem(TEST_DEVICE_NAME2, /* groupId= */ 2, /* isActive= */ false); private static final AudioSharingDeviceItem TEST_DEVICE_ITEM3 = new AudioSharingDeviceItem(TEST_DEVICE_NAME3, /* groupId= */ 3, /* isActive= */ false); + private static final AudioSharingDialogFragment.DialogEventListener EMPTY_EVENT_LISTENER = + new AudioSharingDialogFragment.DialogEventListener() { + @Override + public void onItemClick(AudioSharingDeviceItem item) {} + + @Override + public void onCancelClick() {} + }; + private static final Pair TEST_EVENT_DATA = Pair.create(1, 1); + private static final Pair[] TEST_EVENT_DATA_LIST = + new Pair[] {TEST_EVENT_DATA}; private Fragment mParent; private AudioSharingDialogFragment mFragment; - private ShadowBluetoothAdapter mShadowBluetoothAdapter; + private FakeFeatureFactory mFeatureFactory; @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); + mFeatureFactory = FakeFeatureFactory.setupForTest(); mFragment = new AudioSharingDialogFragment(); mParent = new Fragment(); FragmentController.setupFragment( mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null); } + @Test + public void getMetricsCategory_correctValue() { + assertThat(mFragment.getMetricsCategory()) + .isEqualTo(SettingsEnums.DIALOG_AUDIO_SHARING_ADD_DEVICE); + } + @Test public void onCreateDialog_flagOff_dialogNotExist() { mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); - mFragment.show(mParent, new ArrayList<>(), (item) -> {}); + AudioSharingDialogFragment.show( + mParent, new ArrayList<>(), EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); @@ -105,14 +132,20 @@ public class AudioSharingDialogFragmentTest { @Test public void onCreateDialog_flagOn_noConnectedDevice() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); - mFragment.show(mParent, new ArrayList<>(), (item) -> {}); + AudioSharingDialogFragment.show( + mParent, new ArrayList<>(), EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); + assertThat(dialog).isNotNull(); TextView description = dialog.findViewById(R.id.description_text); + assertThat(description).isNotNull(); ImageView image = dialog.findViewById(R.id.description_image); + assertThat(image).isNotNull(); Button shareBtn = dialog.findViewById(R.id.positive_btn); + assertThat(shareBtn).isNotNull(); Button cancelBtn = dialog.findViewById(R.id.negative_btn); + assertThat(cancelBtn).isNotNull(); assertThat(dialog.isShowing()).isTrue(); assertThat(description.getVisibility()).isEqualTo(View.VISIBLE); assertThat(description.getText().toString()) @@ -125,13 +158,22 @@ public class AudioSharingDialogFragmentTest { @Test public void onCreateDialog_noConnectedDevice_dialogDismiss() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); - mFragment.show(mParent, new ArrayList<>(), (item) -> {}); + AudioSharingDialogFragment.show( + mParent, new ArrayList<>(), EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); - dialog.findViewById(android.R.id.button2).performClick(); + assertThat(dialog).isNotNull(); + View btnView = dialog.findViewById(android.R.id.button2); + assertThat(btnView).isNotNull(); + btnView.performClick(); shadowMainLooper().idle(); assertThat(dialog.isShowing()).isFalse(); + verify(mFeatureFactory.metricsFeatureProvider) + .action( + any(Context.class), + eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_NEGATIVE_BTN_CLICKED), + eq(TEST_EVENT_DATA)); } @Test @@ -139,15 +181,21 @@ public class AudioSharingDialogFragmentTest { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); ArrayList list = new ArrayList<>(); list.add(TEST_DEVICE_ITEM1); - mFragment.show(mParent, list, (item) -> {}); + AudioSharingDialogFragment.show(mParent, list, EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); + assertThat(dialog).isNotNull(); TextView title = dialog.findViewById(R.id.title_text); + assertThat(title).isNotNull(); TextView description = dialog.findViewById(R.id.description_text); + assertThat(description).isNotNull(); ImageView image = dialog.findViewById(R.id.description_image); + assertThat(image).isNotNull(); Button shareBtn = dialog.findViewById(R.id.positive_btn); + assertThat(shareBtn).isNotNull(); Button cancelBtn = dialog.findViewById(R.id.negative_btn); + assertThat(cancelBtn).isNotNull(); assertThat(dialog.isShowing()).isTrue(); assertThat(title.getText().toString()) .isEqualTo( @@ -166,12 +214,22 @@ public class AudioSharingDialogFragmentTest { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); ArrayList list = new ArrayList<>(); list.add(TEST_DEVICE_ITEM1); - mFragment.show(mParent, list, (item) -> {}); + AudioSharingDialogFragment.show(mParent, list, EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); - dialog.findViewById(R.id.negative_btn).performClick(); + assertThat(dialog).isNotNull(); + View btnView = dialog.findViewById(R.id.negative_btn); + assertThat(btnView).isNotNull(); + btnView.performClick(); + shadowMainLooper().idle(); + assertThat(dialog.isShowing()).isFalse(); + verify(mFeatureFactory.metricsFeatureProvider) + .action( + any(Context.class), + eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_NEGATIVE_BTN_CLICKED), + eq(TEST_EVENT_DATA)); } @Test @@ -180,13 +238,35 @@ public class AudioSharingDialogFragmentTest { ArrayList list = new ArrayList<>(); list.add(TEST_DEVICE_ITEM1); AtomicBoolean isShareBtnClicked = new AtomicBoolean(false); - mFragment.show(mParent, list, (item) -> isShareBtnClicked.set(true)); + AudioSharingDialogFragment.show( + mParent, + list, + new AudioSharingDialogFragment.DialogEventListener() { + @Override + public void onItemClick(AudioSharingDeviceItem item) { + isShareBtnClicked.set(true); + } + + @Override + public void onCancelClick() {} + }, + TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); - dialog.findViewById(R.id.positive_btn).performClick(); + assertThat(dialog).isNotNull(); + View btnView = dialog.findViewById(R.id.positive_btn); + assertThat(btnView).isNotNull(); + btnView.performClick(); + shadowMainLooper().idle(); + assertThat(dialog.isShowing()).isFalse(); assertThat(isShareBtnClicked.get()).isTrue(); + verify(mFeatureFactory.metricsFeatureProvider) + .action( + any(Context.class), + eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_POSITIVE_BTN_CLICKED), + eq(TEST_EVENT_DATA)); } @Test @@ -196,15 +276,21 @@ public class AudioSharingDialogFragmentTest { list.add(TEST_DEVICE_ITEM1); list.add(TEST_DEVICE_ITEM2); list.add(TEST_DEVICE_ITEM3); - mFragment.show(mParent, list, (item) -> {}); + AudioSharingDialogFragment.show(mParent, list, EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); + assertThat(dialog).isNotNull(); TextView description = dialog.findViewById(R.id.description_text); + assertThat(description).isNotNull(); ImageView image = dialog.findViewById(R.id.description_image); + assertThat(image).isNotNull(); Button shareBtn = dialog.findViewById(R.id.positive_btn); + assertThat(shareBtn).isNotNull(); Button cancelBtn = dialog.findViewById(R.id.negative_btn); + assertThat(cancelBtn).isNotNull(); RecyclerView recyclerView = dialog.findViewById(R.id.device_btn_list); + assertThat(recyclerView).isNotNull(); assertThat(dialog.isShowing()).isTrue(); assertThat(description.getVisibility()).isEqualTo(View.VISIBLE); assertThat(description.getText().toString()) @@ -223,11 +309,35 @@ public class AudioSharingDialogFragmentTest { list.add(TEST_DEVICE_ITEM1); list.add(TEST_DEVICE_ITEM2); list.add(TEST_DEVICE_ITEM3); - mFragment.show(mParent, list, (item) -> {}); + AtomicBoolean isCancelBtnClicked = new AtomicBoolean(false); + AudioSharingDialogFragment.show( + mParent, + list, + new AudioSharingDialogFragment.DialogEventListener() { + @Override + public void onItemClick(AudioSharingDeviceItem item) {} + + @Override + public void onCancelClick() { + isCancelBtnClicked.set(true); + } + }, + TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); - dialog.findViewById(R.id.negative_btn).performClick(); + assertThat(dialog).isNotNull(); + View btnView = dialog.findViewById(R.id.negative_btn); + assertThat(btnView).isNotNull(); + btnView.performClick(); + shadowMainLooper().idle(); + assertThat(dialog.isShowing()).isFalse(); + assertThat(isCancelBtnClicked.get()).isTrue(); + verify(mFeatureFactory.metricsFeatureProvider) + .action( + any(Context.class), + eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_NEGATIVE_BTN_CLICKED), + eq(TEST_EVENT_DATA)); } } 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 570af1f3c19..633bc06aa30 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandlerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogHandlerTest.java @@ -24,6 +24,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; +import android.app.settings.SettingsEnums; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeBroadcastReceiveState; @@ -32,6 +33,7 @@ import android.bluetooth.BluetoothStatusCodes; import android.content.Context; import android.os.Looper; import android.platform.test.flag.junit.SetFlagsRule; +import android.util.Pair; import androidx.fragment.app.DialogFragment; import androidx.fragment.app.Fragment; @@ -39,6 +41,7 @@ import androidx.fragment.app.FragmentActivity; import androidx.test.core.app.ApplicationProvider; import com.android.settings.bluetooth.Utils; +import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settings.testutils.shadow.ShadowBluetoothUtils; import com.android.settingslib.bluetooth.CachedBluetoothDevice; @@ -51,6 +54,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import com.android.settingslib.flags.Flags; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; import com.google.common.truth.Correspondence; import org.junit.Before; @@ -87,6 +91,7 @@ public class AudioSharingDialogHandlerTest { Correspondence.from( (Fragment fragment, String tag) -> fragment instanceof DialogFragment + && ((DialogFragment) fragment).getTag() != null && ((DialogFragment) fragment).getTag().equals(tag), "is equal to"); @@ -107,20 +112,22 @@ public class AudioSharingDialogHandlerTest { private Fragment mParentFragment; @Mock private BluetoothLeBroadcastReceiveState mState; private Context mContext; - private ShadowBluetoothAdapter mShadowBluetoothAdapter; private AudioSharingDialogHandler mHandler; + private FakeFeatureFactory mFeatureFactory; @Before public void setup() { mContext = ApplicationProvider.getApplicationContext(); ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager; mLocalBtManager = Utils.getLocalBtManager(mContext); - 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); + mFeatureFactory = FakeFeatureFactory.setupForTest(); mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); when(mLocalBtManager.getProfileManager()).thenReturn(mLocalBtProfileManager); when(mLocalBtProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast); @@ -183,9 +190,33 @@ public class AudioSharingDialogHandlerTest { when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mState)); mHandler.handleDeviceConnected(mCachedDevice2, /* userTriggered= */ true); shadowOf(Looper.getMainLooper()).idle(); - assertThat(mParentFragment.getChildFragmentManager().getFragments()) + List childFragments = mParentFragment.getChildFragmentManager().getFragments(); + assertThat(childFragments) .comparingElementsUsing(TAG_EQUALS) .containsExactly(AudioSharingStopDialogFragment.tag()); + + AudioSharingStopDialogFragment fragment = + (AudioSharingStopDialogFragment) Iterables.getOnlyElement(childFragments); + Pair[] eventData = fragment.getEventData(); + assertThat(eventData) + .asList() + .containsExactly( + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_SOURCE_PAGE_ID.ordinal(), + SettingsEnums.SETTINGS_CONNECTED_DEVICE_CATEGORY), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_PAGE_ID.ordinal(), + SettingsEnums.DIALOG_STOP_AUDIO_SHARING), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_USER_TRIGGERED.ordinal(), 1), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_DEVICE_COUNT_IN_SHARING + .ordinal(), + 1), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT + .ordinal(), + 0)); } @Test @@ -211,9 +242,33 @@ public class AudioSharingDialogHandlerTest { when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of()); mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ true); shadowOf(Looper.getMainLooper()).idle(); - assertThat(mParentFragment.getChildFragmentManager().getFragments()) + List childFragments = mParentFragment.getChildFragmentManager().getFragments(); + assertThat(childFragments) .comparingElementsUsing(TAG_EQUALS) .containsExactly(AudioSharingJoinDialogFragment.tag()); + + AudioSharingJoinDialogFragment fragment = + (AudioSharingJoinDialogFragment) Iterables.getOnlyElement(childFragments); + Pair[] eventData = fragment.getEventData(); + assertThat(eventData) + .asList() + .containsExactly( + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_SOURCE_PAGE_ID.ordinal(), + SettingsEnums.SETTINGS_CONNECTED_DEVICE_CATEGORY), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_PAGE_ID.ordinal(), + SettingsEnums.DIALOG_START_AUDIO_SHARING), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_USER_TRIGGERED.ordinal(), 1), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_DEVICE_COUNT_IN_SHARING + .ordinal(), + 0), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT + .ordinal(), + 2)); } @Test @@ -227,9 +282,33 @@ public class AudioSharingDialogHandlerTest { when(mAssistant.getAllSources(mDevice3)).thenReturn(ImmutableList.of(mState)); mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ true); shadowOf(Looper.getMainLooper()).idle(); - assertThat(mParentFragment.getChildFragmentManager().getFragments()) + List childFragments = mParentFragment.getChildFragmentManager().getFragments(); + assertThat(childFragments) .comparingElementsUsing(TAG_EQUALS) .containsExactly(AudioSharingJoinDialogFragment.tag()); + + AudioSharingJoinDialogFragment fragment = + (AudioSharingJoinDialogFragment) Iterables.getOnlyElement(childFragments); + Pair[] eventData = fragment.getEventData(); + assertThat(eventData) + .asList() + .containsExactly( + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_SOURCE_PAGE_ID.ordinal(), + SettingsEnums.SETTINGS_CONNECTED_DEVICE_CATEGORY), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_PAGE_ID.ordinal(), + SettingsEnums.DIALOG_AUDIO_SHARING_ADD_DEVICE), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_USER_TRIGGERED.ordinal(), 1), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_DEVICE_COUNT_IN_SHARING + .ordinal(), + 1), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT + .ordinal(), + 1)); } @Test @@ -245,9 +324,33 @@ public class AudioSharingDialogHandlerTest { when(mAssistant.getAllSources(mDevice4)).thenReturn(ImmutableList.of(mState)); mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ true); shadowOf(Looper.getMainLooper()).idle(); - assertThat(mParentFragment.getChildFragmentManager().getFragments()) + List childFragments = mParentFragment.getChildFragmentManager().getFragments(); + assertThat(childFragments) .comparingElementsUsing(TAG_EQUALS) .containsExactly(AudioSharingDisconnectDialogFragment.tag()); + + AudioSharingDisconnectDialogFragment fragment = + (AudioSharingDisconnectDialogFragment) Iterables.getOnlyElement(childFragments); + Pair[] eventData = fragment.getEventData(); + assertThat(eventData) + .asList() + .containsExactly( + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_SOURCE_PAGE_ID.ordinal(), + SettingsEnums.SETTINGS_CONNECTED_DEVICE_CATEGORY), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_PAGE_ID.ordinal(), + SettingsEnums.DIALOG_AUDIO_SHARING_SWITCH_DEVICE), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_USER_TRIGGERED.ordinal(), 1), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_DEVICE_COUNT_IN_SHARING + .ordinal(), + 2), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT + .ordinal(), + 1)); } @Test @@ -273,9 +376,33 @@ public class AudioSharingDialogHandlerTest { when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mState)); mHandler.handleDeviceConnected(mCachedDevice2, /* userTriggered= */ false); shadowOf(Looper.getMainLooper()).idle(); - assertThat(mParentFragment.getChildFragmentManager().getFragments()) + List childFragments = mParentFragment.getChildFragmentManager().getFragments(); + assertThat(childFragments) .comparingElementsUsing(TAG_EQUALS) .containsExactly(AudioSharingStopDialogFragment.tag()); + + AudioSharingStopDialogFragment fragment = + (AudioSharingStopDialogFragment) Iterables.getOnlyElement(childFragments); + Pair[] eventData = fragment.getEventData(); + assertThat(eventData) + .asList() + .containsExactly( + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_SOURCE_PAGE_ID.ordinal(), + SettingsEnums.SETTINGS_CONNECTED_DEVICE_CATEGORY), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_PAGE_ID.ordinal(), + SettingsEnums.DIALOG_STOP_AUDIO_SHARING), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_USER_TRIGGERED.ordinal(), 0), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_DEVICE_COUNT_IN_SHARING + .ordinal(), + 1), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT + .ordinal(), + 0)); } @Test @@ -301,9 +428,33 @@ public class AudioSharingDialogHandlerTest { when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of()); mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ false); shadowOf(Looper.getMainLooper()).idle(); - assertThat(mParentFragment.getChildFragmentManager().getFragments()) + List childFragments = mParentFragment.getChildFragmentManager().getFragments(); + assertThat(childFragments) .comparingElementsUsing(TAG_EQUALS) .containsExactly(AudioSharingJoinDialogFragment.tag()); + + AudioSharingJoinDialogFragment fragment = + (AudioSharingJoinDialogFragment) Iterables.getOnlyElement(childFragments); + Pair[] eventData = fragment.getEventData(); + assertThat(eventData) + .asList() + .containsExactly( + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_SOURCE_PAGE_ID.ordinal(), + SettingsEnums.SETTINGS_CONNECTED_DEVICE_CATEGORY), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_PAGE_ID.ordinal(), + SettingsEnums.DIALOG_START_AUDIO_SHARING), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_USER_TRIGGERED.ordinal(), 0), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_DEVICE_COUNT_IN_SHARING + .ordinal(), + 0), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT + .ordinal(), + 2)); } @Test @@ -317,9 +468,33 @@ public class AudioSharingDialogHandlerTest { when(mAssistant.getAllSources(mDevice3)).thenReturn(ImmutableList.of(mState)); mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ false); shadowOf(Looper.getMainLooper()).idle(); - assertThat(mParentFragment.getChildFragmentManager().getFragments()) + List childFragments = mParentFragment.getChildFragmentManager().getFragments(); + assertThat(childFragments) .comparingElementsUsing(TAG_EQUALS) .containsExactly(AudioSharingJoinDialogFragment.tag()); + + AudioSharingJoinDialogFragment fragment = + (AudioSharingJoinDialogFragment) Iterables.getOnlyElement(childFragments); + Pair[] eventData = fragment.getEventData(); + assertThat(eventData) + .asList() + .containsExactly( + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_SOURCE_PAGE_ID.ordinal(), + SettingsEnums.SETTINGS_CONNECTED_DEVICE_CATEGORY), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_PAGE_ID.ordinal(), + SettingsEnums.DIALOG_AUDIO_SHARING_ADD_DEVICE), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_USER_TRIGGERED.ordinal(), 0), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_DEVICE_COUNT_IN_SHARING + .ordinal(), + 1), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT + .ordinal(), + 1)); } @Test @@ -334,9 +509,33 @@ public class AudioSharingDialogHandlerTest { when(mAssistant.getAllSources(mDevice4)).thenReturn(ImmutableList.of(mState)); mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ false); shadowOf(Looper.getMainLooper()).idle(); - assertThat(mParentFragment.getChildFragmentManager().getFragments()) + List childFragments = mParentFragment.getChildFragmentManager().getFragments(); + assertThat(childFragments) .comparingElementsUsing(TAG_EQUALS) .containsExactly(AudioSharingDisconnectDialogFragment.tag()); + + AudioSharingDisconnectDialogFragment fragment = + (AudioSharingDisconnectDialogFragment) Iterables.getOnlyElement(childFragments); + Pair[] eventData = fragment.getEventData(); + assertThat(eventData) + .asList() + .containsExactly( + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_SOURCE_PAGE_ID.ordinal(), + SettingsEnums.SETTINGS_CONNECTED_DEVICE_CATEGORY), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_PAGE_ID.ordinal(), + SettingsEnums.DIALOG_AUDIO_SHARING_SWITCH_DEVICE), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_USER_TRIGGERED.ordinal(), 0), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_DEVICE_COUNT_IN_SHARING + .ordinal(), + 2), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT + .ordinal(), + 1)); } @Test @@ -357,6 +556,11 @@ public class AudioSharingDialogHandlerTest { mHandler.closeOpeningDialogsForLeaDevice(mCachedDevice1); shadowOf(Looper.getMainLooper()).idle(); assertThat(mParentFragment.getChildFragmentManager().getFragments()).isEmpty(); + verify(mFeatureFactory.metricsFeatureProvider) + .action( + mContext, + SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_AUTO_DISMISS, + SettingsEnums.DIALOG_START_AUDIO_SHARING); } @Test @@ -377,6 +581,11 @@ public class AudioSharingDialogHandlerTest { mHandler.closeOpeningDialogsForNonLeaDevice(mCachedDevice2); shadowOf(Looper.getMainLooper()).idle(); assertThat(mParentFragment.getChildFragmentManager().getFragments()).isEmpty(); + verify(mFeatureFactory.metricsFeatureProvider) + .action( + mContext, + SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_AUTO_DISMISS, + SettingsEnums.DIALOG_STOP_AUDIO_SHARING); } private void setUpBroadcast(boolean isBroadcasting) { 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 348efbe931b..481c78d2917 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragmentTest.java @@ -18,13 +18,21 @@ package com.android.settings.connecteddevice.audiosharing; 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.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.robolectric.shadows.ShadowLooper.shadowMainLooper; +import android.app.settings.SettingsEnums; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothStatusCodes; +import android.content.Context; import android.platform.test.flag.junit.SetFlagsRule; +import android.util.Pair; +import android.view.View; import android.widget.Button; import androidx.appcompat.app.AlertDialog; @@ -33,6 +41,7 @@ import androidx.fragment.app.FragmentActivity; import androidx.recyclerview.widget.RecyclerView; import com.android.settings.R; +import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settingslib.bluetooth.CachedBluetoothDevice; @@ -78,15 +87,19 @@ public class AudioSharingDisconnectDialogFragmentTest { new AudioSharingDeviceItem(TEST_DEVICE_NAME2, TEST_GROUP_ID2, /* isActive= */ false); private static final AudioSharingDeviceItem TEST_DEVICE_ITEM3 = new AudioSharingDeviceItem(TEST_DEVICE_NAME3, TEST_GROUP_ID3, /* isActive= */ false); + private static final AudioSharingDisconnectDialogFragment.DialogEventListener + EMPTY_EVENT_LISTENER = (AudioSharingDeviceItem item) -> {}; + private static final Pair TEST_EVENT_DATA = Pair.create(1, 1); + private static final Pair[] TEST_EVENT_DATA_LIST = + new Pair[] {TEST_EVENT_DATA}; @Mock private BluetoothDevice mDevice1; @Mock private BluetoothDevice mDevice3; - @Mock private CachedBluetoothDevice mCachedDevice1; @Mock private CachedBluetoothDevice mCachedDevice3; + private FakeFeatureFactory mFeatureFactory; private Fragment mParent; private AudioSharingDisconnectDialogFragment mFragment; - private ShadowBluetoothAdapter mShadowBluetoothAdapter; private ArrayList mDeviceItems = new ArrayList<>(); @Before @@ -96,12 +109,14 @@ public class AudioSharingDisconnectDialogFragmentTest { latestAlertDialog.dismiss(); 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); + mFeatureFactory = FakeFeatureFactory.setupForTest(); when(mDevice1.getAnonymizedAddress()).thenReturn(TEST_ADDRESS1); when(mDevice3.getAnonymizedAddress()).thenReturn(TEST_ADDRESS3); when(mCachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME1); @@ -116,13 +131,20 @@ public class AudioSharingDisconnectDialogFragmentTest { mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null); } + @Test + public void getMetricsCategory_correctValue() { + assertThat(mFragment.getMetricsCategory()) + .isEqualTo(SettingsEnums.DIALOG_AUDIO_SHARING_SWITCH_DEVICE); + } + @Test public void onCreateDialog_flagOff_dialogNotExist() { mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mDeviceItems = new ArrayList<>(); mDeviceItems.add(TEST_DEVICE_ITEM1); mDeviceItems.add(TEST_DEVICE_ITEM2); - mFragment.show(mParent, mDeviceItems, mCachedDevice3, (item) -> {}); + AudioSharingDisconnectDialogFragment.show( + mParent, mDeviceItems, mCachedDevice3, EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); @@ -135,12 +157,15 @@ public class AudioSharingDisconnectDialogFragmentTest { mDeviceItems = new ArrayList<>(); mDeviceItems.add(TEST_DEVICE_ITEM1); mDeviceItems.add(TEST_DEVICE_ITEM2); - mFragment.show(mParent, mDeviceItems, mCachedDevice3, (item) -> {}); + AudioSharingDisconnectDialogFragment.show( + mParent, mDeviceItems, mCachedDevice3, EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); + assertThat(dialog).isNotNull(); assertThat(dialog.isShowing()).isTrue(); RecyclerView view = dialog.findViewById(R.id.device_btn_list); + assertThat(view).isNotNull(); assertThat(view.getAdapter().getItemCount()).isEqualTo(2); } @@ -150,12 +175,14 @@ public class AudioSharingDisconnectDialogFragmentTest { mDeviceItems = new ArrayList<>(); mDeviceItems.add(TEST_DEVICE_ITEM1); mDeviceItems.add(TEST_DEVICE_ITEM2); - mFragment.show(mParent, mDeviceItems, mCachedDevice3, (item) -> {}); + AudioSharingDisconnectDialogFragment.show( + mParent, mDeviceItems, mCachedDevice3, EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); - AtomicBoolean isItemBtnClicked = new AtomicBoolean(false); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); + assertThat(dialog).isNotNull(); assertThat(dialog.isShowing()).isTrue(); RecyclerView view = dialog.findViewById(R.id.device_btn_list); + assertThat(view).isNotNull(); assertThat(view.getAdapter().getItemCount()).isEqualTo(2); Button btn1 = view.findViewHolderForAdapterPosition(0).itemView.findViewById(R.id.device_button); @@ -173,37 +200,71 @@ public class AudioSharingDisconnectDialogFragmentTest { TEST_DEVICE_NAME2)); // Update dialog content for device with same group - mFragment.show(mParent, mDeviceItems, mCachedDevice3, (item) -> isItemBtnClicked.set(true)); + AtomicBoolean isItemBtnClicked = new AtomicBoolean(false); + AudioSharingDisconnectDialogFragment.show( + mParent, + mDeviceItems, + mCachedDevice3, + (AudioSharingDeviceItem item) -> isItemBtnClicked.set(true), + TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog.isShowing()).isTrue(); + verify(mFeatureFactory.metricsFeatureProvider, times(0)) + .action( + any(Context.class), + eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_AUTO_DISMISS), + eq(SettingsEnums.DIALOG_AUDIO_SHARING_SWITCH_DEVICE)); + btn1 = view.findViewHolderForAdapterPosition(0).itemView.findViewById(R.id.device_button); btn1.performClick(); + shadowMainLooper().idle(); + assertThat(dialog.isShowing()).isFalse(); assertThat(isItemBtnClicked.get()).isTrue(); + verify(mFeatureFactory.metricsFeatureProvider) + .action( + any(Context.class), + eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_POSITIVE_BTN_CLICKED), + eq(TEST_EVENT_DATA)); } @Test - public void onCreateDialog_dialogIsShowingForNewGroup_updateDialog() { + public void onCreateDialog_dialogIsShowingForNewGroup_showNewDialog() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); mDeviceItems = new ArrayList<>(); mDeviceItems.add(TEST_DEVICE_ITEM1); mDeviceItems.add(TEST_DEVICE_ITEM2); - mFragment.show(mParent, mDeviceItems, mCachedDevice3, (item) -> {}); + AudioSharingDisconnectDialogFragment.show( + mParent, mDeviceItems, mCachedDevice3, EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); + assertThat(dialog).isNotNull(); assertThat(dialog.isShowing()).isTrue(); RecyclerView view = dialog.findViewById(R.id.device_btn_list); + assertThat(view).isNotNull(); assertThat(view.getAdapter().getItemCount()).isEqualTo(2); // Show new dialog for device with new group ArrayList newDeviceItems = new ArrayList<>(); newDeviceItems.add(TEST_DEVICE_ITEM2); newDeviceItems.add(TEST_DEVICE_ITEM3); - mFragment.show(mParent, newDeviceItems, mCachedDevice1, (item) -> {}); + AudioSharingDisconnectDialogFragment.show( + mParent, + newDeviceItems, + mCachedDevice1, + EMPTY_EVENT_LISTENER, + TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog.isShowing()).isTrue(); + verify(mFeatureFactory.metricsFeatureProvider) + .action( + any(Context.class), + eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_AUTO_DISMISS), + eq(SettingsEnums.DIALOG_AUDIO_SHARING_SWITCH_DEVICE)); + view = dialog.findViewById(R.id.device_btn_list); + assertThat(view).isNotNull(); assertThat(view.getAdapter().getItemCount()).isEqualTo(2); Button btn1 = view.findViewHolderForAdapterPosition(0).itemView.findViewById(R.id.device_button); @@ -227,12 +288,27 @@ public class AudioSharingDisconnectDialogFragmentTest { mDeviceItems = new ArrayList<>(); mDeviceItems.add(TEST_DEVICE_ITEM1); mDeviceItems.add(TEST_DEVICE_ITEM2); - mFragment.show(mParent, mDeviceItems, mCachedDevice3, (item) -> {}); + AudioSharingDisconnectDialogFragment.show( + mParent, mDeviceItems, mCachedDevice3, EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST); + shadowMainLooper().idle(); + AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); + assertThat(dialog).isNotNull(); + assertThat(dialog.isShowing()).isTrue(); + View btnView = dialog.findViewById(R.id.negative_btn); + assertThat(btnView).isNotNull(); + btnView.performClick(); shadowMainLooper().idle(); - AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); - assertThat(dialog.isShowing()).isTrue(); - dialog.findViewById(R.id.negative_btn).performClick(); assertThat(dialog.isShowing()).isFalse(); + verify(mFeatureFactory.metricsFeatureProvider, times(0)) + .action( + any(Context.class), + eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_AUTO_DISMISS), + eq(SettingsEnums.DIALOG_AUDIO_SHARING_SWITCH_DEVICE)); + verify(mFeatureFactory.metricsFeatureProvider) + .action( + any(Context.class), + eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_NEGATIVE_BTN_CLICKED), + eq(TEST_EVENT_DATA)); } } 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 2d55d97d4e1..c7b21ade886 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragmentTest.java @@ -18,13 +18,19 @@ package com.android.settings.connecteddevice.audiosharing; 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.verify; import static org.mockito.Mockito.when; import static org.robolectric.shadows.ShadowLooper.shadowMainLooper; import android.app.settings.SettingsEnums; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothStatusCodes; +import android.content.Context; import android.platform.test.flag.junit.SetFlagsRule; +import android.util.Pair; +import android.view.View; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; @@ -32,6 +38,7 @@ import androidx.fragment.app.FragmentActivity; import com.android.settings.R; import com.android.settings.bluetooth.Utils; +import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settings.testutils.shadow.ShadowBluetoothUtils; @@ -82,6 +89,9 @@ public class AudioSharingJoinDialogFragmentTest { @Override public void onCancelClick() {} }; + private static final Pair TEST_EVENT_DATA = Pair.create(1, 1); + private static final Pair[] TEST_EVENT_DATA_LIST = + new Pair[] {TEST_EVENT_DATA}; @Mock private CachedBluetoothDevice mCachedDevice1; @Mock private CachedBluetoothDevice mCachedDevice2; @@ -90,7 +100,7 @@ public class AudioSharingJoinDialogFragmentTest { @Mock private LocalBluetoothLeBroadcast mBroadcast; private Fragment mParent; private AudioSharingJoinDialogFragment mFragment; - private ShadowBluetoothAdapter mShadowBluetoothAdapter; + private FakeFeatureFactory mFeatureFactory; @Before public void setUp() { @@ -99,12 +109,14 @@ public class AudioSharingJoinDialogFragmentTest { latestAlertDialog.dismiss(); 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); + mFeatureFactory = FakeFeatureFactory.setupForTest(); when(mCachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME1); when(mCachedDevice2.getName()).thenReturn(TEST_DEVICE_NAME2); mFragment = new AudioSharingJoinDialogFragment(); @@ -137,7 +149,12 @@ public class AudioSharingJoinDialogFragmentTest { @Test public void onCreateDialog_flagOff_dialogNotExist() { mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); - mFragment.show(mParent, new ArrayList<>(), mCachedDevice2, EMPTY_EVENT_LISTENER); + AudioSharingJoinDialogFragment.show( + mParent, + new ArrayList<>(), + mCachedDevice2, + EMPTY_EVENT_LISTENER, + TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog).isNull(); @@ -146,7 +163,12 @@ public class AudioSharingJoinDialogFragmentTest { @Test public void onCreateDialog_flagOn_dialogShowTextForSingleDevice() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); - mFragment.show(mParent, new ArrayList<>(), mCachedDevice2, EMPTY_EVENT_LISTENER); + AudioSharingJoinDialogFragment.show( + mParent, + new ArrayList<>(), + mCachedDevice2, + EMPTY_EVENT_LISTENER, + TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog).isNotNull(); @@ -160,7 +182,8 @@ public class AudioSharingJoinDialogFragmentTest { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); ArrayList list = new ArrayList<>(); list.add(TEST_DEVICE_ITEM1); - mFragment.show(mParent, list, mCachedDevice2, EMPTY_EVENT_LISTENER); + AudioSharingJoinDialogFragment.show( + mParent, list, mCachedDevice2, EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog).isNotNull(); @@ -179,7 +202,8 @@ public class AudioSharingJoinDialogFragmentTest { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); ArrayList list = new ArrayList<>(); list.add(TEST_DEVICE_ITEM1); - mFragment.show(mParent, list, mCachedDevice2, EMPTY_EVENT_LISTENER); + AudioSharingJoinDialogFragment.show( + mParent, list, mCachedDevice2, EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog).isNotNull(); @@ -188,7 +212,8 @@ public class AudioSharingJoinDialogFragmentTest { // Update the content ArrayList list2 = new ArrayList<>(); list2.add(TEST_DEVICE_ITEM2); - mFragment.show(mParent, list2, mCachedDevice1, EMPTY_EVENT_LISTENER); + AudioSharingJoinDialogFragment.show( + mParent, list2, mCachedDevice1, EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog).isNotNull(); @@ -205,11 +230,25 @@ public class AudioSharingJoinDialogFragmentTest { @Test public void onCreateDialog_clickCancel_dialogDismiss() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); - mFragment.show(mParent, new ArrayList<>(), mCachedDevice2, EMPTY_EVENT_LISTENER); + AudioSharingJoinDialogFragment.show( + mParent, + new ArrayList<>(), + mCachedDevice2, + EMPTY_EVENT_LISTENER, + TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); - dialog.findViewById(R.id.negative_btn).performClick(); + assertThat(dialog).isNotNull(); + View btnView = dialog.findViewById(R.id.negative_btn); + assertThat(btnView).isNotNull(); + btnView.performClick(); + shadowMainLooper().idle(); assertThat(dialog.isShowing()).isFalse(); + verify(mFeatureFactory.metricsFeatureProvider) + .action( + any(Context.class), + eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_NEGATIVE_BTN_CLICKED), + eq(TEST_EVENT_DATA)); } @Test @@ -228,12 +267,22 @@ public class AudioSharingJoinDialogFragmentTest { @Override public void onCancelClick() {} - }); + }, + TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); - dialog.findViewById(R.id.positive_btn).performClick(); + assertThat(dialog).isNotNull(); + View btnView = dialog.findViewById(R.id.positive_btn); + assertThat(btnView).isNotNull(); + btnView.performClick(); + shadowMainLooper().idle(); assertThat(dialog.isShowing()).isFalse(); assertThat(isShareBtnClicked.get()).isTrue(); + verify(mFeatureFactory.metricsFeatureProvider) + .action( + any(Context.class), + eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_POSITIVE_BTN_CLICKED), + eq(TEST_EVENT_DATA)); } @Test @@ -252,11 +301,21 @@ public class AudioSharingJoinDialogFragmentTest { public void onCancelClick() { isCancelBtnClicked.set(true); } - }); + }, + TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); - dialog.findViewById(R.id.negative_btn).performClick(); + assertThat(dialog).isNotNull(); + View btnView = dialog.findViewById(R.id.negative_btn); + assertThat(btnView).isNotNull(); + btnView.performClick(); + shadowMainLooper().idle(); assertThat(dialog.isShowing()).isFalse(); assertThat(isCancelBtnClicked.get()).isTrue(); + verify(mFeatureFactory.metricsFeatureProvider) + .action( + any(Context.class), + eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_NEGATIVE_BTN_CLICKED), + eq(TEST_EVENT_DATA)); } } 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 84d7a317164..7d46a18a4f2 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragmentTest.java @@ -18,13 +18,21 @@ package com.android.settings.connecteddevice.audiosharing; 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.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.robolectric.shadows.ShadowLooper.shadowMainLooper; +import android.app.settings.SettingsEnums; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothStatusCodes; +import android.content.Context; import android.platform.test.flag.junit.SetFlagsRule; +import android.util.Pair; +import android.view.View; import android.widget.TextView; import androidx.appcompat.app.AlertDialog; @@ -32,6 +40,7 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import com.android.settings.R; +import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settingslib.bluetooth.CachedBluetoothDevice; @@ -76,14 +85,19 @@ public class AudioSharingStopDialogFragmentTest { private static final AudioSharingDeviceItem TEST_DEVICE_ITEM3 = new AudioSharingDeviceItem( TEST_DEVICE_NAME3, TEST_DEVICE_GROUP_ID3, /* isActive= */ false); + private static final AudioSharingStopDialogFragment.DialogEventListener EMPTY_EVENT_LISTENER = + () -> {}; + private static final Pair TEST_EVENT_DATA = Pair.create(1, 1); + private static final Pair[] TEST_EVENT_DATA_LIST = + new Pair[] {TEST_EVENT_DATA}; @Mock private CachedBluetoothDevice mCachedDevice1; @Mock private CachedBluetoothDevice mCachedDevice2; @Mock private BluetoothDevice mDevice1; @Mock private BluetoothDevice mDevice2; + private FakeFeatureFactory mFeatureFactory; private Fragment mParent; private AudioSharingStopDialogFragment mFragment; - private ShadowBluetoothAdapter mShadowBluetoothAdapter; @Before public void setUp() { @@ -92,12 +106,14 @@ public class AudioSharingStopDialogFragmentTest { latestAlertDialog.dismiss(); 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); + mFeatureFactory = FakeFeatureFactory.setupForTest(); when(mCachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME1); when(mCachedDevice1.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID1); when(mCachedDevice1.getDevice()).thenReturn(mDevice1); @@ -110,10 +126,21 @@ public class AudioSharingStopDialogFragmentTest { mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null); } + @Test + public void getMetricsCategory_correctValue() { + assertThat(mFragment.getMetricsCategory()) + .isEqualTo(SettingsEnums.DIALOG_STOP_AUDIO_SHARING); + } + @Test public void onCreateDialog_flagOff_dialogNotExist() { mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); - mFragment.show(mParent, ImmutableList.of(), mCachedDevice1, () -> {}); + AudioSharingStopDialogFragment.show( + mParent, + ImmutableList.of(), + mCachedDevice1, + EMPTY_EVENT_LISTENER, + TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog).isNull(); @@ -122,12 +149,18 @@ public class AudioSharingStopDialogFragmentTest { @Test public void onCreateDialog_oneDeviceInSharing_showDialogWithCorrectMessage() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); - mFragment.show(mParent, ImmutableList.of(TEST_DEVICE_ITEM2), mCachedDevice1, () -> {}); + AudioSharingStopDialogFragment.show( + mParent, + ImmutableList.of(TEST_DEVICE_ITEM2), + mCachedDevice1, + EMPTY_EVENT_LISTENER, + TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog).isNotNull(); assertThat(dialog.isShowing()).isTrue(); TextView view = dialog.findViewById(R.id.description_text); + assertThat(view).isNotNull(); assertThat(view.getText().toString()) .isEqualTo( mParent.getString( @@ -137,16 +170,18 @@ public class AudioSharingStopDialogFragmentTest { @Test public void onCreateDialog_twoDeviceInSharing_showDialogWithCorrectMessage() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); - mFragment.show( + AudioSharingStopDialogFragment.show( mParent, ImmutableList.of(TEST_DEVICE_ITEM2, TEST_DEVICE_ITEM3), mCachedDevice1, - () -> {}); + EMPTY_EVENT_LISTENER, + TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog).isNotNull(); assertThat(dialog.isShowing()).isTrue(); TextView view = dialog.findViewById(R.id.description_text); + assertThat(view).isNotNull(); assertThat(view.getText().toString()) .isEqualTo( mParent.getString( @@ -158,57 +193,99 @@ public class AudioSharingStopDialogFragmentTest { @Test public void onCreateDialog_dialogIsShowingForSameDevice_updateDialog() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); - mFragment.show(mParent, ImmutableList.of(), mCachedDevice1, () -> {}); + AudioSharingStopDialogFragment.show( + mParent, + ImmutableList.of(), + mCachedDevice1, + EMPTY_EVENT_LISTENER, + TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog).isNotNull(); assertThat(dialog.isShowing()).isTrue(); TextView view = dialog.findViewById(R.id.description_text); + assertThat(view).isNotNull(); assertThat(view.getText().toString()) .isEqualTo(mParent.getString(R.string.audio_sharing_stop_dialog_with_more_content)); // Update the content AtomicBoolean isStopBtnClicked = new AtomicBoolean(false); - mFragment.show( - mParent, ImmutableList.of(), mCachedDevice1, () -> isStopBtnClicked.set(true)); + AudioSharingStopDialogFragment.show( + mParent, + ImmutableList.of(), + mCachedDevice1, + () -> isStopBtnClicked.set(true), + TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog).isNotNull(); assertThat(dialog.isShowing()).isTrue(); + verify(mFeatureFactory.metricsFeatureProvider, times(0)) + .action( + any(Context.class), + eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_AUTO_DISMISS), + eq(SettingsEnums.DIALOG_STOP_AUDIO_SHARING)); - dialog.findViewById(android.R.id.button1).performClick(); + View btnView = dialog.findViewById(android.R.id.button1); + assertThat(btnView).isNotNull(); + btnView.performClick(); shadowMainLooper().idle(); assertThat(dialog.isShowing()).isFalse(); assertThat(isStopBtnClicked.get()).isTrue(); + verify(mFeatureFactory.metricsFeatureProvider) + .action( + any(Context.class), + eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_POSITIVE_BTN_CLICKED), + eq(TEST_EVENT_DATA)); } @Test public void onCreateDialog_dialogIsShowingForNewDevice_showNewDialog() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); - mFragment.show(mParent, ImmutableList.of(), mCachedDevice1, () -> {}); + AudioSharingStopDialogFragment.show( + mParent, + ImmutableList.of(), + mCachedDevice1, + EMPTY_EVENT_LISTENER, + TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog).isNotNull(); assertThat(dialog.isShowing()).isTrue(); TextView view = dialog.findViewById(R.id.description_text); + assertThat(view).isNotNull(); assertThat(view.getText().toString()) .isEqualTo(mParent.getString(R.string.audio_sharing_stop_dialog_with_more_content)); TextView title = dialog.findViewById(R.id.title_text); + assertThat(title).isNotNull(); assertThat(title.getText().toString()) .isEqualTo( mParent.getString( R.string.audio_sharing_stop_dialog_title, TEST_DEVICE_NAME1)); // Show new dialog - mFragment.show(mParent, ImmutableList.of(), mCachedDevice2, () -> {}); + AudioSharingStopDialogFragment.show( + mParent, + ImmutableList.of(), + mCachedDevice2, + EMPTY_EVENT_LISTENER, + TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); assertThat(dialog).isNotNull(); assertThat(dialog.isShowing()).isTrue(); + verify(mFeatureFactory.metricsFeatureProvider) + .action( + any(Context.class), + eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_AUTO_DISMISS), + eq(SettingsEnums.DIALOG_STOP_AUDIO_SHARING)); + view = dialog.findViewById(R.id.description_text); + assertThat(view).isNotNull(); assertThat(view.getText().toString()) .isEqualTo(mParent.getString(R.string.audio_sharing_stop_dialog_with_more_content)); title = dialog.findViewById(R.id.title_text); + assertThat(title).isNotNull(); assertThat(title.getText().toString()) .isEqualTo( mParent.getString( @@ -218,25 +295,60 @@ public class AudioSharingStopDialogFragmentTest { @Test public void onCreateDialog_clickCancel_dialogDismiss() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); - mFragment.show(mParent, ImmutableList.of(), mCachedDevice1, () -> {}); + AudioSharingStopDialogFragment.show( + mParent, + ImmutableList.of(), + mCachedDevice1, + EMPTY_EVENT_LISTENER, + TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); - dialog.findViewById(android.R.id.button2).performClick(); + assertThat(dialog).isNotNull(); + View btnView = dialog.findViewById(android.R.id.button2); + assertThat(btnView).isNotNull(); + btnView.performClick(); shadowMainLooper().idle(); assertThat(dialog.isShowing()).isFalse(); + verify(mFeatureFactory.metricsFeatureProvider, times(0)) + .action( + any(Context.class), + eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_AUTO_DISMISS), + eq(SettingsEnums.DIALOG_STOP_AUDIO_SHARING)); + verify(mFeatureFactory.metricsFeatureProvider) + .action( + any(Context.class), + eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_NEGATIVE_BTN_CLICKED), + eq(TEST_EVENT_DATA)); } @Test public void onCreateDialog_clickShare_callbackTriggered() { mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); AtomicBoolean isStopBtnClicked = new AtomicBoolean(false); - mFragment.show( - mParent, ImmutableList.of(), mCachedDevice1, () -> isStopBtnClicked.set(true)); + AudioSharingStopDialogFragment.show( + mParent, + ImmutableList.of(), + mCachedDevice1, + () -> isStopBtnClicked.set(true), + TEST_EVENT_DATA_LIST); shadowMainLooper().idle(); AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); - dialog.findViewById(android.R.id.button1).performClick(); + assertThat(dialog).isNotNull(); + View btnView = dialog.findViewById(android.R.id.button1); + assertThat(btnView).isNotNull(); + btnView.performClick(); shadowMainLooper().idle(); assertThat(dialog.isShowing()).isFalse(); assertThat(isStopBtnClicked.get()).isTrue(); + verify(mFeatureFactory.metricsFeatureProvider, times(0)) + .action( + any(Context.class), + eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_AUTO_DISMISS), + eq(SettingsEnums.DIALOG_STOP_AUDIO_SHARING)); + verify(mFeatureFactory.metricsFeatureProvider) + .action( + any(Context.class), + eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_POSITIVE_BTN_CLICKED), + eq(TEST_EVENT_DATA)); } } 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 0ead2d5d807..8f85feb89fd 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java @@ -23,6 +23,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.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -30,6 +31,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; +import android.app.settings.SettingsEnums; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeBroadcast; @@ -43,12 +45,17 @@ import android.content.IntentFilter; import android.os.Looper; import android.platform.test.flag.junit.SetFlagsRule; import android.util.FeatureFlagUtils; +import android.util.Pair; import android.widget.CompoundButton; +import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; import androidx.lifecycle.LifecycleOwner; import androidx.test.core.app.ApplicationProvider; import com.android.settings.bluetooth.Utils; +import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settings.testutils.shadow.ShadowBluetoothUtils; import com.android.settings.testutils.shadow.ShadowThreadUtils; @@ -65,6 +72,8 @@ import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.flags.Flags; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.truth.Correspondence; import org.junit.Before; import org.junit.Rule; @@ -77,7 +86,9 @@ import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; +import org.robolectric.shadows.androidx.fragment.FragmentController; +import java.util.List; import java.util.concurrent.Executor; @RunWith(RobolectricTestRunner.class) @@ -88,6 +99,18 @@ import java.util.concurrent.Executor; ShadowThreadUtils.class, }) public class AudioSharingSwitchBarControllerTest { + private static final String TEST_DEVICE_NAME1 = "test1"; + private static final String TEST_DEVICE_NAME2 = "test2"; + private static final int TEST_DEVICE_GROUP_ID1 = 1; + private static final int TEST_DEVICE_GROUP_ID2 = 2; + private static final Correspondence TAG_EQUALS = + Correspondence.from( + (Fragment fragment, String tag) -> + fragment instanceof DialogFragment + && ((DialogFragment) fragment).getTag() != null + && ((DialogFragment) fragment).getTag().equals(tag), + "is equal to"); + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -99,17 +122,19 @@ public class AudioSharingSwitchBarControllerTest { @Mock private LocalBluetoothLeBroadcastAssistant mAssistant; @Mock private VolumeControlProfile mVolumeControl; @Mock private CompoundButton mBtnView; - @Mock private CachedBluetoothDevice mCachedDevice; - @Mock private BluetoothDevice mDevice; + @Mock private CachedBluetoothDevice mCachedDevice1; + @Mock private CachedBluetoothDevice mCachedDevice2; + @Mock private BluetoothDevice mDevice1; + @Mock private BluetoothDevice mDevice2; private SettingsMainSwitchBar mSwitchBar; private AudioSharingSwitchBarController mController; - private AudioSharingSwitchBarController.OnAudioSharingStateChangedListener mListener; + private FakeFeatureFactory mFeatureFactory; private Lifecycle mLifecycle; private LifecycleOwner mLifecycleOwner; private boolean mOnAudioSharingStateChanged; private boolean mOnAudioSharingServiceConnected; private ShadowBluetoothAdapter mShadowBluetoothAdapter; - private LocalBluetoothManager mLocalBluetoothManager; + private Fragment mParentFragment; @Before public void setUp() { @@ -122,13 +147,20 @@ public class AudioSharingSwitchBarControllerTest { mLifecycleOwner = () -> mLifecycle; mLifecycle = new Lifecycle(mLifecycleOwner); ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager; - mLocalBluetoothManager = Utils.getLocalBtManager(mContext); - when(mLocalBluetoothManager.getProfileManager()).thenReturn(mBtProfileManager); - when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager); - when(mDeviceManager.findDevice(mDevice)).thenReturn(mCachedDevice); - when(mCachedDevice.getDevice()).thenReturn(mDevice); - when(mCachedDevice.getGroupId()).thenReturn(1); - when(mCachedDevice.getName()).thenReturn("test"); + LocalBluetoothManager localBluetoothManager = Utils.getLocalBtManager(mContext); + mFeatureFactory = FakeFeatureFactory.setupForTest(); + when(localBluetoothManager.getProfileManager()).thenReturn(mBtProfileManager); + when(localBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager); + when(mDeviceManager.findDevice(mDevice1)).thenReturn(mCachedDevice1); + when(mCachedDevice1.getDevice()).thenReturn(mDevice1); + when(mCachedDevice1.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID1); + when(mCachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME1); + when(mCachedDevice1.isActiveDevice(BluetoothProfile.LE_AUDIO)).thenReturn(false); + when(mDeviceManager.findDevice(mDevice2)).thenReturn(mCachedDevice2); + when(mCachedDevice2.getDevice()).thenReturn(mDevice2); + when(mCachedDevice2.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID2); + when(mCachedDevice2.getName()).thenReturn(TEST_DEVICE_NAME2); + when(mCachedDevice2.isActiveDevice(BluetoothProfile.LE_AUDIO)).thenReturn(true); when(mBtProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast); when(mBtProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant); when(mBtProfileManager.getVolumeControlProfile()).thenReturn(mVolumeControl); @@ -153,7 +185,7 @@ public class AudioSharingSwitchBarControllerTest { mSwitchBar.setDisabledByAdmin(mock(RestrictedLockUtils.EnforcedAdmin.class)); mOnAudioSharingStateChanged = false; mOnAudioSharingServiceConnected = false; - mListener = + AudioSharingSwitchBarController.OnAudioSharingStateChangedListener listener = new AudioSharingSwitchBarController.OnAudioSharingStateChangedListener() { @Override public void onAudioSharingStateChanged() { @@ -165,7 +197,14 @@ public class AudioSharingSwitchBarControllerTest { mOnAudioSharingServiceConnected = true; } }; - mController = new AudioSharingSwitchBarController(mContext, mSwitchBar, mListener); + mController = new AudioSharingSwitchBarController(mContext, mSwitchBar, listener); + mParentFragment = new Fragment(); + FragmentController.setupFragment( + mParentFragment, + FragmentActivity.class, + 0 /* containerViewId */, + null /* bundle */); + mController.init(mParentFragment); } @Test @@ -356,7 +395,7 @@ public class AudioSharingSwitchBarControllerTest { when(mBtnView.isEnabled()).thenReturn(true); when(mAssistant.getDevicesMatchingConnectionStates( new int[] {BluetoothProfile.STATE_CONNECTED})) - .thenReturn(ImmutableList.of(mDevice)); + .thenReturn(ImmutableList.of(mDevice1)); doNothing().when(mBroadcast).startPrivateBroadcast(); mController.onCheckedChanged(mBtnView, /* isChecked= */ true); verify(mBroadcast).startPrivateBroadcast(); @@ -380,4 +419,50 @@ public class AudioSharingSwitchBarControllerTest { mController.onCheckedChanged(mBtnView, /* isChecked= */ false); verify(mBroadcast).stopBroadcast(1); } + + @Test + public void onPlaybackStarted_showJoinAudioSharingDialog() { + FeatureFlagUtils.setEnabled( + mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true); + when(mBtnView.isEnabled()).thenReturn(true); + when(mAssistant.getDevicesMatchingConnectionStates( + new int[] {BluetoothProfile.STATE_CONNECTED})) + .thenReturn(ImmutableList.of(mDevice2, mDevice1)); + doNothing().when(mBroadcast).startPrivateBroadcast(); + mController.onCheckedChanged(mBtnView, /* isChecked= */ true); + verify(mBroadcast).startPrivateBroadcast(); + mController.mBroadcastCallback.onPlaybackStarted(0, 0); + shadowOf(Looper.getMainLooper()).idle(); + + verify(mFeatureFactory.metricsFeatureProvider) + .action(any(Context.class), eq(SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING)); + + List childFragments = mParentFragment.getChildFragmentManager().getFragments(); + assertThat(childFragments) + .comparingElementsUsing(TAG_EQUALS) + .containsExactly(AudioSharingDialogFragment.tag()); + + AudioSharingDialogFragment fragment = + (AudioSharingDialogFragment) Iterables.getOnlyElement(childFragments); + Pair[] eventData = fragment.getEventData(); + assertThat(eventData) + .asList() + .containsExactly( + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_SOURCE_PAGE_ID.ordinal(), + SettingsEnums.AUDIO_SHARING_SETTINGS), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_PAGE_ID.ordinal(), + SettingsEnums.DIALOG_AUDIO_SHARING_ADD_DEVICE), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_USER_TRIGGERED.ordinal(), 0), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_DEVICE_COUNT_IN_SHARING + .ordinal(), + 1), + Pair.create( + AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT + .ordinal(), + 1)); + } }