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

Commit b6ba9c3b authored by chelseahao's avatar chelseahao
Browse files

Show qr code in "Share your audio" dialog

Test: atest
Bug: 381775542
Flag: com.android.settingslib.flags.enable_le_audio_sharing
Change-Id: I6fe339beb50803989d4110fca681e609d2d600c1
parent cc0c66a7
Loading
Loading
Loading
Loading
+16 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.settings.connecteddevice.audiosharing;

import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
@@ -147,7 +148,7 @@ public class AudioSharingDialogFactory {
        /**
         * Sets the custom image of the dialog custom body.
         *
         * @param iconRes The text to be used for the title.
         * @param iconRes The iconRes to be used for the image.
         * @return This builder.
         */
        @NonNull
@@ -158,6 +159,20 @@ public class AudioSharingDialogFactory {
            return this;
        }

        /**
         * Sets the custom image of the dialog custom body.
         *
         * @param bitmap The bitmap to be used for the image.
         * @return This builder.
         */
        @NonNull
        public AudioSharingDialogFactory.DialogBuilder setCustomImage(Bitmap bitmap) {
            ImageView image = mCustomBody.findViewById(R.id.description_image);
            image.setImageBitmap(bitmap);
            image.setVisibility(View.VISIBLE);
            return this;
        }

        /**
         * Sets the custom message of the dialog custom body.
         *
+31 −11
Original line number Diff line number Diff line
@@ -17,10 +17,13 @@
package com.android.settings.connecteddevice.audiosharing;

import static com.android.settings.connecteddevice.audiosharing.AudioSharingDashboardFragment.SHARE_THEN_PAIR_REQUEST_CODE;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsQrCodeFragment.getQrCodeBitmap;
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.EXTRA_PAIR_AND_JOIN_SHARING;

import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.util.Log;
import android.util.Pair;
@@ -48,6 +51,7 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment {
    private static final String TAG = "AudioSharingDialog";

    private static final String BUNDLE_KEY_DEVICE_ITEMS = "bundle_key_device_items";
    private static final String BUNDLE_KEY_BROADCAST_METADATA = "bundle_key_broadcast_metadata";

    // The host creates an instance of this dialog fragment must implement this interface to receive
    // event callbacks.
@@ -80,12 +84,14 @@ 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 metadata    The audio sharing metadata, nullable.
     * @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(
            @Nullable Fragment host,
            @NonNull List<AudioSharingDeviceItem> deviceItems,
            @Nullable BluetoothLeBroadcastMetadata metadata,
            @NonNull DialogEventListener listener,
            @NonNull Pair<Integer, Object>[] eventData) {
        if (host == null) {
@@ -116,6 +122,9 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment {
            Log.d(TAG, "Show up the dialog.");
            final Bundle bundle = new Bundle();
            bundle.putParcelableList(BUNDLE_KEY_DEVICE_ITEMS, deviceItems);
            if (metadata != null) {
                bundle.putParcelable(BUNDLE_KEY_BROADCAST_METADATA, metadata);
            }
            AudioSharingDialogFragment dialogFrag = new AudioSharingDialogFragment();
            dialogFrag.setArguments(bundle);
            dialogFrag.show(manager, TAG);
@@ -150,7 +159,6 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment {
        }
        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)
                    .setCustomPositiveButton(
                            R.string.audio_sharing_pair_button_label,
@@ -172,17 +180,29 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment {
                                    launcher.setResultListener(sHost, SHARE_THEN_PAIR_REQUEST_CODE);
                                }
                                launcher.launch();
                            })
                            });
            BluetoothLeBroadcastMetadata metadata = arguments.getParcelable(
                    BUNDLE_KEY_BROADCAST_METADATA, BluetoothLeBroadcastMetadata.class);
            Bitmap qrCodeBitmap = metadata == null ? null : getQrCodeBitmap(metadata,
                    getContext()).orElse(null);
            if (qrCodeBitmap != null) {
                builder.setCustomImage(qrCodeBitmap)
                        .setCustomNegativeButton(com.android.settings.R.string.cancel,
                                v -> onCancelClick());
            } else {
                builder.setCustomImage(R.drawable.audio_sharing_guidance)
                        .setCustomNegativeButton(
                                R.string.audio_sharing_qrcode_button_label,
                                v -> {
                                    onCancelClick();
                                    new SubSettingLauncher(getContext())
                                            .setTitleRes(R.string.audio_streams_qr_code_page_title)
                                        .setDestination(AudioStreamsQrCodeFragment.class.getName())
                                            .setDestination(
                                                    AudioStreamsQrCodeFragment.class.getName())
                                            .setSourceMetricsCategory(getMetricsCategory())
                                            .launch();
                                });
            }
        } else if (deviceItems.size() == 1) {
            AudioSharingDeviceItem deviceItem = Iterables.getOnlyElement(deviceItems);
            builder.setTitle(
+7 −1
Original line number Diff line number Diff line
@@ -727,10 +727,16 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
                        cleanUpStatesForStartSharing();
                    }
                };
        BluetoothLeBroadcastMetadata metadata = mBroadcast == null ? null
                : mBroadcast.getLatestBluetoothLeBroadcastMetadata();
        AudioSharingUtils.postOnMainThread(
                mContext,
                () -> AudioSharingDialogFragment.show(
                        mFragment, mDeviceItemsForSharing, listener, eventData));
                        mFragment,
                        mDeviceItemsForSharing,
                        metadata,
                        listener,
                        eventData));
    }

    private void showErrorDialog() {
+6 −3
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams;

import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.util.Log;
@@ -69,7 +70,7 @@ public class AudioStreamsQrCodeFragment extends InstrumentedFragment {
                    if (broadcastMetadata == null) {
                        return;
                    }
                    Bitmap bm = getQrCodeBitmap(broadcastMetadata).orElse(null);
                    Bitmap bm = getQrCodeBitmap(broadcastMetadata, getActivity()).orElse(null);
                    if (bm == null) {
                        return;
                    }
@@ -100,7 +101,9 @@ public class AudioStreamsQrCodeFragment extends InstrumentedFragment {
                });
    }

    private Optional<Bitmap> getQrCodeBitmap(@Nullable BluetoothLeBroadcastMetadata metadata) {
    /** Gets an optional bitmap from metadata. */
    public static Optional<Bitmap> getQrCodeBitmap(@Nullable BluetoothLeBroadcastMetadata metadata,
            Context context) {
        if (metadata == null) {
            Log.d(TAG, "getQrCodeBitmap: broadcastMetadata is empty!");
            return Optional.empty();
@@ -113,7 +116,7 @@ public class AudioStreamsQrCodeFragment extends InstrumentedFragment {
        Log.d(TAG, "getQrCodeBitmap: metadata : " + metadata);
        try {
            int qrcodeSize =
                    getResources().getDimensionPixelSize(R.dimen.audio_streams_qrcode_size);
                    context.getResources().getDimensionPixelSize(R.dimen.audio_streams_qrcode_size);
            Bitmap bitmap = QrCodeGenerator.encodeQrCode(metadataStr, qrcodeSize);
            return Optional.of(bitmap);
        } catch (WriterException e) {
+59 −8
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import static org.robolectric.shadows.ShadowLooper.shadowMainLooper;

import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothStatusCodes;
import android.content.Context;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -44,6 +45,7 @@ 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.BluetoothLeBroadcastMetadataExt;
import com.android.settingslib.flags.Flags;

import org.junit.After;
@@ -87,6 +89,11 @@ public class AudioSharingDialogFragmentTest {
    private static final Pair<Integer, Object> TEST_EVENT_DATA = Pair.create(1, 1);
    private static final Pair<Integer, Object>[] TEST_EVENT_DATA_LIST =
            new Pair[] {TEST_EVENT_DATA};
    private static final String METADATA_STR =
            "BLUETOOTH:UUID:184F;BN:VGVzdA==;AT:1;AD:00A1A1A1A1A1;BI:1E240;BC:VGVzdENvZGU=;"
                    + "MD:BgNwVGVzdA==;AS:1;PI:A0;NS:1;BS:3;NB:2;SM:BQNUZXN0BARlbmc=;;";
    private static final BluetoothLeBroadcastMetadata METADATA =
            BluetoothLeBroadcastMetadataExt.INSTANCE.convertToBroadcastMetadata(METADATA_STR);

    private Fragment mParent;
    private FakeFeatureFactory mFeatureFactory;
@@ -123,7 +130,7 @@ public class AudioSharingDialogFragmentTest {
    public void onCreateDialog_flagOff_dialogNotExist() {
        mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
        AudioSharingDialogFragment.show(
                mParent, new ArrayList<>(), EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST);
                mParent, new ArrayList<>(), null, EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST);
        shadowMainLooper().idle();
        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
        assertThat(dialog).isNull();
@@ -133,17 +140,18 @@ public class AudioSharingDialogFragmentTest {
    public void onCreateDialog_unattachedFragment_dialogNotExist() {
        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
        AudioSharingDialogFragment.show(
                new Fragment(), new ArrayList<>(), EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST);
                new Fragment(), new ArrayList<>(), null, EMPTY_EVENT_LISTENER,
                TEST_EVENT_DATA_LIST);
        shadowMainLooper().idle();
        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
        assertThat(dialog).isNull();
    }

    @Test
    public void onCreateDialog_flagOn_noExtraConnectedDevice() {
    public void onCreateDialog_flagOn_qrCodeBitmapNull_noExtraConnectedDevice() {
        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
        AudioSharingDialogFragment.show(
                mParent, new ArrayList<>(), EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST);
                mParent, new ArrayList<>(), null, EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST);
        shadowMainLooper().idle();

        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
@@ -176,6 +184,7 @@ public class AudioSharingDialogFragmentTest {
        AudioSharingDialogFragment.show(
                mParent,
                new ArrayList<>(),
                null,
                new AudioSharingDialogFragment.DialogEventListener() {
                    @Override
                    public void onPositiveClick() {
@@ -201,12 +210,13 @@ public class AudioSharingDialogFragmentTest {
    }

    @Test
    public void onCreateDialog_noExtraConnectedDevice_showQRCode() {
    public void onCreateDialog_noExtraConnectedDevice_showQRCodeButton() {
        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
        AtomicBoolean isQrCodeBtnClicked = new AtomicBoolean(false);
        AudioSharingDialogFragment.show(
                mParent,
                new ArrayList<>(),
                null,
                new AudioSharingDialogFragment.DialogEventListener() {
                    @Override
                    public void onCancelClick() {
@@ -219,6 +229,8 @@ public class AudioSharingDialogFragmentTest {
        assertThat(dialog).isNotNull();
        Button qrCodeBtn = dialog.findViewById(R.id.negative_btn);
        assertThat(qrCodeBtn).isNotNull();
        assertThat(qrCodeBtn.getText().toString())
                .isEqualTo(mParent.getString(R.string.audio_sharing_qrcode_button_label));
        qrCodeBtn.performClick();
        shadowMainLooper().idle();

@@ -231,12 +243,47 @@ public class AudioSharingDialogFragmentTest {
        assertThat(dialog.isShowing()).isFalse();
    }

    @Test
    public void onCreateDialog_noExtraConnectedDevice_hasMetadata_showCancelButton() {
        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
        AtomicBoolean isCancelBtnClicked = new AtomicBoolean(false);
        AudioSharingDialogFragment.show(
                mParent,
                new ArrayList<>(),
                METADATA,
                new AudioSharingDialogFragment.DialogEventListener() {
                    @Override
                    public void onCancelClick() {
                        isCancelBtnClicked.set(true);
                    }
                },
                TEST_EVENT_DATA_LIST);
        shadowMainLooper().idle();
        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
        assertThat(dialog).isNotNull();
        ImageView image = dialog.findViewById(R.id.description_image);
        assertThat(image).isNotNull();
        Button cancelBtn = dialog.findViewById(R.id.negative_btn);
        assertThat(cancelBtn).isNotNull();
        cancelBtn.performClick();
        shadowMainLooper().idle();

        verify(mFeatureFactory.metricsFeatureProvider)
                .action(
                        any(Context.class),
                        eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_NEGATIVE_BTN_CLICKED),
                        eq(TEST_EVENT_DATA));
        assertThat(isCancelBtnClicked.get()).isTrue();
        assertThat(dialog.isShowing()).isFalse();
    }

    @Test
    public void onCreateDialog_flagOn_singleExtraConnectedDevice() {
        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
        ArrayList<AudioSharingDeviceItem> list = new ArrayList<>();
        list.add(TEST_DEVICE_ITEM1);
        AudioSharingDialogFragment.show(mParent, list, EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST);
        AudioSharingDialogFragment.show(mParent, list, null, EMPTY_EVENT_LISTENER,
                TEST_EVENT_DATA_LIST);
        shadowMainLooper().idle();

        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
@@ -273,7 +320,8 @@ public class AudioSharingDialogFragmentTest {
        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
        ArrayList<AudioSharingDeviceItem> list = new ArrayList<>();
        list.add(TEST_DEVICE_ITEM1);
        AudioSharingDialogFragment.show(mParent, list, EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST);
        AudioSharingDialogFragment.show(mParent, list, null, EMPTY_EVENT_LISTENER,
                TEST_EVENT_DATA_LIST);
        shadowMainLooper().idle();

        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
@@ -300,6 +348,7 @@ public class AudioSharingDialogFragmentTest {
        AudioSharingDialogFragment.show(
                mParent,
                list,
                null,
                new AudioSharingDialogFragment.DialogEventListener() {
                    @Override
                    public void onItemClick(@NonNull AudioSharingDeviceItem item) {
@@ -332,7 +381,8 @@ public class AudioSharingDialogFragmentTest {
        list.add(TEST_DEVICE_ITEM1);
        list.add(TEST_DEVICE_ITEM2);
        list.add(TEST_DEVICE_ITEM3);
        AudioSharingDialogFragment.show(mParent, list, EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST);
        AudioSharingDialogFragment.show(mParent, list, null, EMPTY_EVENT_LISTENER,
                TEST_EVENT_DATA_LIST);
        shadowMainLooper().idle();

        AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
@@ -371,6 +421,7 @@ public class AudioSharingDialogFragmentTest {
        AudioSharingDialogFragment.show(
                mParent,
                list,
                null,
                new AudioSharingDialogFragment.DialogEventListener() {
                    @Override
                    public void onCancelClick() {
Loading