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