summaryrefslogtreecommitdiffstats
path: root/src/org/cyanogenmod/wallpaperpicker/CropView.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/cyanogenmod/wallpaperpicker/CropView.java')
-rw-r--r--src/org/cyanogenmod/wallpaperpicker/CropView.java321
1 files changed, 321 insertions, 0 deletions
diff --git a/src/org/cyanogenmod/wallpaperpicker/CropView.java b/src/org/cyanogenmod/wallpaperpicker/CropView.java
new file mode 100644
index 0000000..118efd3
--- /dev/null
+++ b/src/org/cyanogenmod/wallpaperpicker/CropView.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * 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 org.cyanogenmod.wallpaperpicker;
+
+import android.content.Context;
+import android.graphics.Matrix;
+import android.graphics.Point;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import android.view.ScaleGestureDetector.OnScaleGestureListener;
+import android.view.ViewConfiguration;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+
+import com.android.photos.views.TiledImageRenderer.TileSource;
+import com.android.photos.views.TiledImageView;
+
+public class CropView extends TiledImageView implements OnScaleGestureListener {
+
+ private ScaleGestureDetector mScaleGestureDetector;
+ private long mTouchDownTime;
+ private float mFirstX, mFirstY;
+ private float mLastX, mLastY;
+ private float mCenterX, mCenterY;
+ private float mMinScale;
+ private boolean mTouchEnabled = true;
+ private RectF mTempEdges = new RectF();
+ private float[] mTempPoint = new float[] { 0, 0 };
+ private float[] mTempCoef = new float[] { 0, 0 };
+ private float[] mTempAdjustment = new float[] { 0, 0 };
+ private float[] mTempImageDims = new float[] { 0, 0 };
+ private float[] mTempRendererCenter = new float[] { 0, 0 };
+ TouchCallback mTouchCallback;
+ Matrix mRotateMatrix;
+ Matrix mInverseRotateMatrix;
+
+ public interface TouchCallback {
+ void onTouchDown();
+ void onTap();
+ void onTouchUp();
+ }
+
+ public CropView(Context context) {
+ this(context, null);
+ }
+
+ public CropView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mScaleGestureDetector = new ScaleGestureDetector(context, this);
+ mRotateMatrix = new Matrix();
+ mInverseRotateMatrix = new Matrix();
+ }
+
+ private float[] getImageDims() {
+ final float imageWidth = mRenderer.source.getImageWidth();
+ final float imageHeight = mRenderer.source.getImageHeight();
+ float[] imageDims = mTempImageDims;
+ imageDims[0] = imageWidth;
+ imageDims[1] = imageHeight;
+ mRotateMatrix.mapPoints(imageDims);
+ imageDims[0] = Math.abs(imageDims[0]);
+ imageDims[1] = Math.abs(imageDims[1]);
+ return imageDims;
+ }
+
+ private void getEdgesHelper(RectF edgesOut) {
+ final float width = getWidth();
+ final float height = getHeight();
+ final float[] imageDims = getImageDims();
+ final float imageWidth = imageDims[0];
+ final float imageHeight = imageDims[1];
+
+ float initialCenterX = mRenderer.source.getImageWidth() / 2f;
+ float initialCenterY = mRenderer.source.getImageHeight() / 2f;
+
+ float[] rendererCenter = mTempRendererCenter;
+ rendererCenter[0] = mCenterX - initialCenterX;
+ rendererCenter[1] = mCenterY - initialCenterY;
+ mRotateMatrix.mapPoints(rendererCenter);
+ rendererCenter[0] += imageWidth / 2;
+ rendererCenter[1] += imageHeight / 2;
+
+ final float scale = mRenderer.scale;
+ float centerX = (width / 2f - rendererCenter[0] + (imageWidth - width) / 2f)
+ * scale + width / 2f;
+ float centerY = (height / 2f - rendererCenter[1] + (imageHeight - height) / 2f)
+ * scale + height / 2f;
+ float leftEdge = centerX - imageWidth / 2f * scale;
+ float rightEdge = centerX + imageWidth / 2f * scale;
+ float topEdge = centerY - imageHeight / 2f * scale;
+ float bottomEdge = centerY + imageHeight / 2f * scale;
+
+ edgesOut.left = leftEdge;
+ edgesOut.right = rightEdge;
+ edgesOut.top = topEdge;
+ edgesOut.bottom = bottomEdge;
+ }
+
+ public int getImageRotation() {
+ return mRenderer.rotation;
+ }
+
+ public RectF getCrop() {
+ final RectF edges = mTempEdges;
+ getEdgesHelper(edges);
+ final float scale = mRenderer.scale;
+
+ float cropLeft = -edges.left / scale;
+ float cropTop = -edges.top / scale;
+ float cropRight = cropLeft + getWidth() / scale;
+ float cropBottom = cropTop + getHeight() / scale;
+
+ return new RectF(cropLeft, cropTop, cropRight, cropBottom);
+ }
+
+ public Point getSourceDimensions() {
+ return new Point(mRenderer.source.getImageWidth(), mRenderer.source.getImageHeight());
+ }
+
+ public void setTileSource(TileSource source, Runnable isReadyCallback) {
+ super.setTileSource(source, isReadyCallback);
+ mCenterX = mRenderer.centerX;
+ mCenterY = mRenderer.centerY;
+ mRotateMatrix.reset();
+ mRotateMatrix.setRotate(mRenderer.rotation);
+ mInverseRotateMatrix.reset();
+ mInverseRotateMatrix.setRotate(-mRenderer.rotation);
+ updateMinScale(getWidth(), getHeight(), source, true);
+ }
+
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ updateMinScale(w, h, mRenderer.source, false);
+ }
+
+ public void setScale(float scale) {
+ synchronized (mLock) {
+ mRenderer.scale = scale;
+ }
+ }
+
+ private void updateMinScale(int w, int h, TileSource source, boolean resetScale) {
+ synchronized (mLock) {
+ if (resetScale) {
+ mRenderer.scale = 1;
+ }
+ if (source != null) {
+ final float[] imageDims = getImageDims();
+ final float imageWidth = imageDims[0];
+ final float imageHeight = imageDims[1];
+ mMinScale = Math.max(w / imageWidth, h / imageHeight);
+ mRenderer.scale =
+ Math.max(mMinScale, resetScale ? Float.MIN_VALUE : mRenderer.scale);
+ }
+ }
+ }
+
+ @Override
+ public boolean onScaleBegin(ScaleGestureDetector detector) {
+ return true;
+ }
+
+ @Override
+ public boolean onScale(ScaleGestureDetector detector) {
+ // Don't need the lock because this will only fire inside of
+ // onTouchEvent
+ mRenderer.scale *= detector.getScaleFactor();
+ mRenderer.scale = Math.max(mMinScale, mRenderer.scale);
+ invalidate();
+ return true;
+ }
+
+ @Override
+ public void onScaleEnd(ScaleGestureDetector detector) {
+ }
+
+ public void moveToLeft() {
+ if (getWidth() == 0 || getHeight() == 0) {
+ final ViewTreeObserver observer = getViewTreeObserver();
+ observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
+ public void onGlobalLayout() {
+ moveToLeft();
+ getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ }
+ });
+ }
+ final RectF edges = mTempEdges;
+ getEdgesHelper(edges);
+ final float scale = mRenderer.scale;
+ mCenterX += Math.ceil(edges.left / scale);
+ updateCenter();
+ }
+
+ private void updateCenter() {
+ mRenderer.centerX = Math.round(mCenterX);
+ mRenderer.centerY = Math.round(mCenterY);
+ }
+
+ public void setTouchEnabled(boolean enabled) {
+ mTouchEnabled = enabled;
+ }
+
+ public void setTouchCallback(TouchCallback cb) {
+ mTouchCallback = cb;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ int action = event.getActionMasked();
+ final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP;
+ final int skipIndex = pointerUp ? event.getActionIndex() : -1;
+
+ // Determine focal point
+ float sumX = 0, sumY = 0;
+ final int count = event.getPointerCount();
+ for (int i = 0; i < count; i++) {
+ if (skipIndex == i)
+ continue;
+ sumX += event.getX(i);
+ sumY += event.getY(i);
+ }
+ final int div = pointerUp ? count - 1 : count;
+ float x = sumX / div;
+ float y = sumY / div;
+
+ if (action == MotionEvent.ACTION_DOWN) {
+ mFirstX = x;
+ mFirstY = y;
+ mTouchDownTime = System.currentTimeMillis();
+ if (mTouchCallback != null) {
+ mTouchCallback.onTouchDown();
+ }
+ } else if (action == MotionEvent.ACTION_UP) {
+ ViewConfiguration config = ViewConfiguration.get(getContext());
+
+ float squaredDist = (mFirstX - x) * (mFirstX - x) + (mFirstY - y) * (mFirstY - y);
+ float slop = config.getScaledTouchSlop() * config.getScaledTouchSlop();
+ long now = System.currentTimeMillis();
+ if (mTouchCallback != null) {
+ // only do this if it's a small movement
+ if (squaredDist < slop &&
+ now < mTouchDownTime + ViewConfiguration.getTapTimeout()) {
+ mTouchCallback.onTap();
+ }
+ mTouchCallback.onTouchUp();
+ }
+ }
+
+ if (!mTouchEnabled) {
+ return true;
+ }
+
+ synchronized (mLock) {
+ mScaleGestureDetector.onTouchEvent(event);
+ switch (action) {
+ case MotionEvent.ACTION_MOVE:
+ float[] point = mTempPoint;
+ point[0] = (mLastX - x) / mRenderer.scale;
+ point[1] = (mLastY - y) / mRenderer.scale;
+ mInverseRotateMatrix.mapPoints(point);
+ mCenterX += point[0];
+ mCenterY += point[1];
+ updateCenter();
+ invalidate();
+ break;
+ }
+ if (mRenderer.source != null) {
+ // Adjust position so that the wallpaper covers the entire area
+ // of the screen
+ final RectF edges = mTempEdges;
+ getEdgesHelper(edges);
+ final float scale = mRenderer.scale;
+
+ float[] coef = mTempCoef;
+ coef[0] = 1;
+ coef[1] = 1;
+ mRotateMatrix.mapPoints(coef);
+ float[] adjustment = mTempAdjustment;
+ mTempAdjustment[0] = 0;
+ mTempAdjustment[1] = 0;
+ if (edges.left > 0) {
+ adjustment[0] = edges.left / scale;
+ } else if (edges.right < getWidth()) {
+ adjustment[0] = (edges.right - getWidth()) / scale;
+ }
+ if (edges.top > 0) {
+ adjustment[1] = (float) Math.ceil(edges.top / scale);
+ } else if (edges.bottom < getHeight()) {
+ adjustment[1] = (edges.bottom - getHeight()) / scale;
+ }
+ for (int dim = 0; dim <= 1; dim++) {
+ if (coef[dim] > 0) adjustment[dim] = (float) Math.ceil(adjustment[dim]);
+ }
+
+ mInverseRotateMatrix.mapPoints(adjustment);
+ mCenterX += adjustment[0];
+ mCenterY += adjustment[1];
+ updateCenter();
+ }
+ }
+
+ mLastX = x;
+ mLastY = y;
+ return true;
+ }
+}