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