[Audiosharing] Impl of two error dialogs.

Bug: 305620450
Test: manual
Change-Id: I5bd4a4b2ab54c382f325c7a6bb4a3029d47786e0
diff --git a/res/xml/bluetooth_audio_streams_dialog.xml b/res/xml/bluetooth_audio_streams_dialog.xml
new file mode 100644
index 0000000..afc5055
--- /dev/null
+++ b/res/xml/bluetooth_audio_streams_dialog.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 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.
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <LinearLayout
+        android:id="@+id/dialog_bg"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="@dimen/broadcast_dialog_margin"
+            android:orientation="vertical">
+
+            <ImageView
+                android:id="@+id/dialog_icon"
+                android:layout_width="36dp"
+                android:layout_height="36dp"
+                android:layout_marginTop="@dimen/broadcast_dialog_icon_margin_top"
+                android:layout_marginBottom="@dimen/broadcast_dialog_title_img_margin_top"
+                android:layout_gravity="center"
+                android:src="@drawable/ic_bt_audio_sharing"/>
+
+            <TextView
+                style="@style/BroadcastDialogTitleStyle"
+                android:id="@+id/dialog_title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:gravity="center"
+                android:layout_gravity="center"/>
+
+            <TextView
+                style="@style/BroadcastDialogBodyStyle"
+                android:id="@+id/dialog_subtitle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:gravity="center"
+                android:layout_gravity="center"
+                android:visibility="gone"/>
+
+            <TextView
+                style="@style/BroadcastDialogBodyStyle"
+                android:id="@+id/dialog_subtitle_2"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:gravity="center"
+                android:layout_gravity="center"
+                android:visibility="gone"/>
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="@dimen/broadcast_dialog_margin"
+            android:orientation="horizontal">
+            <Button
+                android:id="@+id/left_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="16dp"
+                android:layout_weight="1"
+                android:visibility="gone"/>
+            <Button
+                android:id="@+id/right_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:layout_marginRight="16dp"
+                android:visibility="gone"/>
+        </LinearLayout>
+
+    </LinearLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDashboardFragment.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDashboardFragment.java
index b0af7dd..b5c71b6 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDashboardFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDashboardFragment.java
@@ -71,6 +71,7 @@
         super.onAttach(context);
         use(AudioStreamsScanQrCodeController.class).setFragment(this);
         mAudioStreamsProgressCategoryController = use(AudioStreamsProgressCategoryController.class);
+        mAudioStreamsProgressCategoryController.setFragment(this);
     }
 
     @Override
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDialogFragment.java
new file mode 100644
index 0000000..c7d7f16
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDialogFragment.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2024 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.connecteddevice.audiosharing.audiostreams;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+import com.google.common.base.Strings;
+
+import java.util.function.Consumer;
+
+public class AudioStreamsDialogFragment extends InstrumentedDialogFragment {
+    private static final String TAG = "AudioStreamsDialogFragment";
+    private final DialogBuilder mDialogBuilder;
+
+    AudioStreamsDialogFragment(DialogBuilder dialogBuilder) {
+        mDialogBuilder = dialogBuilder;
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        // TODO(chelseahao): update metrics id
+        return 0;
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        return mDialogBuilder.build();
+    }
+
+    static void show(Fragment host, DialogBuilder dialogBuilder) {
+        FragmentManager manager = host.getChildFragmentManager();
+        (new AudioStreamsDialogFragment(dialogBuilder)).show(manager, TAG);
+    }
+
+    static class DialogBuilder {
+        private final Context mContext;
+        private final AlertDialog.Builder mBuilder;
+        private String mTitle;
+        private String mSubTitle1;
+        private String mSubTitle2;
+        private String mLeftButtonText;
+        private String mRightButtonText;
+        private Consumer<AlertDialog> mLeftButtonOnClickListener;
+        private Consumer<AlertDialog> mRightButtonOnClickListener;
+
+        DialogBuilder(Context context) {
+            mContext = context;
+            mBuilder = new AlertDialog.Builder(context);
+        }
+
+        DialogBuilder setTitle(String title) {
+            mTitle = title;
+            return this;
+        }
+
+        DialogBuilder setSubTitle1(String subTitle1) {
+            mSubTitle1 = subTitle1;
+            return this;
+        }
+
+        DialogBuilder setSubTitle2(String subTitle2) {
+            mSubTitle2 = subTitle2;
+            return this;
+        }
+
+        DialogBuilder setLeftButtonText(String text) {
+            mLeftButtonText = text;
+            return this;
+        }
+
+        DialogBuilder setLeftButtonOnClickListener(Consumer<AlertDialog> listener) {
+            mLeftButtonOnClickListener = listener;
+            return this;
+        }
+
+        DialogBuilder setRightButtonText(String text) {
+            mRightButtonText = text;
+            return this;
+        }
+
+        DialogBuilder setRightButtonOnClickListener(Consumer<AlertDialog> listener) {
+            mRightButtonOnClickListener = listener;
+            return this;
+        }
+
+        AlertDialog build() {
+            View rootView =
+                    LayoutInflater.from(mContext)
+                            .inflate(R.xml.bluetooth_audio_streams_dialog, /* parent= */ null);
+
+            AlertDialog dialog = mBuilder.setView(rootView).setCancelable(false).create();
+            dialog.setCanceledOnTouchOutside(false);
+
+            TextView title = rootView.requireViewById(R.id.dialog_title);
+            title.setText(mTitle);
+
+            if (!Strings.isNullOrEmpty(mSubTitle1)) {
+                TextView subTitle1 = rootView.requireViewById(R.id.dialog_subtitle);
+                subTitle1.setText(mSubTitle1);
+                subTitle1.setVisibility(View.VISIBLE);
+            }
+            if (!Strings.isNullOrEmpty(mSubTitle2)) {
+                TextView subTitle2 = rootView.requireViewById(R.id.dialog_subtitle_2);
+                subTitle2.setText(mSubTitle2);
+                subTitle2.setVisibility(View.VISIBLE);
+            }
+            if (!Strings.isNullOrEmpty(mLeftButtonText)) {
+                Button leftButton = rootView.requireViewById(R.id.left_button);
+                leftButton.setText(mLeftButtonText);
+                leftButton.setVisibility(View.VISIBLE);
+                leftButton.setOnClickListener(unused -> mLeftButtonOnClickListener.accept(dialog));
+            }
+            if (!Strings.isNullOrEmpty(mRightButtonText)) {
+                Button rightButton = rootView.requireViewById(R.id.right_button);
+                rightButton.setText(mRightButtonText);
+                rightButton.setVisibility(View.VISIBLE);
+                rightButton.setOnClickListener(
+                        unused -> mRightButtonOnClickListener.accept(dialog));
+            }
+
+            return dialog;
+        }
+    }
+}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java
index ab380c8..cb9975d 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java
@@ -24,8 +24,10 @@
 import android.bluetooth.BluetoothLeBroadcastReceiveState;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
