/* * Copyright (C) 2022 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.bluetooth; import android.bluetooth.BluetoothLeAudioContentMetadata; import android.bluetooth.BluetoothLeBroadcastMetadata; import android.bluetooth.BluetoothLeBroadcastReceiveState; import android.bluetooth.BluetoothLeBroadcastSubgroup; import android.content.Context; import android.graphics.drawable.Drawable; import android.text.TextUtils; import android.view.View; import android.widget.ImageButton; import android.widget.ImageView; import androidx.annotation.NonNull; import androidx.preference.Preference; import androidx.preference.PreferenceViewHolder; import com.android.settings.R; import com.android.settingslib.Utils; import java.util.List; /** * Preference to display a broadcast source in the Broadcast Source List. */ class BluetoothBroadcastSourcePreference extends Preference { private static final int RESOURCE_ID_UNKNOWN_PROGRAM_INFO = R.string.device_info_default; private static final int RESOURCE_ID_ICON = com.android.settingslib.R.drawable.settings_input_antenna; private BluetoothLeBroadcastMetadata mBluetoothLeBroadcastMetadata; private BluetoothLeBroadcastReceiveState mBluetoothLeBroadcastReceiveState; private ImageView mFrictionImageView; private String mTitle; private boolean mStatus; private boolean mIsEncrypted; BluetoothBroadcastSourcePreference(@NonNull Context context) { super(context); initUi(); } @Override public void onBindViewHolder(final PreferenceViewHolder view) { super.onBindViewHolder(view); view.findViewById(com.android.settingslib.widget.preference.twotarget.R.id.two_target_divider) .setVisibility(View.INVISIBLE); final ImageButton imageButton = (ImageButton) view.findViewById(com.android.settingslib.R.id.icon_button); imageButton.setVisibility(View.GONE); mFrictionImageView = (ImageView) view.findViewById(com.android.settingslib.R.id.friction_icon); updateStatusButton(); } private void initUi() { setLayoutResource(com.android.settingslib.R.layout.preference_access_point); setWidgetLayoutResource(com.android.settingslib.R.layout.access_point_friction_widget); mTitle = getContext().getString(RESOURCE_ID_UNKNOWN_PROGRAM_INFO); mStatus = false; final Drawable drawable = getContext().getDrawable(RESOURCE_ID_ICON); if (drawable != null) { drawable.setTint(Utils.getColorAttrDefaultColor(getContext(), android.R.attr.colorControlNormal)); setIcon(drawable); } } private void updateStatusButton() { if (mFrictionImageView == null) { return; } if (mStatus || mIsEncrypted) { Drawable drawable; if (mStatus) { drawable = getContext().getDrawable(R.drawable.bluetooth_broadcast_dialog_done); } else { drawable = getContext().getDrawable(R.drawable.ic_friction_lock_closed); } if (drawable != null) { drawable.setTint(Utils.getColorAttrDefaultColor(getContext(), android.R.attr.colorControlNormal)); mFrictionImageView.setImageDrawable(drawable); } mFrictionImageView.setVisibility(View.VISIBLE); } else { mFrictionImageView.setVisibility(View.GONE); } } /** * Updates the title and status from BluetoothLeBroadcastMetadata. */ public void updateMetadataAndRefreshUi(BluetoothLeBroadcastMetadata source, boolean status) { mBluetoothLeBroadcastMetadata = source; mTitle = getProgramInfo(); mIsEncrypted = mBluetoothLeBroadcastMetadata.isEncrypted(); mStatus = status || mBluetoothLeBroadcastReceiveState != null; refresh(); } /** * Updates the title and status from BluetoothLeBroadcastReceiveState. */ public void updateReceiveStateAndRefreshUi(BluetoothLeBroadcastReceiveState receiveState) { mBluetoothLeBroadcastReceiveState = receiveState; mTitle = getProgramInfo(); mStatus = true; refresh(); } /** * Gets the BluetoothLeBroadcastMetadata. */ public BluetoothLeBroadcastMetadata getBluetoothLeBroadcastMetadata() { return mBluetoothLeBroadcastMetadata; } private void refresh() { setTitle(mTitle); updateStatusButton(); } private String getProgramInfo() { if (mBluetoothLeBroadcastReceiveState != null) { List bluetoothLeAudioContentMetadata = mBluetoothLeBroadcastReceiveState.getSubgroupMetadata(); if (!bluetoothLeAudioContentMetadata.isEmpty()) { return bluetoothLeAudioContentMetadata.stream() .map(i -> i.getProgramInfo()) .findFirst().orElse( getContext().getString(RESOURCE_ID_UNKNOWN_PROGRAM_INFO)); } } if (mBluetoothLeBroadcastMetadata == null) { return getContext().getString(RESOURCE_ID_UNKNOWN_PROGRAM_INFO); } final List subgroups = mBluetoothLeBroadcastMetadata.getSubgroups(); if (subgroups.isEmpty()) { return getContext().getString(RESOURCE_ID_UNKNOWN_PROGRAM_INFO); } return subgroups.stream() .map(i -> i.getContentMetadata().getProgramInfo()) .filter(i -> !TextUtils.isEmpty(i)) .findFirst().orElse(getContext().getString(RESOURCE_ID_UNKNOWN_PROGRAM_INFO)); } /** * Whether the broadcast source is encrypted or not. * @return If true, the broadcast source needs the broadcast code. If false, the broadcast * source does not need the broadcast code. */ public boolean isEncrypted() { return mIsEncrypted; } /** * Whether the broadcast source is connected at the beginging. We will get the * BluetoothLeBroadcastReceiveState from the broadcast source. * See {@link BluetoothFindBroadcastsFragment#addConnectedSourcePreference} * @return If true, the broadcast source is already connected by the broadcast sink. */ public boolean isCreatedByReceiveState() { return mBluetoothLeBroadcastReceiveState != null; } /** * Clear the BluetoothLeBroadcastReceiveState and reset the state when the user clicks the * "leave broadcast" button. */ public void clearReceiveState() { mBluetoothLeBroadcastReceiveState = null; mTitle = getProgramInfo(); mStatus = false; refresh(); } }