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

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

Merge "[Audiosharing] Impl audio sharing main switch." into main

parents bf7010be cded970a
Loading
Loading
Loading
Loading
+52 −13
Original line number Diff line number Diff line
@@ -38,6 +38,26 @@ import java.util.ArrayList;
public class AudioSharingDialogFragment extends InstrumentedDialogFragment {
    private static final String TAG = "AudioSharingDialog";

    private static final String BUNDLE_KEY_DEVICE_NAMES = "bundle_key_device_names";

    // The host creates an instance of this dialog fragment must implement this interface to receive
    // event callbacks.
    public interface DialogEventListener {
        /**
         * Called when users click the device item for sharing in the dialog.
         *
         * @param position The position of the item clicked.
         */
        void onItemClick(int position);

        /**
         * Called when users click the cancel button in the dialog.
         */
        void onCancelClick();
    }

    private static DialogEventListener sListener;

    private View mRootView;

    @Override
@@ -50,40 +70,59 @@ public class AudioSharingDialogFragment extends InstrumentedDialogFragment {
     *
     * @param host The Fragment this dialog will be hosted.
     */
    public static void show(Fragment host) {
    public static void show(
            Fragment host, ArrayList<String> deviceNames, DialogEventListener listener) {
        if (!Flags.enableLeAudioSharing()) return;
        final FragmentManager manager = host.getChildFragmentManager();
        sListener = listener;
        if (manager.findFragmentByTag(TAG) == null) {
            final AudioSharingDialogFragment dialog = new AudioSharingDialogFragment();
            final Bundle bundle = new Bundle();
            bundle.putStringArrayList(BUNDLE_KEY_DEVICE_NAMES, deviceNames);
            AudioSharingDialogFragment dialog = new AudioSharingDialogFragment();
            dialog.setArguments(bundle);
            dialog.show(manager, TAG);
        }
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Bundle arguments = requireArguments();
        ArrayList<String> deviceNames = arguments.getStringArrayList(BUNDLE_KEY_DEVICE_NAMES);
        final AlertDialog.Builder builder =
                new AlertDialog.Builder(getActivity()).setTitle("Share audio");
                new AlertDialog.Builder(getActivity()).setTitle("Share audio").setCancelable(false);
        mRootView =
                LayoutInflater.from(builder.getContext())
                        .inflate(R.layout.dialog_audio_sharing, /* parent= */ null);
        // TODO: use real subtitle according to device count.
        TextView subTitle1 = mRootView.findViewById(R.id.share_audio_subtitle1);
        TextView subTitle2 = mRootView.findViewById(R.id.share_audio_subtitle2);
        if (deviceNames.isEmpty()) {
            subTitle1.setVisibility(View.INVISIBLE);
            subTitle2.setText("To start sharing audio, connect headphones that support LE audio");
            builder.setNegativeButton(
                    "Close",
                    (dialog, which) -> {
                        sListener.onCancelClick();
                    });
        } else if (deviceNames.size() == 1) {
            // TODO: add real impl
            subTitle1.setText("1 devices connected");
            subTitle2.setText("placeholder");
        } else {
            // TODO: add real impl
            subTitle1.setText("2 devices connected");
            subTitle2.setText("placeholder");
        }
        RecyclerView recyclerView = mRootView.findViewById(R.id.btn_list);
        // TODO: use real audio sharing device list.
        ArrayList<String> devices = new ArrayList<>();
        devices.add("Buds 1");
        devices.add("Buds 2");
        recyclerView.setAdapter(
                new AudioSharingDeviceAdapter(
                        devices,
                        deviceNames,
                        (int position) -> {
                            // TODO: add on click callback.
                            sListener.onItemClick(position);
                        }));
        recyclerView.setLayoutManager(
                new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
        return builder.setView(mRootView).create();
        AlertDialog dialog = builder.setView(mRootView).create();
        dialog.setCanceledOnTouchOutside(false);
        return dialog;
    }
}
+133 −8
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.settings.connecteddevice.audiosharing;

import android.bluetooth.BluetoothLeBroadcast;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.content.Context;
import android.util.Log;
import android.widget.Switch;
@@ -24,36 +26,116 @@ import androidx.annotation.NonNull;
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.flags.Flags;
import com.android.settings.widget.SettingsMainSwitchBar;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.OnMainSwitchChangeListener;

import java.util.ArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class AudioSharingSwitchBarController extends BasePreferenceController
        implements DefaultLifecycleObserver, OnMainSwitchChangeListener {
    private static final String TAG = "AudioSharingSwitchBarCtl";
    private static final String PREF_KEY = "audio_sharing_main_switch";

    private final Context mContext;
    private final SettingsMainSwitchBar mSwitchBar;
    private final LocalBluetoothManager mBtManager;
    private final LocalBluetoothLeBroadcast mBroadcast;
    private final Executor mExecutor;
    private DashboardFragment mFragment;

    private final BluetoothLeBroadcast.Callback mBroadcastCallback =
            new BluetoothLeBroadcast.Callback() {
                @Override
                public void onBroadcastStarted(int reason, int broadcastId) {
                    Log.d(
                            TAG,
                            "onBroadcastStarted(), reason = "
                                    + reason
                                    + ", broadcastId = "
                                    + broadcastId);
                    updateSwitch();
                }

                @Override
                public void onBroadcastStartFailed(int reason) {
                    Log.d(TAG, "onBroadcastStartFailed(), reason = " + reason);
                    // TODO: handle broadcast start fail
                    updateSwitch();
                }

                @Override
                public void onBroadcastMetadataChanged(
                        int broadcastId, @NonNull BluetoothLeBroadcastMetadata metadata) {
                    Log.d(
                            TAG,
                            "onBroadcastMetadataChanged(), broadcastId = "
                                    + broadcastId
                                    + ", metadata = "
                                    + metadata);
                    // TODO: handle add sink if there are connected lea devices.
                }

                @Override
                public void onBroadcastStopped(int reason, int broadcastId) {
                    Log.d(
                            TAG,
                            "onBroadcastStopped(), reason = "
                                    + reason
                                    + ", broadcastId = "
                                    + broadcastId);
                    updateSwitch();
                }

                @Override
                public void onBroadcastStopFailed(int reason) {
                    Log.d(TAG, "onBroadcastStopFailed(), reason = " + reason);
                    // TODO: handle broadcast stop fail
                    updateSwitch();
                }

                @Override
                public void onBroadcastUpdated(int reason, int broadcastId) {}

                @Override
                public void onBroadcastUpdateFailed(int reason, int broadcastId) {}

                @Override
                public void onPlaybackStarted(int reason, int broadcastId) {}

                @Override
                public void onPlaybackStopped(int reason, int broadcastId) {}
            };

    AudioSharingSwitchBarController(Context context, SettingsMainSwitchBar switchBar) {
        super(context, PREF_KEY);
        mContext = context;
        mSwitchBar = switchBar;
        mSwitchBar.setChecked(false);
        mBtManager = Utils.getLocalBtManager(context);
        mBroadcast = mBtManager.getProfileManager().getLeAudioBroadcastProfile();
        mExecutor = Executors.newSingleThreadExecutor();
        mSwitchBar.setChecked(isBroadcasting());
    }

    @Override
    public void onStart(@NonNull LifecycleOwner owner) {
        mSwitchBar.addOnSwitchChangeListener(this);
        if (mBroadcast != null) {
            mBroadcast.registerServiceCallBack(mExecutor, mBroadcastCallback);
        }
    }

    @Override
    public void onStop(@NonNull LifecycleOwner owner) {
        mSwitchBar.removeOnSwitchChangeListener(this);
        if (mBroadcast != null) {
            mBroadcast.unregisterServiceCallBack(mBroadcastCallback);
        }
    }

    @Override
@@ -63,7 +145,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
        if (isChecked) {
            startAudioSharing();
        } else {
            // TODO: stop sharing
            stopAudioSharing();
        }
    }

@@ -82,10 +164,53 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
    }

    private void startAudioSharing() {
        if (mFragment != null) {
            AudioSharingDialogFragment.show(mFragment);
        } else {
        mSwitchBar.setEnabled(false);
        if (mBroadcast == null || isBroadcasting()) {
            Log.d(TAG, "Already in broadcasting or broadcast not support, ignore!");
            mSwitchBar.setEnabled(true);
            return;
        }
        if (mFragment == null) {
            Log.w(TAG, "Dialog fail to show due to null fragment.");
            mSwitchBar.setEnabled(true);
            return;
        }
        ArrayList<String> deviceNames = new ArrayList<>();
        AudioSharingDialogFragment.show(
                mFragment,
                deviceNames,
                new AudioSharingDialogFragment.DialogEventListener() {
                    @Override
                    public void onItemClick(int position) {
                        // TODO: handle broadcast based on the dialog device item clicked
                    }

                    @Override
                    public void onCancelClick() {
                        mBroadcast.startBroadcast("test", /* language= */ null);
                    }
                });
    }

    private void stopAudioSharing() {
        mSwitchBar.setEnabled(false);
        if (mBroadcast == null || !isBroadcasting()) {
            Log.d(TAG, "Already not broadcasting or broadcast not support, ignore!");
            mSwitchBar.setEnabled(true);
            return;
        }
        mBroadcast.stopBroadcast(mBroadcast.getLatestBroadcastId());
    }

    private void updateSwitch() {
        ThreadUtils.postOnMainThread(
                () -> {
                    mSwitchBar.setChecked(isBroadcasting());
                    mSwitchBar.setEnabled(true);
                });
    }

    private boolean isBroadcasting() {
        return mBroadcast != null && mBroadcast.isEnabled(null);
    }
}