1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.connecteddevice.audiosharing.audiostreams;
18 
19 import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsDashboardFragment.KEY_BROADCAST_METADATA;
20 
21 import android.app.Activity;
22 import android.app.Dialog;
23 import android.app.settings.SettingsEnums;
24 import android.bluetooth.BluetoothDevice;
25 import android.bluetooth.BluetoothLeBroadcastMetadata;
26 import android.bluetooth.BluetoothProfile;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.os.Bundle;
30 import android.text.TextUtils;
31 import android.util.Log;
32 
33 import androidx.annotation.Nullable;
34 
35 import com.android.settings.R;
36 import com.android.settings.bluetooth.Utils;
37 import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
38 import com.android.settings.connecteddevice.audiosharing.AudioSharingUtils;
39 import com.android.settings.core.SubSettingLauncher;
40 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
41 import com.android.settingslib.bluetooth.BluetoothLeBroadcastMetadataExt;
42 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
43 
44 public class AudioStreamConfirmDialog extends InstrumentedDialogFragment {
45     private static final String TAG = "AudioStreamConfirmDialog";
46     private static final int DEFAULT_DEVICE_NAME = R.string.audio_streams_dialog_default_device;
47     private Context mContext;
48     @Nullable private Activity mActivity;
49     @Nullable private BluetoothLeBroadcastMetadata mBroadcastMetadata;
50     @Nullable private BluetoothDevice mConnectedDevice;
51     private int mAudioStreamConfirmDialogId = SettingsEnums.PAGE_UNKNOWN;
52 
53     @Override
onAttach(Context context)54     public void onAttach(Context context) {
55         mContext = context;
56         mActivity = getActivity();
57         if (mActivity == null) {
58             Log.w(TAG, "onAttach() mActivity is null!");
59             return;
60         }
61         Intent intent = mActivity.getIntent();
62         mBroadcastMetadata = getMetadata(intent);
63         mConnectedDevice = getConnectedDevice();
64         mAudioStreamConfirmDialogId =
65                 getDialogId(mBroadcastMetadata != null, mConnectedDevice != null);
66         super.onAttach(context);
67     }
68 
69     @Override
onCreate(Bundle savedInstanceState)70     public void onCreate(Bundle savedInstanceState) {
71         super.onCreate(savedInstanceState);
72         setShowsDialog(true);
73     }
74 
75     @Override
onCreateDialog(Bundle savedInstanceState)76     public Dialog onCreateDialog(Bundle savedInstanceState) {
77         return switch (mAudioStreamConfirmDialogId) {
78             case SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_FEATURE_UNSUPPORTED ->
79                     getUnsupportedDialog();
80             case SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_NO_LE_DEVICE -> getNoLeDeviceDialog();
81             case SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_LISTEN -> getConfirmDialog();
82             default -> getErrorDialog();
83         };
84     }
85 
86     @Override
getMetricsCategory()87     public int getMetricsCategory() {
88         return mAudioStreamConfirmDialogId;
89     }
90 
getConfirmDialog()91     private Dialog getConfirmDialog() {
92         return new AudioStreamsDialogFragment.DialogBuilder(getActivity())
93                 .setTitle(getString(R.string.audio_streams_dialog_listen_to_audio_stream))
94                 .setSubTitle1(
95                         mBroadcastMetadata != null
96                                 ? AudioStreamsHelper.getBroadcastName(mBroadcastMetadata)
97                                 : "")
98                 .setSubTitle2(
99                         getString(
100                                 R.string.audio_streams_dialog_control_volume,
101                                 getConnectedDeviceName()))
102                 .setLeftButtonText(getString(com.android.settings.R.string.cancel))
103                 .setLeftButtonOnClickListener(
104                         unused -> {
105                             dismiss();
106                             if (mActivity != null) {
107                                 mActivity.finish();
108                             }
109                         })
110                 .setRightButtonText(getString(R.string.audio_streams_dialog_listen))
111                 .setRightButtonOnClickListener(
112                         unused -> {
113                             mMetricsFeatureProvider.action(
114                                     getActivity(),
115                                     SettingsEnums
116                                             .ACTION_AUDIO_STREAM_CONFIRM_LAUNCH_MAIN_BUTTON_CLICK);
117                             launchAudioStreamsActivity();
118                             dismiss();
119                             if (mActivity != null) {
120                                 mActivity.finish();
121                             }
122                         })
123                 .build();
124     }
125 
126     private Dialog getUnsupportedDialog() {
127         return new AudioStreamsDialogFragment.DialogBuilder(getActivity())
128                 .setTitle(getString(R.string.audio_streams_dialog_cannot_listen))
129                 .setSubTitle2(getString(R.string.audio_streams_dialog_unsupported_device_subtitle))
130                 .setRightButtonText(getString(R.string.audio_streams_dialog_close))
131                 .setRightButtonOnClickListener(
132                         unused -> {
133                             dismiss();
134                             if (mActivity != null) {
135                                 mActivity.finish();
136                             }
137                         })
138                 .build();
139     }
140 
141     private Dialog getErrorDialog() {
142         return new AudioStreamsDialogFragment.DialogBuilder(getActivity())
143                 .setTitle(getString(R.string.audio_streams_dialog_cannot_listen))
144                 .setSubTitle2(
145                         getString(
146                                 R.string.audio_streams_dialog_cannot_play,
147                                 getConnectedDeviceName()))
148                 .setRightButtonText(getString(R.string.audio_streams_dialog_close))
149                 .setRightButtonOnClickListener(
150                         unused -> {
151                             dismiss();
152                             if (mActivity != null) {
153                                 mActivity.finish();
154                             }
155                         })
156                 .build();
157     }
158 
159     private Dialog getNoLeDeviceDialog() {
160         return new AudioStreamsDialogFragment.DialogBuilder(getActivity())
161                 .setTitle(getString(R.string.audio_streams_dialog_no_le_device_title))
162                 .setSubTitle2(getString(R.string.audio_streams_dialog_no_le_device_subtitle))
163                 .setLeftButtonText(getString(R.string.audio_streams_dialog_close))
164                 .setLeftButtonOnClickListener(
165                         unused -> {
166                             dismiss();
167                             if (mActivity != null) {
168                                 mActivity.finish();
169                             }
170                         })
171                 .setRightButtonText(getString(R.string.audio_streams_dialog_no_le_device_button))
172                 .setRightButtonOnClickListener(
173                         dialog -> {
174                             new SubSettingLauncher(mContext)
175                                     .setDestination(
176                                             ConnectedDeviceDashboardFragment.class.getName())
177                                     .setSourceMetricsCategory(
178                                             SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_NO_LE_DEVICE)
179                                     .launch();
180                             dismiss();
181                             if (mActivity != null) {
182                                 mActivity.finish();
183                             }
184                         })
185                 .build();
186     }
187 
188     private void launchAudioStreamsActivity() {
189         Bundle bundle = new Bundle();
190         bundle.putParcelable(KEY_BROADCAST_METADATA, mBroadcastMetadata);
191         if (mActivity != null) {
192             new SubSettingLauncher(getActivity())
193                     .setTitleText(getString(R.string.audio_streams_activity_title))
194                     .setDestination(AudioStreamsDashboardFragment.class.getName())
195                     .setArguments(bundle)
196                     .setSourceMetricsCategory(getMetricsCategory())
197                     .launch();
198         }
199     }
200 
201     private @Nullable BluetoothLeBroadcastMetadata getMetadata(Intent intent) {
202         String metadata = intent.getStringExtra(KEY_BROADCAST_METADATA);
203         if (metadata == null || metadata.isEmpty()) {
204             return null;
205         }
206         return BluetoothLeBroadcastMetadataExt.INSTANCE.convertToBroadcastMetadata(metadata);
207     }
208 
209     private int getDialogId(boolean hasMetadata, boolean hasConnectedDevice) {
210         if (!AudioSharingUtils.isFeatureEnabled()) {
211             return SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_FEATURE_UNSUPPORTED;
212         }
213         if (!hasConnectedDevice) {
214             return SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_NO_LE_DEVICE;
215         }
216         return hasMetadata
217                 ? SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_LISTEN
218                 : SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_DATA_ERROR;
219     }
220 
221     @Nullable
222     private BluetoothDevice getConnectedDevice() {
223         var localBluetoothManager = Utils.getLocalBluetoothManager(getActivity());
224         if (localBluetoothManager == null) {
225             return null;
226         }
227         LocalBluetoothLeBroadcastAssistant assistant =
228                 localBluetoothManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
229         if (assistant == null) {
230             return null;
231         }
232         var devices =
233                 assistant.getDevicesMatchingConnectionStates(
234                         new int[] {BluetoothProfile.STATE_CONNECTED});
235         return devices.isEmpty() ? null : devices.get(0);
236     }
237 
238     private String getConnectedDeviceName() {
239         if (mConnectedDevice != null) {
240             String alias = mConnectedDevice.getAlias();
241             return TextUtils.isEmpty(alias) ? getString(DEFAULT_DEVICE_NAME) : alias;
242         }
243         Log.w(TAG, "getConnectedDeviceName : no connected device!");
244         return getString(DEFAULT_DEVICE_NAME);
245     }
246 }
247