blob: d3f9cd4f9034997c947697477252181143b3a92c [file] [log] [blame]
package com.android.settingslib.bluetooth;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.provider.MediaStore;
import android.util.Log;
import android.util.Pair;
import androidx.annotation.DrawableRes;
import com.android.settingslib.R;
import com.android.settingslib.widget.AdaptiveIcon;
import com.android.settingslib.widget.AdaptiveOutlineDrawable;
import java.io.IOException;
import java.util.List;
public class BluetoothUtils {
private static final String TAG = "BluetoothUtils";
public static final boolean V = false; // verbose logging
public static final boolean D = true; // regular logging
public static final int META_INT_ERROR = -1;
private static ErrorListener sErrorListener;
public static int getConnectionStateSummary(int connectionState) {
switch (connectionState) {
case BluetoothProfile.STATE_CONNECTED:
return R.string.bluetooth_connected;
case BluetoothProfile.STATE_CONNECTING:
return R.string.bluetooth_connecting;
case BluetoothProfile.STATE_DISCONNECTED:
return R.string.bluetooth_disconnected;
case BluetoothProfile.STATE_DISCONNECTING:
return R.string.bluetooth_disconnecting;
default:
return 0;
}
}
static void showError(Context context, String name, int messageResId) {
if (sErrorListener != null) {
sErrorListener.onShowError(context, name, messageResId);
}
}
public static void setErrorListener(ErrorListener listener) {
sErrorListener = listener;
}
public interface ErrorListener {
void onShowError(Context context, String name, int messageResId);
}
public static Pair<Drawable, String> getBtClassDrawableWithDescription(Context context,
CachedBluetoothDevice cachedDevice) {
BluetoothClass btClass = cachedDevice.getBtClass();
if (btClass != null) {
switch (btClass.getMajorDeviceClass()) {
case BluetoothClass.Device.Major.COMPUTER:
return new Pair<>(getBluetoothDrawable(context,
com.android.internal.R.drawable.ic_bt_laptop),
context.getString(R.string.bluetooth_talkback_computer));
case BluetoothClass.Device.Major.PHONE:
return new Pair<>(
getBluetoothDrawable(context,
com.android.internal.R.drawable.ic_phone),
context.getString(R.string.bluetooth_talkback_phone));
case BluetoothClass.Device.Major.PERIPHERAL:
return new Pair<>(
getBluetoothDrawable(context, HidProfile.getHidClassDrawable(btClass)),
context.getString(R.string.bluetooth_talkback_input_peripheral));
case BluetoothClass.Device.Major.IMAGING:
return new Pair<>(
getBluetoothDrawable(context,
com.android.internal.R.drawable.ic_settings_print),
context.getString(R.string.bluetooth_talkback_imaging));
default:
// unrecognized device class; continue
}
}
List<LocalBluetoothProfile> profiles = cachedDevice.getProfiles();
for (LocalBluetoothProfile profile : profiles) {
int resId = profile.getDrawableResource(btClass);
if (resId != 0) {
return new Pair<>(getBluetoothDrawable(context, resId), null);
}
}
if (btClass != null) {
if (btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
return new Pair<>(
getBluetoothDrawable(context,
com.android.internal.R.drawable.ic_bt_headset_hfp),
context.getString(R.string.bluetooth_talkback_headset));
}
if (btClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
return new Pair<>(
getBluetoothDrawable(context,
com.android.internal.R.drawable.ic_bt_headphones_a2dp),
context.getString(R.string.bluetooth_talkback_headphone));
}
}
return new Pair<>(
getBluetoothDrawable(context,
com.android.internal.R.drawable.ic_settings_bluetooth).mutate(),
context.getString(R.string.bluetooth_talkback_bluetooth));
}
/**
* Get bluetooth drawable by {@code resId}
*/
public static Drawable getBluetoothDrawable(Context context, @DrawableRes int resId) {
return context.getDrawable(resId);
}
/**
* Get colorful bluetooth icon with description
*/
public static Pair<Drawable, String> getBtRainbowDrawableWithDescription(Context context,
CachedBluetoothDevice cachedDevice) {
final Pair<Drawable, String> pair = BluetoothUtils.getBtClassDrawableWithDescription(
context, cachedDevice);
final BluetoothDevice bluetoothDevice = cachedDevice.getDevice();
final boolean untetheredHeadset = getBooleanMetaData(
bluetoothDevice, BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET);
final int iconSize = context.getResources().getDimensionPixelSize(
R.dimen.bt_nearby_icon_size);
final Resources resources = context.getResources();
// Deal with untethered headset
if (untetheredHeadset) {
final Uri iconUri = getUriMetaData(bluetoothDevice,
BluetoothDevice.METADATA_MAIN_ICON);
if (iconUri != null) {
try {
context.getContentResolver().takePersistableUriPermission(iconUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
} catch (SecurityException e) {
Log.e(TAG, "Failed to take persistable permission for: " + iconUri, e);
}
try {
final Bitmap bitmap = MediaStore.Images.Media.getBitmap(
context.getContentResolver(), iconUri);
if (bitmap != null) {
final Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, iconSize,
iconSize, false);
bitmap.recycle();
final AdaptiveOutlineDrawable drawable = new AdaptiveOutlineDrawable(
resources, resizedBitmap);
return new Pair<>(drawable, pair.second);
}
} catch (IOException e) {
Log.e(TAG, "Failed to get drawable for: " + iconUri, e);
} catch (SecurityException e) {
Log.e(TAG, "Failed to get permission for: " + iconUri, e);
}
}
}
return new Pair<>(buildBtRainbowDrawable(context,
pair.first, cachedDevice.getAddress().hashCode()), pair.second);
}
/**
* Build Bluetooth device icon with rainbow
*/
public static Drawable buildBtRainbowDrawable(Context context, Drawable drawable,
int hashCode) {
final Resources resources = context.getResources();
// Deal with normal headset
final int[] iconFgColors = resources.getIntArray(R.array.bt_icon_fg_colors);
final int[] iconBgColors = resources.getIntArray(R.array.bt_icon_bg_colors);
// get color index based on mac address
final int index = Math.abs(hashCode % iconBgColors.length);
drawable.setTint(iconFgColors[index]);
final Drawable adaptiveIcon = new AdaptiveIcon(context, drawable);
((AdaptiveIcon) adaptiveIcon).setBackgroundColor(iconBgColors[index]);
return adaptiveIcon;
}
/**
* Get boolean Bluetooth metadata
*
* @param bluetoothDevice the BluetoothDevice to get metadata
* @param key key value within the list of BluetoothDevice.METADATA_*
* @return the boolean metdata
*/
public static boolean getBooleanMetaData(BluetoothDevice bluetoothDevice, int key) {
if (bluetoothDevice == null) {
return false;
}
final byte[] data = bluetoothDevice.getMetadata(key);
if (data == null) {
return false;
}
return Boolean.parseBoolean(new String(data));
}
/**
* Get String Bluetooth metadata
*
* @param bluetoothDevice the BluetoothDevice to get metadata
* @param key key value within the list of BluetoothDevice.METADATA_*
* @return the String metdata
*/
public static String getStringMetaData(BluetoothDevice bluetoothDevice, int key) {
if (bluetoothDevice == null) {
return null;
}
final byte[] data = bluetoothDevice.getMetadata(key);
if (data == null) {
return null;
}
return new String(data);
}
/**
* Get integer Bluetooth metadata
*
* @param bluetoothDevice the BluetoothDevice to get metadata
* @param key key value within the list of BluetoothDevice.METADATA_*
* @return the int metdata
*/
public static int getIntMetaData(BluetoothDevice bluetoothDevice, int key) {
if (bluetoothDevice == null) {
return META_INT_ERROR;
}
final byte[] data = bluetoothDevice.getMetadata(key);
if (data == null) {
return META_INT_ERROR;
}
try {
return Integer.parseInt(new String(data));
} catch (NumberFormatException e) {
return META_INT_ERROR;
}
}
/**
* Get URI Bluetooth metadata
*
* @param bluetoothDevice the BluetoothDevice to get metadata
* @param key key value within the list of BluetoothDevice.METADATA_*
* @return the URI metdata
*/
public static Uri getUriMetaData(BluetoothDevice bluetoothDevice, int key) {
String data = getStringMetaData(bluetoothDevice, key);
if (data == null) {
return null;
}
return Uri.parse(data);
}
}