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.os.Environment; 24 import android.util.Log; 25 26 import java.io.File; 27 import java.io.FileOutputStream; 28 import java.io.IOException; 29 30 /** 31 * An abstraction of the file system storage of the downloaded cover art images. 32 */ 33 public class AvrcpCoverArtStorage { 34 private static final String TAG = "AvrcpCoverArtStorage"; 35 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); 36 37 private final Context mContext; 38 39 /** 40 * Create and initialize this Cover Art storage interface 41 */ AvrcpCoverArtStorage(Context context)42 public AvrcpCoverArtStorage(Context context) { 43 mContext = context; 44 } 45 46 /** 47 * Determine if an image already exists in storage 48 * 49 * @param device - The device the images was downloaded from 50 * @param imageHandle - The handle that identifies the image 51 */ doesImageExist(BluetoothDevice device, String imageHandle)52 public boolean doesImageExist(BluetoothDevice device, String imageHandle) { 53 if (device == null || imageHandle == null || "".equals(imageHandle)) return false; 54 String path = getImagePath(device, imageHandle); 55 if (path == null) return false; 56 File file = new File(path); 57 return file.exists(); 58 } 59 60 /** 61 * Retrieve an image file from storage 62 * 63 * @param device - The device the images was downloaded from 64 * @param imageHandle - The handle that identifies the image 65 * @return A file descriptor for the image 66 */ getImageFile(BluetoothDevice device, String imageHandle)67 public File getImageFile(BluetoothDevice device, String imageHandle) { 68 if (device == null || imageHandle == null || "".equals(imageHandle)) return null; 69 String path = getImagePath(device, imageHandle); 70 if (path == null) return null; 71 File file = new File(path); 72 return file.exists() ? file : null; 73 } 74 75 /** 76 * Add an image to storage 77 * 78 * @param device - The device the images was downloaded from 79 * @param imageHandle - The handle that identifies the image 80 * @param image - The image 81 */ addImage(BluetoothDevice device, String imageHandle, Bitmap image)82 public Uri addImage(BluetoothDevice device, String imageHandle, Bitmap image) { 83 debug("Storing image '" + imageHandle + "' from device " + device); 84 if (device == null || imageHandle == null || "".equals(imageHandle) || image == null) { 85 debug("Cannot store image. Improper aruguments"); 86 return null; 87 } 88 89 String path = getImagePath(device, imageHandle); 90 if (path == null) { 91 error("Cannot store image. Cannot provide a valid path to storage"); 92 return null; 93 } 94 95 try { 96 String deviceDirectoryPath = getDevicePath(device); 97 if (deviceDirectoryPath == null) { 98 error("Cannot store image. Cannot get a valid path to per-device storage"); 99 return null; 100 } 101 File deviceDirectory = new File(deviceDirectoryPath); 102 if (!deviceDirectory.exists()) { 103 deviceDirectory.mkdirs(); 104 } 105 106 FileOutputStream outputStream = new FileOutputStream(path); 107 image.compress(Bitmap.CompressFormat.PNG, 100, outputStream); 108 outputStream.flush(); 109 outputStream.close(); 110 } catch (IOException e) { 111 error("Failed to store '" + imageHandle + "' to '" + path + "'"); 112 return null; 113 } 114 Uri uri = AvrcpCoverArtProvider.getImageUri(device, imageHandle); 115 mContext.getContentResolver().notifyChange(uri, null); 116 debug("Image stored at '" + path + "'"); 117 return uri; 118 } 119 120 /** 121 * Remove a specific image 122 * 123 * @param device The device you wish to have images removed for 124 * @param imageHandle The handle that identifies the image to delete 125 */ removeImage(BluetoothDevice device, String imageHandle)126 public void removeImage(BluetoothDevice device, String imageHandle) { 127 debug("Removing image '" + imageHandle + "' from device " + device); 128 if (device == null || imageHandle == null || "".equals(imageHandle)) return; 129 String path = getImagePath(device, imageHandle); 130 if (path == null) { 131 error("Cannot remove image. Cannot get a valid path to storage"); 132 return; 133 } 134 File file = new File(path); 135 if (!file.exists()) return; 136 file.delete(); 137 debug("Image deleted at '" + path + "'"); 138 } 139 140 /** 141 * Remove all stored images associated with a device 142 * 143 * @param device The device you wish to have images removed for 144 */ removeImagesForDevice(BluetoothDevice device)145 public void removeImagesForDevice(BluetoothDevice device) { 146 if (device == null) return; 147 debug("Remove cover art for device " + device.getAddress()); 148 String deviceDirectoryPath = getDevicePath(device); 149 if (deviceDirectoryPath == null) { 150 error("Cannot remove images for device. Cannot get a valid path to storage"); 151 return; 152 } 153 File deviceDirectory = new File(deviceDirectoryPath); 154 deleteStorageDirectory(deviceDirectory); 155 } 156 157 /** 158 * Clear the entirety of storage 159 */ clear()160 public void clear() { 161 String storageDirectoryPath = getStorageDirectory(); 162 if (storageDirectoryPath == null) { 163 error("Cannot remove images, cannot get a valid path to storage. Is it mounted?"); 164 return; 165 } 166 File storageDirectory = new File(storageDirectoryPath); 167 deleteStorageDirectory(storageDirectory); 168 } 169 getStorageDirectory()170 private String getStorageDirectory() { 171 String dir = null; 172 if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { 173 dir = mContext.getExternalFilesDir(null).getAbsolutePath() + "/coverart"; 174 } else { 175 error("Cannot get storage directory, state=" + Environment.getExternalStorageState()); 176 } 177 return dir; 178 } 179 getDevicePath(BluetoothDevice device)180 private String getDevicePath(BluetoothDevice device) { 181 String storageDir = getStorageDirectory(); 182 if (storageDir == null) return null; 183 return storageDir + "/" + device.getAddress().replace(":", ""); 184 } 185 getImagePath(BluetoothDevice device, String imageHandle)186 private String getImagePath(BluetoothDevice device, String imageHandle) { 187 String deviceDir = getDevicePath(device); 188 if (deviceDir == null) return null; 189 return deviceDir + "/" + imageHandle + ".png"; 190 } 191 deleteStorageDirectory(File directory)192 private void deleteStorageDirectory(File directory) { 193 if (directory == null) { 194 error("Cannot delete directory, file is null"); 195 return; 196 } 197 if (!directory.exists()) return; 198 File[] files = directory.listFiles(); 199 if (files == null) { 200 return; 201 } 202 for (int i = 0; i < files.length; i++) { 203 debug("Deleting " + files[i].getAbsolutePath()); 204 if (files[i].isDirectory()) { 205 deleteStorageDirectory(files[i]); 206 } else { 207 files[i].delete(); 208 } 209 } 210 directory.delete(); 211 } 212 213 @Override toString()214 public String toString() { 215 String s = "CoverArtStorage:\n"; 216 String storageDirectory = getStorageDirectory(); 217 s += " Storage Directory: " + storageDirectory + "\n"; 218 if (storageDirectory == null) { 219 return s; 220 } 221 222 File storage = new File(storageDirectory); 223 File[] devices = storage.listFiles(); 224 if (devices != null) { 225 for (File deviceDirectory : devices) { 226 s += " " + deviceDirectory.getName() + ":\n"; 227 File[] images = deviceDirectory.listFiles(); 228 if (images == null) continue; 229 for (File image : images) { 230 s += " " + image.getName() + "\n"; 231 } 232 } 233 } 234 return s; 235 } 236 debug(String msg)237 private void debug(String msg) { 238 if (DBG) { 239 Log.d(TAG, msg); 240 } 241 } 242 error(String msg)243 private void error(String msg) { 244 Log.e(TAG, msg); 245 } 246 } 247