[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())));
}
};
}