Friday 31 January 2014

Android : Properly rotated image (Landscape or Portrait)

I've been struggling for a few hours, how to rotate properly image after it's been taken trough Android app (Camera Intent).
I assume that this code example will be in some help for you also.
In the picture we can see one photo made in portrait mode and one in landscape mode. The both pictures are properly rotated, which is impossible with out additional fixing.




package com.example.testtakingandrotatingimage;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;

/**
 * @author Peter.Gostincar
 *
 */
public class MainFragment extends Fragment {

 
 private Uri mPictureFileUri;
 private static final int REQUEST_PHOTO = 0;
 private LinearLayout imageHolder;


 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState){
  View v = inflater.inflate(R.layout.fragment_main, parent, false);

  imageHolder = (LinearLayout) v.findViewById(R.id.image_holder);

  Button c = (Button) v.findViewById(R.id.take_image);
  c.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View arg0) {
    takePicture();
   }
  });
 
  return v;
 }


 @Override
 public void onActivityResult(int requestCode, int resultCode, Intent data){
  if(resultCode != Activity.RESULT_OK)
   return;
  if(requestCode == REQUEST_PHOTO){
   Bitmap image = rotateImage(mPictureFileUri);//rotate if necessary 
   showImageOnLayout(image);//show
  }
 }

 
 /**
  * 
  */
 protected void takePicture() {
  mPictureFileUri = Helpers.getGlobalPictureFileUri(getActivity());
  //is intent available & is mPictureFileUri created (is there space...)
  if((mPictureFileUri != null) && Helpers.
    isIntentAvailable(MediaStore.ACTION_IMAGE_CAPTURE, getActivity())){

   // create Intent to take a picture and return control to the calling application
   Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
   intent.putExtra(MediaStore.EXTRA_OUTPUT, mPictureFileUri);
   startActivityForResult(intent, REQUEST_PHOTO);
  }
  else {
   Toast.makeText(getActivity(), "something wrong under the hood", 
     Toast.LENGTH_SHORT).show();
  }
 }
 

 /**
  * 
  * @param image
  */
 private void showImageOnLayout(Bitmap image) {
  ImageView tmpImage = new ImageView(getActivity());
  tmpImage.setImageBitmap(image);
  tmpImage.setPadding(10, 10, 10, 10);

  ((ViewGroup) imageHolder).addView(tmpImage);
 }


 /**
  * here is the magic
  * @param uri
  * @return
  */
 private Bitmap rotateImage(Uri uri) {
  Bitmap image = Helpers.decodeSampledBitmapFromResource(getResources(), 
    uri.getPath(), 200, 200);
  try {
   Matrix matrix = new Matrix();

   ExifInterface exifInterface = new ExifInterface(uri.getPath());
   int rotation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
     ExifInterface.ORIENTATION_NORMAL);
   switch(rotation){
    case ExifInterface.ORIENTATION_NORMAL:{
     break;
    }
    case ExifInterface.ORIENTATION_ROTATE_90:{
     matrix.postRotate(90);
     image = Bitmap.createBitmap(image, 0, 0, image.getWidth(), image.getHeight(), 
       matrix, true);
     break;
    }
    case ExifInterface.ORIENTATION_ROTATE_180:{
     matrix.postRotate(180);
     image = Bitmap.createBitmap(image, 0, 0, image.getWidth(), image.getHeight(),
       matrix, true);
     break;
    }
    case ExifInterface.ORIENTATION_ROTATE_270:{
     matrix.postRotate(270);
     image = Bitmap.createBitmap(image, 0, 0, image.getWidth(), image.getHeight(), 
       matrix, true);
     break;
    }
   }
  } catch (Exception e) {
   // TODO: handle exception
  }
  return image;
 }
}


class Helpers {
 
 private static final String MY_APPLICATION = "somepath";

 /**
  * 
  * @param mAppContext
  * @return
  */
 public static Uri getGlobalPictureFileUri(FragmentActivity mAppContext) {
  //To be safe, you should check that the SDCard is mounted
  // using Environment.getExternalStorageState() before doing this.

  if(!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()))
   return null;

  File mediaStorageDir = new File(mAppContext.getExternalFilesDir(
    Environment.DIRECTORY_PICTURES) , MY_APPLICATION);

  if(mediaStorageDir.toString().equals(MY_APPLICATION))
   mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
     Environment.DIRECTORY_PICTURES), MY_APPLICATION);

  // This location works best if you want the created images to be shared
  // between applications and persist after your app has been uninstalled.
  // Create the storage directory if it does not exist
  if (! mediaStorageDir.exists()){
   if (! mediaStorageDir.mkdirs()){
    return null;
   }
  }
  // Create a media file name
  String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
  File mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_"+
    timeStamp + ".jpg");

  return Uri.fromFile(mediaFile);
 }


 /**
  * 
  * @param action
  * @param mAppContext
  * @return
  */
 public static boolean isIntentAvailable(String action, FragmentActivity mAppContext) {
  final PackageManager packageManager = mAppContext.getPackageManager();
  final Intent intent = new Intent(action);
  List list =
    packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
  return list.size() > 0;
 }
 
 
 /**
  * 
  * @param res
  * @param pathName
  * @param reqWidth
  * @param reqHeight
  * @return
  */
 public static Bitmap decodeSampledBitmapFromResource(Resources res, String pathName,
   int reqWidth, int reqHeight) {
  // First decode with inJustDecodeBounds=true to check dimensions
  final BitmapFactory.Options options = new BitmapFactory.Options();
  options.inJustDecodeBounds = true;
  BitmapFactory.decodeFile(pathName, options);
  // Calculate inSampleSize
  options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
  // Decode bitmap with inSampleSize set
  options.inJustDecodeBounds = false;
  Bitmap bitmap = BitmapFactory.decodeFile(pathName, options);
  return bitmap;
 }

 
 /**
  * 
  * @param options
  * @param reqWidth
  * @param reqHeight
  * @return
  */
 private static int calculateInSampleSize(
   BitmapFactory.Options options, int reqWidth, int reqHeight) {
  // Raw height and width of image
  final int height = options.outHeight;
  final int width = options.outWidth;
  int inSampleSize = 1;

  if (height > reqHeight || width > reqWidth) {

   final int halfHeight = height / 2;
   final int halfWidth = width / 2;

   // Calculate the largest inSampleSize value that is a power of 2 and keeps both
   // height and width larger than the requested height and width.
   while ((halfHeight / inSampleSize) > reqHeight
     && (halfWidth / inSampleSize) > reqWidth) {
    inSampleSize *= 2;
   }
  }
  return inSampleSize;
 }
}