1 /* 2 * Copyright (C) 2019 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.bluetooth.avrcpcontroller; 18 19 import android.bluetooth.BluetoothDevice; 20 import android.content.Context; 21 import android.graphics.Bitmap; 22 import android.net.Uri; 23 import android.util.Log; 24 25 import java.util.Map; 26 import java.util.concurrent.ConcurrentHashMap; 27 28 /** 29 * An abstraction of the cover art image storage mechanism. 30 */ 31 public class AvrcpCoverArtStorage { 32 private static final String TAG = "AvrcpCoverArtStorage"; 33 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); 34 35 private final Context mContext; 36 37 /* Each device gets its own place to land images. This makes it easier to clean things up on a 38 * per device basis. This also allows us to be confident that acting on one device will not 39 * impact the images of another. 40 * 41 * The "landing place" is simply a map that will direct a given UUID to the proper bitmap image 42 */ 43 private final Map<BluetoothDevice, Map<String, Bitmap>> mDeviceImages = 44 new ConcurrentHashMap<>(1); 45 46 /** 47 * Create and initialize this Cover Art storage interface 48 */ AvrcpCoverArtStorage(Context context)49 public AvrcpCoverArtStorage(Context context) { 50 mContext = context; 51 } 52 53 /** 54 * Determine if an image already exists in storage 55 * 56 * @param device - The device the images was downloaded from 57 * @param imageUuid - The UUID that identifies the image 58 */ doesImageExist(BluetoothDevice device, String imageUuid)59 public boolean doesImageExist(BluetoothDevice device, String imageUuid) { 60 if (device == null || imageUuid == null || "".equals(imageUuid)) return false; 61 Map<String, Bitmap> images = mDeviceImages.get(device); 62 if (images == null) return false; 63 return images.containsKey(imageUuid); 64 } 65 66 /** 67 * Retrieve an image file from storage 68 * 69 * @param device - The device the images was downloaded from 70 * @param imageUuid - The UUID that identifies the image 71 * @return A Bitmap object of the image 72 */ getImage(BluetoothDevice device, String imageUuid)73 public Bitmap getImage(BluetoothDevice device, String imageUuid) { 74 if (device == null || imageUuid == null || "".equals(imageUuid)) return null; 75 Map<String, Bitmap> images = mDeviceImages.get(device); 76 if (images == null) return null; 77 return images.get(imageUuid); 78 } 79 80 /** 81 * Add an image to storage 82 * 83 * @param device - The device the images was downloaded from 84 * @param imageUuid - The UUID that identifies the image 85 * @param image - The image 86 */ addImage(BluetoothDevice device, String imageUuid, Bitmap image)87 public Uri addImage(BluetoothDevice device, String imageUuid, Bitmap image) { 88 debug("Storing image '" + imageUuid + "' from device " + device); 89 if (device == null || imageUuid == null || "".equals(imageUuid) || image == null) { 90 debug("Cannot store image. Improper aruguments"); 91 return null; 92 } 93 94 // A Thread safe way of creating a new UUID->Image set for a device. The putIfAbsent() 95 // function will return the value of the key if it wasn't absent. If it returns null, then 96 // there was no value there and we are to assume the reference we passed in was added. 97 Map<String, Bitmap> newImageSet = new ConcurrentHashMap<String, Bitmap>(1); 98 Map<String, Bitmap> images = mDeviceImages.putIfAbsent(device, newImageSet); 99 if (images == null) { 100 newImageSet.put(imageUuid, image); 101 } else { 102 images.put(imageUuid, image); 103 } 104 105 Uri uri = AvrcpCoverArtProvider.getImageUri(device, imageUuid); 106 mContext.getContentResolver().notifyChange(uri, null); 107 debug("Image '" + imageUuid + "' stored for device '" + device.getAddress() + "'"); 108 return uri; 109 } 110 111 /** 112 * Remove a specific image 113 * 114 * @param device The device the image belongs to 115 * @param imageUuid - The UUID that identifies the image 116 */ removeImage(BluetoothDevice device, String imageUuid)117 public void removeImage(BluetoothDevice device, String imageUuid) { 118 debug("Removing image '" + imageUuid + "' from device " + device); 119 if (device == null || imageUuid == null || "".equals(imageUuid)) return; 120 121 Map<String, Bitmap> images = mDeviceImages.get(device); 122 if (images == null) { 123 return; 124 } 125 126 images.remove(imageUuid); 127 if (images.size() == 0) { 128 mDeviceImages.remove(device); 129 } 130 131 debug("Image '" + imageUuid + "' removed for device '" + device.getAddress() + "'"); 132 } 133 134 /** 135 * Remove all stored images associated with a device 136 * 137 * @param device The device you wish to have images removed for 138 */ removeImagesForDevice(BluetoothDevice device)139 public void removeImagesForDevice(BluetoothDevice device) { 140 if (device == null) return; 141 debug("Remove cover art for device " + device.getAddress()); 142 mDeviceImages.remove(device); 143 } 144 145 /** 146 * Clear the entirety of storage 147 */ clear()148 public void clear() { 149 debug("Clearing all images"); 150 mDeviceImages.clear(); 151 } 152 153 @Override toString()154 public String toString() { 155 String s = "CoverArtStorage:\n"; 156 for (BluetoothDevice device : mDeviceImages.keySet()) { 157 Map<String, Bitmap> images = mDeviceImages.get(device); 158 s += " " + device.getAddress() + " (" + images.size() + "):"; 159 for (String uuid : images.keySet()) { 160 s += "\n " + uuid; 161 } 162 s += "\n"; 163 } 164 return s; 165 } 166 debug(String msg)167 private void debug(String msg) { 168 if (DBG) { 169 Log.d(TAG, msg); 170 } 171 } 172 error(String msg)173 private void error(String msg) { 174 Log.e(TAG, msg); 175 } 176 } 177