10d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard/* 20d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard * Copyright (C) 2010 The Android Open Source Project 30d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard * 40d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard * Licensed under the Apache License, Version 2.0 (the "License"); 50d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard * you may not use this file except in compliance with the License. 60d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard * You may obtain a copy of the License at 70d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard * 80d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard * http://www.apache.org/licenses/LICENSE-2.0 90d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard * 100d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard * Unless required by applicable law or agreed to in writing, software 110d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard * distributed under the License is distributed on an "AS IS" BASIS, 120d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 130d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard * See the License for the specific language governing permissions and 140d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard * limitations under the License. 150d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard */ 160d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 170d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardpackage com.android.gallery3d.filtershow.tools; 180d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 190d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardimport android.content.ContentResolver; 200d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardimport android.content.ContentValues; 210d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardimport android.content.Context; 220d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardimport android.database.Cursor; 230d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardimport android.graphics.Bitmap; 24014a111f10a7523415ca210bc6fe1e4f00183d31John Reckimport android.graphics.BitmapFactory; 250d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardimport android.graphics.Bitmap.CompressFormat; 26014a111f10a7523415ca210bc6fe1e4f00183d31John Reckimport android.media.ExifInterface; 270d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardimport android.net.Uri; 280d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardimport android.os.AsyncTask; 290d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardimport android.os.Environment; 300d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardimport android.provider.MediaStore.Images; 310d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardimport android.provider.MediaStore.Images.ImageColumns; 32014a111f10a7523415ca210bc6fe1e4f00183d31John Reckimport android.util.Log; 330d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 34014a111f10a7523415ca210bc6fe1e4f00183d31John Reckimport com.android.gallery3d.filtershow.cache.ImageLoader; 350ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroardimport com.android.gallery3d.filtershow.presets.ImagePreset; 36014a111f10a7523415ca210bc6fe1e4f00183d31John Reckimport com.android.gallery3d.util.XmpUtilHelper; 370d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 380d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardimport java.io.Closeable; 390d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardimport java.io.File; 400d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardimport java.io.FileNotFoundException; 410d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardimport java.io.FileOutputStream; 420d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardimport java.io.IOException; 43014a111f10a7523415ca210bc6fe1e4f00183d31John Reckimport java.io.InputStream; 440d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardimport java.io.OutputStream; 450d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardimport java.sql.Date; 460d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardimport java.text.SimpleDateFormat; 470d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 480d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard/** 490d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard * Asynchronous task for saving edited photo as a new copy. 500d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard */ 51014a111f10a7523415ca210bc6fe1e4f00183d31John Reckpublic class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> { 52014a111f10a7523415ca210bc6fe1e4f00183d31John Reck 530d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 54014a111f10a7523415ca210bc6fe1e4f00183d31John Reck private static final String LOGTAG = "SaveCopyTask"; 550d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard private static final int DEFAULT_COMPRESS_QUALITY = 95; 560addfc7f6342184a67cdd8b5cc3872c6a5c87e55Doris Liu private static final String DEFAULT_SAVE_DIRECTORY = "EditedOnlinePhotos"; 570d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 580d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard /** 590ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard * Saves the bitmap in the final destination 600d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard */ 61014a111f10a7523415ca210bc6fe1e4f00183d31John Reck public static void saveBitmap(Bitmap bitmap, File destination, Object xmp) { 620d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard OutputStream os = null; 630d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard try { 640ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard os = new FileOutputStream(destination); 650ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard bitmap.compress(CompressFormat.JPEG, DEFAULT_COMPRESS_QUALITY, os); 660d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } catch (FileNotFoundException e) { 67014a111f10a7523415ca210bc6fe1e4f00183d31John Reck Log.v(LOGTAG,"Error in writing "+destination.getAbsolutePath()); 680d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } finally { 690d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard closeStream(os); 700d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } 71014a111f10a7523415ca210bc6fe1e4f00183d31John Reck if (xmp != null) { 72014a111f10a7523415ca210bc6fe1e4f00183d31John Reck XmpUtilHelper.writeXMPMeta(destination.getAbsolutePath(), xmp); 73014a111f10a7523415ca210bc6fe1e4f00183d31John Reck } 740d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } 750d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 760ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard private static void closeStream(Closeable stream) { 770d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard if (stream != null) { 780d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard try { 790d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard stream.close(); 800d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } catch (IOException e) { 810d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard e.printStackTrace(); 820d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } 830d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } 840d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } 850d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 860d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard /** 870d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard * Callback for the completed asynchronous task. 880d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard */ 890d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard public interface Callback { 900d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 910d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard void onComplete(Uri result); 920d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } 930d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 940d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard private interface ContentResolverQueryCallback { 950d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 960d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard void onCursorResult(Cursor cursor); 970d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } 980d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 990d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard private static final String TIME_STAMP_NAME = "'IMG'_yyyyMMdd_HHmmss"; 1000d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 1010d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard private final Context context; 1020d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard private final Uri sourceUri; 1030d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard private final Callback callback; 1040d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard private final String saveFileName; 1050ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard private final File destinationFile; 1060d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 1070ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard public SaveCopyTask(Context context, Uri sourceUri, File destination, Callback callback) { 1080d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard this.context = context; 1090d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard this.sourceUri = sourceUri; 1100d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard this.callback = callback; 1110d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 1120ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard if (destination == null) { 1130ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard this.destinationFile = getNewFile(context, sourceUri); 1140ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard } else { 1150ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard this.destinationFile = destination; 1160ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard } 1170ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard 1180d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard saveFileName = new SimpleDateFormat(TIME_STAMP_NAME).format(new Date( 1190d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard System.currentTimeMillis())); 1200d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } 1210d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 1220addfc7f6342184a67cdd8b5cc3872c6a5c87e55Doris Liu public static File getFinalSaveDirectory(Context context, Uri sourceUri) { 1230ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard File saveDirectory = getSaveDirectory(context, sourceUri); 1240ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard if ((saveDirectory == null) || !saveDirectory.canWrite()) { 1250ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard saveDirectory = new File(Environment.getExternalStorageDirectory(), 1260addfc7f6342184a67cdd8b5cc3872c6a5c87e55Doris Liu DEFAULT_SAVE_DIRECTORY); 1270ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard } 12841a99ab167b83fc41d604c82ceda33a875883e98Doris Liu // Create the directory if it doesn't exist 12941a99ab167b83fc41d604c82ceda33a875883e98Doris Liu if (!saveDirectory.exists()) saveDirectory.mkdirs(); 1300addfc7f6342184a67cdd8b5cc3872c6a5c87e55Doris Liu return saveDirectory; 1310addfc7f6342184a67cdd8b5cc3872c6a5c87e55Doris Liu } 1320ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard 1330addfc7f6342184a67cdd8b5cc3872c6a5c87e55Doris Liu public static File getNewFile(Context context, Uri sourceUri) { 1340addfc7f6342184a67cdd8b5cc3872c6a5c87e55Doris Liu File saveDirectory = getFinalSaveDirectory(context, sourceUri); 1350ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard String filename = new SimpleDateFormat(TIME_STAMP_NAME).format(new Date( 1360ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard System.currentTimeMillis())); 1370ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard return new File(saveDirectory, filename + ".JPG"); 1380ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard } 1390ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard 140014a111f10a7523415ca210bc6fe1e4f00183d31John Reck private Bitmap loadMutableBitmap() throws FileNotFoundException { 141014a111f10a7523415ca210bc6fe1e4f00183d31John Reck BitmapFactory.Options options = new BitmapFactory.Options(); 142014a111f10a7523415ca210bc6fe1e4f00183d31John Reck // TODO: on <3.x we need a copy of the bitmap (inMutable doesn't 143014a111f10a7523415ca210bc6fe1e4f00183d31John Reck // exist) 144014a111f10a7523415ca210bc6fe1e4f00183d31John Reck options.inMutable = true; 145014a111f10a7523415ca210bc6fe1e4f00183d31John Reck 146014a111f10a7523415ca210bc6fe1e4f00183d31John Reck InputStream is = context.getContentResolver().openInputStream(sourceUri); 147014a111f10a7523415ca210bc6fe1e4f00183d31John Reck Bitmap bitmap = BitmapFactory.decodeStream(is, null, options); 148014a111f10a7523415ca210bc6fe1e4f00183d31John Reck int orientation = ImageLoader.getOrientation(context, sourceUri); 149014a111f10a7523415ca210bc6fe1e4f00183d31John Reck bitmap = ImageLoader.rotateToPortrait(bitmap, orientation); 150014a111f10a7523415ca210bc6fe1e4f00183d31John Reck return bitmap; 151014a111f10a7523415ca210bc6fe1e4f00183d31John Reck } 152014a111f10a7523415ca210bc6fe1e4f00183d31John Reck 153014a111f10a7523415ca210bc6fe1e4f00183d31John Reck private static final String[] COPY_EXIF_ATTRIBUTES = new String[] { 154014a111f10a7523415ca210bc6fe1e4f00183d31John Reck ExifInterface.TAG_APERTURE, 155014a111f10a7523415ca210bc6fe1e4f00183d31John Reck ExifInterface.TAG_DATETIME, 156014a111f10a7523415ca210bc6fe1e4f00183d31John Reck ExifInterface.TAG_EXPOSURE_TIME, 157014a111f10a7523415ca210bc6fe1e4f00183d31John Reck ExifInterface.TAG_FLASH, 158014a111f10a7523415ca210bc6fe1e4f00183d31John Reck ExifInterface.TAG_FOCAL_LENGTH, 159014a111f10a7523415ca210bc6fe1e4f00183d31John Reck ExifInterface.TAG_GPS_ALTITUDE, 160014a111f10a7523415ca210bc6fe1e4f00183d31John Reck ExifInterface.TAG_GPS_ALTITUDE_REF, 161014a111f10a7523415ca210bc6fe1e4f00183d31John Reck ExifInterface.TAG_GPS_DATESTAMP, 162014a111f10a7523415ca210bc6fe1e4f00183d31John Reck ExifInterface.TAG_GPS_LATITUDE, 163014a111f10a7523415ca210bc6fe1e4f00183d31John Reck ExifInterface.TAG_GPS_LATITUDE_REF, 164014a111f10a7523415ca210bc6fe1e4f00183d31John Reck ExifInterface.TAG_GPS_LONGITUDE, 165014a111f10a7523415ca210bc6fe1e4f00183d31John Reck ExifInterface.TAG_GPS_LONGITUDE_REF, 166014a111f10a7523415ca210bc6fe1e4f00183d31John Reck ExifInterface.TAG_GPS_PROCESSING_METHOD, 167014a111f10a7523415ca210bc6fe1e4f00183d31John Reck ExifInterface.TAG_GPS_DATESTAMP, 168014a111f10a7523415ca210bc6fe1e4f00183d31John Reck ExifInterface.TAG_ISO, 169014a111f10a7523415ca210bc6fe1e4f00183d31John Reck ExifInterface.TAG_MAKE, 170014a111f10a7523415ca210bc6fe1e4f00183d31John Reck ExifInterface.TAG_MODEL, 171014a111f10a7523415ca210bc6fe1e4f00183d31John Reck ExifInterface.TAG_WHITE_BALANCE, 172014a111f10a7523415ca210bc6fe1e4f00183d31John Reck }; 173014a111f10a7523415ca210bc6fe1e4f00183d31John Reck 174014a111f10a7523415ca210bc6fe1e4f00183d31John Reck private static void copyExif(String sourcePath, String destPath) { 175014a111f10a7523415ca210bc6fe1e4f00183d31John Reck try { 176014a111f10a7523415ca210bc6fe1e4f00183d31John Reck ExifInterface source = new ExifInterface(sourcePath); 177014a111f10a7523415ca210bc6fe1e4f00183d31John Reck ExifInterface dest = new ExifInterface(destPath); 178014a111f10a7523415ca210bc6fe1e4f00183d31John Reck boolean needsSave = false; 179014a111f10a7523415ca210bc6fe1e4f00183d31John Reck for (String tag : COPY_EXIF_ATTRIBUTES) { 180014a111f10a7523415ca210bc6fe1e4f00183d31John Reck String value = source.getAttribute(tag); 181014a111f10a7523415ca210bc6fe1e4f00183d31John Reck if (value != null) { 182014a111f10a7523415ca210bc6fe1e4f00183d31John Reck needsSave = true; 183014a111f10a7523415ca210bc6fe1e4f00183d31John Reck dest.setAttribute(tag, value); 184014a111f10a7523415ca210bc6fe1e4f00183d31John Reck } 185014a111f10a7523415ca210bc6fe1e4f00183d31John Reck } 186014a111f10a7523415ca210bc6fe1e4f00183d31John Reck if (needsSave) { 187014a111f10a7523415ca210bc6fe1e4f00183d31John Reck dest.saveAttributes(); 188014a111f10a7523415ca210bc6fe1e4f00183d31John Reck } 189014a111f10a7523415ca210bc6fe1e4f00183d31John Reck } catch (IOException ex) { 190014a111f10a7523415ca210bc6fe1e4f00183d31John Reck Log.w(LOGTAG, "Failed to copy exif metadata", ex); 191014a111f10a7523415ca210bc6fe1e4f00183d31John Reck } 192014a111f10a7523415ca210bc6fe1e4f00183d31John Reck } 193014a111f10a7523415ca210bc6fe1e4f00183d31John Reck 194014a111f10a7523415ca210bc6fe1e4f00183d31John Reck private void copyExif(Uri sourceUri, String destPath) { 195014a111f10a7523415ca210bc6fe1e4f00183d31John Reck if (ContentResolver.SCHEME_FILE.equals(sourceUri.getScheme())) { 196014a111f10a7523415ca210bc6fe1e4f00183d31John Reck copyExif(sourceUri.getPath(), destPath); 197014a111f10a7523415ca210bc6fe1e4f00183d31John Reck return; 198014a111f10a7523415ca210bc6fe1e4f00183d31John Reck } 199014a111f10a7523415ca210bc6fe1e4f00183d31John Reck 200014a111f10a7523415ca210bc6fe1e4f00183d31John Reck final String[] PROJECTION = new String[] { 201014a111f10a7523415ca210bc6fe1e4f00183d31John Reck ImageColumns.DATA 202014a111f10a7523415ca210bc6fe1e4f00183d31John Reck }; 203014a111f10a7523415ca210bc6fe1e4f00183d31John Reck try { 204014a111f10a7523415ca210bc6fe1e4f00183d31John Reck Cursor c = context.getContentResolver().query(sourceUri, PROJECTION, 205014a111f10a7523415ca210bc6fe1e4f00183d31John Reck null, null, null); 206014a111f10a7523415ca210bc6fe1e4f00183d31John Reck if (c.moveToFirst()) { 207014a111f10a7523415ca210bc6fe1e4f00183d31John Reck String path = c.getString(0); 208014a111f10a7523415ca210bc6fe1e4f00183d31John Reck if (new File(path).exists()) { 209014a111f10a7523415ca210bc6fe1e4f00183d31John Reck copyExif(path, destPath); 210014a111f10a7523415ca210bc6fe1e4f00183d31John Reck } 211014a111f10a7523415ca210bc6fe1e4f00183d31John Reck } 212014a111f10a7523415ca210bc6fe1e4f00183d31John Reck c.close(); 213014a111f10a7523415ca210bc6fe1e4f00183d31John Reck } catch (Exception e) { 214014a111f10a7523415ca210bc6fe1e4f00183d31John Reck Log.w(LOGTAG, "Failed to copy exif", e); 215014a111f10a7523415ca210bc6fe1e4f00183d31John Reck } 216014a111f10a7523415ca210bc6fe1e4f00183d31John Reck } 217014a111f10a7523415ca210bc6fe1e4f00183d31John Reck 2180d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard /** 2190d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard * The task should be executed with one given bitmap to be saved. 2200d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard */ 2210d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard @Override 222014a111f10a7523415ca210bc6fe1e4f00183d31John Reck protected Uri doInBackground(ImagePreset... params) { 2230d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard // TODO: Support larger dimensions for photo saving. 2240d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard if (params[0] == null) { 2250d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard return null; 2260d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } 2270d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 228014a111f10a7523415ca210bc6fe1e4f00183d31John Reck ImagePreset preset = params[0]; 2290d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 230014a111f10a7523415ca210bc6fe1e4f00183d31John Reck try { 231014a111f10a7523415ca210bc6fe1e4f00183d31John Reck Bitmap bitmap = preset.apply(loadMutableBitmap()); 232014a111f10a7523415ca210bc6fe1e4f00183d31John Reck 233014a111f10a7523415ca210bc6fe1e4f00183d31John Reck Object xmp = null; 234014a111f10a7523415ca210bc6fe1e4f00183d31John Reck InputStream is = null; 235014a111f10a7523415ca210bc6fe1e4f00183d31John Reck if (preset.isPanoramaSafe()) { 236014a111f10a7523415ca210bc6fe1e4f00183d31John Reck is = context.getContentResolver().openInputStream(sourceUri); 237014a111f10a7523415ca210bc6fe1e4f00183d31John Reck xmp = XmpUtilHelper.extractXMPMeta(is); 238014a111f10a7523415ca210bc6fe1e4f00183d31John Reck } 239014a111f10a7523415ca210bc6fe1e4f00183d31John Reck saveBitmap(bitmap, this.destinationFile, xmp); 240014a111f10a7523415ca210bc6fe1e4f00183d31John Reck copyExif(sourceUri, destinationFile.getAbsolutePath()); 241014a111f10a7523415ca210bc6fe1e4f00183d31John Reck 242014a111f10a7523415ca210bc6fe1e4f00183d31John Reck Uri uri = insertContent(context, sourceUri, this.destinationFile, saveFileName); 243014a111f10a7523415ca210bc6fe1e4f00183d31John Reck bitmap.recycle(); 244014a111f10a7523415ca210bc6fe1e4f00183d31John Reck return uri; 2450d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 246014a111f10a7523415ca210bc6fe1e4f00183d31John Reck } catch (FileNotFoundException ex) { 247014a111f10a7523415ca210bc6fe1e4f00183d31John Reck Log.w(LOGTAG, "Failed to save image!", ex); 248014a111f10a7523415ca210bc6fe1e4f00183d31John Reck return null; 249014a111f10a7523415ca210bc6fe1e4f00183d31John Reck } 2500d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } 2510d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 2520d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard @Override 2530d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard protected void onPostExecute(Uri result) { 2540d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard if (callback != null) { 2550d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard callback.onComplete(result); 2560d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } 2570d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } 2580d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 2590ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard private static void querySource(Context context, Uri sourceUri, String[] projection, 2600d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard ContentResolverQueryCallback callback) { 2610d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard ContentResolver contentResolver = context.getContentResolver(); 2620d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard Cursor cursor = null; 2630d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard try { 2640d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard cursor = contentResolver.query(sourceUri, projection, null, null, 2650d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard null); 2660d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard if ((cursor != null) && cursor.moveToNext()) { 2670d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard callback.onCursorResult(cursor); 2680d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } 2690d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } catch (Exception e) { 2700d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard // Ignore error for lacking the data column from the source. 2710d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } finally { 2720d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard if (cursor != null) { 2730d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard cursor.close(); 2740d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } 2750d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } 2760d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } 2770d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 2780ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard private static File getSaveDirectory(Context context, Uri sourceUri) { 2790d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard final File[] dir = new File[1]; 2800ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard querySource(context, sourceUri, new String[] { 2810ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard ImageColumns.DATA 2820d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard }, 2830d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard new ContentResolverQueryCallback() { 2840d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 2850d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard @Override 2860d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard public void onCursorResult(Cursor cursor) { 2870d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard dir[0] = new File(cursor.getString(0)).getParentFile(); 2880d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } 2890d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard }); 2900d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard return dir[0]; 2910d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } 2920d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 2930d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard /** 2940d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard * Insert the content (saved file) with proper source photo properties. 2950d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard */ 2960ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard public static Uri insertContent(Context context, Uri sourceUri, File file, String saveFileName) { 2970d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard long now = System.currentTimeMillis() / 1000; 2980d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 2990d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard final ContentValues values = new ContentValues(); 3000d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard values.put(Images.Media.TITLE, saveFileName); 3010d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard values.put(Images.Media.DISPLAY_NAME, file.getName()); 3020d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard values.put(Images.Media.MIME_TYPE, "image/jpeg"); 3030d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard values.put(Images.Media.DATE_TAKEN, now); 3040d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard values.put(Images.Media.DATE_MODIFIED, now); 3050d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard values.put(Images.Media.DATE_ADDED, now); 3060d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard values.put(Images.Media.ORIENTATION, 0); 3070d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard values.put(Images.Media.DATA, file.getAbsolutePath()); 3080d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard values.put(Images.Media.SIZE, file.length()); 3090d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 310014a111f10a7523415ca210bc6fe1e4f00183d31John Reck final String[] projection = new String[] { 3110d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard ImageColumns.DATE_TAKEN, 3120d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard ImageColumns.LATITUDE, ImageColumns.LONGITUDE, 3130d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard }; 314014a111f10a7523415ca210bc6fe1e4f00183d31John Reck querySource(context, sourceUri, projection, 315014a111f10a7523415ca210bc6fe1e4f00183d31John Reck new ContentResolverQueryCallback() { 3160d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 3170d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard @Override 3180d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard public void onCursorResult(Cursor cursor) { 3190d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard values.put(Images.Media.DATE_TAKEN, cursor.getLong(0)); 3200d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 3210d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard double latitude = cursor.getDouble(1); 3220d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard double longitude = cursor.getDouble(2); 3230d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard // TODO: Change || to && after the default location issue is 3240d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard // fixed. 3250d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard if ((latitude != 0f) || (longitude != 0f)) { 3260d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard values.put(Images.Media.LATITUDE, latitude); 3270d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard values.put(Images.Media.LONGITUDE, longitude); 3280d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } 3290d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } 3300d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard }); 3310d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard 3320d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard return context.getContentResolver().insert( 3330d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard Images.Media.EXTERNAL_CONTENT_URI, values); 3340d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard } 3350ee91a2b74808fa3f51d370daaddf99f588d72efnicolasroard 3360d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard} 337