package org.sana.android.task; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.sana.android.db.EventDAO; import org.sana.android.db.ImageProvider; import org.sana.android.db.SanaDB.ImageSQLFormat; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.media.ExifInterface; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; /** * Task for handling image quirks * * @author Sana Development Team * */ public class ImageProcessingTask extends AsyncTask<ImageProcessingTaskRequest, Void, Void> { public static final String TAG = ImageProcessingTask.class.getSimpleName(); private void logDebugInformationAboutCameraIntents(Intent data) { // Print some diagnostic information about the Intent sent back // from the Camera app so that if a manufacturer does goofy // things in the future we have a fighting chance at remotely // debugging it. if (data != null) { Log.i(TAG, "Received intent from Camera application. May need to " +"enable workaround. The Intent's Action is: " + data.getAction()); // The HTC Tattoo's Camera app just sends back a content:// // Uri pointing to the image in the Intent. It also included // a Parcelable android.graphics.Bitmap object in the extras // bundle, except it's a tiny image (320x240). Uri returnedUri = data.getData(); if (returnedUri != null) { Log.i(TAG, "Received Uri from Camera application: " + returnedUri); } Bundle b = data.getExtras(); if (b != null) { Log.d(TAG, "Camera intent had bundle."); for (String key : b.keySet()) { Log.d(TAG, "Camera Intent Bundle has key: " + key); } // Try to get the bitmap and poke at it. Bitmap bitmap = b.getParcelable("data"); if (bitmap != null) { int iWidth = bitmap.getWidth(); int iHeight = bitmap.getHeight(); Log.i(TAG, "Intent had a bitmap Parcel in the 'data' extra." +" width: " + iWidth + " height: " + iHeight); } } } } /** * Some Android manufacturers have decided to replace the * Google Camera application with their own, buggy camera * application. This method attempts to smooth over the * differences and undo the damage done by HTC to Android. * * This currently has a workaround for the HTC Tattoo only. * * @return An InputStream to the image we just captured with the Camera app. * @throws FileNotFoundException */ private InputStream getImageInputStreamWithWorkaround( ImageProcessingTaskRequest request) throws FileNotFoundException { InputStream is = null; // Workaround for phones using a custom Camera application like the HTC Tattoo. if (request.tempImageFile.exists()) { Log.i(TAG, "Temp file exists, no workaround needed."); is = new FileInputStream(request.tempImageFile); } else if (request.intent != null) { Log.i(TAG, "HTC Sense Workaround active."); // The Tattoo's Camera app will not store the file in the // tempImageFile path. Instead it will return a Uri pointing to the // file. Uri fileUri = request.intent.getData(); Log.i(TAG, "HTC Sense Workaround active. File uri is: " + fileUri.toString()); if (fileUri != null) { is = request.c.getContentResolver().openInputStream(fileUri); } } return is; } /** {@inheritDoc} */ @Override protected Void doInBackground(ImageProcessingTaskRequest... params) { ImageProcessingTaskRequest request = params[0]; if (request == null) { Log.e(TAG, "Didn't receive valid ImageProcessingTaskRequest"); return null; } File tempImageFile = request.tempImageFile; String savedProcedureId = request.savedProcedureId; String elementId = request.elementId; Context c = request.c; logDebugInformationAboutCameraIntents(request.intent); // Get the image parameters stored in the Intent ContentValues values = new ContentValues(); values.put(ImageSQLFormat.ENCOUNTER_ID, savedProcedureId); values.put(ImageSQLFormat.ELEMENT_ID, elementId); Uri imageUri = c.getContentResolver().insert(ImageSQLFormat.CONTENT_URI, values); Uri thumbUri = ImageProvider.getThumbUri(imageUri); Log.d(TAG, "...Old URI: " + imageUri); Log.d(TAG, "...Thumb URI: " + thumbUri); try { InputStream is = getImageInputStreamWithWorkaround(request); BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; options.inSampleSize = 1; // Only decode the size, not the image itself. If we // decode the full image then we sometimes get Out // of Memory errors. Bitmap bmp = BitmapFactory.decodeStream(is, null, options); //BitmapFactory.decodeFile(tempImageFile.getAbsolutePath(), options); //Works for Android 1.5 is.close(); //bmp.recycle(); int iWidth = options.outWidth; int iHeight = options.outHeight; Log.i(TAG, "Image capture activity returned bitmap of width " + iWidth + " and height " + iHeight); OutputStream os = c.getContentResolver().openOutputStream(imageUri); is = getImageInputStreamWithWorkaround(request); //Bitmap bmp = BitmapFactory.decodeStream(is); final int bufSize = 4096; byte[] buffer = new byte[bufSize]; int bytesRead = 0; int bytesWritten = 0; while (bytesRead != -1) { bytesWritten += bytesRead; os.write(buffer, 0, bytesRead); bytesRead = is.read(buffer, 0, bufSize); } is.close(); os.flush(); os.close(); Log.d(TAG, "...copy: read="+bytesRead + ", written="+bytesWritten); // Correct orientation of original file try { Log.d(TAG, "...correcting orientation"); ImageProvider.correctOrientation(request.c, imageUri); Log.d(TAG, "...correcting orientation success"); } catch (OutOfMemoryError e){ Log.e(TAG, "OutOfMemoryError! Orientation correction"); } catch (Exception e){ e.printStackTrace(); } int thumbCompression = 50; int thumbMaxSize = 100; int largestDimension = (iWidth > iHeight) ? iWidth : iHeight; Log.i(TAG, "Saving thumbnail for " + imageUri + " with " + thumbCompression + "% quality."); // We want a picture with it's largest side 100 pixels. int scaleFactor = largestDimension / thumbMaxSize; options = new BitmapFactory.Options(); options.inSampleSize = scaleFactor; is = getImageInputStreamWithWorkaround(request); Bitmap thumbBitmap = BitmapFactory.decodeStream(is, null, options); os = c.getContentResolver().openOutputStream(thumbUri); thumbBitmap.compress(Bitmap.CompressFormat.JPEG, thumbCompression, os); os.flush(); os.close(); is.close(); thumbBitmap.recycle(); // Correct thumb orientation of original file try { Log.d(TAG, "...correcting thumb orientation"); ImageProvider.correctOrientation(request.c, thumbUri); Log.d(TAG, "...correcting thumb orientation success"); } catch (OutOfMemoryError e){ Log.e(TAG, "OutOfMemoryError! Orientation correction"); } catch (Exception e){ e.printStackTrace(); } // Flag the file as saved - does not record image size values = new ContentValues(); values.put(ImageSQLFormat.FILE_VALID, true); c.getContentResolver().update(imageUri, values, null, null); if (tempImageFile.exists()) { Log.d(TAG, "...temp image file exists"); Log.d(TAG, "...path=" + tempImageFile.getAbsolutePath()); //tempImageFile.delete(); } Log.i(TAG, "Successfully saved " + imageUri); } catch (FileNotFoundException e) { Log.e(TAG, "While storing the image, got an exception: " + e.toString()); EventDAO.logException(c, e); } catch (IOException e) { Log.e(TAG, "While storing the image, got an exception: " + e.toString()); EventDAO.logException(c, e); } return null; } protected int rotateBitmap(File file, Bitmap bitmap) throws IOException { ExifInterface exif = new ExifInterface(file.getAbsolutePath()); int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); return orientation; } }