package org.commcare.views; import android.annotation.SuppressLint; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.graphics.drawable.Drawable; import android.net.Uri; import android.util.DisplayMetrics; import android.util.Pair; import android.util.TypedValue; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.Toast; import org.commcare.dalvik.R; import org.javarosa.core.reference.InvalidReferenceException; import org.javarosa.core.reference.ReferenceManager; import java.io.File; /** * @author wspride * Class used by MediaLayout for form images. Can be set to resize the * image using different algorithms based on the preference specified * by PreferencesActivity.KEY_RESIZE. Overrides setMaxWidth, setMaxHeight, * and onMeasure from the ImageView super class. */ @SuppressLint("NewApi") public class ResizingImageView extends ImageView { public static String resizeMethod; private int mMaxWidth; private int mMaxHeight; private final GestureDetector gestureDetector; private final ScaleGestureDetector scaleGestureDetector; private final String imageURI; private final String bigImageURI; private float scaleFactor = 1.0f; private final static float scaleFactorThreshhold = 1.2f; public ResizingImageView(Context context) { this(context, null, null); } public ResizingImageView(Context context, String imageURI, String bigImageURI) { super(context); gestureDetector = new GestureDetector(context, new GestureListener()); scaleGestureDetector = new ScaleGestureDetector(context, new ScaleListener()); this.imageURI = imageURI; this.bigImageURI = bigImageURI; ViewGroup.MarginLayoutParams imageViewParams = new ViewGroup.MarginLayoutParams( ViewGroup.MarginLayoutParams.WRAP_CONTENT, ViewGroup.MarginLayoutParams.WRAP_CONTENT); this.setLayoutParams(imageViewParams); } @Override public boolean onTouchEvent(MotionEvent e) { scaleGestureDetector.onTouchEvent(e); return gestureDetector.onTouchEvent(e); } @Override public void setMaxWidth(int maxWidth) { super.setMaxWidth(maxWidth); mMaxWidth = maxWidth; } @Override public void setMaxHeight(int maxHeight) { super.setMaxHeight(maxHeight); mMaxHeight = maxHeight; } private class GestureListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDown(MotionEvent e) { return true; } @Override public boolean onDoubleTap(MotionEvent e) { getSuggestedMinimumHeight(); setFullScreen(); return true; } } private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { @Override public boolean onScale(ScaleGestureDetector detector) { scaleFactor *= detector.getScaleFactor(); // don't let the object get too small or too large. scaleFactor = Math.max(0.1f, Math.min(scaleFactor, 5.0f)); if (scaleFactor > scaleFactorThreshhold) { setFullScreen(); } return true; } } private void setFullScreen() { String imageFileURI; if (bigImageURI != null) { imageFileURI = bigImageURI; } else if (imageURI != null) { imageFileURI = imageURI; } else { return; } try { String imageFilename = ReferenceManager.instance() .DeriveReference(imageFileURI).getLocalURI(); File bigImage = new File(imageFilename); Intent i = new Intent("android.intent.action.VIEW"); i.setDataAndType(Uri.fromFile(bigImage), "image/*"); getContext().startActivity(i); } catch (InvalidReferenceException e1) { e1.printStackTrace(); } catch (ActivityNotFoundException e) { Toast.makeText( getContext(), getContext().getString(R.string.activity_not_found, "view image"), Toast.LENGTH_SHORT).show(); } } private Pair<Integer, Integer> getWidthHeight(int widthMeasureSpec, int heightMeasureSpec, double imageScaleFactor) { int maxWidth = mMaxWidth; int maxHeight = mMaxHeight; if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) { maxWidth = Math.min(MeasureSpec.getSize(widthMeasureSpec), mMaxWidth); } if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { maxHeight = Math.min(MeasureSpec.getSize(heightMeasureSpec), mMaxHeight); } Drawable drawable = getDrawable(); float dWidth = dipToPixels(getContext(), drawable.getIntrinsicWidth()); float dHeight = dipToPixels(getContext(), drawable.getIntrinsicHeight()); float ratio = (dWidth) / dHeight; int width = (int)Math.min(Math.max(dWidth, getSuggestedMinimumWidth()), maxWidth); int height = (int)(width / ratio); height = Math.min(Math.max(height, getSuggestedMinimumHeight()), maxHeight); width = (int)(height * ratio); if (width > maxWidth) { width = maxWidth; height = (int)(width / ratio); } return new Pair<>(new Double(width * imageScaleFactor).intValue(), new Double(height * imageScaleFactor).intValue()); } /* * The meat and potatoes of the class. Determines what algorithm to use * to resize the image based on the KEY_RESIZE preference. Currently can be * "full", "width", or "none". Will always preserve aspect ratio. * * "full" attempts to use both the calculated height and width to scale the image. however, * its worth noting that the available height is dynamic and difficult to determine * "width" will always stretch/compress the image to make it the exact width of the screen while * maintaining the aspect ratio * "none" will leave the picture unchanged */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (resizeMethod.equals("full")) { Drawable drawable = getDrawable(); if (drawable != null) { Pair<Integer, Integer> mPair = this.getWidthHeight(widthMeasureSpec, heightMeasureSpec, 1); setMeasuredDimension(mPair.first, mPair.second); } } if (resizeMethod.equals("half")) { Drawable drawable = getDrawable(); if (drawable != null) { Pair<Integer, Integer> mPair = this.getWidthHeight(widthMeasureSpec, heightMeasureSpec, .5); setMeasuredDimension(mPair.first, mPair.second); } } else if (resizeMethod.equals("width")) { Drawable d = getDrawable(); if (d != null) { // ceil not round - avoid thin vertical gaps along the left/right edges int width = MeasureSpec.getSize(widthMeasureSpec); int height = (int)Math.ceil((float)width * (float)d.getIntrinsicHeight() / (float)d.getIntrinsicWidth()); setMeasuredDimension(width, height); } } } // helper method for algorithm above private static float dipToPixels(Context context, float dipValue) { DisplayMetrics metrics = context.getResources().getDisplayMetrics(); return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dipValue, metrics); } }