• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
RotationTask(Context context, LocalDataAdapter adapter, int currentDataId, boolean clockwise)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
onPreExecute()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
doInBackground(LocalData... data)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      */
rotateInJpegExif(LocalData data)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
onPostExecute(LocalData result)146     protected void onPostExecute(LocalData result) {
147         mProgress.dismiss();
148         if (result != null) {
149             mAdapter.updateData(mCurrentDataId, result);
150         }
151     }
152 }
153