[Audiosharing] Impl of two error dialogs.
Bug: 305620450 Test: manual Change-Id: I5bd4a4b2ab54c382f325c7a6bb4a3029d47786e0
This commit is contained in:
92
res/xml/bluetooth_audio_streams_dialog.xml
Normal file
92
res/xml/bluetooth_audio_streams_dialog.xml
Normal file
@@ -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>
|
||||||
@@ -71,6 +71,7 @@ public class AudioStreamsDashboardFragment extends DashboardFragment {
|
|||||||
super.onAttach(context);
|
super.onAttach(context);
|
||||||
use(AudioStreamsScanQrCodeController.class).setFragment(this);
|
use(AudioStreamsScanQrCodeController.class).setFragment(this);
|
||||||
mAudioStreamsProgressCategoryController = use(AudioStreamsProgressCategoryController.class);
|
mAudioStreamsProgressCategoryController = use(AudioStreamsProgressCategoryController.class);
|
||||||
|
mAudioStreamsProgressCategoryController.setFragment(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,8 +24,10 @@ import android.bluetooth.BluetoothLeBroadcastMetadata;
|
|||||||
import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
||||||
import android.bluetooth.BluetoothProfile;
|
import android.bluetooth.BluetoothProfile;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.CountDownTimer;
|
import android.os.CountDownTimer;
|
||||||
|
import android.provider.Settings;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -92,6 +94,7 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
|||||||
new ConcurrentHashMap<>();
|
new ConcurrentHashMap<>();
|
||||||
private TimedSourceFromQrCode mTimedSourceFromQrCode;
|
private TimedSourceFromQrCode mTimedSourceFromQrCode;
|
||||||
private AudioStreamsProgressCategoryPreference mCategoryPreference;
|
private AudioStreamsProgressCategoryPreference mCategoryPreference;
|
||||||
|
private AudioStreamsDashboardFragment mFragment;
|
||||||
|
|
||||||
public AudioStreamsProgressCategoryController(Context context, String preferenceKey) {
|
public AudioStreamsProgressCategoryController(Context context, String preferenceKey) {
|
||||||
super(context, preferenceKey);
|
super(context, preferenceKey);
|
||||||
@@ -135,10 +138,13 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
|||||||
mExecutor.execute(this::stopScanning);
|
mExecutor.execute(this::stopScanning);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setFragment(AudioStreamsDashboardFragment fragment) {
|
||||||
|
mFragment = fragment;
|
||||||
|
}
|
||||||
|
|
||||||
void setSourceFromQrCode(BluetoothLeBroadcastMetadata source) {
|
void setSourceFromQrCode(BluetoothLeBroadcastMetadata source) {
|
||||||
mTimedSourceFromQrCode =
|
mTimedSourceFromQrCode =
|
||||||
new TimedSourceFromQrCode(
|
new TimedSourceFromQrCode(source, () -> handleSourceLost(source.getBroadcastId()));
|
||||||
mContext, source, () -> handleSourceLost(source.getBroadcastId()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setScanning(boolean isScanning) {
|
void setScanning(boolean isScanning) {
|
||||||
@@ -324,6 +330,8 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
|||||||
startScanning();
|
startScanning();
|
||||||
} else {
|
} else {
|
||||||
stopScanning();
|
stopScanning();
|
||||||
|
ThreadUtils.postOnMainThread(
|
||||||
|
() -> AudioStreamsDialogFragment.show(mFragment, getNoLeDeviceDialog()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -463,15 +471,41 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
|||||||
alertDialog.show();
|
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 static final int WAIT_FOR_SYNC_TIMEOUT_MILLIS = 15000;
|
||||||
private final CountDownTimer mTimer;
|
private final CountDownTimer mTimer;
|
||||||
private BluetoothLeBroadcastMetadata mSourceFromQrCode;
|
private BluetoothLeBroadcastMetadata mSourceFromQrCode;
|
||||||
|
|
||||||
private TimedSourceFromQrCode(
|
private TimedSourceFromQrCode(
|
||||||
Context context,
|
BluetoothLeBroadcastMetadata sourceFromQrCode, Runnable timeoutAction) {
|
||||||
BluetoothLeBroadcastMetadata sourceFromQrCode,
|
|
||||||
Runnable timeoutAction) {
|
|
||||||
mSourceFromQrCode = sourceFromQrCode;
|
mSourceFromQrCode = sourceFromQrCode;
|
||||||
mTimer =
|
mTimer =
|
||||||
new CountDownTimer(WAIT_FOR_SYNC_TIMEOUT_MILLIS, 1000) {
|
new CountDownTimer(WAIT_FOR_SYNC_TIMEOUT_MILLIS, 1000) {
|
||||||
@@ -481,7 +515,12 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
|
|||||||
@Override
|
@Override
|
||||||
public void onFinish() {
|
public void onFinish() {
|
||||||
timeoutAction.run();
|
timeoutAction.run();
|
||||||
AudioSharingUtils.toastMessage(context, "Audio steam isn't available");
|
ThreadUtils.postOnMainThread(
|
||||||
|
() ->
|
||||||
|
AudioStreamsDialogFragment.show(
|
||||||
|
mFragment,
|
||||||
|
getBroadcastUnavailableDialog(
|
||||||
|
sourceFromQrCode.getBroadcastName())));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user