1f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka/* 2f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * Copyright (C) 2013 The Android Open Source Project 3f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * 4f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * Licensed under the Apache License, Version 2.0 (the "License"); 5f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * you may not use this file except in compliance with the License. 6f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * You may obtain a copy of the License at 7f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * 8f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * http://www.apache.org/licenses/LICENSE-2.0 9f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * 10f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * Unless required by applicable law or agreed to in writing, software 11f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * distributed under the License is distributed on an "AS IS" BASIS, 12f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * See the License for the specific language governing permissions and 14f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * limitations under the License. 15f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka */ 16f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 17748dfe025b128ea88ba626c8bfb7da315d5af29cSunny Goyalpackage com.android.wallpaperpicker; 18f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 19f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.content.Context; 20f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.graphics.Matrix; 21f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.graphics.Point; 22f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.graphics.RectF; 23f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.util.AttributeSet; 24f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.view.MotionEvent; 25f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.view.ScaleGestureDetector; 26f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.view.ScaleGestureDetector.OnScaleGestureListener; 27f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.view.ViewConfiguration; 28f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.view.ViewTreeObserver; 29f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.view.ViewTreeObserver.OnGlobalLayoutListener; 30f624f0bf0a28f1fcb193d6ed2a01c49e22b96e4aTony Wickham 31f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport com.android.photos.views.TiledImageRenderer.TileSource; 32f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport com.android.photos.views.TiledImageView; 33f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 34f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkapublic class CropView extends TiledImageView implements OnScaleGestureListener { 35f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 36f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka private ScaleGestureDetector mScaleGestureDetector; 37f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka private long mTouchDownTime; 38f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka private float mFirstX, mFirstY; 39f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka private float mLastX, mLastY; 40f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka private float mCenterX, mCenterY; 41f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka private float mMinScale; 42f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka private boolean mTouchEnabled = true; 43f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka private RectF mTempEdges = new RectF(); 44f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka private float[] mTempPoint = new float[] { 0, 0 }; 45f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka private float[] mTempCoef = new float[] { 0, 0 }; 46f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka private float[] mTempAdjustment = new float[] { 0, 0 }; 47f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka private float[] mTempImageDims = new float[] { 0, 0 }; 48f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka private float[] mTempRendererCenter = new float[] { 0, 0 }; 49f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka TouchCallback mTouchCallback; 50f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka Matrix mRotateMatrix; 51f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka Matrix mInverseRotateMatrix; 52f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 53f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka public interface TouchCallback { 54f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka void onTouchDown(); 55f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka void onTap(); 56f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka void onTouchUp(); 57f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 58f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 59f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka public CropView(Context context) { 60f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka this(context, null); 61f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 62f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 63f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka public CropView(Context context, AttributeSet attrs) { 64f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka super(context, attrs); 65f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mScaleGestureDetector = new ScaleGestureDetector(context, this); 66f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mRotateMatrix = new Matrix(); 67f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mInverseRotateMatrix = new Matrix(); 68f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 69f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 70f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka private float[] getImageDims() { 71f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka final float imageWidth = mRenderer.source.getImageWidth(); 72f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka final float imageHeight = mRenderer.source.getImageHeight(); 73f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka float[] imageDims = mTempImageDims; 74f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka imageDims[0] = imageWidth; 75f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka imageDims[1] = imageHeight; 76f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mRotateMatrix.mapPoints(imageDims); 77f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka imageDims[0] = Math.abs(imageDims[0]); 78f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka imageDims[1] = Math.abs(imageDims[1]); 79f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka return imageDims; 80f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 81f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 82f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka private void getEdgesHelper(RectF edgesOut) { 83f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka final float width = getWidth(); 84f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka final float height = getHeight(); 85f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka final float[] imageDims = getImageDims(); 86f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka final float imageWidth = imageDims[0]; 87f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka final float imageHeight = imageDims[1]; 88f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 89f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka float initialCenterX = mRenderer.source.getImageWidth() / 2f; 90f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka float initialCenterY = mRenderer.source.getImageHeight() / 2f; 91f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 92f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka float[] rendererCenter = mTempRendererCenter; 93f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka rendererCenter[0] = mCenterX - initialCenterX; 94f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka rendererCenter[1] = mCenterY - initialCenterY; 95f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mRotateMatrix.mapPoints(rendererCenter); 96f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka rendererCenter[0] += imageWidth / 2; 97f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka rendererCenter[1] += imageHeight / 2; 98f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 99f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka final float scale = mRenderer.scale; 100f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka float centerX = (width / 2f - rendererCenter[0] + (imageWidth - width) / 2f) 101f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * scale + width / 2f; 102f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka float centerY = (height / 2f - rendererCenter[1] + (imageHeight - height) / 2f) 103f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * scale + height / 2f; 104f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka float leftEdge = centerX - imageWidth / 2f * scale; 105f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka float rightEdge = centerX + imageWidth / 2f * scale; 106f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka float topEdge = centerY - imageHeight / 2f * scale; 107f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka float bottomEdge = centerY + imageHeight / 2f * scale; 108f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 109f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka edgesOut.left = leftEdge; 110f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka edgesOut.right = rightEdge; 111f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka edgesOut.top = topEdge; 112f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka edgesOut.bottom = bottomEdge; 113f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 114f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 115f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka public int getImageRotation() { 116f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka return mRenderer.rotation; 117f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 118f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 119f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka public RectF getCrop() { 120f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka final RectF edges = mTempEdges; 121f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka getEdgesHelper(edges); 122f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka final float scale = mRenderer.scale; 123f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 124f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka float cropLeft = -edges.left / scale; 125f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka float cropTop = -edges.top / scale; 126f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka float cropRight = cropLeft + getWidth() / scale; 127f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka float cropBottom = cropTop + getHeight() / scale; 128f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 129f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka return new RectF(cropLeft, cropTop, cropRight, cropBottom); 130f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 131f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 132f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka public Point getSourceDimensions() { 133f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka return new Point(mRenderer.source.getImageWidth(), mRenderer.source.getImageHeight()); 134f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 135f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 136f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka public void setTileSource(TileSource source, Runnable isReadyCallback) { 137f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka super.setTileSource(source, isReadyCallback); 138f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mCenterX = mRenderer.centerX; 139f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mCenterY = mRenderer.centerY; 140f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mRotateMatrix.reset(); 141f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mRotateMatrix.setRotate(mRenderer.rotation); 142f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mInverseRotateMatrix.reset(); 143f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mInverseRotateMatrix.setRotate(-mRenderer.rotation); 144f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka updateMinScale(getWidth(), getHeight(), source, true); 145f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 146f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 147f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka protected void onSizeChanged(int w, int h, int oldw, int oldh) { 148f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka updateMinScale(w, h, mRenderer.source, false); 149f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 150f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 151f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka public void setScale(float scale) { 152f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka synchronized (mLock) { 153f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mRenderer.scale = scale; 154f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 155f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 156f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 157f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka private void updateMinScale(int w, int h, TileSource source, boolean resetScale) { 158f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka synchronized (mLock) { 159f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka if (resetScale) { 160f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mRenderer.scale = 1; 161f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 162f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka if (source != null) { 163f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka final float[] imageDims = getImageDims(); 164f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka final float imageWidth = imageDims[0]; 165f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka final float imageHeight = imageDims[1]; 166f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mMinScale = Math.max(w / imageWidth, h / imageHeight); 167f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mRenderer.scale = 168f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka Math.max(mMinScale, resetScale ? Float.MIN_VALUE : mRenderer.scale); 169f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 170f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 171f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 172f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 173f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka @Override 174f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka public boolean onScaleBegin(ScaleGestureDetector detector) { 175f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka return true; 176f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 177f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 178f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka @Override 179f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka public boolean onScale(ScaleGestureDetector detector) { 180f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka // Don't need the lock because this will only fire inside of 181f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka // onTouchEvent 182f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mRenderer.scale *= detector.getScaleFactor(); 183f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mRenderer.scale = Math.max(mMinScale, mRenderer.scale); 184f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka invalidate(); 185f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka return true; 186f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 187f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 188f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka @Override 189f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka public void onScaleEnd(ScaleGestureDetector detector) { 190f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 191f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 1927bd5e80dc5e26102e59b300dec8dfd8abd9184b4Tony Wickham /** 1937bd5e80dc5e26102e59b300dec8dfd8abd9184b4Tony Wickham * Offsets wallpaper preview according to the state it will be displayed in upon returning home. 1947bd5e80dc5e26102e59b300dec8dfd8abd9184b4Tony Wickham * @param offset Ranges from 0 to 1, where 0 is the leftmost parallax and 1 is the rightmost. 1957bd5e80dc5e26102e59b300dec8dfd8abd9184b4Tony Wickham */ 19613d3a076ce25c531c450d139b37b3e5bbd6964e1Tony Wickham public void setParallaxOffset(float offset, RectF crop) { 1977bd5e80dc5e26102e59b300dec8dfd8abd9184b4Tony Wickham offset = Math.max(0, Math.min(offset, 1)); // Make sure the offset is in the correct range. 198f624f0bf0a28f1fcb193d6ed2a01c49e22b96e4aTony Wickham float screenWidth = getWidth() / mRenderer.scale; 19913d3a076ce25c531c450d139b37b3e5bbd6964e1Tony Wickham mCenterX = screenWidth / 2 + offset * (crop.width() - screenWidth) + crop.left; 2007bd5e80dc5e26102e59b300dec8dfd8abd9184b4Tony Wickham updateCenter(); 2017bd5e80dc5e26102e59b300dec8dfd8abd9184b4Tony Wickham } 2027bd5e80dc5e26102e59b300dec8dfd8abd9184b4Tony Wickham 203f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka public void moveToLeft() { 204f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka if (getWidth() == 0 || getHeight() == 0) { 205f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka final ViewTreeObserver observer = getViewTreeObserver(); 206f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { 207f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka public void onGlobalLayout() { 208f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka moveToLeft(); 209f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka getViewTreeObserver().removeOnGlobalLayoutListener(this); 210f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 211f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka }); 212f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 213f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka final RectF edges = mTempEdges; 214f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka getEdgesHelper(edges); 215f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka final float scale = mRenderer.scale; 216f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mCenterX += Math.ceil(edges.left / scale); 217f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka updateCenter(); 218f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 219f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 220f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka private void updateCenter() { 221f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mRenderer.centerX = Math.round(mCenterX); 222f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mRenderer.centerY = Math.round(mCenterY); 223f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 224f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 225f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka public void setTouchEnabled(boolean enabled) { 226f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mTouchEnabled = enabled; 227f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 228f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 229f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka public void setTouchCallback(TouchCallback cb) { 230f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mTouchCallback = cb; 231f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 232f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 233f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka @Override 234f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka public boolean onTouchEvent(MotionEvent event) { 235f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka int action = event.getActionMasked(); 236f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP; 237f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka final int skipIndex = pointerUp ? event.getActionIndex() : -1; 238f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 239f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka // Determine focal point 240f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka float sumX = 0, sumY = 0; 241f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka final int count = event.getPointerCount(); 242f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka for (int i = 0; i < count; i++) { 243f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka if (skipIndex == i) 244f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka continue; 245f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka sumX += event.getX(i); 246f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka sumY += event.getY(i); 247f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 248f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka final int div = pointerUp ? count - 1 : count; 249f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka float x = sumX / div; 250f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka float y = sumY / div; 251f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 252f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka if (action == MotionEvent.ACTION_DOWN) { 253f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mFirstX = x; 254f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mFirstY = y; 255f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mTouchDownTime = System.currentTimeMillis(); 256f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka if (mTouchCallback != null) { 257f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mTouchCallback.onTouchDown(); 258f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 259f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } else if (action == MotionEvent.ACTION_UP) { 260f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka ViewConfiguration config = ViewConfiguration.get(getContext()); 261f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 262f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka float squaredDist = (mFirstX - x) * (mFirstX - x) + (mFirstY - y) * (mFirstY - y); 263f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka float slop = config.getScaledTouchSlop() * config.getScaledTouchSlop(); 264f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka long now = System.currentTimeMillis(); 265f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka if (mTouchCallback != null) { 266f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka // only do this if it's a small movement 267f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka if (squaredDist < slop && 268f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka now < mTouchDownTime + ViewConfiguration.getTapTimeout()) { 269f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mTouchCallback.onTap(); 270f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 271f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mTouchCallback.onTouchUp(); 272f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 273f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 274f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 275f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka if (!mTouchEnabled) { 276f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka return true; 277f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 278f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 279f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka synchronized (mLock) { 280f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mScaleGestureDetector.onTouchEvent(event); 281f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka switch (action) { 282f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka case MotionEvent.ACTION_MOVE: 283f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka float[] point = mTempPoint; 284f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka point[0] = (mLastX - x) / mRenderer.scale; 285f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka point[1] = (mLastY - y) / mRenderer.scale; 286f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mInverseRotateMatrix.mapPoints(point); 287f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mCenterX += point[0]; 288f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mCenterY += point[1]; 289f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka updateCenter(); 290f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka invalidate(); 291f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka break; 292f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 293f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka if (mRenderer.source != null) { 294f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka // Adjust position so that the wallpaper covers the entire area 295f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka // of the screen 296f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka final RectF edges = mTempEdges; 297f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka getEdgesHelper(edges); 298f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka final float scale = mRenderer.scale; 299f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 300f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka float[] coef = mTempCoef; 301f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka coef[0] = 1; 302f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka coef[1] = 1; 303f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mRotateMatrix.mapPoints(coef); 304f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka float[] adjustment = mTempAdjustment; 305f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mTempAdjustment[0] = 0; 306f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mTempAdjustment[1] = 0; 307f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka if (edges.left > 0) { 308f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka adjustment[0] = edges.left / scale; 309f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } else if (edges.right < getWidth()) { 310f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka adjustment[0] = (edges.right - getWidth()) / scale; 311f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 312f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka if (edges.top > 0) { 313afb348680c86b73f61c8886cb1c05b9f76dd4b00Neil Fuller adjustment[1] = (float) Math.ceil(edges.top / scale); 314f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } else if (edges.bottom < getHeight()) { 315f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka adjustment[1] = (edges.bottom - getHeight()) / scale; 316f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 317f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka for (int dim = 0; dim <= 1; dim++) { 318afb348680c86b73f61c8886cb1c05b9f76dd4b00Neil Fuller if (coef[dim] > 0) adjustment[dim] = (float) Math.ceil(adjustment[dim]); 319f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 320f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 321f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mInverseRotateMatrix.mapPoints(adjustment); 322f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mCenterX += adjustment[0]; 323f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mCenterY += adjustment[1]; 324f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka updateCenter(); 325f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 326f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 327f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka 328f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mLastX = x; 329f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka mLastY = y; 330f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka return true; 331f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka } 332f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka} 333