11e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved. 21e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 31e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// found in the LICENSE file. 41e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 51e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)package org.chromium.chromoting; 61e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 71e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)import android.content.Context; 81e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)import android.graphics.Matrix; 91e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)import android.graphics.PointF; 101e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)import android.view.GestureDetector; 111e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)import android.view.MotionEvent; 121e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)import android.view.ScaleGestureDetector; 13f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)import android.widget.Scroller; 141e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 151e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)/** 161e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * This class implements the cursor-tracking behavior and gestures. 171e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) */ 181e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)public class TrackingInputHandler implements TouchInputHandler { 191e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) /** 201e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * Minimum change to the scaling factor to be recognized as a zoom gesture. Setting lower 211e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * values here will result in more frequent canvas redraws during zooming. 221e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) */ 231e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) private static final double MIN_ZOOM_DELTA = 0.05; 241e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 251e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) /** 261e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * Maximum allowed zoom level - see {@link #repositionImageWithZoom()}. 271e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) */ 281e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) private static final float MAX_ZOOM_FACTOR = 100.0f; 291e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 301e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) private DesktopViewInterface mViewer; 311e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) private RenderData mRenderData; 321e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 331e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) private GestureDetector mScroller; 341e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) private ScaleGestureDetector mZoomer; 350f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) private TapGestureDetector mTapDetector; 361e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 37f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) /** Used to calculate the physics for flinging the cursor. */ 38f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) private Scroller mFlingScroller; 39f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 40f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) /** Used to disambiguate a 2-finger gesture as a swipe or a pinch. */ 41f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) private SwipePinchDetector mSwipePinchDetector; 42f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 431e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) /** 441e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * The current cursor position is stored here as floats, so that the desktop image can be 451e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * positioned with sub-pixel accuracy, to give a smoother panning animation at high zoom levels. 461e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) */ 471e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) private PointF mCursorPosition; 481e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 490f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) /** 500f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) * Used for tracking swipe gestures. Only the Y-direction is needed for responding to swipe-up 510f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) * or swipe-down. 520f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) */ 530f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) private float mTotalMotionY = 0; 540f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) 550f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) /** 560f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) * Distance in pixels beyond which a motion gesture is considered to be a swipe. This is 570f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) * initialized using the Context passed into the ctor. 581e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) */ 590f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) private float mSwipeThreshold; 601e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 61f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) /** Mouse-button currently held down, or BUTTON_UNDEFINED otherwise. */ 62f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) private int mHeldButton = BUTTON_UNDEFINED; 63f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 64f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) /** 65f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * Set to true to prevent any further movement of the cursor, for example, when showing the 66f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * keyboard to prevent the cursor wandering from the area where keystrokes should be sent. 67f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) */ 68f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) private boolean mSuppressCursorMovement = false; 69f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 70f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) /** 71f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * Set to true to suppress the fling animation at the end of a gesture, for example, when 72f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * dragging whilst a button is held down. 73f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) */ 74f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) private boolean mSuppressFling = false; 75f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 76f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) /** 77f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * Set to true when 3-finger swipe gesture is complete, so that further movement doesn't 78f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * trigger more swipe actions. 79f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) */ 80f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) private boolean mSwipeCompleted = false; 81f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 821e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) public TrackingInputHandler(DesktopViewInterface viewer, Context context, 831e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) RenderData renderData) { 841e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mViewer = viewer; 851e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mRenderData = renderData; 861e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 871e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) GestureListener listener = new GestureListener(); 881e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mScroller = new GestureDetector(context, listener, null, false); 891e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 901e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // If long-press is enabled, the gesture-detector will not emit any further onScroll 911e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // notifications after the onLongPress notification. Since onScroll is being used for 921e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // moving the cursor, it means that the cursor would become stuck if the finger were held 931e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // down too long. 941e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mScroller.setIsLongpressEnabled(false); 951e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 961e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mZoomer = new ScaleGestureDetector(context, listener); 970f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) mTapDetector = new TapGestureDetector(context, listener); 98f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mFlingScroller = new Scroller(context); 99f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mSwipePinchDetector = new SwipePinchDetector(context); 1001e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1011e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mCursorPosition = new PointF(); 1021e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1030f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) // The threshold needs to be bigger than the ScaledTouchSlop used by the gesture-detectors, 1040f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) // so that a gesture cannot be both a tap and a swipe. It also needs to be small enough so 1050f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) // that intentional swipes are usually detected. 1060f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) float density = context.getResources().getDisplayMetrics().density; 1070f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) mSwipeThreshold = 40 * density; 1081e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 1091e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1101e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) /** 1111e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * Moves the mouse-cursor, injects a mouse-move event and repositions the image. 1121e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) */ 1131e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) private void moveCursor(float newX, float newY) { 1141e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) synchronized (mRenderData) { 1151e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // Constrain cursor to the image area. 1161e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (newX < 0) newX = 0; 1171e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (newY < 0) newY = 0; 1181e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (newX > mRenderData.imageWidth) newX = mRenderData.imageWidth; 1191e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (newY > mRenderData.imageHeight) newY = mRenderData.imageHeight; 1201e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mCursorPosition.set(newX, newY); 1211e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) repositionImage(); 1221e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 1231e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci mViewer.injectMouseEvent((int) newX, (int) newY, BUTTON_UNDEFINED, false); 1251e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 1261e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1271e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) /** 1281e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * Repositions the image by translating it (without affecting the zoom level) to place the 1291e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * cursor close to the center of the screen. 1301e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) */ 1311e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) private void repositionImage() { 1321e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) synchronized (mRenderData) { 1331e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // Get the current cursor position in screen coordinates. 1341e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) float[] cursorScreen = {mCursorPosition.x, mCursorPosition.y}; 1351e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mRenderData.transform.mapPoints(cursorScreen); 1361e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1371e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // Translate so the cursor is displayed in the middle of the screen. 1381e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mRenderData.transform.postTranslate( 1391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci (float) mRenderData.screenWidth / 2 - cursorScreen[0], 1401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci (float) mRenderData.screenHeight / 2 - cursorScreen[1]); 1411e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1421e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // Now the cursor is displayed in the middle of the screen, see if the image can be 1431e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // panned so that more of it is visible. The primary goal is to show as much of the 1441e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // image as possible. The secondary goal is to keep the cursor in the middle. 1451e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1461e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // Get the coordinates of the desktop rectangle (top-left/bottom-right corners) in 1471e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // screen coordinates. Order is: left, top, right, bottom. 1481e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) float[] rectScreen = {0, 0, mRenderData.imageWidth, mRenderData.imageHeight}; 1491e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mRenderData.transform.mapPoints(rectScreen); 1501e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1511e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) float leftDelta = rectScreen[0]; 1521e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) float rightDelta = rectScreen[2] - mRenderData.screenWidth; 1531e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) float topDelta = rectScreen[1]; 1541e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) float bottomDelta = rectScreen[3] - mRenderData.screenHeight; 1551e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) float xAdjust = 0; 1561e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) float yAdjust = 0; 1571e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1581e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (rectScreen[2] - rectScreen[0] < mRenderData.screenWidth) { 1591e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // Image is narrower than the screen, so center it. 1601e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) xAdjust = -(rightDelta + leftDelta) / 2; 1611e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } else if (leftDelta > 0 && rightDelta > 0) { 1621e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // Panning the image left will show more of it. 1631e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) xAdjust = -Math.min(leftDelta, rightDelta); 1641e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } else if (leftDelta < 0 && rightDelta < 0) { 1651e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // Pan the image right. 1661e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) xAdjust = Math.min(-leftDelta, -rightDelta); 1671e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 1681e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1691e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // Apply similar logic for yAdjust. 1701e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (rectScreen[3] - rectScreen[1] < mRenderData.screenHeight) { 1711e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) yAdjust = -(bottomDelta + topDelta) / 2; 1721e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } else if (topDelta > 0 && bottomDelta > 0) { 1731e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) yAdjust = -Math.min(topDelta, bottomDelta); 1741e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } else if (topDelta < 0 && bottomDelta < 0) { 1751e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) yAdjust = Math.min(-topDelta, -bottomDelta); 1761e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 1771e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1781e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mRenderData.transform.postTranslate(xAdjust, yAdjust); 1791e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 1801e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mViewer.transformationChanged(); 1811e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 1821e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1831e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) /** 1841e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * Repositions the image by translating and zooming it, to keep the zoom level within sensible 1851e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * limits. The minimum zoom level is chosen to avoid black space around all 4 sides. The 1861e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * maximum zoom level is set arbitrarily, so that the user can zoom out again in a reasonable 1871e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * time, and to prevent arithmetic overflow problems from displaying the image. 1881e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) */ 1891e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) private void repositionImageWithZoom() { 1901e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) synchronized (mRenderData) { 1911e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // Avoid division by zero in case this gets called before the image size is initialized. 1921e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (mRenderData.imageWidth == 0 || mRenderData.imageHeight == 0) { 1931e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return; 1941e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 1951e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1961e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // Zoom out if the zoom level is too high. 1971e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) float currentZoomLevel = mRenderData.transform.mapRadius(1.0f); 1981e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (currentZoomLevel > MAX_ZOOM_FACTOR) { 1991e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mRenderData.transform.setScale(MAX_ZOOM_FACTOR, MAX_ZOOM_FACTOR); 2001e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 2011e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 2021e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // Get image size scaled to screen coordinates. 2031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci float[] imageSize = {mRenderData.imageWidth, mRenderData.imageHeight}; 2041e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mRenderData.transform.mapVectors(imageSize); 2051e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 2061e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (imageSize[0] < mRenderData.screenWidth && imageSize[1] < mRenderData.screenHeight) { 2071e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // Displayed image is too small in both directions, so apply the minimum zoom 2081e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // level needed to fit either the width or height. 2091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci float scale = Math.min((float) mRenderData.screenWidth / mRenderData.imageWidth, 2101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci (float) mRenderData.screenHeight / mRenderData.imageHeight); 2111e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mRenderData.transform.setScale(scale, scale); 2121e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 2131e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 2141e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) repositionImage(); 2151e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 2161e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 2171e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 2181e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) /** Injects a button event using the current cursor location. */ 2191e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) private void injectButtonEvent(int button, boolean pressed) { 2201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci mViewer.injectMouseEvent((int) mCursorPosition.x, (int) mCursorPosition.y, button, pressed); 2211e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 2221e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 2230f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) /** Processes a (multi-finger) swipe gesture. */ 2240f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) private boolean onSwipe() { 2250f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) if (mTotalMotionY > mSwipeThreshold) { 2260f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) // Swipe down occurred. 2270f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) mViewer.showActionBar(); 2280f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) } else if (mTotalMotionY < -mSwipeThreshold) { 2290f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) // Swipe up occurred. 2300f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) mViewer.showKeyboard(); 231f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } else { 232f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return false; 233f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 234f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 235f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mSuppressCursorMovement = true; 236f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mSuppressFling = true; 237f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mSwipeCompleted = true; 238f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return true; 239f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 240f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 241f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) /** Injects a button-up event if the button is currently held down (during a drag event). */ 242f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) private void releaseAnyHeldButton() { 243f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (mHeldButton != BUTTON_UNDEFINED) { 244f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) injectButtonEvent(mHeldButton, false); 245f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mHeldButton = BUTTON_UNDEFINED; 2460f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) } 2470f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) } 2480f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) 2491e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) @Override 2501e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) public boolean onTouchEvent(MotionEvent event) { 251f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Avoid short-circuit logic evaluation - ensure all gesture detectors see all events so 2521e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // that they generate correct notifications. 2531e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) boolean handled = mScroller.onTouchEvent(event); 2540f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) handled |= mZoomer.onTouchEvent(event); 2550f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) handled |= mTapDetector.onTouchEvent(event); 256f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mSwipePinchDetector.onTouchEvent(event); 257f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 258f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) switch (event.getActionMasked()) { 259f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) case MotionEvent.ACTION_DOWN: 260f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mViewer.setAnimationEnabled(false); 261f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mSuppressCursorMovement = false; 262f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mSuppressFling = false; 263f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mSwipeCompleted = false; 264f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) break; 265f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 266f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) case MotionEvent.ACTION_POINTER_DOWN: 267f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mTotalMotionY = 0; 268f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) break; 269f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 270f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) case MotionEvent.ACTION_UP: 271f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) releaseAnyHeldButton(); 272f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) break; 273f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 274f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) default: 275f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) break; 2761e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 2771e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return handled; 2781e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 2791e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 2801e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) @Override 2811e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) public void onScreenConfigurationChanged() { 2821e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 2831e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 2841e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) @Override 2851e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) public void onClientSizeChanged(int width, int height) { 2861e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) repositionImageWithZoom(); 2871e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 2881e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 2891e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) @Override 2901e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) public void onHostSizeChanged(int width, int height) { 2911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci moveCursor((float) width / 2, (float) height / 2); 2921e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) repositionImageWithZoom(); 2931e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 2941e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 295f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) @Override 296f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) public void processAnimation() { 297f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) int previousX = mFlingScroller.getCurrX(); 298f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) int previousY = mFlingScroller.getCurrY(); 299f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (!mFlingScroller.computeScrollOffset()) { 300f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mViewer.setAnimationEnabled(false); 301f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return; 302f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 303f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) int deltaX = mFlingScroller.getCurrX() - previousX; 304f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) int deltaY = mFlingScroller.getCurrY() - previousY; 3051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci float[] delta = {deltaX, deltaY}; 306f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) synchronized (mRenderData) { 307f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) Matrix canvasToImage = new Matrix(); 308f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mRenderData.transform.invert(canvasToImage); 309f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) canvasToImage.mapVectors(delta); 310f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 311f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 312f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) moveCursor(mCursorPosition.x + delta[0], mCursorPosition.y + delta[1]); 313f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 314f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 3151e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) /** Responds to touch events filtered by the gesture detectors. */ 3161e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) private class GestureListener extends GestureDetector.SimpleOnGestureListener 3170f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) implements ScaleGestureDetector.OnScaleGestureListener, 3180f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) TapGestureDetector.OnTapListener { 3191e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) /** 3201e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * Called when the user drags one or more fingers across the touchscreen. 3211e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) */ 3221e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) @Override 3231e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 324f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) int pointerCount = e2.getPointerCount(); 325f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (pointerCount == 3 && !mSwipeCompleted) { 3260f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) // Note that distance values are reversed. For example, dragging a finger in the 3270f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) // direction of increasing Y coordinate (downwards) results in distanceY being 3280f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) // negative. 3290f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) mTotalMotionY -= distanceY; 330f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return onSwipe(); 331f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 332f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 333f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (pointerCount == 2 && mSwipePinchDetector.isSwiping()) { 3341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci mViewer.injectMouseWheelDeltaEvent(-(int) distanceX, -(int) distanceY); 335f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 336f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Prevent the cursor being moved or flung by the gesture. 337f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mSuppressCursorMovement = true; 338f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return true; 3390f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) } 3400f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) 341f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (pointerCount != 1 || mSuppressCursorMovement) { 3421e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return false; 3431e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 3441e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 3451e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) float[] delta = {distanceX, distanceY}; 3461e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) synchronized (mRenderData) { 3471e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) Matrix canvasToImage = new Matrix(); 3481e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mRenderData.transform.invert(canvasToImage); 3491e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) canvasToImage.mapVectors(delta); 3501e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 3511e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 3521e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) moveCursor(mCursorPosition.x - delta[0], mCursorPosition.y - delta[1]); 3531e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return true; 3541e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 3551e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 356f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) /** 357f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * Called when a fling gesture is recognized. 358f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) */ 359f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) @Override 360f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 361f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // If cursor movement is suppressed, fling also needs to be suppressed, as the 362f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // gesture-detector will still generate onFling() notifications based on movement of 363f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // the fingers, which would result in unwanted cursor movement. 364f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (mSuppressCursorMovement || mSuppressFling) { 365f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return false; 366f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 367f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 368f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // The fling physics calculation is based on screen coordinates, so that it will 369f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // behave consistently at different zoom levels (and will work nicely at high zoom 370f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // levels, since |mFlingScroller| outputs integer coordinates). However, the desktop 371f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // will usually be panned as the cursor is moved across the desktop, which means the 372f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // transformation mapping from screen to desktop coordinates will change. To deal with 373f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // this, the cursor movement is computed from relative coordinate changes from 374f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // |mFlingScroller|. This means the fling can be started at (0, 0) with no bounding 375f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // constraints - the cursor is already constrained by the desktop size. 3761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci mFlingScroller.fling(0, 0, (int) velocityX, (int) velocityY, Integer.MIN_VALUE, 377f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE); 378f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Initialize the scroller's current offset coordinates, since they are used for 379f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // calculating the delta values. 380f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mFlingScroller.computeScrollOffset(); 381f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mViewer.setAnimationEnabled(true); 382f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return true; 383f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 384f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 3851e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) /** Called when the user is in the process of pinch-zooming. */ 3861e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) @Override 3871e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) public boolean onScale(ScaleGestureDetector detector) { 388f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (!mSwipePinchDetector.isPinching()) { 389f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return false; 390f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 391f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 3921e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (Math.abs(detector.getScaleFactor() - 1) < MIN_ZOOM_DELTA) { 3931e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return false; 3941e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 3951e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 3961e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) float scaleFactor = detector.getScaleFactor(); 3971e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) synchronized (mRenderData) { 3981e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mRenderData.transform.postScale( 3991e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY()); 4001e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 4011e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) repositionImageWithZoom(); 4021e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return true; 4031e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 4041e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 4051e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) /** Called whenever a gesture starts. Always accepts the gesture so it isn't ignored. */ 4061e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) @Override 4071e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) public boolean onDown(MotionEvent e) { 4081e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return true; 4091e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 4101e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 4111e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) /** 4121e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * Called when the user starts to zoom. Always accepts the zoom so that 4131e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * onScale() can decide whether to respond to it. 4141e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) */ 4151e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) @Override 4161e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) public boolean onScaleBegin(ScaleGestureDetector detector) { 4171e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return true; 4181e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 4191e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 4201e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) /** Called when the user is done zooming. Defers to onScale()'s judgement. */ 4211e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) @Override 4221e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) public void onScaleEnd(ScaleGestureDetector detector) { 4231e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) onScale(detector); 4241e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 4250f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) 426f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) /** Maps the number of fingers in a tap or long-press gesture to a mouse-button. */ 427f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) private int mouseButtonFromPointerCount(int pointerCount) { 4280f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) switch (pointerCount) { 4290f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) case 1: 430f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return BUTTON_LEFT; 4310f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) case 2: 432f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return BUTTON_RIGHT; 4330f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) case 3: 434f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return BUTTON_MIDDLE; 4350f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) default: 436f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return BUTTON_UNDEFINED; 4370f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) } 438f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 4390f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) 440f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) /** Called when the user taps the screen with one or more fingers. */ 441f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) @Override 442f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) public boolean onTap(int pointerCount) { 443f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) int button = mouseButtonFromPointerCount(pointerCount); 444f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (button == BUTTON_UNDEFINED) { 445f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return false; 446f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } else { 447f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) injectButtonEvent(button, true); 448f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) injectButtonEvent(button, false); 449f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return true; 450f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 451f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 452f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 453f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) /** Called when a long-press is triggered for one or more fingers. */ 454f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) @Override 455f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) public void onLongPress(int pointerCount) { 456f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mHeldButton = mouseButtonFromPointerCount(pointerCount); 457f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (mHeldButton != BUTTON_UNDEFINED) { 458f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) injectButtonEvent(mHeldButton, true); 459f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mViewer.showLongPressFeedback(); 460f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mSuppressFling = true; 461f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 4620f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) } 4631e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 4641e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)} 465