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; 24e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.view.MotionEvent; 25e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.view.ScaleGestureDetector; 26e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.view.ScaleGestureDetector.OnScaleGestureListener; 27e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurkaimport android.view.ViewConfiguration; 28e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.view.ViewTreeObserver; 29e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.view.ViewTreeObserver.OnGlobalLayoutListener; 30e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 31e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport com.android.photos.views.TiledImageRenderer.TileSource; 32e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport com.android.photos.views.TiledImageView; 33e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 34e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkapublic class CropView extends TiledImageView implements OnScaleGestureListener { 35e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 36e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private ScaleGestureDetector mScaleGestureDetector; 37e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private long mTouchDownTime; 38e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private float mFirstX, mFirstY; 39e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private float mLastX, mLastY; 4069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka private float mCenterX, mCenterY; 41e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private float mMinScale; 42e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private boolean mTouchEnabled = true; 43e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private RectF mTempEdges = new RectF(); 4469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka private float[] mTempPoint = new float[] { 0, 0 }; 4569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka private float[] mTempCoef = new float[] { 0, 0 }; 4669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka private float[] mTempAdjustment = new float[] { 0, 0 }; 4769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka private float[] mTempImageDims = new float[] { 0, 0 }; 4869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka private float[] mTempRendererCenter = new float[] { 0, 0 }; 49e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka TouchCallback mTouchCallback; 5069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Matrix mRotateMatrix; 5169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Matrix mInverseRotateMatrix; 52e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 53e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public interface TouchCallback { 54e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka void onTouchDown(); 55e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka void onTap(); 56e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka void onTouchUp(); 57e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 58e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 59e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public CropView(Context context) { 60e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka this(context, null); 61e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 62e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 63e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public CropView(Context context, AttributeSet attrs) { 64e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka super(context, attrs); 65e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mScaleGestureDetector = new ScaleGestureDetector(context, this); 6669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mRotateMatrix = new Matrix(); 6769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mInverseRotateMatrix = new Matrix(); 6869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } 6969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 7069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka private float[] getImageDims() { 7169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka final float imageWidth = mRenderer.source.getImageWidth(); 7269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka final float imageHeight = mRenderer.source.getImageHeight(); 7369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka float[] imageDims = mTempImageDims; 7469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka imageDims[0] = imageWidth; 7569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka imageDims[1] = imageHeight; 7669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mRotateMatrix.mapPoints(imageDims); 7769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka imageDims[0] = Math.abs(imageDims[0]); 7869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka imageDims[1] = Math.abs(imageDims[1]); 7969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka return imageDims; 80e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 81e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 82e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void getEdgesHelper(RectF edgesOut) { 83e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final float width = getWidth(); 84e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final float height = getHeight(); 8569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka final float[] imageDims = getImageDims(); 8669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka final float imageWidth = imageDims[0]; 8769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka final float imageHeight = imageDims[1]; 8869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 8969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka float initialCenterX = mRenderer.source.getImageWidth() / 2f; 9069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka float initialCenterY = mRenderer.source.getImageHeight() / 2f; 9169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 9269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka float[] rendererCenter = mTempRendererCenter; 9369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rendererCenter[0] = mCenterX - initialCenterX; 9469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rendererCenter[1] = mCenterY - initialCenterY; 9569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mRotateMatrix.mapPoints(rendererCenter); 9669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rendererCenter[0] += imageWidth / 2; 9769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rendererCenter[1] += imageHeight / 2; 9869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 99e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final float scale = mRenderer.scale; 10069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka float centerX = (width / 2f - rendererCenter[0] + (imageWidth - width) / 2f) 101e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * scale + width / 2f; 10269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka float centerY = (height / 2f - rendererCenter[1] + (imageHeight - height) / 2f) 103e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * scale + height / 2f; 104e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float leftEdge = centerX - imageWidth / 2f * scale; 105e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float rightEdge = centerX + imageWidth / 2f * scale; 106e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float topEdge = centerY - imageHeight / 2f * scale; 107e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float bottomEdge = centerY + imageHeight / 2f * scale; 108e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 109e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka edgesOut.left = leftEdge; 110e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka edgesOut.right = rightEdge; 111e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka edgesOut.top = topEdge; 112e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka edgesOut.bottom = bottomEdge; 113e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 114e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 11569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka public int getImageRotation() { 11669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka return mRenderer.rotation; 11769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } 11869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 119e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public RectF getCrop() { 120e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final RectF edges = mTempEdges; 121e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka getEdgesHelper(edges); 122e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final float scale = mRenderer.scale; 123e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 124e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float cropLeft = -edges.left / scale; 125e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float cropTop = -edges.top / scale; 126e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float cropRight = cropLeft + getWidth() / scale; 127e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float cropBottom = cropTop + getHeight() / scale; 128e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 129e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return new RectF(cropLeft, cropTop, cropRight, cropBottom); 130e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 131e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 132e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public Point getSourceDimensions() { 133e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return new Point(mRenderer.source.getImageWidth(), mRenderer.source.getImageHeight()); 134e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 135e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 136e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setTileSource(TileSource source, Runnable isReadyCallback) { 137e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka super.setTileSource(source, isReadyCallback); 13869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCenterX = mRenderer.centerX; 13969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCenterY = mRenderer.centerY; 14069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mRotateMatrix.reset(); 14169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mRotateMatrix.setRotate(mRenderer.rotation); 14269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mInverseRotateMatrix.reset(); 14369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mInverseRotateMatrix.setRotate(-mRenderer.rotation); 144e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka updateMinScale(getWidth(), getHeight(), source, true); 145e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 146e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 147e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected void onSizeChanged(int w, int h, int oldw, int oldh) { 148e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka updateMinScale(w, h, mRenderer.source, false); 149e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 150e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 151e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setScale(float scale) { 152e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka synchronized (mLock) { 153e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mRenderer.scale = scale; 154e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 155e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 156e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 157e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void updateMinScale(int w, int h, TileSource source, boolean resetScale) { 158e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka synchronized (mLock) { 159e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (resetScale) { 160e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mRenderer.scale = 1; 161e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 162e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (source != null) { 16369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka final float[] imageDims = getImageDims(); 16469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka final float imageWidth = imageDims[0]; 16569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka final float imageHeight = imageDims[1]; 16669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mMinScale = Math.max(w / imageWidth, h / imageHeight); 167c82618c660bbb76df05c11c548cc01724f5e5fc9Michael Jurka mRenderer.scale = 168c82618c660bbb76df05c11c548cc01724f5e5fc9Michael Jurka Math.max(mMinScale, resetScale ? Float.MIN_VALUE : 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) { 30233253a4baa6279f81a73425b49dfb6abe5f5416eNeil Fuller adjustment[1] = (float) Math.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++) { 30733253a4baa6279f81a73425b49dfb6abe5f5416eNeil Fuller if (coef[dim] > 0) adjustment[dim] = (float) Math.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