SaveCopyTask.java revision 0addfc7f6342184a67cdd8b5cc3872c6a5c87e55
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 17package com.android.gallery3d.filtershow.tools; 18 19import android.content.ContentResolver; 20import android.content.ContentValues; 21import android.content.Context; 22import android.database.Cursor; 23import android.graphics.Bitmap; 24import android.graphics.Bitmap.CompressFormat; 25import android.net.Uri; 26import android.os.AsyncTask; 27import android.os.Environment; 28import android.provider.MediaStore.Images; 29import android.provider.MediaStore.Images.ImageColumns; 30import android.view.Gravity; 31import android.widget.Toast; 32 33import com.android.camera.R; 34import com.android.gallery3d.filtershow.presets.ImagePreset; 35 36//import com.android.gallery3d.R; 37//import com.android.gallery3d.util.BucketNames; 38 39import java.io.Closeable; 40import java.io.File; 41import java.io.FileNotFoundException; 42import java.io.FileOutputStream; 43import java.io.IOException; 44import java.io.OutputStream; 45import java.sql.Date; 46import java.text.SimpleDateFormat; 47 48/** 49 * Asynchronous task for saving edited photo as a new copy. 50 */ 51public class SaveCopyTask extends AsyncTask<ProcessedBitmap, Void, Uri> { 52 53 private static final int DEFAULT_COMPRESS_QUALITY = 95; 54 private static final String DEFAULT_SAVE_DIRECTORY = "EditedOnlinePhotos"; 55 56 /** 57 * Saves the bitmap in the final destination 58 */ 59 public static void saveBitmap(Bitmap bitmap, File destination) { 60 OutputStream os = null; 61 try { 62 os = new FileOutputStream(destination); 63 bitmap.compress(CompressFormat.JPEG, DEFAULT_COMPRESS_QUALITY, os); 64 } catch (FileNotFoundException e) { 65 e.printStackTrace(); 66 } finally { 67 closeStream(os); 68 } 69 } 70 71 private static void closeStream(Closeable stream) { 72 if (stream != null) { 73 try { 74 stream.close(); 75 } catch (IOException e) { 76 e.printStackTrace(); 77 } 78 } 79 } 80 81 /** 82 * Callback for the completed asynchronous task. 83 */ 84 public interface Callback { 85 86 void onComplete(Uri result); 87 } 88 89 private interface ContentResolverQueryCallback { 90 91 void onCursorResult(Cursor cursor); 92 } 93 94 private static final String TIME_STAMP_NAME = "'IMG'_yyyyMMdd_HHmmss"; 95 96 private final Context context; 97 private final Uri sourceUri; 98 private final Callback callback; 99 private final String saveFileName; 100 private final File destinationFile; 101 102 public SaveCopyTask(Context context, Uri sourceUri, File destination, Callback callback) { 103 this.context = context; 104 this.sourceUri = sourceUri; 105 this.callback = callback; 106 107 if (destination == null) { 108 this.destinationFile = getNewFile(context, sourceUri); 109 } else { 110 this.destinationFile = destination; 111 } 112 113 saveFileName = new SimpleDateFormat(TIME_STAMP_NAME).format(new Date( 114 System.currentTimeMillis())); 115 } 116 117 public static File getFinalSaveDirectory(Context context, Uri sourceUri) { 118 File saveDirectory = getSaveDirectory(context, sourceUri); 119 if ((saveDirectory == null) || !saveDirectory.canWrite()) { 120 saveDirectory = new File(Environment.getExternalStorageDirectory(), 121 DEFAULT_SAVE_DIRECTORY); 122 } 123 return saveDirectory; 124 } 125 126 public static File getNewFile(Context context, Uri sourceUri) { 127 File saveDirectory = getFinalSaveDirectory(context, sourceUri); 128 String filename = new SimpleDateFormat(TIME_STAMP_NAME).format(new Date( 129 System.currentTimeMillis())); 130 return new File(saveDirectory, filename + ".JPG"); 131 } 132 133 /** 134 * The task should be executed with one given bitmap to be saved. 135 */ 136 @Override 137 protected Uri doInBackground(ProcessedBitmap... params) { 138 // TODO: Support larger dimensions for photo saving. 139 if (params[0] == null) { 140 return null; 141 } 142 143 ProcessedBitmap processedBitmap = params[0]; 144 145 Bitmap bitmap = processedBitmap.apply(); 146 saveBitmap(bitmap, this.destinationFile); 147 148 Uri uri = insertContent(context, sourceUri, this.destinationFile, saveFileName); 149 bitmap.recycle(); 150 return uri; 151 } 152 153 @Override 154 protected void onPostExecute(Uri result) { 155 if (callback != null) { 156 callback.onComplete(result); 157 } 158 } 159 160 private static void querySource(Context context, Uri sourceUri, String[] projection, 161 ContentResolverQueryCallback callback) { 162 ContentResolver contentResolver = context.getContentResolver(); 163 Cursor cursor = null; 164 try { 165 cursor = contentResolver.query(sourceUri, projection, null, null, 166 null); 167 if ((cursor != null) && cursor.moveToNext()) { 168 callback.onCursorResult(cursor); 169 } 170 } catch (Exception e) { 171 // Ignore error for lacking the data column from the source. 172 } finally { 173 if (cursor != null) { 174 cursor.close(); 175 } 176 } 177 } 178 179 private static File getSaveDirectory(Context context, Uri sourceUri) { 180 final File[] dir = new File[1]; 181 querySource(context, sourceUri, new String[] { 182 ImageColumns.DATA 183 }, 184 new ContentResolverQueryCallback() { 185 186 @Override 187 public void onCursorResult(Cursor cursor) { 188 dir[0] = new File(cursor.getString(0)).getParentFile(); 189 } 190 }); 191 return dir[0]; 192 } 193 194 /** 195 * Insert the content (saved file) with proper source photo properties. 196 */ 197 public static Uri insertContent(Context context, Uri sourceUri, File file, String saveFileName) { 198 long now = System.currentTimeMillis() / 1000; 199 200 final ContentValues values = new ContentValues(); 201 values.put(Images.Media.TITLE, saveFileName); 202 values.put(Images.Media.DISPLAY_NAME, file.getName()); 203 values.put(Images.Media.MIME_TYPE, "image/jpeg"); 204 values.put(Images.Media.DATE_TAKEN, now); 205 values.put(Images.Media.DATE_MODIFIED, now); 206 values.put(Images.Media.DATE_ADDED, now); 207 values.put(Images.Media.ORIENTATION, 0); 208 values.put(Images.Media.DATA, file.getAbsolutePath()); 209 values.put(Images.Media.SIZE, file.length()); 210 211 String[] projection = new String[] { 212 ImageColumns.DATE_TAKEN, 213 ImageColumns.LATITUDE, ImageColumns.LONGITUDE, 214 }; 215 querySource(context, sourceUri, projection, new ContentResolverQueryCallback() { 216 217 @Override 218 public void onCursorResult(Cursor cursor) { 219 values.put(Images.Media.DATE_TAKEN, cursor.getLong(0)); 220 221 double latitude = cursor.getDouble(1); 222 double longitude = cursor.getDouble(2); 223 // TODO: Change || to && after the default location issue is 224 // fixed. 225 if ((latitude != 0f) || (longitude != 0f)) { 226 values.put(Images.Media.LATITUDE, latitude); 227 values.put(Images.Media.LONGITUDE, longitude); 228 } 229 } 230 }); 231 232 return context.getContentResolver().insert( 233 Images.Media.EXTERNAL_CONTENT_URI, values); 234 } 235 236} 237