Home | History | Annotate | Download | only in devcamera
      1 /*
      2  * Copyright (C) 2016 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 package com.android.devcamera;
     17 
     18 import android.content.ContentResolver;
     19 import android.content.ContentValues;
     20 import android.content.Context;
     21 import android.content.SharedPreferences;
     22 import android.os.SystemClock;
     23 import android.provider.MediaStore;
     24 import android.util.Log;
     25 
     26 import java.io.File;
     27 import java.io.FileOutputStream;
     28 import java.io.IOException;
     29 import java.io.OutputStream;
     30 import java.nio.ByteBuffer;
     31 import java.nio.channels.FileChannel;
     32 
     33 /**
     34  * This class has methods required to save a JPEG to disk as well as update the
     35  * MediaStore database.
     36  */
     37 
     38 
     39 public class MediaSaver {
     40     private static final String TAG = "Snappy_MediaSaver";
     41     private static final String MY_PREFS_NAME = "SnappyPrefs";
     42 
     43     // MediaStore is slow/broken
     44     private static final boolean UDPATE_MEDIA_STORE = true;
     45 
     46 
     47     public static int getNextInt(Context context, String id) {
     48         SharedPreferences prefs = context.getSharedPreferences(MY_PREFS_NAME, Context.MODE_PRIVATE);
     49         int i = prefs.getInt(id, 1);
     50         SharedPreferences.Editor editor = prefs.edit();
     51         editor.putInt(id, i+1);
     52         editor.commit();
     53         return i;
     54     }
     55 
     56     /**
     57      * @param context Application context.
     58      * @param depthCloudData Depth cloud byte buffer.
     59      */
     60     public static String saveDepth(Context context, ByteBuffer depthCloudData) {
     61         String filename = "";
     62         try {
     63             File file;
     64             int i = getNextInt(context, "depthCounter");
     65             filename = String.format("/sdcard/DCIM/Depth_%05d.img", i);
     66             file = new File(filename);
     67             if (!file.createNewFile()) {
     68                 throw new IOException(filename);
     69             }
     70 
     71             long t0 = SystemClock.uptimeMillis();
     72             FileOutputStream fos = new FileOutputStream(file);
     73             FileChannel channel = fos.getChannel();
     74             int bytesWritten = 0;
     75             int byteCount = 0;
     76             while (depthCloudData.hasRemaining()) {
     77                 byteCount = channel.write(depthCloudData);
     78                 if (0 == byteCount) {
     79                     throw new IOException(filename);
     80                 } else {
     81                     bytesWritten += byteCount;
     82                 }
     83             }
     84             channel.close();
     85             fos.flush();
     86             fos.close();
     87             long t1 = SystemClock.uptimeMillis();
     88 
     89             Log.v(TAG, String.format("Wrote Depth %d bytes as %s in %.3f seconds",
     90                     bytesWritten, file, (t1 - t0) * 0.001));
     91         } catch (IOException e) {
     92             Log.e(TAG, "Error creating new file: ", e);
     93         }
     94         return filename;
     95     }
     96 
     97     /**
     98      * @param context Application context.
     99      * @param jpegData JPEG byte stream.
    100      */
    101     public static String saveJpeg(Context context, byte[] jpegData, ContentResolver resolver) {
    102         String filename = "";
    103         try {
    104             File file;
    105             while (true) {
    106                 int i = getNextInt(context, "counter");
    107                 filename = String.format("/sdcard/DCIM/Camera/SNAP_%05d.JPG", i);
    108                 file = new File(filename);
    109                 if (file.createNewFile()) {
    110                     break;
    111                 }
    112             }
    113 
    114             long t0 = SystemClock.uptimeMillis();
    115             OutputStream os = new FileOutputStream(file);
    116             os.write(jpegData);
    117             os.flush();
    118             os.close();
    119             long t1 = SystemClock.uptimeMillis();
    120 
    121             // update MediaStore so photos apps can find photos right away.
    122             if (UDPATE_MEDIA_STORE) {
    123                 // really slow for some reason: MediaStore.Images.Media.insertImage(resolver, file.getAbsolutePath(), file.getName(), file.getName());
    124                 insertImage(resolver, file);
    125             }
    126             long t2 = SystemClock.uptimeMillis();
    127 
    128             Log.v(TAG, String.format("Wrote JPEG %d bytes as %s in %.3f seconds; mediastore update = %.3f secs",
    129                     jpegData.length, file, (t1 - t0) * 0.001, (t2 - t1) * 0.001)    );
    130         } catch (IOException e) {
    131             Log.e(TAG, "Error creating new file: ", e);
    132         }
    133         return filename;
    134     }
    135 
    136 
    137     // We use this instead of MediaStore.Images.Media.insertImage() because we want to add date metadata
    138     public static void insertImage(ContentResolver cr, File file) {
    139 
    140         ContentValues values = new ContentValues();
    141         values.put(MediaStore.Images.Media.TITLE, file.getName());
    142         values.put(MediaStore.Images.Media.DISPLAY_NAME, file.getName());
    143         values.put(MediaStore.Images.Media.DESCRIPTION, file.getName());
    144         values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
    145         values.put(MediaStore.Images.Media.DATA, file.getAbsolutePath());
    146         // Add the date meta data to ensure the image is added at the front of the gallery
    147         values.put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis());
    148         values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
    149 
    150         try {
    151             cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
    152         } catch (Exception e) {
    153             Log.w(TAG, "Error updating media store for  " + file, e);
    154         }
    155     }
    156 
    157 }
    158