1 /* 2 * Copyright (C) 2013 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.data; 18 19 import android.app.ProgressDialog; 20 import android.content.ContentValues; 21 import android.content.Context; 22 import android.os.AsyncTask; 23 import android.provider.MediaStore.Images; 24 import android.util.Log; 25 26 import com.android.camera.data.LocalMediaData.PhotoData; 27 import com.android.camera.exif.ExifInterface; 28 import com.android.camera.exif.ExifTag; 29 import com.android.camera2.R; 30 31 import java.io.File; 32 import java.io.FileNotFoundException; 33 import java.io.IOException; 34 35 /** 36 * RotationTask can be used to rotate a {@link LocalData} by updating the exif 37 * data from jpeg file. Note that only {@link PhotoData} can be rotated. 38 */ 39 public class RotationTask extends AsyncTask<LocalData, Void, LocalData> { 40 private static final String TAG = "CAM_RotationTask"; 41 private final Context mContext; 42 private final LocalDataAdapter mAdapter; 43 private final int mCurrentDataId; 44 private final boolean mClockwise; 45 private ProgressDialog mProgress; 46 47 public RotationTask(Context context, LocalDataAdapter adapter, 48 int currentDataId, boolean clockwise) { 49 mContext = context; 50 mAdapter = adapter; 51 mCurrentDataId = currentDataId; 52 mClockwise = clockwise; 53 } 54 55 @Override 56 protected void onPreExecute() { 57 // Show a progress bar since the rotation could take long. 58 mProgress = new ProgressDialog(mContext); 59 int titleStringId = mClockwise ? R.string.rotate_right : R.string.rotate_left; 60 mProgress.setTitle(mContext.getString(titleStringId)); 61 mProgress.setMessage(mContext.getString(R.string.please_wait)); 62 mProgress.setCancelable(false); 63 mProgress.show(); 64 } 65 66 @Override 67 protected LocalData doInBackground(LocalData... data) { 68 return rotateInJpegExif(data[0]); 69 } 70 71 /** 72 * Rotates the image by updating the exif. Done in background thread. 73 * The worst case is the whole file needed to be re-written with 74 * modified exif data. 75 * 76 * @return A new {@link LocalData} object which containing the new info. 77 */ 78 private LocalData rotateInJpegExif(LocalData data) { 79 if (!(data instanceof PhotoData)) { 80 Log.w(TAG, "Rotation can only happen on PhotoData."); 81 return null; 82 } 83 84 PhotoData imageData = (PhotoData) data; 85 int originRotation = imageData.getOrientation(); 86 int finalRotationDegrees; 87 if (mClockwise) { 88 finalRotationDegrees = (originRotation + 90) % 360; 89 } else { 90 finalRotationDegrees = (originRotation + 270) % 360; 91 } 92 93 String filePath = imageData.getPath(); 94 ContentValues values = new ContentValues(); 95 boolean success = false; 96 int newOrientation = 0; 97 if (imageData.getMimeType().equalsIgnoreCase(LocalData.MIME_TYPE_JPEG)) { 98 ExifInterface exifInterface = new ExifInterface(); 99 ExifTag tag = exifInterface.buildTag( 100 ExifInterface.TAG_ORIENTATION, 101 ExifInterface.getOrientationValueForRotation( 102 finalRotationDegrees)); 103 if (tag != null) { 104 exifInterface.setTag(tag); 105 try { 106 // Note: This only works if the file already has some EXIF. 107 exifInterface.forceRewriteExif(filePath); 108 long fileSize = new File(filePath).length(); 109 values.put(Images.Media.SIZE, fileSize); 110 newOrientation = finalRotationDegrees; 111 success = true; 112 } catch (FileNotFoundException e) { 113 Log.w(TAG, "Cannot find file to set exif: " + filePath); 114 } catch (IOException e) { 115 Log.w(TAG, "Cannot set exif data: " + filePath); 116 } 117 } else { 118 Log.w(TAG, "Cannot build tag: " + ExifInterface.TAG_ORIENTATION); 119 } 120 } 121 122 PhotoData result = null; 123 if (success) { 124 // MediaStore using SQLite is thread safe. 125 values.put(Images.Media.ORIENTATION, finalRotationDegrees); 126 mContext.getContentResolver().update(imageData.getContentUri(), 127 values, null, null); 128 double[] latLong = data.getLatLong(); 129 double latitude = 0; 130 double longitude = 0; 131 if (latLong != null) { 132 latitude = latLong[0]; 133 longitude = latLong[1]; 134 } 135 136 result = new PhotoData(data.getContentId(), data.getTitle(), 137 data.getMimeType(), data.getDateTaken(), data.getDateModified(), 138 data.getPath(), newOrientation, imageData.getWidth(), 139 imageData.getHeight(), data.getSizeInBytes(), latitude, longitude); 140 } 141 142 return result; 143 } 144 145 @Override 146 protected void onPostExecute(LocalData result) { 147 mProgress.dismiss(); 148 if (result != null) { 149 mAdapter.updateData(mCurrentDataId, result); 150 } 151 } 152 } 153