1 /* 2 * Copyright (C) 2010 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.camera; 18 19 import android.annotation.TargetApi; 20 import android.content.ContentResolver; 21 import android.content.ContentValues; 22 import android.location.Location; 23 import android.net.Uri; 24 import android.os.Build; 25 import android.os.Environment; 26 import android.os.StatFs; 27 import android.provider.MediaStore.Images; 28 import android.provider.MediaStore.Images.ImageColumns; 29 import android.provider.MediaStore.MediaColumns; 30 import android.util.Log; 31 32 import com.android.gallery3d.common.ApiHelper; 33 34 import java.io.File; 35 import java.io.FileOutputStream; 36 37 public class Storage { 38 private static final String TAG = "CameraStorage"; 39 40 public static final String DCIM = 41 Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).toString(); 42 43 public static final String DIRECTORY = DCIM + "/Camera"; 44 45 // Match the code in MediaProvider.computeBucketValues(). 46 public static final String BUCKET_ID = 47 String.valueOf(DIRECTORY.toLowerCase().hashCode()); 48 49 public static final long UNAVAILABLE = -1L; 50 public static final long PREPARING = -2L; 51 public static final long UNKNOWN_SIZE = -3L; 52 public static final long LOW_STORAGE_THRESHOLD= 50000000; 53 54 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) setImageSize(ContentValues values, int width, int height)55 private static void setImageSize(ContentValues values, int width, int height) { 56 // The two fields are available since ICS but got published in JB 57 if (ApiHelper.HAS_MEDIA_COLUMNS_WIDTH_AND_HEIGHT) { 58 values.put(MediaColumns.WIDTH, width); 59 values.put(MediaColumns.HEIGHT, height); 60 } 61 } 62 writeFile(String path, byte[] data)63 public static void writeFile(String path, byte[] data) { 64 FileOutputStream out = null; 65 try { 66 out = new FileOutputStream(path); 67 out.write(data); 68 } catch (Exception e) { 69 Log.e(TAG, "Failed to write data", e); 70 } finally { 71 try { 72 out.close(); 73 } catch (Exception e) { 74 } 75 } 76 } 77 78 // Save the image and add it to media store. addImage(ContentResolver resolver, String title, long date, Location location, int orientation, byte[] jpeg, int width, int height)79 public static Uri addImage(ContentResolver resolver, String title, 80 long date, Location location, int orientation, byte[] jpeg, 81 int width, int height) { 82 // Save the image. 83 String path = generateFilepath(title); 84 writeFile(path, jpeg); 85 return addImage(resolver, title, date, location, orientation, 86 jpeg.length, path, width, height); 87 } 88 89 // Add the image to media store. addImage(ContentResolver resolver, String title, long date, Location location, int orientation, int jpegLength, String path, int width, int height)90 public static Uri addImage(ContentResolver resolver, String title, 91 long date, Location location, int orientation, int jpegLength, 92 String path, int width, int height) { 93 // Insert into MediaStore. 94 ContentValues values = new ContentValues(9); 95 values.put(ImageColumns.TITLE, title); 96 values.put(ImageColumns.DISPLAY_NAME, title + ".jpg"); 97 values.put(ImageColumns.DATE_TAKEN, date); 98 values.put(ImageColumns.MIME_TYPE, "image/jpeg"); 99 // Clockwise rotation in degrees. 0, 90, 180, or 270. 100 values.put(ImageColumns.ORIENTATION, orientation); 101 values.put(ImageColumns.DATA, path); 102 values.put(ImageColumns.SIZE, jpegLength); 103 104 setImageSize(values, width, height); 105 106 if (location != null) { 107 values.put(ImageColumns.LATITUDE, location.getLatitude()); 108 values.put(ImageColumns.LONGITUDE, location.getLongitude()); 109 } 110 111 Uri uri = null; 112 try { 113 uri = resolver.insert(Images.Media.EXTERNAL_CONTENT_URI, values); 114 } catch (Throwable th) { 115 // This can happen when the external volume is already mounted, but 116 // MediaScanner has not notify MediaProvider to add that volume. 117 // The picture is still safe and MediaScanner will find it and 118 // insert it into MediaProvider. The only problem is that the user 119 // cannot click the thumbnail to review the picture. 120 Log.e(TAG, "Failed to write MediaStore" + th); 121 } 122 return uri; 123 } 124 deleteImage(ContentResolver resolver, Uri uri)125 public static void deleteImage(ContentResolver resolver, Uri uri) { 126 try { 127 resolver.delete(uri, null, null); 128 } catch (Throwable th) { 129 Log.e(TAG, "Failed to delete image: " + uri); 130 } 131 } 132 generateFilepath(String title)133 public static String generateFilepath(String title) { 134 return DIRECTORY + '/' + title + ".jpg"; 135 } 136 getAvailableSpace()137 public static long getAvailableSpace() { 138 String state = Environment.getExternalStorageState(); 139 Log.d(TAG, "External storage state=" + state); 140 if (Environment.MEDIA_CHECKING.equals(state)) { 141 return PREPARING; 142 } 143 if (!Environment.MEDIA_MOUNTED.equals(state)) { 144 return UNAVAILABLE; 145 } 146 147 File dir = new File(DIRECTORY); 148 dir.mkdirs(); 149 if (!dir.isDirectory() || !dir.canWrite()) { 150 return UNAVAILABLE; 151 } 152 153 try { 154 StatFs stat = new StatFs(DIRECTORY); 155 return stat.getAvailableBlocks() * (long) stat.getBlockSize(); 156 } catch (Exception e) { 157 Log.i(TAG, "Fail to access external storage", e); 158 } 159 return UNKNOWN_SIZE; 160 } 161 162 /** 163 * OSX requires plugged-in USB storage to have path /DCIM/NNNAAAAA to be 164 * imported. This is a temporary fix for bug#1655552. 165 */ ensureOSXCompatible()166 public static void ensureOSXCompatible() { 167 File nnnAAAAA = new File(DCIM, "100ANDRO"); 168 if (!(nnnAAAAA.exists() || nnnAAAAA.mkdirs())) { 169 Log.e(TAG, "Failed to create " + nnnAAAAA.getPath()); 170 } 171 } 172 } 173