Home | History | Annotate | Download | only in bluetooth
      1 package com.android.settingslib.bluetooth;
      2 
      3 import android.bluetooth.BluetoothClass;
      4 import android.bluetooth.BluetoothDevice;
      5 import android.bluetooth.BluetoothProfile;
      6 import android.content.Context;
      7 import android.content.Intent;
      8 import android.content.res.Resources;
      9 import android.graphics.Bitmap;
     10 import android.graphics.drawable.Drawable;
     11 import android.net.Uri;
     12 import android.provider.MediaStore;
     13 import android.util.Log;
     14 import android.util.Pair;
     15 
     16 import androidx.annotation.DrawableRes;
     17 
     18 import com.android.settingslib.R;
     19 import com.android.settingslib.widget.AdaptiveIcon;
     20 import com.android.settingslib.widget.AdaptiveOutlineDrawable;
     21 
     22 import java.io.IOException;
     23 import java.util.List;
     24 
     25 public class BluetoothUtils {
     26     private static final String TAG = "BluetoothUtils";
     27 
     28     public static final boolean V = false; // verbose logging
     29     public static final boolean D = true;  // regular logging
     30 
     31     public static final int META_INT_ERROR = -1;
     32 
     33     private static ErrorListener sErrorListener;
     34 
     35     public static int getConnectionStateSummary(int connectionState) {
     36         switch (connectionState) {
     37             case BluetoothProfile.STATE_CONNECTED:
     38                 return R.string.bluetooth_connected;
     39             case BluetoothProfile.STATE_CONNECTING:
     40                 return R.string.bluetooth_connecting;
     41             case BluetoothProfile.STATE_DISCONNECTED:
     42                 return R.string.bluetooth_disconnected;
     43             case BluetoothProfile.STATE_DISCONNECTING:
     44                 return R.string.bluetooth_disconnecting;
     45             default:
     46                 return 0;
     47         }
     48     }
     49 
     50     static void showError(Context context, String name, int messageResId) {
     51         if (sErrorListener != null) {
     52             sErrorListener.onShowError(context, name, messageResId);
     53         }
     54     }
     55 
     56     public static void setErrorListener(ErrorListener listener) {
     57         sErrorListener = listener;
     58     }
     59 
     60     public interface ErrorListener {
     61         void onShowError(Context context, String name, int messageResId);
     62     }
     63 
     64     public static Pair<Drawable, String> getBtClassDrawableWithDescription(Context context,
     65             CachedBluetoothDevice cachedDevice) {
     66         BluetoothClass btClass = cachedDevice.getBtClass();
     67         if (btClass != null) {
     68             switch (btClass.getMajorDeviceClass()) {
     69                 case BluetoothClass.Device.Major.COMPUTER:
     70                     return new Pair<>(getBluetoothDrawable(context,
     71                             com.android.internal.R.drawable.ic_bt_laptop),
     72                             context.getString(R.string.bluetooth_talkback_computer));
     73 
     74                 case BluetoothClass.Device.Major.PHONE:
     75                     return new Pair<>(
     76                             getBluetoothDrawable(context,
     77                                     com.android.internal.R.drawable.ic_phone),
     78                             context.getString(R.string.bluetooth_talkback_phone));
     79 
     80                 case BluetoothClass.Device.Major.PERIPHERAL:
     81                     return new Pair<>(
     82                             getBluetoothDrawable(context, HidProfile.getHidClassDrawable(btClass)),
     83                             context.getString(R.string.bluetooth_talkback_input_peripheral));
     84 
     85                 case BluetoothClass.Device.Major.IMAGING:
     86                     return new Pair<>(
     87                             getBluetoothDrawable(context,
     88                                     com.android.internal.R.drawable.ic_settings_print),
     89                             context.getString(R.string.bluetooth_talkback_imaging));
     90 
     91                 default:
     92                     // unrecognized device class; continue
     93             }
     94         }
     95 
     96         List<LocalBluetoothProfile> profiles = cachedDevice.getProfiles();
     97         for (LocalBluetoothProfile profile : profiles) {
     98             int resId = profile.getDrawableResource(btClass);
     99             if (resId != 0) {
    100                 return new Pair<>(getBluetoothDrawable(context, resId), null);
    101             }
    102         }
    103         if (btClass != null) {
    104             if (btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
    105                 return new Pair<>(
    106                         getBluetoothDrawable(context,
    107                                 com.android.internal.R.drawable.ic_bt_headset_hfp),
    108                         context.getString(R.string.bluetooth_talkback_headset));
    109             }
    110             if (btClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
    111                 return new Pair<>(
    112                         getBluetoothDrawable(context,
    113                                 com.android.internal.R.drawable.ic_bt_headphones_a2dp),
    114                         context.getString(R.string.bluetooth_talkback_headphone));
    115             }
    116         }
    117         return new Pair<>(
    118                 getBluetoothDrawable(context,
    119                         com.android.internal.R.drawable.ic_settings_bluetooth),
    120                 context.getString(R.string.bluetooth_talkback_bluetooth));
    121     }
    122 
    123     /**
    124      * Get bluetooth drawable by {@code resId}
    125      */
    126     public static Drawable getBluetoothDrawable(Context context, @DrawableRes int resId) {
    127         return context.getDrawable(resId);
    128     }
    129 
    130     /**
    131      * Get colorful bluetooth icon with description
    132      */
    133     public static Pair<Drawable, String> getBtRainbowDrawableWithDescription(Context context,
    134             CachedBluetoothDevice cachedDevice) {
    135         final Pair<Drawable, String> pair = BluetoothUtils.getBtClassDrawableWithDescription(
    136                 context, cachedDevice);
    137         final BluetoothDevice bluetoothDevice = cachedDevice.getDevice();
    138         final boolean untetheredHeadset = getBooleanMetaData(
    139                 bluetoothDevice, BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET);
    140         final int iconSize = context.getResources().getDimensionPixelSize(
    141                 R.dimen.bt_nearby_icon_size);
    142         final Resources resources = context.getResources();
    143 
    144         // Deal with untethered headset
    145         if (untetheredHeadset) {
    146             final Uri iconUri = getUriMetaData(bluetoothDevice,
    147                     BluetoothDevice.METADATA_MAIN_ICON);
    148             if (iconUri != null) {
    149                 try {
    150                     context.getContentResolver().takePersistableUriPermission(iconUri,
    151                             Intent.FLAG_GRANT_READ_URI_PERMISSION);
    152                 } catch (SecurityException e) {
    153                     Log.e(TAG, "Failed to take persistable permission for: " + iconUri);
    154                 }
    155                 try {
    156                     final Bitmap bitmap = MediaStore.Images.Media.getBitmap(
    157                             context.getContentResolver(), iconUri);
    158                     if (bitmap != null) {
    159                         final Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, iconSize,
    160                                 iconSize, false);
    161                         bitmap.recycle();
    162                         final AdaptiveOutlineDrawable drawable = new AdaptiveOutlineDrawable(
    163                                 resources, resizedBitmap);
    164                         return new Pair<>(drawable, pair.second);
    165                     }
    166                 } catch (IOException e) {
    167                     Log.e(TAG, "Failed to get drawable for: " + iconUri, e);
    168                 }
    169             }
    170         }
    171 
    172         return new Pair<>(buildBtRainbowDrawable(context,
    173                 pair.first, cachedDevice.getAddress().hashCode()), pair.second);
    174     }
    175 
    176     /**
    177      * Build Bluetooth device icon with rainbow
    178      */
    179     public static Drawable buildBtRainbowDrawable(Context context, Drawable drawable,
    180             int hashCode) {
    181         final Resources resources = context.getResources();
    182 
    183         // Deal with normal headset
    184         final int[] iconFgColors = resources.getIntArray(R.array.bt_icon_fg_colors);
    185         final int[] iconBgColors = resources.getIntArray(R.array.bt_icon_bg_colors);
    186 
    187         // get color index based on mac address
    188         final int index = Math.abs(hashCode % iconBgColors.length);
    189         drawable.setTint(iconFgColors[index]);
    190         final Drawable adaptiveIcon = new AdaptiveIcon(context, drawable);
    191         ((AdaptiveIcon) adaptiveIcon).setBackgroundColor(iconBgColors[index]);
    192 
    193         return adaptiveIcon;
    194     }
    195 
    196     /**
    197      * Get boolean Bluetooth metadata
    198      *
    199      * @param bluetoothDevice the BluetoothDevice to get metadata
    200      * @param key key value within the list of BluetoothDevice.METADATA_*
    201      * @return the boolean metdata
    202      */
    203     public static boolean getBooleanMetaData(BluetoothDevice bluetoothDevice, int key) {
    204         if (bluetoothDevice == null) {
    205             return false;
    206         }
    207         final byte[] data = bluetoothDevice.getMetadata(key);
    208         if (data == null) {
    209             return false;
    210         }
    211         return Boolean.parseBoolean(new String(data));
    212     }
    213 
    214     /**
    215      * Get String Bluetooth metadata
    216      *
    217      * @param bluetoothDevice the BluetoothDevice to get metadata
    218      * @param key key value within the list of BluetoothDevice.METADATA_*
    219      * @return the String metdata
    220      */
    221     public static String getStringMetaData(BluetoothDevice bluetoothDevice, int key) {
    222         if (bluetoothDevice == null) {
    223             return null;
    224         }
    225         final byte[] data = bluetoothDevice.getMetadata(key);
    226         if (data == null) {
    227             return null;
    228         }
    229         return new String(data);
    230     }
    231 
    232     /**
    233      * Get integer Bluetooth metadata
    234      *
    235      * @param bluetoothDevice the BluetoothDevice to get metadata
    236      * @param key key value within the list of BluetoothDevice.METADATA_*
    237      * @return the int metdata
    238      */
    239     public static int getIntMetaData(BluetoothDevice bluetoothDevice, int key) {
    240         if (bluetoothDevice == null) {
    241             return META_INT_ERROR;
    242         }
    243         final byte[] data = bluetoothDevice.getMetadata(key);
    244         if (data == null) {
    245             return META_INT_ERROR;
    246         }
    247         try {
    248             return Integer.parseInt(new String(data));
    249         } catch (NumberFormatException e) {
    250             return META_INT_ERROR;
    251         }
    252     }
    253 
    254     /**
    255      * Get URI Bluetooth metadata
    256      *
    257      * @param bluetoothDevice the BluetoothDevice to get metadata
    258      * @param key key value within the list of BluetoothDevice.METADATA_*
    259      * @return the URI metdata
    260      */
    261     public static Uri getUriMetaData(BluetoothDevice bluetoothDevice, int key) {
    262         String data = getStringMetaData(bluetoothDevice, key);
    263         if (data == null) {
    264             return null;
    265         }
    266         return Uri.parse(data);
    267     }
    268 }
    269