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