content: /*
   * Copyright (c) 2012 Jason Polites
   * 
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   * 
   *     http://www.apache.org/licenses/LICENSE-2.0
   * 
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package com.polites;
  
  import java.io.InputStream;
  import java.util.concurrent.Semaphore;
  import java.util.concurrent.TimeUnit;
  
  import android.content.Context;
  import android.content.res.Configuration;
  import android.database.Cursor;
  import android.graphics.Bitmap;
  import android.graphics.BitmapFactory;
  import android.graphics.Canvas;
  import android.graphics.ColorFilter;
  import android.graphics.Matrix;
  import android.graphics.drawable.BitmapDrawable;
  import android.graphics.drawable.Drawable;
  import android.net.Uri;
  import android.provider.MediaStore;
  import android.util.AttributeSet;
  import android.util.Log;
  import android.view.MotionEvent;
  import android.view.View;
  import android.view.ViewGroup.LayoutParams;
  import android.widget.ImageView;
  
  public class GestureImageView extends ImageView  {
  
  	public static final String GLOBAL_NS = "http://schemas.android.com/apk/res/android";
  	public static final String LOCAL_NS = "http://schemas.polites.com/android";
  
  	private final Semaphore drawLock = new Semaphore(0);
  	private Animator animator;
  
  	private Drawable drawable;
  
  	private float x = 0, y = 0;
  
  	private boolean layout = false;
  
  	private float scaleAdjust = 1.0f;
  	private float startingScale = -1.0f;
  
  	private float scale = 1.0f;
  	private float maxScale = 5.0f;
  	private float minScale = 0.75f;
  	private float fitScaleHorizontal = 1.0f;
  	private float fitScaleVertical = 1.0f;
  	private float rotation = 0.0f;
  
  	private float centerX;
  	private float centerY;
  	
  	private Float startX, startY;
  
  	private int hWidth;
  	private int hHeight;
  
  	private int resId = -1;
  	private boolean recycle = false;
  	private boolean strict = false;
  
  	private int displayHeight;
  	private int displayWidth;
  
  	private int alpha = 255;
  	private ColorFilter colorFilter;
  
  	private int deviceOrientation = -1;
  	private int imageOrientation;
  
  	private GestureImageViewListener gestureImageViewListener;
  	private GestureImageViewTouchListener gestureImageViewTouchListener;
  	
  	private OnTouchListener customOnTouchListener;
  	private OnClickListener onClickListener;
  
  	public GestureImageView(Context context, AttributeSet attrs, int defStyle) {
  		this(context, attrs);
  	}
  
  	public GestureImageView(Context context, AttributeSet attrs) {
  		super(context, attrs);
  		
  		String scaleType = attrs.getAttributeValue(GLOBAL_NS, "scaleType");
  		
  		if(scaleType == null || scaleType.trim().length() == 0) {
  			setScaleType(ScaleType.CENTER_INSIDE);
  		}
  		
  		String strStartX = attrs.getAttributeValue(LOCAL_NS, "start-x");
  		String strStartY = attrs.getAttributeValue(LOCAL_NS, "start-y");
  		
  		if(strStartX != null && strStartX.trim().length() > 0) {
  			startX = Float.parseFloat(strStartX);
  		}
  		
  		if(strStartY != null && strStartY.trim().length() > 0) {
  			startY = Float.parseFloat(strStartY);
  		}
  		
  		setStartingScale(attrs.getAttributeFloatValue(LOCAL_NS, "start-scale", startingScale));
  		setMinScale(attrs.getAttributeFloatValue(LOCAL_NS, "min-scale", minScale));
  		setMaxScale(attrs.getAttributeFloatValue(LOCAL_NS, "max-scale", maxScale));
  		setStrict(attrs.getAttributeBooleanValue(LOCAL_NS, "strict", strict));
  		setRecycle(attrs.getAttributeBooleanValue(LOCAL_NS, "recycle", recycle));
  		
  		initImage();
  		setGestureImageViewListener(defaultImageViewListener);
  	}
  
  	public GestureImageView(Context context) {
  		super(context);
  		setScaleType(ScaleType.CENTER_INSIDE);
  		initImage();
  		
  		setGestureImageViewListener(defaultImageViewListener);
  	}
  
  	@Override
  	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  
  		if(drawable != null) {
  			int orientation = getResources().getConfiguration().orientation;
  			if(orientation == Configuration.ORIENTATION_LANDSCAPE) {
  				displayHeight = MeasureSpec.getSize(heightMeasureSpec);
  
  				if(getLayoutParams().width == LayoutParams.WRAP_CONTENT) {
  					float ratio = (float) getImageWidth() / (float) getImageHeight();
  					displayWidth = Math.round( (float) displayHeight * ratio) ;
  				}
  				else {
  					displayWidth = MeasureSpec.getSize(widthMeasureSpec);
  				}
  			}
  			else {
  				displayWidth = MeasureSpec.getSize(widthMeasureSpec);
  				if(getLayoutParams().height == LayoutParams.WRAP_CONTENT) {
  					float ratio = (float) getImageHeight() / (float) getImageWidth();
  					displayHeight = Math.round( (float) displayWidth * ratio) ;
  				}
  				else {
  					displayHeight = MeasureSpec.getSize(heightMeasureSpec);
  				}				
  			}
  		}
  		else {
  			displayHeight = MeasureSpec.getSize(heightMeasureSpec);
  			displayWidth = MeasureSpec.getSize(widthMeasureSpec);
  		}
  
  		setMeasuredDimension(displayWidth, displayHeight);
  	}
  
  	@Override
  	protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
  		super.onLayout(changed, left, top, right, bottom);
  		if(changed || !layout) {
  			setupCanvas(displayWidth, displayHeight, getResources().getConfiguration().orientation);
  		}
  	}
  
  	protected void setupCanvas(int measuredWidth, int measuredHeight, int orientation) {
  
  		if(deviceOrientation != orientation) {
  			layout = false;
  			deviceOrientation = orientation;
  		}
  
  		if(drawable != null && !layout) {
  			int imageWidth = getImageWidth();
  			int imageHeight = getImageHeight();
  
  			hWidth = Math.round(((float)imageWidth / 2.0f));
  			hHeight = Math.round(((float)imageHeight / 2.0f));
  			
  			measuredWidth -= (getPaddingLeft() + getPaddingRight());
  			measuredHeight -= (getPaddingTop() + getPaddingBottom());
  			
  			computeCropScale(imageWidth, imageHeight, measuredWidth, measuredHeight);
  			
  			if(startingScale <= 0.0f) {
  				computeStartingScale(imageWidth, imageHeight, measuredWidth, measuredHeight);
  			}
  
  			scaleAdjust = startingScale;
  
  			this.centerX = (float) measuredWidth / 2.0f;
  			this.centerY = (float) measuredHeight / 2.0f;
  			
  			if(startX == null) {
  				x = centerX;
  			}
  			else {
  				x = startX;
  			}
  
  			if(startY == null) {
  				y = centerY;
  			}
  			else {
  				y = startY;
  			}	
  
  			gestureImageViewTouchListener = new GestureImageViewTouchListener(this, measuredWidth, measuredHeight);
  			
  			if(isLandscape()) {
  				gestureImageViewTouchListener.setMinScale(minScale * fitScaleHorizontal);
  			}
  			else {
  				gestureImageViewTouchListener.setMinScale(minScale * fitScaleVertical);
  			}
  			
  			
  			gestureImageViewTouchListener.setMaxScale(maxScale * startingScale);
  			
  			gestureImageViewTouchListener.setFitScaleHorizontal(fitScaleHorizontal);
  			gestureImageViewTouchListener.setFitScaleVertical(fitScaleVertical);
  			gestureImageViewTouchListener.setCanvasWidth(measuredWidth);
  			gestureImageViewTouchListener.setCanvasHeight(measuredHeight);
  			gestureImageViewTouchListener.setOnClickListener(onClickListener);
  
  			drawable.setBounds(-hWidth,-hHeight,hWidth,hHeight);
  
  			super.setOnTouchListener(new OnTouchListener() {
  				@Override
  				public boolean onTouch(View v, MotionEvent event) {
  					if(customOnTouchListener != null) {
  						customOnTouchListener.onTouch(v, event);
  					}
  					return gestureImageViewTouchListener.onTouch(v, event);
  				}
  			});	
  
  			layout = true;
  		}
  	}
  	
  	protected void computeCropScale(int imageWidth, int imageHeight, int measuredWidth, int measuredHeight) {
  		fitScaleHorizontal = (float) measuredWidth / (float) imageWidth;
  		fitScaleVertical = (float) measuredHeight / (float) imageHeight;
  	}
  	
  	@SuppressWarnings("incomplete-switch")
  	protected void computeStartingScale(int imageWidth, int imageHeight, int measuredWidth, int measuredHeight) {
  		switch(getScaleType()) {
  			case CENTER:
  				// Center the image in the view, but perform no scaling.
  				startingScale = 1.0f;
  				break;
  				
  			case CENTER_CROP:
  				// Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions
  				// (width and height) of the image will be equal to or larger than the corresponding dimension of the view (minus padding).
  				startingScale = Math.max((float) measuredHeight / (float) imageHeight, (float) measuredWidth/ (float) imageWidth);
  				break;
  				
  			case CENTER_INSIDE:
  
  				// Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions
  				// (width and height) of the image will be equal to or less than the corresponding dimension of the view (minus padding).
  				float wRatio = (float) imageWidth / (float) measuredWidth;
  				float hRatio = (float) imageHeight / (float) measuredHeight;
  
  				if(wRatio > hRatio) {
  					startingScale = fitScaleHorizontal;
  				}
  				else {
  					startingScale = fitScaleVertical;
  				}
  
  				break;
  		}
  	}
  
  	protected boolean isRecycled() {
  		if(drawable != null && drawable instanceof BitmapDrawable) {
  			Bitmap bitmap = ((BitmapDrawable)drawable).getBitmap();
  			if(bitmap != null) {
  				return bitmap.isRecycled();
  			}
  		}
  		return false;
  	}
  
  	protected void recycle() {
  		if(recycle && drawable != null && drawable instanceof BitmapDrawable) {
  			Bitmap bitmap = ((BitmapDrawable)drawable).getBitmap();
  			if(bitmap != null) {
  				bitmap.recycle();
  			}
  		}
  	}
  	
  	@Override
  	protected void onDraw(Canvas canvas) {
  		if(layout) {
  			if(drawable != null && !isRecycled()) {
  				canvas.save();
  				
  				float adjustedScale = scale * scaleAdjust;
  
  				canvas.translate(x, y);
  
  				if(rotation != 0.0f) {
  					canvas.rotate(rotation);
  				}
  
  				if(adjustedScale != 1.0f) {
  					canvas.scale(adjustedScale, adjustedScale);
  				}
  
  				drawable.draw(canvas);
  
  				canvas.restore();
  			}
  
  			if(drawLock.availablePermits() <= 0) {
  				drawLock.release();
  			}
  		}
  	}
  
  	/**
  	 * Waits for a draw
  	 * @param max time to wait for draw (ms)
  	 * @throws InterruptedException
  	 */
  	public boolean waitForDraw(long timeout) throws InterruptedException {
  		return drawLock.tryAcquire(timeout, TimeUnit.MILLISECONDS);
  	}
  
  	@Override
  	protected void onAttachedToWindow() {
  		animator = new Animator(this, "GestureImageViewAnimator");
  		animator.start();
  
  		if(resId >= 0 && drawable == null) {
  			setImageResource(resId);
  		}
  
  		super.onAttachedToWindow();
  	}
  
  	public void animationStart(Animation animation) {
  		if(animator != null) {
  			animator.play(animation);
  		}
  	}
  
  	public void animationStop() {
  		if(animator != null) {
  			animator.cancel();
  		}
  	}
  
  	@Override
  	protected void onDetachedFromWindow() {
  		if(animator != null) {
  			animator.finish();
  		}
  		if(recycle && drawable != null && !isRecycled()) {
  			recycle();
  			drawable = null;
  		}
  		super.onDetachedFromWindow();
  	}
  
  	protected void initImage() {
  		if(this.drawable != null) {
  			this.drawable.setAlpha(alpha);
  			this.drawable.setFilterBitmap(true);
  			if(colorFilter != null) {
  				this.drawable.setColorFilter(colorFilter);
  			}
  		}
  		
  		if(!layout) {
  			requestLayout();
  			redraw();
  		}
  	}
  
  	public void setImageBitmap(Bitmap image) {
  		this.drawable = new BitmapDrawable(getResources(), image);
  		initImage();
  	}
  
  	@Override
  	public void setImageDrawable(Drawable drawable) {
  		this.drawable = drawable;
  		initImage();
  	}
  
  	public void setImageResource(int id) {
  		if(this.drawable != null) {
  			this.recycle();
  		}
  		if(id >= 0) {
  			this.resId = id;
  			setImageDrawable(getContext().getResources().getDrawable(id));
  		}
  	}
  
  	public int getScaledWidth() {
  		return Math.round(getImageWidth() * getScale());
  	}
  	
  	public int getScaledHeight() {
  		return Math.round(getImageHeight() * getScale());
  	}
  	
  	public int getImageWidth() {
  		if(drawable != null) {
  			return drawable.getIntrinsicWidth();
  		}
  		return 0;
  	}
  
  	public int getImageHeight() {
  		if(drawable != null) {
  			return drawable.getIntrinsicHeight();
  		}
  		return 0;
  	}
  
  	public void moveBy(float x, float y) {
  		this.x += x;
  		this.y += y;
  	}
  
  	public void setPosition(float x, float y) {
  		this.x = x;
  		this.y = y;
  	}
  
  	public void redraw() {
  		postInvalidate();
  	}
  
  	public void setMinScale(float min) {
  		this.minScale = min;
  		if(gestureImageViewTouchListener != null) {
  			gestureImageViewTouchListener.setMinScale(min * fitScaleHorizontal);
  		}
  	}
  
  	public void setMaxScale(float max) {
  		this.maxScale = max;
  		if(gestureImageViewTouchListener != null) {
  			gestureImageViewTouchListener.setMaxScale(max * startingScale);
  		}
  	}
  
  	public void setScale(float scale) {
  		scaleAdjust = scale;
  	}
  
  	public float getScale() {
  		return scaleAdjust;
  	}
  
  	public float getImageX() {
  		return x;
  	}
  
  	public float getImageY() {
  		return y;
  	}
  
  	public boolean isStrict() {
  		return strict;
  	}
  
  	public void setStrict(boolean strict) {
  		this.strict = strict;
  	}
  
  	public boolean isRecycle() {
  		return recycle;
  	}
  
  	public void setRecycle(boolean recycle) {
  		this.recycle = recycle;
  	}
  
  	public void reset() {
  		x = centerX;
  		y = centerY;
  		scaleAdjust = startingScale;
  		if (gestureImageViewTouchListener != null) {
  		    gestureImageViewTouchListener.reset();
  		}
  		redraw();
  	}
  
  	public void setRotation(float rotation) {
  		this.rotation = rotation;
  	}
  
  	public void setGestureImageViewListener(GestureImageViewListener pinchImageViewListener) {
  		this.gestureImageViewListener = pinchImageViewListener;
  	}
  
  	public GestureImageViewListener getGestureImageViewListener() {
  		return gestureImageViewListener;
  	}
  
  	@Override
  	public Drawable getDrawable() {
  		return drawable;
  	}
  
  	@Override
  	public void setAlpha(int alpha) {
  		this.alpha = alpha;
  		if(drawable != null) {
  			drawable.setAlpha(alpha);
  		}
  	}
  
  	@Override
  	public void setColorFilter(ColorFilter cf) {
  		this.colorFilter = cf;
  		if(drawable != null) {
  			drawable.setColorFilter(cf);
  		}
  	}
  
  	@Override
  	public void setImageURI(Uri mUri) {
  		if ("content".equals(mUri.getScheme())) {
  			try {
  				String[] orientationColumn = {MediaStore.Images.Media.ORIENTATION};
  				
  				Cursor cur = getContext().getContentResolver().query(mUri, orientationColumn, null, null, null);
  				
  				if (cur != null && cur.moveToFirst()) {
  					imageOrientation = cur.getInt(cur.getColumnIndex(orientationColumn[0]));
  				}  
  				
  				InputStream in = null;
  				
  				try {
  					in = getContext().getContentResolver().openInputStream(mUri);
  					Bitmap bmp = BitmapFactory.decodeStream(in);
  					
  					if(imageOrientation != 0) {
  						Matrix m = new Matrix();
  						m.postRotate(imageOrientation);
  						Bitmap rotated = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);
  						bmp.recycle();
  						setImageDrawable(new BitmapDrawable(getResources(), rotated));
  					}
  					else {
  						setImageDrawable(new BitmapDrawable(getResources(), bmp));
  					}
  				}
  				finally {
  					if(in != null) {
  						in.close();
  					}
  					
  					if(cur != null) {
  						cur.close();
  					}
  				}
  			}
  			catch (Exception e) {
  				Log.w("GestureImageView", "Unable to open content: " + mUri, e);
  			}
  		}
  		else {
  			setImageDrawable(Drawable.createFromPath(mUri.toString()));
  		}
  
  		if (drawable == null) {
  			Log.e("GestureImageView", "resolveUri failed on bad bitmap uri: " + mUri);
  			// Don't try again.
  			mUri = null;
  		}
  	}
  
  	@Override
  	public Matrix getImageMatrix() {
  		if(strict) {
  			throw new UnsupportedOperationException("Not supported");
  		}		
  		return super.getImageMatrix();
  	}
  
  	@Override
  	public void setScaleType(ScaleType scaleType) {
  		if(scaleType == ScaleType.CENTER ||
  			scaleType == ScaleType.CENTER_CROP ||
  			scaleType == ScaleType.CENTER_INSIDE) {
  			
  			super.setScaleType(scaleType);
  		}
  		else if(strict) {
  			throw new UnsupportedOperationException("Not supported");
  		}
  	}
  
  	@Override
  	public void invalidateDrawable(Drawable dr) {
  		if(strict) {
  			throw new UnsupportedOperationException("Not supported");
  		}
  		super.invalidateDrawable(dr);
  	}
  
  	@Override
  	public int[] onCreateDrawableState(int extraSpace) {
  		if(strict) {
  			throw new UnsupportedOperationException("Not supported");
  		}
  		return super.onCreateDrawableState(extraSpace);
  	}
  
  	@Override
  	public void setAdjustViewBounds(boolean adjustViewBounds) {
  		if(strict) {
  			throw new UnsupportedOperationException("Not supported");
  		}
  		super.setAdjustViewBounds(adjustViewBounds);
  	}
  
  	@Override
  	public void setImageLevel(int level) {
  		if(strict) {
  			throw new UnsupportedOperationException("Not supported");
  		}
  		super.setImageLevel(level);
  	}
  
  	@Override
  	public void setImageMatrix(Matrix matrix) {
  		if(strict) {
  			throw new UnsupportedOperationException("Not supported");
  		}
  	}
  
  	@Override
  	public void setImageState(int[] state, boolean merge) {
  		if(strict) {
  			throw new UnsupportedOperationException("Not supported");
  		}
  	}
  
  	@Override
  	public void setSelected(boolean selected) {
  		if(strict) {
  			throw new UnsupportedOperationException("Not supported");
  		}
  		super.setSelected(selected);
  	}
  
  	@Override
  	public void setOnTouchListener(OnTouchListener l) {
  		this.customOnTouchListener = l;
  	}
  	
  	public float getCenterX() {
  		return centerX;
  	}
  	
  	public float getCenterY() {
  		return centerY;
  	}
  	
  	public boolean isLandscape() {
  		return getImageWidth() >= getImageHeight();
  	}
  	
  	public boolean isPortrait() {
  		return getImageWidth() <= getImageHeight();
  	}
  	
  	public void setStartingScale(float startingScale) {
  		this.startingScale = startingScale;
  	}
  	
  	public void setStartingPosition(float x, float y) {
  		this.startX = x;
  		this.startY = y;
  	}
  
  	@Override
  	public void setOnClickListener(OnClickListener l) {
  		this.onClickListener = l;
  		
  		if(gestureImageViewTouchListener != null) {
  			gestureImageViewTouchListener.setOnClickListener(l);
  		}
  	}
  
  	/**
  	 * Returns true if the image dimensions are aligned with the orientation of the device.
  	 * @return
  	 */
  	public boolean isOrientationAligned() {
  		if(deviceOrientation == Configuration.ORIENTATION_LANDSCAPE) {
  			return isLandscape();
  		}
  		else if(deviceOrientation == Configuration.ORIENTATION_PORTRAIT) {
  			return isPortrait();
  		}
  		return true;
  	}
  	
  	public int getDeviceOrientation() {
  		return deviceOrientation;
  	}
  	
  	public GestureImageViewListener defaultImageViewListener = new GestureImageViewListener() {
  
  		@Override
  		public void onTouch(float x, float y) {
  			// TODO Auto-generated method stub
  		}
  
  		@Override
  		public void onScale(float scale) {
  			// TODO Auto-generated method stub
  		}
  
  		@Override
  		public void onPosition(float x, float y) {
  			/** positionıʱ */
  			/** position = ͼƬֱViewԵľ */
  			/** position = viewˮƽ - ͼƬˮƽȵһ */
  			double halfImage = GestureImageView.this.getScaledWidth() / 2.0;
  			double viewWidth = GestureImageView.this.getWidth();
  			System.out.println(x);
  			System.out.println(halfImage + "::" + (viewWidth - halfImage));
  			if (x == halfImage || x == (viewWidth - halfImage)) {
  				GestureImageView.this.getParent().requestDisallowInterceptTouchEvent(false);
  			} else {
  				GestureImageView.this.getParent().requestDisallowInterceptTouchEvent(true);
  			}
  		}
  	};
  }
  