1 /* 2 * Copyright (C) 2023 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 android.bluetooth.BluetoothLeBroadcastMetadata; 20 import android.bluetooth.BluetoothLeBroadcastReceiveState; 21 import android.content.Context; 22 import android.util.AttributeSet; 23 import android.view.View; 24 25 import androidx.annotation.Nullable; 26 import androidx.annotation.VisibleForTesting; 27 import androidx.preference.PreferenceViewHolder; 28 29 import com.android.settings.R; 30 import com.android.settingslib.utils.ThreadUtils; 31 import com.android.settingslib.widget.TwoTargetPreference; 32 33 /** 34 * Custom preference class for managing audio stream preferences with an optional lock icon. Extends 35 * {@link TwoTargetPreference}. 36 */ 37 class AudioStreamPreference extends TwoTargetPreference { 38 private boolean mIsConnected = false; 39 private boolean mIsEncrypted = true; 40 @Nullable private AudioStream mAudioStream; 41 42 /** 43 * Update preference UI based on connection status 44 * 45 * @param isConnected Is this stream connected 46 */ setIsConnected(boolean isConnected)47 void setIsConnected(boolean isConnected) { 48 if (mIsConnected != isConnected) { 49 mIsConnected = isConnected; 50 notifyChanged(); 51 } 52 } 53 54 @VisibleForTesting AudioStreamPreference(Context context, @Nullable AttributeSet attrs)55 AudioStreamPreference(Context context, @Nullable AttributeSet attrs) { 56 super(context, attrs); 57 setIcon(com.android.settingslib.R.drawable.ic_bt_le_audio_sharing); 58 } 59 setAudioStreamState(AudioStreamsProgressCategoryController.AudioStreamState state)60 void setAudioStreamState(AudioStreamsProgressCategoryController.AudioStreamState state) { 61 if (mAudioStream != null) { 62 mAudioStream.setState(state); 63 } 64 } 65 setAudioStreamMetadata(BluetoothLeBroadcastMetadata metadata)66 void setAudioStreamMetadata(BluetoothLeBroadcastMetadata metadata) { 67 if (mAudioStream != null) { 68 mAudioStream.setMetadata(metadata); 69 // Update title based on the metadata 70 String broadcastName = AudioStreamsHelper.getBroadcastName(metadata); 71 ThreadUtils.postOnMainThread(() -> setTitle(broadcastName)); 72 } 73 } 74 getAudioStreamBroadcastId()75 int getAudioStreamBroadcastId() { 76 return mAudioStream != null ? mAudioStream.getBroadcastId() : -1; 77 } 78 79 @Nullable getAudioStreamBroadcastName()80 String getAudioStreamBroadcastName() { 81 return mAudioStream != null ? mAudioStream.getBroadcastName() : null; 82 } 83 getAudioStreamRssi()84 int getAudioStreamRssi() { 85 return mAudioStream != null ? mAudioStream.getRssi() : -1; 86 } 87 88 @Nullable getAudioStreamMetadata()89 BluetoothLeBroadcastMetadata getAudioStreamMetadata() { 90 return mAudioStream != null ? mAudioStream.getMetadata() : null; 91 } 92 getAudioStreamState()93 AudioStreamsProgressCategoryController.AudioStreamState getAudioStreamState() { 94 return mAudioStream != null 95 ? mAudioStream.getState() 96 : AudioStreamsProgressCategoryController.AudioStreamState.UNKNOWN; 97 } 98 getSourceOriginForLogging()99 SourceOriginForLogging getSourceOriginForLogging() { 100 return mAudioStream != null 101 ? mAudioStream.getSourceOriginForLogging() 102 : SourceOriginForLogging.UNKNOWN; 103 } 104 105 @Override shouldHideSecondTarget()106 protected boolean shouldHideSecondTarget() { 107 return mIsConnected || !mIsEncrypted; 108 } 109 110 @Override getSecondTargetResId()111 protected int getSecondTargetResId() { 112 return R.layout.preference_widget_lock; 113 } 114 115 @Override onBindViewHolder(PreferenceViewHolder holder)116 public void onBindViewHolder(PreferenceViewHolder holder) { 117 super.onBindViewHolder(holder); 118 View divider = 119 holder.findViewById( 120 com.android.settingslib.widget.preference.twotarget.R.id 121 .two_target_divider); 122 if (divider != null) { 123 divider.setVisibility(View.GONE); 124 } 125 } 126 fromMetadata( Context context, BluetoothLeBroadcastMetadata source, SourceOriginForLogging sourceOriginForLogging)127 static AudioStreamPreference fromMetadata( 128 Context context, 129 BluetoothLeBroadcastMetadata source, 130 SourceOriginForLogging sourceOriginForLogging) { 131 AudioStreamPreference preference = new AudioStreamPreference(context, /* attrs= */ null); 132 preference.setIsEncrypted(source.isEncrypted()); 133 preference.setTitle(AudioStreamsHelper.getBroadcastName(source)); 134 preference.setAudioStream(new AudioStream(source, sourceOriginForLogging)); 135 return preference; 136 } 137 fromReceiveState( Context context, BluetoothLeBroadcastReceiveState receiveState)138 static AudioStreamPreference fromReceiveState( 139 Context context, BluetoothLeBroadcastReceiveState receiveState) { 140 AudioStreamPreference preference = new AudioStreamPreference(context, /* attrs= */ null); 141 preference.setTitle(AudioStreamsHelper.getBroadcastName(receiveState)); 142 preference.setAudioStream(new AudioStream(receiveState)); 143 return preference; 144 } 145 setAudioStream(AudioStream audioStream)146 private void setAudioStream(AudioStream audioStream) { 147 mAudioStream = audioStream; 148 } 149 setIsEncrypted(boolean isEncrypted)150 private void setIsEncrypted(boolean isEncrypted) { 151 mIsEncrypted = isEncrypted; 152 } 153 154 private static final class AudioStream { 155 private static final int UNAVAILABLE = -1; 156 @Nullable private BluetoothLeBroadcastMetadata mMetadata; 157 @Nullable private BluetoothLeBroadcastReceiveState mReceiveState; 158 private SourceOriginForLogging mSourceOriginForLogging = SourceOriginForLogging.UNKNOWN; 159 private AudioStreamsProgressCategoryController.AudioStreamState mState = 160 AudioStreamsProgressCategoryController.AudioStreamState.UNKNOWN; 161 AudioStream( BluetoothLeBroadcastMetadata metadata, SourceOriginForLogging sourceOriginForLogging)162 private AudioStream( 163 BluetoothLeBroadcastMetadata metadata, 164 SourceOriginForLogging sourceOriginForLogging) { 165 mMetadata = metadata; 166 mSourceOriginForLogging = sourceOriginForLogging; 167 } 168 AudioStream(BluetoothLeBroadcastReceiveState receiveState)169 private AudioStream(BluetoothLeBroadcastReceiveState receiveState) { 170 mReceiveState = receiveState; 171 } 172 getBroadcastId()173 private int getBroadcastId() { 174 return mMetadata != null 175 ? mMetadata.getBroadcastId() 176 : mReceiveState != null ? mReceiveState.getBroadcastId() : UNAVAILABLE; 177 } 178 getBroadcastName()179 private @Nullable String getBroadcastName() { 180 return mMetadata != null 181 ? AudioStreamsHelper.getBroadcastName(mMetadata) 182 : mReceiveState != null 183 ? AudioStreamsHelper.getBroadcastName(mReceiveState) 184 : null; 185 } 186 getRssi()187 private int getRssi() { 188 return mMetadata != null ? mMetadata.getRssi() : Integer.MAX_VALUE; 189 } 190 getState()191 private AudioStreamsProgressCategoryController.AudioStreamState getState() { 192 return mState; 193 } 194 getSourceOriginForLogging()195 private SourceOriginForLogging getSourceOriginForLogging() { 196 return mSourceOriginForLogging; 197 } 198 199 @Nullable getMetadata()200 private BluetoothLeBroadcastMetadata getMetadata() { 201 return mMetadata; 202 } 203 setState(AudioStreamsProgressCategoryController.AudioStreamState state)204 private void setState(AudioStreamsProgressCategoryController.AudioStreamState state) { 205 mState = state; 206 } 207 setMetadata(BluetoothLeBroadcastMetadata metadata)208 private void setMetadata(BluetoothLeBroadcastMetadata metadata) { 209 mMetadata = metadata; 210 } 211 } 212 } 213