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