package com.magnet.wru;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.provider.MediaStore;
import android.support.v4.app.Fragment;
import com.magnet.mmx.client.common.Log;
import com.magnet.mmx.util.DisposableBinFile;
import com.magnet.mmx.util.DisposableFile;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public final class MediaUtil {
public static final int LOAD_IMAGE_RESULTS_REQUEST = 1000;
private static final float BASE_64_FACTOR = 1.37f;
private static final String TAG = MediaUtil.class.getSimpleName();
/**
* Converts the specified file to a DisposableBinFile that fits into the maxSizeBytes.
* This takes into account a factor for Base64 encoding.
*
* @param imageFile the file to convert
* @param maxSizeBytes the maximum size in bytes
* @return the DisposableBinFile for this resized image
* @throws IOException
*/
public static DisposableBinFile convertImageToDisposableFile(File imageFile, long maxSizeBytes)
throws IOException {
if (imageFile == null || imageFile.isDirectory() || !imageFile.exists()) {
throw new IOException("Invalid file specified: " + imageFile);
}
long originalSize = imageFile.length();
Log.d(TAG, "convertImageToDisposableFile(): original size=" + originalSize);
//account for base64 encoding, 1.37x
if (originalSize * BASE_64_FACTOR < maxSizeBytes) {
//return a disposable file
Log.d(TAG, "convertImageToDisposableFile(): original file is within bounds, just returning the disposable file");
return new DisposableBinFile(imageFile.getAbsolutePath(), false);
} else {
BitmapFactory.Options options = new BitmapFactory.Options();
Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(imageFile), null, options);
Log.d(TAG, "convertImageToDisposableFile(): original dimensions: " + options.outWidth + "x" + options.outHeight);
//determine the scale factor
float compressionRatio = (float) originalSize / ((float) options.outWidth * options.outHeight * 32 / 8);
float scaleFactor = (float) Math.sqrt(maxSizeBytes / BASE_64_FACTOR / compressionRatio / options.outWidth / options.outHeight);
Log.d(TAG, "convertImageToDisposableFile(): scale factor = " + String.valueOf(scaleFactor));
File scaledFile = File.createTempFile("FileUtil_processed_", null);
Bitmap processedBitmap;
int quality = 60;
if (scaleFactor < 1) {
int newWidth = Math.round(options.outWidth * scaleFactor);
int newHeight = Math.round(options.outHeight * scaleFactor);
Log.d(TAG, "convertImageToDisposableFile(): new dimensions: " + newWidth + "x" + newHeight);
processedBitmap = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, false);
} else {
//at this point, the file is bigger than we want it to be, but we don't want to scale
//just adjust quality.
processedBitmap = bitmap;
}
boolean success = true;
while (success && (scaledFile.length() == 0 || scaledFile.length() * BASE_64_FACTOR > maxSizeBytes)) {
Log.d(TAG, "convertImageToDisposableFile(): scale pass");
FileOutputStream fos = new FileOutputStream(scaledFile);
success = processedBitmap.compress(Bitmap.CompressFormat.JPEG, quality, fos);
quality = quality - 10;
}
if (success) {
Log.d(TAG, "convertImageToDisposableFile(): successfully compressed image new size: " + scaledFile.length() +
", estimated size (after base64 encoding): " + String.valueOf(scaledFile.length() * BASE_64_FACTOR));
return new DisposableBinFile(scaledFile.getAbsolutePath(), true);
} else {
throw new IOException("unable to compress image");
}
}
}
/**
* The calling activity should verify that the requestCode returned in the onActivityResult callback
* is the one defined in LOAD_IMAGE_RESULTS_REQUEST and call this method to retrieve the
* resized DisposableFile.
*
* @see MediaUtil#LOAD_IMAGE_RESULTS_REQUEST
* @see Activity#startActivityForResult(Intent, int)
* @see Activity#onActivityResult(int, int, Intent)
*
* @param context the context
* @param resultCode the result code return in onActivityResult()
* @param data the intent returned in onActivityResult()
* @return the DisposableFile image that was picked
* @throws IOException
*/
public static DisposableFile getDisposableImageFromActivityResult(
Context context, int resultCode, Intent data) throws IOException {
// Here we need to check if the activity that was triggers was the Image Gallery.
// If it is the requestCode will match the LOAD_IMAGE_RESULTS value.
// If the resultCode is RESULT_OK and there is some data we know that an image was picked.
if (resultCode == Activity.RESULT_OK && data != null) {
// Let's read picked image data - its URI
Uri pickedImage = data.getData();
// Let's read picked image path using content resolver
String[] filePath = { MediaStore.Images.Media.DATA };
Cursor cursor = context.getContentResolver().query(pickedImage, filePath, null, null, null);
try {
cursor.moveToFirst();
String imagePath = cursor.getString(cursor.getColumnIndex(filePath[0]));
return convertImageToDisposableFile(new File(imagePath), 200 * 1024);
} finally {
cursor.close();
}
}
return null;
}
/**
* Calls startActivityForResult for the image picker. Will use the LOAD_IMAGE_RESULTS_REQUEST
* request code.
*
* @see #LOAD_IMAGE_RESULTS_REQUEST
* @param activity the activity
*/
public static void startImagePickerActivityWithResult(Activity activity) {
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
activity.startActivityForResult(intent, LOAD_IMAGE_RESULTS_REQUEST);
}
/**
* Calls startActivityForResult for the image picker. Will use the LOAD_IMAGE_RESULTS_REQUEST
* request code.
*
* @see #LOAD_IMAGE_RESULTS_REQUEST
* @param fragment the fragment
*/
public static void startImagePickerActivityWithResult(Fragment fragment) {
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
fragment.startActivityForResult(intent, LOAD_IMAGE_RESULTS_REQUEST);
}
}