[Audiosharing] Impl active device switch for calls and alarms

Flagged with enable_le_audio_sharing

Bug: 305620450
Test: Manual
Change-Id: Id1e417cd1ae48bbb4ddebb4b30e2001e18bef7ee
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceItem.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceItem.java
index a68117a..5998e30 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceItem.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDeviceItem.java
@@ -22,10 +22,12 @@
 public final class AudioSharingDeviceItem implements Parcelable {
     private final String mName;
     private final int mGroupId;
+    private final boolean mIsActive;
 
-    public AudioSharingDeviceItem(String name, int groupId) {
+    public AudioSharingDeviceItem(String name, int groupId, boolean isActive) {
         mName = name;
         mGroupId = groupId;
+        mIsActive = isActive;
     }
 
     public String getName() {
@@ -36,15 +38,21 @@
         return mGroupId;
     }
 
+    public boolean isActive() {
+        return mIsActive;
+    }
+
     public AudioSharingDeviceItem(Parcel in) {
         mName = in.readString();
         mGroupId = in.readInt();
+        mIsActive = in.readBoolean();
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(mName);
         dest.writeInt(mGroupId);
+        dest.writeBoolean(mIsActive);
     }
 
     @Override
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java
index 4ece70e..a0d44ff 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java
@@ -120,6 +120,9 @@
     /** Build {@link AudioSharingDeviceItem} from {@link CachedBluetoothDevice}. */
     public static AudioSharingDeviceItem buildAudioSharingDeviceItem(
             CachedBluetoothDevice cachedDevice) {
-        return new AudioSharingDeviceItem(cachedDevice.getName(), cachedDevice.getGroupId());
+        return new AudioSharingDeviceItem(
+                cachedDevice.getName(),
+                cachedDevice.getGroupId(),
+                BluetoothUtils.isActiveLeAudioDevice(cachedDevice));
     }
 }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsDialogFragment.java
index 0577f70..47f70c7 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsDialogFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsDialogFragment.java
@@ -28,9 +28,25 @@
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
 import com.android.settings.flags.Flags;
 
+import java.util.ArrayList;
+
 /** Provides a dialog to choose the active device for calls and alarms. */
 public class CallsAndAlarmsDialogFragment extends InstrumentedDialogFragment {
     private static final String TAG = "CallsAndAlarmsDialog";
+    private static final String BUNDLE_KEY_DEVICE_ITEMS = "bundle_key_device_items";
+
+    // 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 to set active for calls and alarms in the dialog.
+         *
+         * @param item The device item clicked.
+         */
+        void onItemClick(AudioSharingDeviceItem item);
+    }
+
+    private static DialogEventListener sListener;
 
     @Override
     public int getMetricsCategory() {
@@ -41,28 +57,43 @@
      * Display the {@link CallsAndAlarmsDialogFragment} dialog.
      *
      * @param host The Fragment this dialog will be hosted.
+     * @param deviceItems The connected device items in audio sharing session.
+     * @param listener The callback to handle the user action on this dialog.
      */
-    public static void show(Fragment host) {
+    public static void show(
+            Fragment host,
+            ArrayList<AudioSharingDeviceItem> deviceItems,
+            DialogEventListener listener) {
         if (!Flags.enableLeAudioSharing()) return;
         final FragmentManager manager = host.getChildFragmentManager();
+        sListener = listener;
         if (manager.findFragmentByTag(TAG) == null) {
+            final Bundle bundle = new Bundle();
+            bundle.putParcelableArrayList(BUNDLE_KEY_DEVICE_ITEMS, deviceItems);
             final CallsAndAlarmsDialogFragment dialog = new CallsAndAlarmsDialogFragment();
+            dialog.setArguments(bundle);
             dialog.show(manager, TAG);
         }
     }
 
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
-        // TODO: use real device names
-        String[] choices = {"Buds 1", "Buds 2"};
+        Bundle arguments = requireArguments();
+        ArrayList<AudioSharingDeviceItem> deviceItems =
+                arguments.getParcelableArrayList(BUNDLE_KEY_DEVICE_ITEMS);
+        int checkedItem = -1;
+        // deviceItems is ordered. The active device is put in the first place if it does exist
+        if (!deviceItems.isEmpty() && deviceItems.get(0).isActive()) checkedItem = 0;
+        String[] choices =
+                deviceItems.stream().map(AudioSharingDeviceItem::getName).toArray(String[]::new);
         AlertDialog.Builder builder =
                 new AlertDialog.Builder(getActivity())
                         .setTitle(R.string.calls_and_alarms_device_title)
                         .setSingleChoiceItems(
                                 choices,
-                                0, // TODO: set to current active device.
+                                checkedItem,
                                 (dialog, which) -> {
-                                    // TODO: set device to active device for calls and alarms.
+                                    sListener.onItemClick(deviceItems.get(which));
                                 });
         return builder.create();
     }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsPreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsPreferenceController.java
index 44e75ec..a7d18e7 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsPreferenceController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsPreferenceController.java
@@ -16,23 +16,42 @@
 
 package com.android.settings.connecteddevice.audiosharing;
 
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
 import androidx.preference.PreferenceScreen;
 
+import com.android.settings.bluetooth.Utils;
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.bluetooth.BluetoothCallback;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /** PreferenceController to control the dialog to choose the active device for calls and alarms */
-public class CallsAndAlarmsPreferenceController extends AudioSharingBasePreferenceController {
+public class CallsAndAlarmsPreferenceController extends AudioSharingBasePreferenceController
+        implements BluetoothCallback, DefaultLifecycleObserver {
 
     private static final String TAG = "CallsAndAlarmsPreferenceController";
-
     private static final String PREF_KEY = "calls_and_alarms";
+
+    private final LocalBluetoothManager mLocalBtManager;
     private DashboardFragment mFragment;
+    Map<Integer, List<CachedBluetoothDevice>> mGroupedConnectedDevices = new HashMap<>();
+    private ArrayList<AudioSharingDeviceItem> mDeviceItemsInSharingSession = new ArrayList<>();
 
     public CallsAndAlarmsPreferenceController(Context context) {
         super(context, PREF_KEY);
+        mLocalBtManager = Utils.getLocalBtManager(mContext);
     }
 
     @Override
@@ -43,17 +62,60 @@
     @Override
     public void displayPreference(PreferenceScreen screen) {
         super.displayPreference(screen);
+        updateDeviceItemsInSharingSession();
+        // mDeviceItemsInSharingSession is ordered. The active device is the first place if exits.
+        if (!mDeviceItemsInSharingSession.isEmpty()
+                && mDeviceItemsInSharingSession.get(0).isActive()) {
+            mPreference.setSummary(mDeviceItemsInSharingSession.get(0).getName());
+        } else {
+            mPreference.setSummary("");
+        }
         mPreference.setOnPreferenceClickListener(
                 preference -> {
-                    if (mFragment != null) {
-                        CallsAndAlarmsDialogFragment.show(mFragment);
-                    } else {
+                    if (mFragment == null) {
                         Log.w(TAG, "Dialog fail to show due to null host.");
+                        return true;
+                    }
+                    updateDeviceItemsInSharingSession();
+                    if (mDeviceItemsInSharingSession.size() >= 2) {
+                        CallsAndAlarmsDialogFragment.show(
+                                mFragment,
+                                mDeviceItemsInSharingSession,
+                                (AudioSharingDeviceItem item) -> {
+                                    for (CachedBluetoothDevice device :
+                                            mGroupedConnectedDevices.get(item.getGroupId())) {
+                                        device.setActive();
+                                    }
+                                });
                     }
                     return true;
                 });
     }
 
+    @Override
+    public void onStart(@NonNull LifecycleOwner owner) {
+        if (mLocalBtManager != null) {
+            mLocalBtManager.getEventManager().registerCallback(this);
+        }
+    }
+
+    @Override
+    public void onStop(@NonNull LifecycleOwner owner) {
+        if (mLocalBtManager != null) {
+            mLocalBtManager.getEventManager().unregisterCallback(this);
+        }
+    }
+
+    @Override
+    public void onActiveDeviceChanged(
+            @Nullable CachedBluetoothDevice activeDevice, int bluetoothProfile) {
+        if (bluetoothProfile != BluetoothProfile.LE_AUDIO) {
+            Log.d(TAG, "Ignore onActiveDeviceChanged, not LE_AUDIO profile");
+            return;
+        }
+        mPreference.setSummary(activeDevice == null ? "" : activeDevice.getName());
+    }
+
     /**
      * Initialize the controller.
      *
@@ -62,4 +124,12 @@
     public void init(DashboardFragment fragment) {
         this.mFragment = fragment;
     }
+
+    private void updateDeviceItemsInSharingSession() {
+        mGroupedConnectedDevices =
+                AudioSharingUtils.fetchConnectedDevicesByGroupId(mLocalBtManager);
+        mDeviceItemsInSharingSession =
+                AudioSharingUtils.buildOrderedDeviceItemsInSharingSession(
+                        mGroupedConnectedDevices, mLocalBtManager);
+    }
 }