1e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka/* 2e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Copyright (C) 2013 The Android Open Source Project 3e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * 4e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Licensed under the Apache License, Version 2.0 (the "License"); 5e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * you may not use this file except in compliance with the License. 6e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * You may obtain a copy of the License at 7e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * 8e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * http://www.apache.org/licenses/LICENSE-2.0 9e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * 10e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Unless required by applicable law or agreed to in writing, software 11e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * distributed under the License is distributed on an "AS IS" BASIS, 12e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * See the License for the specific language governing permissions and 14e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * limitations under the License. 15e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka */ 16e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka/* Copied from Launcher3 */ 17e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkapackage com.android.wallpapercropper; 18e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 19e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.content.Context; 2069784065ea78550944b7eddb559c0dac952e20e8Michael Jurkaimport android.graphics.Matrix; 21e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Point; 22e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.RectF; 23e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.util.AttributeSet; 2469784065ea78550944b7eddb559c0dac952e20e8Michael Jurkaimport android.util.FloatMath; 25e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.view.MotionEvent; 26e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.view.ScaleGestureDetector; 27e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.view.ScaleGestureDetector.OnScaleGestureListener; 28e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurkaimport android.view.ViewConfiguration; 29e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.view.ViewTreeObserver; 30e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.view.ViewTreeObserver.OnGlobalLayoutListener; 31e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 32e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport com.android.photos.views.TiledImageRenderer.TileSource; 33e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport com.android.photos.views.TiledImageView; 34e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 35e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkapublic class CropView extends TiledImageView implements OnScaleGestureListener { 36e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 37e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private ScaleGestureDetector mScaleGestureDetector; 38e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private long mTouchDownTime; 39e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private float mFirstX, mFirstY; 40e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private float mLastX, mLastY; 4169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka private float mCenterX, mCenterY; 42e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private float mMinScale; 43e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private boolean mTouchEnabled = true; 44e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private RectF mTempEdges = new RectF(); 4569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka private float[] mTempPoint = new float[] { 0, 0 }; 4669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka private float[] mTempCoef = new float[] { 0, 0 }; 4769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka private float[] mTempAdjustment = new float[] { 0, 0 }; 4869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka private float[] mTempImageDims = new float[] { 0, 0 }; 4969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka private float[] mTempRendererCenter = new float[] { 0, 0 }; 50e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka TouchCallback mTouchCallback; 5169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Matrix mRotateMatrix; 5269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Matrix mInverseRotateMatrix; 53e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 54e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public interface TouchCallback { 55e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka void onTouchDown(); 56e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka void onTap(); 57e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka void onTouchUp(); 58e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 59e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 60e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public CropView(Context context) { 61e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka this(context, null); 62e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 63e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 64e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public CropView(Context context, AttributeSet attrs) { 65e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka super(context, attrs); 66e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mScaleGestureDetector = new ScaleGestureDetector(context, this); 6769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mRotateMatrix = new Matrix(); 6869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mInverseRotateMatrix = new Matrix(); 6969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } 7069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 7169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka private float[] getImageDims() { 7269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka final float imageWidth = mRenderer.source.getImageWidth(); 7369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka final float imageHeight = mRenderer.source.getImageHeight(); 7469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka float[] imageDims = mTempImageDims; 7569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka imageDims[0] = imageWidth; 7669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka imageDims[1] = imageHeight; 7769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mRotateMatrix.mapPoints(imageDims); 7869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka imageDims[0] = Math.abs(imageDims[0]); 7969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka imageDims[1] = Math.abs(imageDims[1]); 8069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka return imageDims; 81e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 82e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 83e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void getEdgesHelper(RectF edgesOut) { 84e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final float width = getWidth(); 85e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final float height = getHeight(); 8669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka final float[] imageDims = getImageDims(); 8769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka final float imageWidth = imageDims[0]; 8869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka final float imageHeight = imageDims[1]; 8969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 9069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka float initialCenterX = mRenderer.source.getImageWidth() / 2f; 9169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka float initialCenterY = mRenderer.source.getImageHeight() / 2f; 9269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 9369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka float[] rendererCenter = mTempRendererCenter; 9469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rendererCenter[0] = mCenterX - initialCenterX; 9569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rendererCenter[1] = mCenterY - initialCenterY; 9669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mRotateMatrix.mapPoints(rendererCenter); 9769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rendererCenter[0] += imageWidth / 2; 9869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rendererCenter[1] += imageHeight / 2; 9969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 100e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final float scale = mRenderer.scale; 10169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka float centerX = (width / 2f - rendererCenter[0] + (imageWidth - width) / 2f) 102e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * scale + width / 2f; 10369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka float centerY = (height / 2f - rendererCenter[1] + (imageHeight - height) / 2f) 104e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * scale + height / 2f; 105e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float leftEdge = centerX - imageWidth / 2f * scale; 106e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float rightEdge = centerX + imageWidth / 2f * scale; 107e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float topEdge = centerY - imageHeight / 2f * scale; 108e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float bottomEdge = centerY + imageHeight / 2f * scale; 109e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 110e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka edgesOut.left = leftEdge; 111e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka edgesOut.right = rightEdge; 112e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka edgesOut.top = topEdge; 113e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka edgesOut.bottom = bottomEdge; 114e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 115e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 11669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka public int getImageRotation() { 11769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka return mRenderer.rotation; 11869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } 11969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 120e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public RectF getCrop() { 121e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final RectF edges = mTempEdges; 122e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka getEdgesHelper(edges); 123e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final float scale = mRenderer.scale; 124e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 125e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float cropLeft = -edges.left / scale; 126e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float cropTop = -edges.top / scale; 127e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float cropRight = cropLeft + getWidth() / scale; 128e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float cropBottom = cropTop + getHeight() / scale; 129e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 130e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return new RectF(cropLeft, cropTop, cropRight, cropBottom); 131e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 132e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 133e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public Point getSourceDimensions() { 134e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return new Point(mRenderer.source.getImageWidth(), mRenderer.source.getImageHeight()); 135e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 136e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 137e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setTileSource(TileSource source, Runnable isReadyCallback) { 138e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka super.setTileSource(source, isReadyCallback); 13969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCenterX = mRenderer.centerX; 14069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCenterY = mRenderer.centerY; 14169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mRotateMatrix.reset(); 14269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mRotateMatrix.setRotate(mRenderer.rotation); 14369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mInverseRotateMatrix.reset(); 14469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mInverseRotateMatrix.setRotate(-mRenderer.rotation); 145e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka updateMinScale(getWidth(), getHeight(), source, true); 146e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 147e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 148e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected void onSizeChanged(int w, int h, int oldw, int oldh) { 149e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka updateMinScale(w, h, mRenderer.source, false); 150e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 151e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 152e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setScale(float scale) { 153e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka synchronized (mLock) { 154e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mRenderer.scale = scale; 155e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 156e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 157e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 158e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void updateMinScale(int w, int h, TileSource source, boolean resetScale) { 159e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka synchronized (mLock) { 160e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (resetScale) { 161e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mRenderer.scale = 1; 162e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 163e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (source != null) { 16469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka final float[] imageDims = getImageDims(); 16569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka final float imageWidth = imageDims[0]; 16669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka final float imageHeight = imageDims[1]; 16769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mMinScale = Math.max(w / imageWidth, h / imageHeight); 168e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mRenderer.scale = Math.max(mMinScale, mRenderer.scale); 169e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 170e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 171e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 172e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 173e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 174e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public boolean onScaleBegin(ScaleGestureDetector detector) { 175e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return true; 176e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 177e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 178e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 179e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public boolean onScale(ScaleGestureDetector detector) { 180e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Don't need the lock because this will only fire inside of 181e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // onTouchEvent 182e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mRenderer.scale *= detector.getScaleFactor(); 183e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mRenderer.scale = Math.max(mMinScale, mRenderer.scale); 184e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka invalidate(); 185e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return true; 186e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 187e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 188e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 189e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void onScaleEnd(ScaleGestureDetector detector) { 190e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 191e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 192e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka public void moveToLeft() { 193e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (getWidth() == 0 || getHeight() == 0) { 194e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final ViewTreeObserver observer = getViewTreeObserver(); 195e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { 196e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void onGlobalLayout() { 197e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka moveToLeft(); 198e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka getViewTreeObserver().removeOnGlobalLayoutListener(this); 199e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 200e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka }); 201e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 202e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final RectF edges = mTempEdges; 203e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka getEdgesHelper(edges); 204e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final float scale = mRenderer.scale; 20569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCenterX += Math.ceil(edges.left / scale); 20669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka updateCenter(); 20769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } 20869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 20969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka private void updateCenter() { 21069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mRenderer.centerX = Math.round(mCenterX); 21169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mRenderer.centerY = Math.round(mCenterY); 212e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 213e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 214e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setTouchEnabled(boolean enabled) { 215e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTouchEnabled = enabled; 216e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 217e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 218e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setTouchCallback(TouchCallback cb) { 219e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTouchCallback = cb; 220e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 221e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 222e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 223e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public boolean onTouchEvent(MotionEvent event) { 224e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int action = event.getActionMasked(); 225e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP; 226e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final int skipIndex = pointerUp ? event.getActionIndex() : -1; 227e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 228e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Determine focal point 229e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float sumX = 0, sumY = 0; 230e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final int count = event.getPointerCount(); 231e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (int i = 0; i < count; i++) { 232e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (skipIndex == i) 233e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka continue; 234e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka sumX += event.getX(i); 235e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka sumY += event.getY(i); 236e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 237e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final int div = pointerUp ? count - 1 : count; 238e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float x = sumX / div; 239e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float y = sumY / div; 240e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 241e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (action == MotionEvent.ACTION_DOWN) { 242e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mFirstX = x; 243e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mFirstY = y; 244e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTouchDownTime = System.currentTimeMillis(); 245e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mTouchCallback != null) { 246e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTouchCallback.onTouchDown(); 247e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 248e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else if (action == MotionEvent.ACTION_UP) { 249e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka ViewConfiguration config = ViewConfiguration.get(getContext()); 250e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 251e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float squaredDist = (mFirstX - x) * (mFirstX - x) + (mFirstY - y) * (mFirstY - y); 252e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float slop = config.getScaledTouchSlop() * config.getScaledTouchSlop(); 253e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka long now = System.currentTimeMillis(); 254e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka if (mTouchCallback != null) { 255e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka // only do this if it's a small movement 256e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka if (squaredDist < slop && 25769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka now < mTouchDownTime + ViewConfiguration.getTapTimeout()) { 258e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka mTouchCallback.onTap(); 259e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka } 260e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka mTouchCallback.onTouchUp(); 261e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 262e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 263e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 264e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (!mTouchEnabled) { 265e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return true; 266e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 267e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 268e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka synchronized (mLock) { 269e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mScaleGestureDetector.onTouchEvent(event); 270e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka switch (action) { 271e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka case MotionEvent.ACTION_MOVE: 27269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka float[] point = mTempPoint; 27369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka point[0] = (mLastX - x) / mRenderer.scale; 27469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka point[1] = (mLastY - y) / mRenderer.scale; 27569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mInverseRotateMatrix.mapPoints(point); 27669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCenterX += point[0]; 27769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCenterY += point[1]; 27869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka updateCenter(); 279e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka invalidate(); 280e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka break; 281e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 282e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mRenderer.source != null) { 283e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Adjust position so that the wallpaper covers the entire area 284e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // of the screen 285e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final RectF edges = mTempEdges; 286e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka getEdgesHelper(edges); 287e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final float scale = mRenderer.scale; 28869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 28969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka float[] coef = mTempCoef; 29069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka coef[0] = 1; 29169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka coef[1] = 1; 29269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mRotateMatrix.mapPoints(coef); 29369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka float[] adjustment = mTempAdjustment; 29469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mTempAdjustment[0] = 0; 29569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mTempAdjustment[1] = 0; 296e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (edges.left > 0) { 29769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka adjustment[0] = edges.left / scale; 29869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } else if (edges.right < getWidth()) { 29969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka adjustment[0] = (edges.right - getWidth()) / scale; 300e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 301e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (edges.top > 0) { 30269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka adjustment[1] = FloatMath.ceil(edges.top / scale); 30369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } else if (edges.bottom < getHeight()) { 30469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka adjustment[1] = (edges.bottom - getHeight()) / scale; 305e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 30669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka for (int dim = 0; dim <= 1; dim++) { 30769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka if (coef[dim] > 0) adjustment[dim] = FloatMath.ceil(adjustment[dim]); 308e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 30969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 31069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mInverseRotateMatrix.mapPoints(adjustment); 31169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCenterX += adjustment[0]; 31269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCenterY += adjustment[1]; 31369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka updateCenter(); 314e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 315e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 316e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 317e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mLastX = x; 318e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mLastY = y; 319e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return true; 320e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 321e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka} 322