1 /* 2 * Copyright (C) 2022 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.bluetooth; 18 19 import android.bluetooth.BluetoothLeAudioContentMetadata; 20 import android.bluetooth.BluetoothLeBroadcastMetadata; 21 import android.bluetooth.BluetoothLeBroadcastReceiveState; 22 import android.bluetooth.BluetoothLeBroadcastSubgroup; 23 import android.content.Context; 24 import android.graphics.drawable.Drawable; 25 import android.text.TextUtils; 26 import android.view.View; 27 import android.widget.ImageButton; 28 import android.widget.ImageView; 29 30 import androidx.annotation.NonNull; 31 import androidx.preference.Preference; 32 import androidx.preference.PreferenceViewHolder; 33 34 import com.android.settings.R; 35 import com.android.settingslib.Utils; 36 37 import java.util.List; 38 39 /** 40 * Preference to display a broadcast source in the Broadcast Source List. 41 */ 42 class BluetoothBroadcastSourcePreference extends Preference { 43 44 private static final int RESOURCE_ID_UNKNOWN_PROGRAM_INFO = R.string.device_info_default; 45 private static final int RESOURCE_ID_ICON = R.drawable.settings_input_antenna; 46 47 private BluetoothLeBroadcastMetadata mBluetoothLeBroadcastMetadata; 48 private BluetoothLeBroadcastReceiveState mBluetoothLeBroadcastReceiveState; 49 private ImageView mFrictionImageView; 50 private String mTitle; 51 private boolean mStatus; 52 private boolean mIsEncrypted; 53 BluetoothBroadcastSourcePreference(@onNull Context context)54 BluetoothBroadcastSourcePreference(@NonNull Context context) { 55 super(context); 56 initUi(); 57 } 58 59 @Override onBindViewHolder(final PreferenceViewHolder view)60 public void onBindViewHolder(final PreferenceViewHolder view) { 61 super.onBindViewHolder(view); 62 view.findViewById(R.id.two_target_divider).setVisibility(View.INVISIBLE); 63 final ImageButton imageButton = (ImageButton) view.findViewById(R.id.icon_button); 64 imageButton.setVisibility(View.GONE); 65 mFrictionImageView = (ImageView) view.findViewById(R.id.friction_icon); 66 updateStatusButton(); 67 } 68 initUi()69 private void initUi() { 70 setLayoutResource(R.layout.preference_access_point); 71 setWidgetLayoutResource(R.layout.access_point_friction_widget); 72 mTitle = getContext().getString(RESOURCE_ID_UNKNOWN_PROGRAM_INFO); 73 mStatus = false; 74 final Drawable drawable = getContext().getDrawable(RESOURCE_ID_ICON); 75 if (drawable != null) { 76 drawable.setTint(Utils.getColorAttrDefaultColor(getContext(), 77 android.R.attr.colorControlNormal)); 78 setIcon(drawable); 79 } 80 } 81 updateStatusButton()82 private void updateStatusButton() { 83 if (mFrictionImageView == null) { 84 return; 85 } 86 if (mStatus || mIsEncrypted) { 87 Drawable drawable; 88 if (mStatus) { 89 drawable = getContext().getDrawable(R.drawable.bluetooth_broadcast_dialog_done); 90 } else { 91 drawable = getContext().getDrawable(R.drawable.ic_friction_lock_closed); 92 } 93 if (drawable != null) { 94 drawable.setTint(Utils.getColorAttrDefaultColor(getContext(), 95 android.R.attr.colorControlNormal)); 96 mFrictionImageView.setImageDrawable(drawable); 97 } 98 mFrictionImageView.setVisibility(View.VISIBLE); 99 } else { 100 mFrictionImageView.setVisibility(View.GONE); 101 } 102 } 103 104 /** 105 * Updates the title and status from BluetoothLeBroadcastMetadata. 106 */ updateMetadataAndRefreshUi(BluetoothLeBroadcastMetadata source, boolean status)107 public void updateMetadataAndRefreshUi(BluetoothLeBroadcastMetadata source, boolean status) { 108 mBluetoothLeBroadcastMetadata = source; 109 mTitle = getProgramInfo(); 110 mIsEncrypted = mBluetoothLeBroadcastMetadata.isEncrypted(); 111 mStatus = status || mBluetoothLeBroadcastReceiveState != null; 112 113 refresh(); 114 } 115 116 /** 117 * Updates the title and status from BluetoothLeBroadcastReceiveState. 118 */ updateReceiveStateAndRefreshUi(BluetoothLeBroadcastReceiveState receiveState)119 public void updateReceiveStateAndRefreshUi(BluetoothLeBroadcastReceiveState receiveState) { 120 mBluetoothLeBroadcastReceiveState = receiveState; 121 mTitle = getProgramInfo(); 122 mStatus = true; 123 124 refresh(); 125 } 126 127 /** 128 * Gets the BluetoothLeBroadcastMetadata. 129 */ getBluetoothLeBroadcastMetadata()130 public BluetoothLeBroadcastMetadata getBluetoothLeBroadcastMetadata() { 131 return mBluetoothLeBroadcastMetadata; 132 } 133 refresh()134 private void refresh() { 135 setTitle(mTitle); 136 updateStatusButton(); 137 } 138 getProgramInfo()139 private String getProgramInfo() { 140 if (mBluetoothLeBroadcastReceiveState != null) { 141 List<BluetoothLeAudioContentMetadata> bluetoothLeAudioContentMetadata = 142 mBluetoothLeBroadcastReceiveState.getSubgroupMetadata(); 143 if (!bluetoothLeAudioContentMetadata.isEmpty()) { 144 return bluetoothLeAudioContentMetadata.stream() 145 .map(i -> i.getProgramInfo()) 146 .findFirst().orElse( 147 getContext().getString(RESOURCE_ID_UNKNOWN_PROGRAM_INFO)); 148 } 149 } 150 if (mBluetoothLeBroadcastMetadata == null) { 151 return getContext().getString(RESOURCE_ID_UNKNOWN_PROGRAM_INFO); 152 } 153 final List<BluetoothLeBroadcastSubgroup> subgroups = 154 mBluetoothLeBroadcastMetadata.getSubgroups(); 155 if (subgroups.isEmpty()) { 156 return getContext().getString(RESOURCE_ID_UNKNOWN_PROGRAM_INFO); 157 } 158 return subgroups.stream() 159 .map(i -> i.getContentMetadata().getProgramInfo()) 160 .filter(i -> !TextUtils.isEmpty(i)) 161 .findFirst().orElse(getContext().getString(RESOURCE_ID_UNKNOWN_PROGRAM_INFO)); 162 } 163 164 /** 165 * Whether the broadcast source is encrypted or not. 166 * @return If true, the broadcast source needs the broadcast code. If false, the broadcast 167 * source does not need the broadcast code. 168 */ isEncrypted()169 public boolean isEncrypted() { 170 return mIsEncrypted; 171 } 172 173 /** 174 * Clear the BluetoothLeBroadcastReceiveState and reset the state when the user clicks the 175 * "leave broadcast" button. 176 */ clearReceiveState()177 public void clearReceiveState() { 178 mBluetoothLeBroadcastReceiveState = null; 179 mTitle = getProgramInfo(); 180 mStatus = false; 181 refresh(); 182 } 183 } 184