+import android.content.Intent;
 import android.os.Bundle;
 import android.os.CountDownTimer;
+import android.provider.Settings;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -92,6 +94,7 @@
             new ConcurrentHashMap<>();
     private TimedSourceFromQrCode mTimedSourceFromQrCode;
     private AudioStreamsProgressCategoryPreference mCategoryPreference;
+    private AudioStreamsDashboardFragment mFragment;
 
     public AudioStreamsProgressCategoryController(Context context, String preferenceKey) {
         super(context, preferenceKey);
@@ -135,10 +138,13 @@
         mExecutor.execute(this::stopScanning);
     }
 
+    void setFragment(AudioStreamsDashboardFragment fragment) {
+        mFragment = fragment;
+    }
+
     void setSourceFromQrCode(BluetoothLeBroadcastMetadata source) {
         mTimedSourceFromQrCode =
-                new TimedSourceFromQrCode(
-                        mContext, source, () -> handleSourceLost(source.getBroadcastId()));
+                new TimedSourceFromQrCode(source, () -> handleSourceLost(source.getBroadcastId()));
     }
 
     void setScanning(boolean isScanning) {
@@ -324,6 +330,8 @@
             startScanning();
         } else {
             stopScanning();
+            ThreadUtils.postOnMainThread(
+                    () -> AudioStreamsDialogFragment.show(mFragment, getNoLeDeviceDialog()));
         }
     }
 
@@ -463,15 +471,41 @@
         alertDialog.show();
     }
 
-    private static class TimedSourceFromQrCode {
+    private AudioStreamsDialogFragment.DialogBuilder getNoLeDeviceDialog() {
+        return new AudioStreamsDialogFragment.DialogBuilder(mContext)
+                .setTitle("Connect compatible headphones")
+                .setSubTitle1(
+                        "To listen to an audio stream, first connect headphones that support LE"
+                                + " Audio to this device. Learn more")
+                .setLeftButtonText("Close")
+                .setLeftButtonOnClickListener(AlertDialog::dismiss)
+                .setRightButtonText("Connect a device")
+                .setRightButtonOnClickListener(
+                        unused ->
+                                mContext.startActivity(
+                                        new Intent(Settings.ACTION_BLUETOOTH_SETTINGS)));
+    }
+
+    private AudioStreamsDialogFragment.DialogBuilder getBroadcastUnavailableDialog(
+            String broadcastName) {
+        return new AudioStreamsDialogFragment.DialogBuilder(mContext)
+                .setTitle("Audio stream isn't available")
+                .setSubTitle1(broadcastName)
+                .setSubTitle2("This audio stream isn't playing anything right now")
+                .setLeftButtonText("Close")
+                .setLeftButtonOnClickListener(AlertDialog::dismiss)
+                .setRightButtonText("Retry")
+                // TODO(chelseahao): Add retry action
+                .setRightButtonOnClickListener(AlertDialog::dismiss);
+    }
+
+    private class TimedSourceFromQrCode {
         private static final int WAIT_FOR_SYNC_TIMEOUT_MILLIS = 15000;
         private final CountDownTimer mTimer;
         private BluetoothLeBroadcastMetadata mSourceFromQrCode;
 
         private TimedSourceFromQrCode(
-                Context context,
-                BluetoothLeBroadcastMetadata sourceFromQrCode,
-                Runnable timeoutAction) {
+                BluetoothLeBroadcastMetadata sourceFromQrCode, Runnable timeoutAction) {
             mSourceFromQrCode = sourceFromQrCode;
             mTimer =
                     new CountDownTimer(WAIT_FOR_SYNC_TIMEOUT_MILLIS, 1000) {
@@ -481,7 +515,12 @@
                         @Override
                         public void onFinish() {
                             timeoutAction.run();
-                            AudioSharingUtils.toastMessage(context, "Audio steam isn't available");
+                            ThreadUtils.postOnMainThread(
+                                    () ->
+                                            AudioStreamsDialogFragment.show(
+                                                    mFragment,
+                                                    getBroadcastUnavailableDialog(
+                                                            sourceFromQrCode.getBroadcastName())));
                         }
                     };
         }