Chromium Code Reviews| Index: remoting/android/java/src/org/chromium/chromoting/DesktopCanvas.java |
| diff --git a/remoting/android/java/src/org/chromium/chromoting/DesktopCanvas.java b/remoting/android/java/src/org/chromium/chromoting/DesktopCanvas.java |
| index e93364f249bf980843f5ad4556cb0376ba473eeb..9e11e73f4fe4e99d203f978926f922c604321403 100644 |
| --- a/remoting/android/java/src/org/chromium/chromoting/DesktopCanvas.java |
| +++ b/remoting/android/java/src/org/chromium/chromoting/DesktopCanvas.java |
| @@ -8,67 +8,54 @@ import android.graphics.Matrix; |
| import android.graphics.PointF; |
| import android.graphics.Rect; |
| import android.graphics.RectF; |
| +import android.os.SystemClock; |
| +import android.view.animation.DecelerateInterpolator; |
| +import android.view.animation.Interpolator; |
| /** |
| * This class is responsible for transforming the desktop image matrix. |
| */ |
| public class DesktopCanvas { |
| - /** |
| - * Maximum allowed zoom level - see {@link #scaleAndRepositionImage()}. |
| - */ |
| - private static final float MAX_ZOOM_FACTOR = 100.0f; |
| + /** Used for floating point comparisons. */ |
| + private static final float EPSILON = 0.0001f; |
| - /** |
| - * Used to smoothly reduce the amount of padding while the user is zooming. |
| - */ |
| - private static final float PADDING_REDUCTION_FACTOR = 0.85f; |
| + /** Maximum allowed zoom level - see {@link #scaleAndRepositionImage()}. */ |
| + private static final float MAX_ZOOM_FACTOR = 100.0f; |
| private final RenderStub mRenderStub; |
| private final RenderData mRenderData; |
| /** |
| - * Represents the actual center of the viewport in image space. This value needs to be a pair |
| - * of floats so the desktop image can be positioned with sub-pixel accuracy for smoother panning |
| - * animations at high zoom levels. |
| - */ |
| - // TODO(joedow): See if we can collapse Viewport and Cursor position members. They were needed |
| - // in the past due to how we calculated the center positions but may not be needed now. |
| - private PointF mViewportPosition = new PointF(); |
| - |
| - /** |
| * Represents the desired center of the viewport in image space. This value may not represent |
| * the actual center of the viewport as adjustments are made to ensure as much of the desktop is |
| * visible as possible. This value needs to be a pair of floats so the desktop image can be |
| * positioned with sub-pixel accuracy for smoother panning animations at high zoom levels. |
| - * Note: The internal cursor position may be placed outside of the image boundary, however the |
| - * cursor position we inject on the host side is restricted to the actual image dimensions. |
| */ |
| - private PointF mCursorPosition = new PointF(); |
| + private PointF mDesiredCenter = new PointF(); |
| /** |
| - * Represents the amount of space, in pixels, used by System UI on each edge of the screen. |
| + * If System UI exists, this contains the area of the screen which is unobscured by it, |
| + * otherwise it is empty. |
| */ |
| - // TODO(joedow): Update usage of this member so it is a true Rect instead of a set of offsets. |
| - private Rect mSystemUiScreenSize = new Rect(); |
| + private Rect mSystemUiScreenRect = new Rect(); |
| /** |
| - * Represents the amount of padding, in screen pixels, added to each edge of the desktop image. |
| - * This extra space allows the user to reveal portions of the desktop image which are obscured |
| - * by System UI. |
| + * Represents the amount of space, in pixels, to shift the image under the viewport. This value |
| + * is used to allow panning the image further than would be possible when using the normal |
| + * boundary values to account for System UI. This functionality ensures the user can view and |
| + * interact with any area of the remote image, even when System UI might otherwise obscure it. |
| */ |
| - private RectF mVisibleImagePadding = new RectF(); |
| + private PointF mViewportOffset = new PointF(); |
| /** |
| - * Tracks whether to adjust the viewport to account for System UI. If false, the viewport |
| - * center is the center of the screen. If true, then System UI offsets will be used to |
| - * adjust the position of the viewport to ensure the cursor is visible. |
| + * Tracks whether to adjust the size of the viewport to account for System UI. If false, the |
| + * viewport center is mapped to the center of the screen. If true, then System UI sizes will be |
| + * used to determine the center of the viewport. |
| */ |
| - private boolean mAdjustViewportForSystemUi = false; |
| + private boolean mAdjustViewportSizeForSystemUi = false; |
| - /** |
| - * Represents the amount of space, in pixels, to adjust the cursor center along the y-axis. |
| - */ |
| - private float mCursorOffsetScreenY = 0.0f; |
| + /* Used to perform per-frame rendering tasks. */ |
| + private Event.ParameterCallback<Boolean, Void> mFrameRenderedCallback; |
| public DesktopCanvas(RenderStub renderStub, RenderData renderData) { |
| mRenderStub = renderStub; |
| @@ -76,52 +63,47 @@ public class DesktopCanvas { |
| } |
| /** |
| - * Sets the desired center position of the viewport (a.k.a. the cursor position) and ensures |
| - * the viewport is updated to include the cursor within it. |
| + * Sets the desired center position of the viewport (a.k.a. the cursor position). |
| * |
| * @param newX The new x coordinate value for the desired center position. |
| * @param newY The new y coordinate value for the desired center position. |
| */ |
| public void setCursorPosition(float newX, float newY) { |
| - // First set the cursor position since its potential values are a superset of the viewport. |
| - mCursorPosition.set(newX, newY); |
| - constrainPointToBounds(mCursorPosition, getImageBounds()); |
| - |
| - // Now set the viewport position based on the cursor. |
| - mViewportPosition.set(mCursorPosition); |
| - constrainPointToBounds(mViewportPosition, getViewportBounds()); |
| - |
| - repositionImage(); |
| + updateCursorPosition(newX, newY, getImageBounds()); |
| } |
| /** |
| - * Shifts the viewport by the passed in values (in image coordinates). |
| + * Sets the center of the viewport using an absolute position (in image coordinates). |
| * |
| - * @param deltaX The distance (in image coordinates) to move the viewport along the x-axis. |
| - * @param deltaY The distance (in image coordinates) to move the viewport along the y-axis. |
| + * @param newX The new x coordinate value for the center position of the viewport. |
| + * @param newY The new y coordinate value for the center position of the viewport. |
| */ |
| - public void moveViewportCenter(float deltaX, float deltaY) { |
| - // Offset and adjust the viewport center position to fit the screen. |
| - mViewportPosition.offset(deltaX, deltaY); |
| - constrainPointToBounds(mViewportPosition, getViewportBounds()); |
| - |
| - // We don't need to constrain the cursor position as the viewport position is always within |
| - // the bounds of the potential cursor positions. |
| - mCursorPosition.set(mViewportPosition); |
| - |
| - repositionImage(); |
| + public void setViewportCenter(float newX, float newY) { |
| + updateCursorPosition(newX, newY, getViewportBounds()); |
| } |
| /** |
| - * Shifts the cursor by the passed in values (in image coordinates) and adjusts the viewport. |
| + * Shifts the cursor by the passed in values (in image coordinates). |
| * |
| * @param deltaX The distance (in image coordinates) to move the cursor along the x-axis. |
| * @param deltaY The distance (in image coordinates) to move the cursor along the y-axis. |
| * @return A point representing the new cursor position. |
| */ |
| public PointF moveCursorPosition(float deltaX, float deltaY) { |
| - setCursorPosition(mCursorPosition.x + deltaX, mCursorPosition.y + deltaY); |
| - return new PointF(mCursorPosition.x, mCursorPosition.y); |
| + updateCursorPosition( |
| + mDesiredCenter.x + deltaX, mDesiredCenter.y + deltaY, getImageBounds()); |
| + return new PointF(mDesiredCenter.x, mDesiredCenter.y); |
| + } |
| + |
| + /** |
| + * Shifts the viewport by the passed in values (in image coordinates). |
| + * |
| + * @param deltaX The distance (in image coordinates) to move the viewport along the x-axis. |
| + * @param deltaY The distance (in image coordinates) to move the viewport along the y-axis. |
| + */ |
| + public void moveViewportCenter(float deltaX, float deltaY) { |
| + updateCursorPosition( |
| + mDesiredCenter.x + deltaX, mDesiredCenter.y + deltaY, getViewportBounds()); |
| } |
| /** |
| @@ -131,33 +113,23 @@ public class DesktopCanvas { |
| */ |
| public void onSystemUiVisibilityChanged(SystemUiVisibilityChangedEventParameter parameter) { |
| if (parameter.softInputMethodVisible) { |
| - mSystemUiScreenSize.set(parameter.left, parameter.top, |
| - mRenderData.screenWidth - parameter.right, |
| - mRenderData.screenHeight - parameter.bottom); |
| - |
| - if (mAdjustViewportForSystemUi) { |
| - // Adjust the cursor position to ensure it's visible when large System UI (1/3 or |
| - // more of the total screen size) is displayed (typically the Soft Keyboard). |
| - // Without this change, it is difficult for users to enter text into edit controls |
| - // which are located bottom of the screen and may not see the cursor at all. |
| - if (mSystemUiScreenSize.bottom > (mRenderData.screenHeight / 3)) { |
| - // Center the cursor within the viewable area (not obscured by System UI). |
| - mCursorOffsetScreenY = (float) parameter.bottom / 2.0f; |
| - } else { |
| - mCursorOffsetScreenY = 0.0f; |
| - } |
| + mSystemUiScreenRect.set( |
| + parameter.left, parameter.top, parameter.right, parameter.bottom); |
| - // Apply the cursor offset. |
| - setCursorPosition(mCursorPosition.x, mCursorPosition.y); |
| - } |
| + stopOffsetReductionAnimation(); |
| } else { |
| - mCursorOffsetScreenY = 0.0f; |
| - mSystemUiScreenSize.setEmpty(); |
| + mSystemUiScreenRect.setEmpty(); |
| + startOffsetReductionAnimation(); |
| } |
| + |
| + repositionImage(); |
| } |
| public void adjustViewportForSystemUi(boolean adjustViewportForSystemUi) { |
| - mAdjustViewportForSystemUi = adjustViewportForSystemUi; |
| + mAdjustViewportSizeForSystemUi = adjustViewportForSystemUi; |
| + |
| + // The viewport center may have changed so reposition the image to reflect the new value. |
| + repositionImage(); |
| } |
| /** Resizes the image by zooming it such that the image is displayed without borders. */ |
| @@ -180,7 +152,7 @@ public class DesktopCanvas { |
| /** |
| * Repositions the image by translating and zooming it, to keep the zoom level within sensible |
| - * limits. The minimum zoom level is chosen to avoid black space around all 4 sides. The |
| + * limits. The minimum zoom level is chosen to avoid letterboxing on all 4 sides. The |
| * maximum zoom level is set arbitrarily, so that the user can zoom out again in a reasonable |
| * time, and to prevent arithmetic overflow problems from displaying the image. |
| * |
| @@ -217,47 +189,74 @@ public class DesktopCanvas { |
| mRenderData.transform.setScale(scale, scale); |
| } |
| - // Trim the image padding if the user is zooming out. This prevents cases where the image |
| - // pops to the center when it reaches its minimum size. Note that we do not need to do |
| - // anything when the user is zooming in as the canvas will expand and absorb the padding. |
| - if (scaleFactor < 1.0f) { |
| - mVisibleImagePadding.set(mVisibleImagePadding.left * PADDING_REDUCTION_FACTOR, |
| - mVisibleImagePadding.top * PADDING_REDUCTION_FACTOR, |
| - mVisibleImagePadding.right * PADDING_REDUCTION_FACTOR, |
| - mVisibleImagePadding.bottom * PADDING_REDUCTION_FACTOR); |
| - } |
| - |
| if (centerOnCursor) { |
| - setCursorPosition(mCursorPosition.x, mCursorPosition.y); |
| + setCursorPosition(mDesiredCenter.x, mDesiredCenter.y); |
| } else { |
| - // Find the new screen center (it was probably changed during the zoom operation) and |
| - // update the viewport and cursor. |
| - float[] mappedPoints = { |
| - ((float) mRenderData.screenWidth / 2), ((float) mRenderData.screenHeight / 2)}; |
| + // Find the new screen center (it probably changed during the zoom operation) and update |
| + // the viewport to smoothly track the zoom gesture. |
| + float[] mappedPoints = {((float) mRenderData.screenWidth / 2) - mViewportOffset.x, |
| + ((float) mRenderData.screenHeight / 2) - mViewportOffset.y}; |
| Matrix screenToImage = new Matrix(); |
| mRenderData.transform.invert(screenToImage); |
| screenToImage.mapPoints(mappedPoints); |
| // The cursor is mapped to the center of the viewport in this case. |
| - setCursorPosition(mappedPoints[0], mappedPoints[1]); |
| + setViewportCenter(mappedPoints[0], mappedPoints[1]); |
| } |
| } |
| /** |
| + * Sets the new cursor position, bounded by the given rect, and updates the image transform to |
| + * reflect the new position. |
| + */ |
| + private void updateCursorPosition(float newX, float newY, RectF bounds) { |
| + mDesiredCenter.set(newX, newY); |
| + constrainPointToBounds(mDesiredCenter, bounds); |
| + |
| + calculateViewportOffset(newX - mDesiredCenter.x, newY - mDesiredCenter.y); |
| + |
| + repositionImage(); |
| + } |
| + |
| + /** |
| + * Returns the height of the screen (in screen coordinates) for use in calculations involving |
| + * viewport positioning. |
| + */ |
| + private float getAdjustedScreenHeight() { |
| + float adjustedScreenHeight; |
| + if (mAdjustViewportSizeForSystemUi && !mSystemUiScreenRect.isEmpty()) { |
| + // Find the center point of the viewport on the screen. |
| + adjustedScreenHeight = mSystemUiScreenRect.bottom; |
| + } else { |
| + adjustedScreenHeight = ((float) mRenderData.screenHeight); |
| + } |
| + |
| + return adjustedScreenHeight; |
| + } |
| + |
| + /** |
| + * Returns the center position of the viewport (in screen coordinates) including adjustments. |
|
Lambros
2016/10/25 19:26:31
Clarify 'adjustments' by referring to {@link #adju
joedow
2016/10/25 20:12:06
Done.
|
| + */ |
| + private PointF getViewportCenter() { |
|
Lambros
2016/10/25 19:26:31
I think this name is misleading, because this is i
joedow
2016/10/25 20:12:06
Done.
|
| + return new PointF((float) mRenderData.screenWidth / 2, getAdjustedScreenHeight() / 2); |
| + } |
| + |
| + /** |
| * Repositions the image by translating it (without affecting the zoom level). |
| */ |
| private void repositionImage() { |
| - // Map the current viewport position to screen coordinates and adjust the image position. |
| - float[] viewportPosition = {mViewportPosition.x, mViewportPosition.y}; |
| - mRenderData.transform.mapPoints(viewportPosition); |
| + PointF viewportPosition = new PointF(mDesiredCenter.x, mDesiredCenter.y); |
| + constrainPointToBounds(viewportPosition, getViewportBounds()); |
| + float[] viewportAdjustment = {viewportPosition.x, viewportPosition.y}; |
| + mRenderData.transform.mapPoints(viewportAdjustment); |
| - float viewportTransX = ((float) mRenderData.screenWidth / 2) - viewportPosition[0]; |
| - float viewportTransY = |
| - ((float) mRenderData.screenHeight / 2) - viewportPosition[1] - mCursorOffsetScreenY; |
| + // Adjust the viewport to include the overpan amount. |
| + viewportAdjustment[0] += mViewportOffset.x; |
| + viewportAdjustment[1] += mViewportOffset.y; |
| // Translate the image to move the viewport to the expected screen location. |
| - mRenderData.transform.postTranslate(viewportTransX, viewportTransY); |
| - |
| - updateVisibleImagePadding(); |
| + PointF viewportCenter = getViewportCenter(); |
| + mRenderData.transform.postTranslate( |
| + viewportCenter.x - viewportAdjustment[0], viewportCenter.y - viewportAdjustment[1]); |
| mRenderStub.setTransformation(mRenderData.transform); |
| } |
| @@ -284,27 +283,7 @@ public class DesktopCanvas { |
| /** Returns a region which defines the set of valid cursor positions in image space. */ |
| private RectF getImageBounds() { |
| - // The set of valid cursor positions includes any point on the image as well as the padding. |
| - // Padding is additional space added to the image which is the larger value of: |
| - // - Potential overlap of the System UI and image content |
| - // - Actual amount of padding already being used |
| - // |
| - // By expanding the area, we allow the user to move the cursor 'under' the System UI which |
| - // pulls the content out from under it and allows it to be visible. Once the System UI has |
| - // been dismissed or changes size, we use the actual padding value instead which prevents |
| - // the desktop image from 'snapping' back to pre-System UI state. |
| - RectF systemUiOverlap = getSystemUiOverlap(); |
| - float[] padding = {Math.max(mVisibleImagePadding.left, systemUiOverlap.left), |
| - Math.max(mVisibleImagePadding.top + mCursorOffsetScreenY, systemUiOverlap.top), |
| - Math.max(mVisibleImagePadding.right, systemUiOverlap.right), |
| - Math.max(mVisibleImagePadding.bottom - mCursorOffsetScreenY, |
| - systemUiOverlap.bottom)}; |
| - Matrix screenToImage = new Matrix(); |
| - mRenderData.transform.invert(screenToImage); |
| - screenToImage.mapVectors(padding); |
| - |
| - return new RectF(-padding[0], -padding[1], mRenderData.imageWidth + padding[2], |
| - mRenderData.imageHeight + padding[3]); |
| + return new RectF(0, 0, mRenderData.imageWidth, mRenderData.imageHeight); |
| } |
| /** Returns a region which defines the set of valid viewport center values in image space. */ |
| @@ -315,7 +294,8 @@ public class DesktopCanvas { |
| Matrix screenToImage = new Matrix(); |
| mRenderData.transform.invert(screenToImage); |
| - float[] screenVectors = {(float) mRenderData.screenWidth, (float) mRenderData.screenHeight}; |
| + PointF viewportCenter = getViewportCenter(); |
| + float[] screenVectors = {viewportCenter.x, viewportCenter.y}; |
| screenToImage.mapVectors(screenVectors); |
| PointF letterboxPadding = getLetterboxPadding(); |
| @@ -323,14 +303,36 @@ public class DesktopCanvas { |
| screenToImage.mapVectors(letterboxPaddingVectors); |
| // screenCenter values are 1/2 of a particular screen dimension mapped to image space. |
| - float screenCenterX = (screenVectors[0] / 2.0f) - letterboxPaddingVectors[0]; |
| - float screenCenterY = (screenVectors[1] / 2.0f) - letterboxPaddingVectors[1]; |
| + float screenCenterX = screenVectors[0] - letterboxPaddingVectors[0]; |
| + float screenCenterY = screenVectors[1] - letterboxPaddingVectors[1]; |
| RectF imageBounds = getImageBounds(); |
| imageBounds.inset(screenCenterX, screenCenterY); |
| return imageBounds; |
| } |
| /** |
| + * Returns a region defining the maximum offset distance required to view the entire image |
| + * given the current amount of System UI overlapping it. |
| + */ |
| + private RectF getViewportOffsetBounds() { |
| + // The allowable region is determined by: |
| + // - Overlap of the System UI and image content |
| + // - Current viewport offset |
| + // |
| + // The System UI overlap represents the maximum allowable offset, this is used to bound the |
| + // viewport movement in each direction. The current offset is used to prevent 'snapping' |
| + // the image when the System UI overlap is reduced. |
| + RectF viewportOffsetRect = new RectF(); |
| + viewportOffsetRect.union(mViewportOffset.x, mViewportOffset.y); |
| + |
| + RectF systemUiOverlap = getSystemUiOverlap(); |
| + return new RectF(Math.min(viewportOffsetRect.left, -systemUiOverlap.left), |
| + Math.min(viewportOffsetRect.top, -systemUiOverlap.top), |
| + Math.max(viewportOffsetRect.right, systemUiOverlap.right), |
| + Math.max(viewportOffsetRect.bottom, systemUiOverlap.bottom)); |
| + } |
| + |
| + /** |
| * Provides the amount of padding needed to center the image content on the screen. |
| */ |
| private PointF getLetterboxPadding() { |
| @@ -339,10 +341,8 @@ public class DesktopCanvas { |
| // We want to letterbox when the image is smaller than the screen in a specific dimension. |
| // Since we center the image, split the difference so it is equally distributed. |
| - float widthAdjust = |
| - Math.max(((float) mRenderData.screenWidth - imageVectors[0]) / 2.0f, 0.0f); |
| - float heightAdjust = |
| - Math.max(((float) mRenderData.screenHeight - imageVectors[1]) / 2.0f, 0.0f); |
| + float widthAdjust = Math.max(((float) mRenderData.screenWidth - imageVectors[0]) / 2, 0); |
| + float heightAdjust = Math.max((getAdjustedScreenHeight() - imageVectors[1]) / 2, 0); |
| return new PointF(widthAdjust, heightAdjust); |
| } |
| @@ -352,29 +352,98 @@ public class DesktopCanvas { |
| * desktop image below it. This is the maximum amount that could overlap, not the actual value. |
| */ |
| private RectF getSystemUiOverlap() { |
| - // letterBox padding represents the space added to the image to center it on the screen. |
| + if (mSystemUiScreenRect.isEmpty()) { |
| + return new RectF(); |
| + } |
| + |
| + // LetterBox padding represents the space added to the image to center it on the screen. |
|
Lambros
2016/10/25 19:26:31
nit: lower-case B.
joedow
2016/10/25 20:12:06
Done.
|
| // Since it does not contain any interactable UI, we ignore it when calculating the overlap |
| // between the System UI and the remote desktop image. |
| // Note: Ignore negative padding (clamp to 0) since that means no overlap exists. |
| + float adjustedScreenHeight = getAdjustedScreenHeight(); |
| PointF letterboxPadding = getLetterboxPadding(); |
| - return new RectF(Math.max(mSystemUiScreenSize.left - letterboxPadding.x, 0.0f), |
| - Math.max(mSystemUiScreenSize.top - letterboxPadding.y + mCursorOffsetScreenY, 0.0f), |
| - Math.max(mSystemUiScreenSize.right - letterboxPadding.x, 0.0f), |
| - Math.max(mSystemUiScreenSize.bottom - letterboxPadding.y - mCursorOffsetScreenY, |
| + return new RectF(Math.max(mSystemUiScreenRect.left - letterboxPadding.x, 0.0f), |
| + Math.max(mSystemUiScreenRect.top - letterboxPadding.y, 0.0f), |
| + Math.max(mRenderData.screenWidth - mSystemUiScreenRect.right - letterboxPadding.x, |
| + 0.0f), |
| + Math.max(adjustedScreenHeight - mSystemUiScreenRect.bottom - letterboxPadding.y, |
| 0.0f)); |
| } |
| /** |
| - * Calculates the amount of padding visible on each edge of the desktop image. |
| + * Applies the given offset, as needed, to allow moving the image outside its normal bounds. |
| */ |
| - private void updateVisibleImagePadding() { |
| - PointF letterboxPadding = getLetterboxPadding(); |
| - float[] imagePoints = {0.0f, 0.0f, mRenderData.imageWidth, mRenderData.imageHeight}; |
| - mRenderData.transform.mapPoints(imagePoints); |
| + private void calculateViewportOffset(float offsetX, float offsetY) { |
| + if (mSystemUiScreenRect.isEmpty()) { |
| + // We only want to directly change the viewport offset when System UI is present. |
| + return; |
| + } |
| + |
| + float[] offsets = {offsetX, offsetY}; |
| + mRenderData.transform.mapVectors(offsets); |
| - mVisibleImagePadding.set(Math.max(imagePoints[0] - letterboxPadding.x, 0.0f), |
| - Math.max(imagePoints[1] - letterboxPadding.y, 0.0f), |
| - Math.max(mRenderData.screenWidth - imagePoints[2] - letterboxPadding.x, 0.0f), |
| - Math.max(mRenderData.screenHeight - imagePoints[3] - letterboxPadding.y, 0.0f)); |
| + // Use a temporary variable here as {@link #getViewportOffsetBounds()} uses the current |
| + // viewport offset as a maximum boundary. If we add the offset first, the result ends up |
| + // being unbounded. Thus we use a temporary object for the boundary calculation. |
| + PointF requestedOffset = |
| + new PointF(mViewportOffset.x + offsets[0], mViewportOffset.y + offsets[1]); |
| + constrainPointToBounds(requestedOffset, getViewportOffsetBounds()); |
| + mViewportOffset.set(requestedOffset); |
| + } |
| + |
| + /** |
| + * Starts an animation to smoothly reduce the viewport offset. Does nothing if an animation is |
| + * already running or the offset is already 0. |
| + */ |
| + private void startOffsetReductionAnimation() { |
| + if (mFrameRenderedCallback != null || Math.abs(mViewportOffset.length()) < EPSILON) { |
|
Lambros
2016/10/25 19:26:31
Math.abs() is unnecessary - length() is never nega
joedow
2016/10/25 20:12:06
Good point!
|
| + return; |
| + } |
| + |
| + mFrameRenderedCallback = new Event.ParameterCallback<Boolean, Void>() { |
| + private static final float DURATION_MS = 250.0f; |
| + |
| + private final Interpolator mInterpolator = new DecelerateInterpolator(); |
| + |
| + private long mStartTime = 0; |
| + private float mOriginalX = 0.0f; |
| + private float mOriginalY = 0.0f; |
| + |
| + @Override |
| + public Boolean run(Void p) { |
| + if (mFrameRenderedCallback == null) { |
| + return false; |
| + } |
| + |
| + if (mStartTime == 0) { |
| + mStartTime = SystemClock.elapsedRealtime(); |
| + mOriginalX = mViewportOffset.x; |
| + mOriginalY = mViewportOffset.y; |
| + } |
| + |
| + float progress = (SystemClock.elapsedRealtime() - mStartTime) / DURATION_MS; |
| + if (progress < 1.0f) { |
| + float reductionFactor = 1.0f - mInterpolator.getInterpolation(progress); |
| + mViewportOffset.set(mOriginalX * reductionFactor, mOriginalY * reductionFactor); |
| + } else { |
| + mViewportOffset.set(0.0f, 0.0f); |
| + mFrameRenderedCallback = null; |
| + } |
| + |
| + repositionImage(); |
| + |
| + return mFrameRenderedCallback != null; |
| + } |
| + }; |
| + |
| + mRenderStub.onCanvasRendered().addSelfRemovable(mFrameRenderedCallback); |
| + } |
| + |
| + /** |
| + * Stops an existing offset reduction animation. |
| + */ |
| + private void stopOffsetReductionAnimation() { |
| + // Setting this value this null will prevent it from continuing to execute. |
| + mFrameRenderedCallback = null; |
| } |
| } |