Home | History | Annotate | Download | only in camera
      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 import com.android.gallery3d.exif.ExifInterface;
     34 
     35 import java.io.File;
     36 import java.io.FileOutputStream;
     37 
     38 public 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;
     53     public static final long LOW_STORAGE_THRESHOLD = 50000000;
     54 
     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,
     81             long date, Location location, int orientation, ExifInterface exif,
     82             byte[] jpeg, int width, int height) {
     83         // Save the image.
     84         String path = generateFilepath(title);
     85         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         }
     94         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 }
    182