1 package com.android.testingcamera;
2 
3 import java.io.File;
4 import java.io.FileOutputStream;
5 import java.io.IOException;
6 
7 import android.Manifest;
8 import android.content.pm.PackageManager;
9 import android.graphics.Bitmap;
10 import android.graphics.BitmapFactory;
11 import android.media.ExifInterface;
12 import android.media.MediaScannerConnection.OnScanCompletedListener;
13 import android.net.Uri;
14 import android.os.AsyncTask;
15 import android.os.Bundle;
16 import android.app.DialogFragment;
17 import android.content.Intent;
18 import android.text.method.ScrollingMovementMethod;
19 import android.view.LayoutInflater;
20 import android.view.View;
21 import android.view.View.OnClickListener;
22 import android.view.ViewGroup;
23 import android.widget.Button;
24 import android.widget.ImageView;
25 import android.widget.TextView;
26 
27 public class SnapshotDialogFragment extends DialogFragment
28                 implements OnScanCompletedListener{
29 
30     private ImageView mInfoImage;
31     private TextView mInfoText;
32     private Button mOkButton;
33     private Button mSaveButton;
34     private Button mSaveAndViewButton;
35 
36     private byte[] mJpegImage;
37     private boolean mSaved = false;
38     private boolean mViewWhenReady = false;
39         private Uri mSavedUri = null;
40 
SnapshotDialogFragment()41     public SnapshotDialogFragment() {
42         // Empty constructor required for DialogFragment
43     }
44 
45     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)46     public View onCreateView(LayoutInflater inflater, ViewGroup container,
47             Bundle savedInstanceState) {
48         View view = inflater.inflate(R.layout.fragment_snapshot, container);
49 
50         mOkButton = (Button) view.findViewById(R.id.snapshot_ok);
51         mOkButton.setOnClickListener(mOkButtonListener);
52 
53         mSaveButton = (Button) view.findViewById(R.id.snapshot_save);
54         mSaveButton.setOnClickListener(mSaveButtonListener);
55 
56         mSaveAndViewButton = (Button) view.findViewById(R.id.snapshot_view);
57         mSaveAndViewButton.setOnClickListener(mSaveAndViewButtonListener);
58 
59         mInfoImage = (ImageView) view.findViewById(R.id.snapshot_image);
60         mInfoText= (TextView) view.findViewById(R.id.snapshot_text);
61         mInfoText.setMovementMethod(new ScrollingMovementMethod());
62 
63         if (mJpegImage != null) {
64             new AsyncTask<byte[], Integer, Bitmap>() {
65                 @Override
66                 protected Bitmap doInBackground(byte[]... params) {
67                     byte[] jpegImage = params[0];
68                     BitmapFactory.Options opts = new BitmapFactory.Options();
69                     opts.inJustDecodeBounds = true;
70                     BitmapFactory.decodeByteArray(jpegImage, 0,
71                             jpegImage.length, opts);
72                     // Keep image at less than 1 MP.
73                     if (opts.outWidth > 1024 || opts.outHeight > 1024) {
74                         int scaleFactorX = opts.outWidth / 1024 + 1;
75                         int scaleFactorY = opts.outHeight / 1024 + 1;
76                         int scaleFactor = scaleFactorX > scaleFactorY ?
77                             scaleFactorX : scaleFactorY;
78                         opts.inSampleSize = scaleFactor;
79                     }
80                     opts.inJustDecodeBounds = false;
81                     Bitmap img = BitmapFactory.decodeByteArray(jpegImage, 0,
82                             jpegImage.length, opts);
83                     return img;
84                 }
85 
86                 @Override
87                 protected void onPostExecute(Bitmap img) {
88                     mInfoImage.setImageBitmap(img);
89                 }
90             }.execute(mJpegImage);
91         }
92 
93         getDialog().setTitle("Snapshot result");
94         return view;
95     }
96 
97     public OnClickListener mOkButtonListener = new OnClickListener() {
98         @Override
99         public void onClick(View v) {
100             dismiss();
101         }
102     };
103 
104     public OnClickListener mSaveButtonListener = new OnClickListener() {
105         @Override
106         public void onClick(View v) {
107             saveFile();
108         }
109     };
110 
111     public OnClickListener mSaveAndViewButtonListener = new OnClickListener() {
112         @Override
113         public void onClick(View v) {
114             saveFile();
115             viewFile();
116         }
117     };
118 
updateImage(byte[] image)119     public void updateImage(byte[] image) {
120         mJpegImage = image;
121     }
122 
onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)123     public void onRequestPermissionsResult(int requestCode, String[] permissions,
124             int[] grantResults) {
125         TestingCamera parent = (TestingCamera) getActivity();
126         if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
127             parent.log("Storage write permission granted");
128             saveFile();
129         } else {
130             parent.log("Denied storage write permission; cannot save");
131         }
132     }
133 
getAttrib(ExifInterface exif, String tag)134     private String getAttrib(ExifInterface exif, String tag) {
135         String attribute = exif.getAttribute(tag);
136         return (attribute == null) ? "???" : attribute;
137     }
138 
saveFile()139     private void saveFile() {
140         if (!mSaved) {
141             TestingCamera parent = (TestingCamera) getActivity();
142             parent.log("Saving image");
143 
144             if (parent.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
145                     != PackageManager.PERMISSION_GRANTED) {
146                 parent.log("Requesting storage write permission");
147                 parent.requestPermissions(new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},
148                         parent.PERMISSIONS_REQUEST_SNAPSHOT);
149                 return;
150             }
151 
152             File targetFile = parent.getOutputMediaFile(TestingCamera.MEDIA_TYPE_IMAGE);
153             if (targetFile == null) {
154                 parent.logE("Unable to create file name");
155                 return;
156             }
157             parent.logIndent(1);
158             parent.log("File name: " + targetFile.toString());
159             try {
160                 FileOutputStream out = new FileOutputStream(targetFile);
161                 out.write(mJpegImage);
162                 out.close();
163                 mSaved = true;
164                 parent.notifyMediaScannerOfFile(targetFile, this);
165                 updateExif(targetFile);
166             } catch (IOException e) {
167                 parent.logE("Unable to save file: " + e.getMessage());
168             }
169             parent.logIndent(-1);
170         }
171     }
172 
updateExif(File targetFile)173     private void updateExif(File targetFile) {
174         ((TestingCamera) getActivity()).log("Extracting EXIF");
175         try {
176             ExifInterface exif = new ExifInterface(targetFile.toString());
177 
178             String aperture = getAttrib(exif, ExifInterface.TAG_APERTURE);
179 
180             String dateTime = getAttrib(exif, ExifInterface.TAG_DATETIME);
181             String exposureTime = getAttrib(exif, ExifInterface.TAG_EXPOSURE_TIME);
182             int flash = exif.getAttributeInt(ExifInterface.TAG_FLASH, 0);
183             double focalLength = exif.getAttributeDouble(ExifInterface.TAG_FOCAL_LENGTH, 0);
184 
185             double gpsAltitude = exif.getAltitude(Double.NaN);
186             String gpsDatestamp = getAttrib(exif, ExifInterface.TAG_GPS_DATESTAMP);
187             float[] gpsCoords = new float[2];
188             if (!exif.getLatLong(gpsCoords)) {
189                 gpsCoords[0] = Float.NaN;
190                 gpsCoords[1] = Float.NaN;
191             }
192             String gpsProcessingMethod = getAttrib(exif, ExifInterface.TAG_GPS_PROCESSING_METHOD);
193             String gpsTimestamp = getAttrib(exif, ExifInterface.TAG_GPS_TIMESTAMP);
194 
195             int width = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0);
196             int height = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0);
197             String iso = getAttrib(exif, ExifInterface.TAG_ISO);
198             String make = getAttrib(exif, ExifInterface.TAG_MAKE);
199             String model = getAttrib(exif, ExifInterface.TAG_MODEL);
200             int orientationVal = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1);
201             int whiteBalance = exif.getAttributeInt(ExifInterface.TAG_WHITE_BALANCE, 0);
202 
203             final String[] orientationStrings= new String[] {
204                 "UNDEFINED",
205                 "NORMAL",
206                 "FLIP_HORIZONTAL",
207                 "ROTATE_180",
208                 "FLIP_VERTICAL",
209                 "TRANSPOSE",
210                 "ROTATE_90",
211                 "TRANSVERSE",
212                 "ROTATE_270"
213             };
214             if (orientationVal >= orientationStrings.length) {
215                 orientationVal = 0;
216             }
217             String orientation = orientationStrings[orientationVal];
218 
219             StringBuilder exifInfo = new StringBuilder();
220             exifInfo.append("EXIF information for ").
221                 append(targetFile.toString()).append("\n\n");
222             exifInfo.append("Size: ").
223                 append(width).append(" x ").append(height).append("\n");
224             exifInfo.append("Make: ").
225                 append(make).append("\n");
226             exifInfo.append("Model: ").
227                 append(model).append("\n");
228             exifInfo.append("Orientation: ").
229                 append(orientation).append("\n");
230             exifInfo.append("Aperture: ").
231                 append(aperture).append("\n");
232             exifInfo.append("Focal length: ").
233                 append(focalLength).append("\n");
234             exifInfo.append("Exposure time: ").
235                 append(exposureTime).append("\n");
236             exifInfo.append("ISO: ").
237                 append(iso).append("\n");
238             exifInfo.append("Flash: ").
239                 append(flash).append("\n");
240             exifInfo.append("White balance: ").
241                 append(whiteBalance).append("\n");
242             exifInfo.append("Date/Time: ").
243                 append(dateTime).append("\n");
244             exifInfo.append("GPS altitude: ").
245                 append(gpsAltitude).append("\n");
246             exifInfo.append("GPS latitude: ").
247                 append(gpsCoords[0]).append("\n");
248             exifInfo.append("GPS longitude: ").
249                 append(gpsCoords[1]).append("\n");
250             exifInfo.append("GPS datestamp: ").
251                 append(gpsDatestamp).append("\n");
252             exifInfo.append("GPS timestamp: ").
253                 append(gpsTimestamp).append("\n");
254             exifInfo.append("GPS processing method: ").
255                 append(gpsProcessingMethod).append("\n");
256             mInfoText.setText(exifInfo.toString());
257 
258         } catch (IOException e) {
259             ((TestingCamera) getActivity()).logE("Unable to extract EXIF: " + e.getMessage());
260         }
261     }
262 
viewFile()263     private synchronized void viewFile() {
264         if (!mSaved) return;
265         if (mSavedUri != null) {
266             ((TestingCamera) getActivity()).log("Viewing file");
267             mViewWhenReady = false;
268             getActivity().startActivity(new Intent(Intent.ACTION_VIEW, mSavedUri));
269         } else {
270             mViewWhenReady = true;
271         }
272     }
273 
274     @Override
onScanCompleted(String path, Uri uri)275     public synchronized void onScanCompleted(String path, Uri uri) {
276         mSavedUri = uri;
277         if (mViewWhenReady) viewFile();
278     }
279 }
280