[Audiosharing] Impl audio sharing main switch.

Start/stop broadcast when no eligible  buds connected.

Flagged with enable_le_audio_sharing

Bug: 305620450
Test: Manual
Change-Id: I04359c0954e336ceb1a89a7836199b5be0b5e0c5
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java
index 5b99907..1fd0b87 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragment.java
@@ -38,6 +38,26 @@
 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 @@
      *
      * @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);
-        subTitle1.setText("2 devices connected");
-        subTitle2.setText("placeholder");
+        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;
     }
 }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
index a375a3c..bd8027c 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
@@ -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.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 @@
         if (isChecked) {
             startAudioSharing();
         } else {
-            // TODO: stop sharing
+            stopAudioSharing();
         }
     }
 
@@ -82,10 +164,53 @@
     }
 
     private void startAudioSharing() {
-        if (mFragment != null) {
-            AudioSharingDialogFragment.show(mFragment);
-        } else {
-            Log.w(TAG, "Dialog fail to show due to null fragment.");
+        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);
     }
 }