[LE Audio] Broadcast Sink UI

 - Add dialog for Broadcast and find broadcast source

 - Add Activity for find broadcast source

Bug: 228274114
Test: Manual test
Change-Id: I830efc3514fc42aaa4e53f491ed3a7459bd9bb41
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 9282d92..f294e7d 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -360,6 +360,8 @@
     public static class WebViewAppPickerActivity extends SettingsActivity { /* empty */ }
     public static class AdvancedConnectedDeviceActivity extends SettingsActivity { /* empty */ }
     public static class BluetoothDeviceDetailActivity extends SettingsActivity { /* empty */ }
+    public static class BluetoothBroadcastActivity extends SettingsActivity { /* empty */ }
+    public static class BluetoothFindBroadcastsActivity extends SettingsActivity { /* empty */ }
     public static class WifiCallingDisclaimerActivity extends SettingsActivity { /* empty */ }
     public static class MobileNetworkListActivity extends SettingsActivity {}
     public static class PowerMenuSettingsActivity extends SettingsActivity {}
diff --git a/src/com/android/settings/bluetooth/BluetoothBroadcastDialog.java b/src/com/android/settings/bluetooth/BluetoothBroadcastDialog.java
new file mode 100644
index 0000000..c743653
--- /dev/null
+++ b/src/com/android/settings/bluetooth/BluetoothBroadcastDialog.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.bluetooth;
+
+import android.app.Dialog;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import androidx.appcompat.app.AlertDialog;
+
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+import java.util.ArrayList;
+
+/**
+ * This Dialog allowed users to do some actions for broadcast media or find the
+ * nearby broadcast sources.
+ */
+public class BluetoothBroadcastDialog extends InstrumentedDialogFragment {
+
+    private static final String TAG = "BTBroadcastsDialog";
+
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setShowsDialog(true);
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        final Context context = getActivity();
+        final boolean isMediaPlaying = isMediaPlaying();
+
+        final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+        builder.setTitle(isMediaPlaying ? R.string.bluetooth_find_broadcast
+                : R.string.bluetooth_broadcast_dialog_title);
+        builder.setMessage(isMediaPlaying ? R.string.bluetooth_broadcast_dialog_find_message
+                : R.string.bluetooth_broadcast_dialog_broadcast_message);
+
+        ArrayList<String> optionList = new ArrayList<String>();
+        if (!isMediaPlaying) {
+            optionList.add(context.getString(R.string.bluetooth_broadcast_dialog_title));
+        }
+        optionList.add(context.getString(R.string.bluetooth_find_broadcast));
+        optionList.add(context.getString(android.R.string.cancel));
+
+        View content = LayoutInflater.from(context).inflate(
+                R.layout.sim_confirm_dialog_multiple_enabled_profiles_supported, null);
+
+        if (content != null) {
+            Log.i(TAG, "list =" + optionList.toString());
+
+            final ArrayAdapter<String> arrayAdapterItems = new ArrayAdapter<String>(
+                    context,
+                    R.layout.sim_confirm_dialog_item_multiple_enabled_profiles_supported,
+                    optionList);
+            final ListView lvItems = content.findViewById(R.id.carrier_list);
+            if (lvItems != null) {
+                lvItems.setVisibility(View.VISIBLE);
+                lvItems.setAdapter(arrayAdapterItems);
+                lvItems.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+                    @Override
+                    public void onItemClick(AdapterView<?> parent, View view, int position,
+                            long id) {
+                        Log.i(TAG, "list onClick =" + position);
+                        Log.i(TAG, "list item =" + optionList.get(position));
+
+                        if (position == optionList.size() - 1) {
+                            // The last position in the options is the Cancel button. So when
+                            // the user clicks the button, we do nothing but dismiss the dialog.
+                            dismiss();
+                        } else {
+                            if (optionList.get(position).equals(
+                                    context.getString(R.string.bluetooth_find_broadcast))) {
+                                launchFindBroadcastsActivity();
+                            } else {
+                                launchMediaOutputBroadcastDialog();
+                            }
+                        }
+                    }
+                });
+            }
+            builder.setView(content);
+        } else {
+            Log.i(TAG, "optionList is empty");
+        }
+
+        AlertDialog dialog = builder.create();
+        dialog.setCanceledOnTouchOutside(false);
+        return dialog;
+    }
+
+    private boolean isMediaPlaying() {
+        return true;
+    }
+
+
+    @Override
+    public void onStart() {
+        super.onStart();
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        //TODO(b/228255796) : add new enum for find broadcast fragment
+        return SettingsEnums.PAGE_UNKNOWN;
+    }
+
+    private void launchFindBroadcastsActivity() {
+
+    }
+
+    private void launchMediaOutputBroadcastDialog() {
+
+    }
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothFindBroadcastsFragment.java b/src/com/android/settings/bluetooth/BluetoothFindBroadcastsFragment.java
new file mode 100644
index 0000000..f251db5
--- /dev/null
+++ b/src/com/android/settings/bluetooth/BluetoothFindBroadcastsFragment.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.bluetooth;
+
+import static android.bluetooth.BluetoothDevice.BOND_NONE;
+import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
+
+import android.app.settings.SettingsEnums;
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.RestrictedDashboardFragment;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This fragment allowed users to find the nearby broadcast sources.
+ */
+public class BluetoothFindBroadcastsFragment extends RestrictedDashboardFragment {
+
+    private static final String TAG = "BTFindBroadcastsFrg";
+
+    public static final String KEY_DEVICE_ADDRESS = "device_address";
+
+    public static final String PREF_KEY_BROADCAST_SOURCE = "broadcast_source";
+
+    @VisibleForTesting
+    String mDeviceAddress;
+    @VisibleForTesting
+    LocalBluetoothManager mManager;
+    @VisibleForTesting
+    CachedBluetoothDevice mCachedDevice;
+
+    public BluetoothFindBroadcastsFragment() {
+        super(DISALLOW_CONFIG_BLUETOOTH);
+    }
+
+    @VisibleForTesting
+    LocalBluetoothManager getLocalBluetoothManager(Context context) {
+        return Utils.getLocalBtManager(context);
+    }
+
+    @VisibleForTesting
+    CachedBluetoothDevice getCachedDevice(String deviceAddress) {
+        BluetoothDevice remoteDevice =
+                mManager.getBluetoothAdapter().getRemoteDevice(deviceAddress);
+        return mManager.getCachedDeviceManager().findDevice(remoteDevice);
+    }
+
+    @Override
+    public void onAttach(Context context) {
+        mDeviceAddress = getArguments().getString(KEY_DEVICE_ADDRESS);
+        mManager = getLocalBluetoothManager(context);
+        mCachedDevice = getCachedDevice(mDeviceAddress);
+        super.onAttach(context);
+        if (mCachedDevice == null) {
+            //Close this page if device is null with invalid device mac address
+            Log.w(TAG, "onAttach() CachedDevice is null!");
+            finish();
+            return;
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        finishFragmentIfNecessary();
+    }
+
+    @VisibleForTesting
+    void finishFragmentIfNecessary() {
+        if (mCachedDevice.getBondState() == BOND_NONE) {
+            finish();
+            return;
+        }
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        //TODO(b/228255796) : add new enum for find broadcast fragment
+        return SettingsEnums.PAGE_UNKNOWN;
+    }
+
+    @Override
+    protected String getLogTag() {
+        return TAG;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.bluetooth_find_broadcasts_fragment;
+    }
+
+    @Override
+    protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+        ArrayList<AbstractPreferenceController> controllers = new ArrayList<>();
+
+        if (mCachedDevice != null) {
+            Lifecycle lifecycle = getSettingsLifecycle();
+            controllers.add(new BluetoothFindBroadcastsHeaderController(context, this,
+                    mCachedDevice, lifecycle, mManager));
+        }
+        return controllers;
+    }
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothFindBroadcastsHeaderController.java b/src/com/android/settings/bluetooth/BluetoothFindBroadcastsHeaderController.java
new file mode 100644
index 0000000..dfdcae0
--- /dev/null
+++ b/src/com/android/settings/bluetooth/BluetoothFindBroadcastsHeaderController.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.bluetooth;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.widget.LayoutPreference;
+
+/**
+ * This class adds a header to display the action button for joining the broadcast session
+ * by scanning QR code and leaving the broadcast session
+ */
+public class BluetoothFindBroadcastsHeaderController extends BluetoothDetailsController {
+    private static final String TAG = "BtFindBroadcastCtrl";
+
+    private static final String KEY_BROADCAST_HEADER = "bluetooth_find_broadcast_header";
+    private static final String KEY_BROADCAST_SOURCE_LIST = "broadcast_source_list";
+
+    LayoutPreference mLayoutPreference;
+    PreferenceCategory mBroadcastSourceList;
+    TextView mTitle;
+    TextView mSummary;
+    Button mBtnFindBroadcast;
+    LinearLayout mBtnBroadcastLayout;
+    Button mBtnLeaveBroadcast;
+    Button mBtnScanQrCode;
+
+    public BluetoothFindBroadcastsHeaderController(Context context,
+            PreferenceFragmentCompat fragment, CachedBluetoothDevice device, Lifecycle lifecycle,
+            LocalBluetoothManager bluetoothManager) {
+        super(context, fragment, device, lifecycle);
+    }
+
+    @Override
+    protected void init(PreferenceScreen screen) {
+        mLayoutPreference = screen.findPreference(KEY_BROADCAST_HEADER);
+        mBroadcastSourceList = screen.findPreference(KEY_BROADCAST_SOURCE_LIST);
+
+        refresh();
+    }
+
+    @Override
+    protected void refresh() {
+        if (mLayoutPreference == null || mCachedDevice == null) {
+            return;
+        }
+
+        mTitle = mLayoutPreference.findViewById(R.id.entity_header_title);
+        mTitle.setText(mCachedDevice.getName());
+        mSummary = mLayoutPreference.findViewById(R.id.entity_header_summary);
+        mSummary.setText("");
+
+        mBtnFindBroadcast = mLayoutPreference.findViewById(R.id.button_find_broadcast);
+        mBtnFindBroadcast.setOnClickListener(v -> scanBroadcastSource());
+        mBtnBroadcastLayout = mLayoutPreference.findViewById(R.id.button_broadcast_layout);
+        mBtnLeaveBroadcast = mLayoutPreference.findViewById(R.id.button_leave_broadcast);
+        mBtnLeaveBroadcast.setOnClickListener(v -> leaveBroadcastSession());
+        mBtnScanQrCode = mLayoutPreference.findViewById(R.id.button_scan_qr_code);
+        mBtnScanQrCode.setOnClickListener(v -> launchQrCodeScanner());
+
+        updateHeaderLayout();
+    }
+
+    private boolean isBroadcastSourceExist() {
+        return mBroadcastSourceList.getPreferenceCount() > 0;
+    }
+
+    private void updateHeaderLayout() {
+        if (isBroadcastSourceExist()) {
+            mBtnFindBroadcast.setVisibility(View.GONE);
+            mBtnBroadcastLayout.setVisibility(View.VISIBLE);
+        } else {
+            mBtnFindBroadcast.setVisibility(View.VISIBLE);
+            mBtnBroadcastLayout.setVisibility(View.GONE);
+        }
+    }
+
+    private void scanBroadcastSource() {
+        // TODO(b/228258236) : Call the LocalBluetoothLeBroadcastAssistant
+        //  to start searching for source
+    }
+
+    private void leaveBroadcastSession() {
+        // TODO(b/228258236) : Call the LocalBluetoothLeBroadcastAssistant
+        //  to leave the broadcast session
+    }
+
+    private void launchQrCodeScanner() {
+        // TODO(b/228259065) : Launch the QR code scanner page by intent
+    }
+
+    @Override
+    public void onDeviceAttributesChanged() {
+        if (mCachedDevice != null) {
+            refresh();
+        }
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_BROADCAST_HEADER;
+    }
+}
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index b11ce01..81de756 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -71,7 +71,9 @@
 import com.android.settings.biometrics.combination.CombinedBiometricSettings;
 import com.android.settings.biometrics.face.FaceSettings;
 import com.android.settings.biometrics.fingerprint.FingerprintSettings;
+import com.android.settings.bluetooth.BluetoothBroadcastDialog;
 import com.android.settings.bluetooth.BluetoothDeviceDetailsFragment;
+import com.android.settings.bluetooth.BluetoothFindBroadcastsFragment;
 import com.android.settings.bluetooth.BluetoothPairingDetail;
 import com.android.settings.bugreporthandler.BugReportHandlerPicker;
 import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment;
@@ -317,6 +319,8 @@
             WebViewAppPicker.class.getName(),
             LockscreenDashboardFragment.class.getName(),
             BluetoothDeviceDetailsFragment.class.getName(),
+            BluetoothBroadcastDialog.class.getName(),
+            BluetoothFindBroadcastsFragment.class.getName(),
             DataUsageList.class.getName(),
             ToggleBackupSettingFragment.class.getName(),
             PreviouslyConnectedDeviceDashboardFragment.class.getName(),