Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7f55253f authored by Yiyi Shen's avatar Yiyi Shen Committed by Android (Google) Code Review
Browse files

Merge "[Audiosharing] Log action in audio sharing dialogs" into main

parents 3f6cbec0 fec890ea
Loading
Loading
Loading
Loading
+48 −5
Original line number Diff line number Diff line
@@ -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<Integer, Object>[] 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<AudioSharingDeviceItem> deviceItems,
            @NonNull DialogEventListener listener) {
            @NonNull DialogEventListener listener,
            @NonNull Pair<Integer, Object>[] 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<Integer, Object>[] getEventData() {
        return sEventData;
    }

    @Override
    @NonNull
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Bundle arguments = requireArguments();
        List<AudioSharingDeviceItem> 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();
    }
}
+127 −56
Original line number Diff line number Diff line
@@ -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<BluetoothDevice> 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<AudioSharingDeviceItem> deviceItemsInSharingSession =
                    AudioSharingUtils.buildOrderedConnectedLeadAudioSharingDeviceItem(
                            mLocalBtManager, groupedDevices, /* filterByInSharing= */ true);
            AudioSharingStopDialogFragment.DialogEventListener listener =
                    () -> {
                        cachedDevice.setActive();
                        AudioSharingUtils.stopBroadcasting(mLocalBtManager);
                    };
            Pair<Integer, Object>[] 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<Integer, Object>[] eventData =
                        AudioSharingUtils.buildAudioSharingDialogEventData(
                                SettingsEnums.SETTINGS_CONNECTED_DEVICE_CATEGORY,
                                SettingsEnums.DIALOG_AUDIO_SHARING_SWITCH_DEVICE,
                                userTriggered,
                                deviceItemsInSharingSession.size(),
                                /* candidateDeviceCount= */ 1);
                postOnMainThread(
                        () -> {
                            closeOpeningDialogsOtherThan(
@@ -260,23 +291,13 @@ 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.
                postOnMainThread(
                        () -> {
                            closeOpeningDialogsOtherThan(AudioSharingJoinDialogFragment.tag());
                            AudioSharingJoinDialogFragment.show(
                                    mHostFragment,
                                    deviceItemsInSharingSession,
                                    cachedDevice,
                AudioSharingJoinDialogFragment.DialogEventListener listener =
                        new AudioSharingJoinDialogFragment.DialogEventListener() {
                            @Override
                            public void onShareClick() {
@@ -285,7 +306,23 @@ public class AudioSharingDialogHandler {

                            @Override
                            public void onCancelClick() {}
                                    });
                        };
                Pair<Integer, Object>[] eventData =
                        AudioSharingUtils.buildAudioSharingDialogEventData(
                                SettingsEnums.SETTINGS_CONNECTED_DEVICE_CATEGORY,
                                SettingsEnums.DIALOG_AUDIO_SHARING_ADD_DEVICE,
                                userTriggered,
                                deviceItemsInSharingSession.size(),
                                /* candidateDeviceCount= */ 1);
                postOnMainThread(
                        () -> {
                            closeOpeningDialogsOtherThan(AudioSharingJoinDialogFragment.tag());
                            AudioSharingJoinDialogFragment.show(
                                    mHostFragment,
                                    deviceItemsInSharingSession,
                                    cachedDevice,
                                    listener,
                                    eventData);
                        });
            }
        } else {
@@ -302,13 +339,7 @@ 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) {
                postOnMainThread(
                        () -> {
                            closeOpeningDialogsOtherThan(AudioSharingJoinDialogFragment.tag());
                            AudioSharingJoinDialogFragment.show(
                                    mHostFragment,
                                    deviceItems,
                                    cachedDevice,
                AudioSharingJoinDialogFragment.DialogEventListener listener =
                        new AudioSharingJoinDialogFragment.DialogEventListener() {
                            @Override
                            public void onShareClick() {
@@ -319,10 +350,7 @@ public class AudioSharingDialogHandler {
                                        mTargetSinks.add(device.getDevice());
                                    }
                                }
                                            Log.d(
                                                    TAG,
                                                    "Start broadcast with sinks: "
                                                            + mTargetSinks.size());
                                Log.d(TAG, "Start broadcast with sinks = " + mTargetSinks.size());
                                if (mBroadcast != null) {
                                    mBroadcast.startPrivateBroadcast();
                                }
@@ -334,7 +362,20 @@ public class AudioSharingDialogHandler {
                                    cachedDevice.setActive();
                                }
                            }
                                    });
                        };

                Pair<Integer, Object>[] 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, listener, eventData);
                        });
            } else if (userTriggered) {
                cachedDevice.setActive();
@@ -346,9 +387,12 @@ public class AudioSharingDialogHandler {
        if (mHostFragment == null) return;
        List<Fragment> 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);
                        });
    }
}
+64 −18

File changed.

Preview size limit exceeded, changes collapsed.

+29 −2
Original line number Diff line number Diff line
@@ -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<Integer, Object>[] 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<AudioSharingDeviceItem> deviceItems,
            @NonNull CachedBluetoothDevice newDevice,
            @NonNull DialogEventListener listener) {
            @NonNull DialogEventListener listener,
            @NonNull Pair<Integer, Object>[] 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<Integer, Object>[] getEventData() {
        return sEventData;
    }

    @Override
    @NonNull
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        Bundle arguments = requireArguments();
        List<AudioSharingDeviceItem> 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();
        if (deviceItems == null) {
            Log.d(TAG, "Fail to create dialog: null deviceItems");
        } else {
            updateDialog(deviceItems, newDeviceName, dialog);
        }
        dialog.show();
        AudioSharingDialogHelper.updateMessageStyle(dialog);
        return dialog;
+54 −13

File changed.

Preview size limit exceeded, changes collapsed.

Loading