package kr.kdev.dg1s.biowiki.util; import android.app.Activity; import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.media.ExifInterface; import android.net.Uri; import android.os.AsyncTask; import android.provider.MediaStore; import android.provider.MediaStore.Images; import android.text.TextUtils; import android.view.Display; import android.widget.ImageView; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; public class ImageHelper { public static int[] getImageSize(Uri uri, Context context) { String path = null; BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; if (uri.toString().contains("content:")) { String[] projection = new String[]{Images.Media._ID, Images.Media.DATA}; Cursor cur = context.getContentResolver().query(uri, projection, null, null, null); if (cur != null) { if (cur.moveToFirst()) { int dataColumn = cur.getColumnIndex(Images.Media.DATA); path = cur.getString(dataColumn); } cur.close(); } } if (TextUtils.isEmpty(path)) { //The file isn't ContentResolver, or it can't be access by ContentResolver. Try to access the file directly. path = uri.toString().replace("content://media", ""); path = path.replace("file://", ""); } BitmapFactory.decodeFile(path, options); int imageHeight = options.outHeight; int imageWidth = options.outWidth; return new int[]{imageWidth, imageHeight}; } public static Bitmap downloadBitmap(String url) { final DefaultHttpClient client = new DefaultHttpClient(); final HttpGet getRequest = new HttpGet(url); try { HttpResponse response = client.execute(getRequest); final int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != HttpStatus.SC_OK) { AppLog.w(AppLog.T.UTILS, "ImageDownloader Error " + statusCode + " while retrieving bitmap from " + url); return null; } final HttpEntity entity = response.getEntity(); if (entity != null) { InputStream inputStream = null; try { inputStream = entity.getContent(); return BitmapFactory.decodeStream(inputStream); } finally { if (inputStream != null) { inputStream.close(); } entity.consumeContent(); } } } catch (Exception e) { // Could provide a more explicit error message for IOException or // IllegalStateException getRequest.abort(); AppLog.w(AppLog.T.UTILS, "ImageDownloader Error while retrieving bitmap from " + url); } return null; } /** * From http://developer.android.com/training/displaying-bitmaps/load-bitmap.html * */ public 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) { // Calculate ratios of height and width to requested height and width final int heightRatio = Math.round((float) height / (float) reqHeight); final int widthRatio = Math.round((float) width / (float) reqWidth); // Choose the smallest ratio as inSampleSize value, this will guarantee // a final image with both dimensions larger than or equal to the // requested height and width. inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } return inSampleSize; } // Read the orientation from ContentResolver. If it fails, read from EXIF. public int getImageOrientation(Context ctx, String filePath) { Uri curStream; int orientation = 0; if (!filePath.contains("content://")) curStream = Uri.parse("content://media" + filePath); else curStream = Uri.parse(filePath); try { Cursor cur = ctx.getContentResolver().query(curStream, new String[]{Images.Media.ORIENTATION}, null, null, null); if (cur != null) { if (cur.moveToFirst()) { orientation = cur.getInt(cur.getColumnIndex(Images.Media.ORIENTATION)); } cur.close(); } } catch (Exception errReadingContentResolver) { AppLog.e(AppLog.T.UTILS, errReadingContentResolver); } if (orientation == 0) { orientation = getExifOrientation(filePath); } return orientation; } public int getExifOrientation(String path) { ExifInterface exif; try { exif = new ExifInterface(path); } catch (IOException e) { AppLog.e(AppLog.T.UTILS, e); return 0; } int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0); switch (exifOrientation) { case ExifInterface.ORIENTATION_NORMAL: return 0; case ExifInterface.ORIENTATION_ROTATE_90: return 90; case ExifInterface.ORIENTATION_ROTATE_180: return 180; case ExifInterface.ORIENTATION_ROTATE_270: return 270; default: return 0; } } public String getTitleForWPImageSpan(Context ctx, String filePath) { if (filePath == null) return null; Uri curStream; String title; if (!filePath.contains("content://")) curStream = Uri.parse("content://media" + filePath); else curStream = Uri.parse(filePath); if (filePath.contains("video")) { return "Video"; } else { String[] projection = new String[]{Images.Thumbnails.DATA}; Cursor cur; try { cur = ctx.getContentResolver().query(curStream, projection, null, null, null); } catch (Exception e1) { AppLog.e(AppLog.T.UTILS, e1); return null; } File jpeg; if (cur != null) { String thumbData = ""; if (cur.moveToFirst()) { int dataColumn = cur.getColumnIndex(Images.Media.DATA); thumbData = cur.getString(dataColumn); } cur.close(); if (thumbData == null) { return null; } jpeg = new File(thumbData); } else { String path = filePath.toString().replace("file://", ""); jpeg = new File(path); } title = jpeg.getName(); return title; } } /** * Resizes an image to be placed in the Post Content Editor * * @param ctx * @param filePath * @return resized bitmap */ public Bitmap getThumbnailForWPImageSpan(Context ctx, String filePath) { if (filePath == null) return null; Display display = ((Activity) ctx).getWindowManager().getDefaultDisplay(); int width = display.getWidth(); int height = display.getHeight(); if (width > height) width = height; Uri curUri; if (!filePath.contains("content://")) curUri = Uri.parse("content://media" + filePath); else curUri = Uri.parse(filePath); if (filePath.contains("video")) { int videoId = 0; try { videoId = Integer.parseInt(curUri.getLastPathSegment()); } catch (NumberFormatException e) { } ContentResolver crThumb = ctx.getContentResolver(); BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 1; return MediaStore.Video.Thumbnails.getThumbnail(crThumb, videoId, MediaStore.Video.Thumbnails.MINI_KIND, options); } else { int[] dimensions = getImageSize(curUri, ctx); float conversionFactor = 0.40f; if (dimensions[0] > dimensions[1]) //width > height conversionFactor = 0.60f; int resizedWidth = (int) (width * conversionFactor); // create resized picture int rotation = getImageOrientation(ctx, filePath); byte[] bytes = createThumbnailFromUri(ctx, curUri, resizedWidth, null, rotation); // upload resized picture if (bytes != null && bytes.length > 0) { return BitmapFactory.decodeByteArray(bytes, 0, bytes.length); } else { return null; } } } public Bitmap getThumbnailForWPImageSpan(Bitmap largeBitmap, int resizeWidth) { if (largeBitmap.getWidth() < resizeWidth) return largeBitmap; //Do not resize. float percentage = (float) resizeWidth / largeBitmap.getWidth(); float proportionateHeight = largeBitmap.getHeight() * percentage; int resizeHeight = (int) Math.rint(proportionateHeight); return Bitmap.createScaledBitmap(largeBitmap, resizeWidth, resizeHeight, true); } /** * nbradbury - 21-Feb-2014 - similar to createThumbnail but more efficient since it doesn't * require passing the full-size image as an array of bytes[] */ public byte[] createThumbnailFromUri(Context context, Uri imageUri, int maxWidth, String fileExtension, int rotation) { if (context == null || imageUri == null) return null; String filePath = null; if (imageUri.toString().contains("content:")) { String[] projection = new String[]{Images.Media.DATA}; Cursor cur = context.getContentResolver().query(imageUri, projection, null, null, null); if (cur != null) { if (cur.moveToFirst()) { int dataColumn = cur.getColumnIndex(Images.Media.DATA); filePath = cur.getString(dataColumn); } cur.close(); } } if (TextUtils.isEmpty(filePath)) { //access the file directly filePath = imageUri.toString().replace("content://media", ""); filePath = filePath.replace("file://", ""); } // get just the image bounds BitmapFactory.Options optBounds = new BitmapFactory.Options(); optBounds.inJustDecodeBounds = true; try { BitmapFactory.decodeFile(filePath, optBounds); } catch (OutOfMemoryError e) { BWMobileStatsUtil.trackEventForSelfHostedAndWPCom(BWMobileStatsUtil.StatsEventMediaOutOfMemory); return null; } // determine correct scale value (should be power of 2) // http://stackoverflow.com/questions/477572/android-strange-out-of-memory-issue/3549021#3549021 int scale = 1; if (maxWidth > 0 && optBounds.outWidth > maxWidth) { double d = Math.pow(2, (int) Math.round(Math.log(maxWidth / (double) optBounds.outWidth) / Math.log(0.5))); scale = (int) d; } BitmapFactory.Options optActual = new BitmapFactory.Options(); optActual.inSampleSize = scale; // Get the roughly resized bitmap Bitmap bmpResized; try { bmpResized = BitmapFactory.decodeFile(filePath, optActual); } catch (OutOfMemoryError e) { BWMobileStatsUtil.trackEventForSelfHostedAndWPCom(BWMobileStatsUtil.StatsEventMediaOutOfMemory); return null; } if (bmpResized == null) return null; ByteArrayOutputStream stream = new ByteArrayOutputStream(); // Now calculate exact scale in order to resize accurately float percentage = (float) maxWidth / bmpResized.getWidth(); float proportionateHeight = bmpResized.getHeight() * percentage; int finalHeight = (int) Math.rint(proportionateHeight); float scaleWidth = ((float) maxWidth) / bmpResized.getWidth(); float scaleHeight = ((float) finalHeight) / bmpResized.getHeight(); float scaleBy = Math.min(scaleWidth, scaleHeight); // Resize the bitmap to exact size Matrix matrix = new Matrix(); matrix.postScale(scaleBy, scaleBy); // apply rotation if (rotation != 0) { matrix.setRotate(rotation); } Bitmap.CompressFormat fmt; if (fileExtension != null && fileExtension.equalsIgnoreCase("png")) { fmt = Bitmap.CompressFormat.PNG; } else { fmt = Bitmap.CompressFormat.JPEG; } final Bitmap bmpRotated; try { bmpRotated = Bitmap.createBitmap(bmpResized, 0, 0, bmpResized.getWidth(), bmpResized.getHeight(), matrix, true); } catch (OutOfMemoryError e) { BWMobileStatsUtil.trackEventForSelfHostedAndWPCom(BWMobileStatsUtil.StatsEventMediaOutOfMemory); return null; } bmpRotated.compress(fmt, 100, stream); bmpResized.recycle(); bmpRotated.recycle(); return stream.toByteArray(); } public interface BitmapWorkerCallback { public void onBitmapReady(String filePath, ImageView imageView, Bitmap bitmap); } public static class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> { private final WeakReference<ImageView> imageViewReference; private final BitmapWorkerCallback callback; private int targetWidth; private int targetHeight; private String path; public BitmapWorkerTask(ImageView imageView, int width, int height, BitmapWorkerCallback callback) { // Use a WeakReference to ensure the ImageView can be garbage collected imageViewReference = new WeakReference<ImageView>(imageView); this.callback = callback; targetWidth = width; targetHeight = height; } // Decode image in background. @Override protected Bitmap doInBackground(String... params) { path = params[0]; BitmapFactory.Options bfo = new BitmapFactory.Options(); bfo.inJustDecodeBounds = true; BitmapFactory.decodeFile(path, bfo); bfo.inSampleSize = calculateInSampleSize(bfo, targetWidth, targetHeight); bfo.inJustDecodeBounds = false; // get proper rotation try { File f = new File(path); ExifInterface exif = new ExifInterface(f.getPath()); int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); int angle = 0; if (orientation == ExifInterface.ORIENTATION_NORMAL) { // no need to rotate return BitmapFactory.decodeFile(path, bfo); } else if (orientation == ExifInterface.ORIENTATION_ROTATE_90) { angle = 90; } else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) { angle = 180; } else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) { angle = 270; } Matrix mat = new Matrix(); mat.postRotate(angle); Bitmap bmp = BitmapFactory.decodeStream(new FileInputStream(f), null, bfo); return Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), mat, true); } catch (IOException e) { AppLog.e(AppLog.T.UTILS, "Error in setting image", e); } catch (OutOfMemoryError oom) { BWMobileStatsUtil.trackEventForSelfHostedAndWPCom(BWMobileStatsUtil.StatsEventMediaOutOfMemory); AppLog.e(AppLog.T.UTILS, "OutOfMemoryError Error in setting image: " + oom); } return null; } // Once complete, see if ImageView is still around and set bitmap. @Override protected void onPostExecute(Bitmap bitmap) { if (imageViewReference == null || bitmap == null) return; final ImageView imageView = imageViewReference.get(); if (callback != null) callback.onBitmapReady(path, imageView, bitmap); } } }