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..cf8353b58a1fd9d41f70a6ead29d1b6d5b517d89 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,75 @@ 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) taking System UI into |
+ * account. |
+ */ |
+ private PointF getViewportScreenCenter() { |
+ 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 = getViewportScreenCenter(); |
+ mRenderData.transform.postTranslate( |
+ viewportCenter.x - viewportAdjustment[0], viewportCenter.y - viewportAdjustment[1]); |
mRenderStub.setTransformation(mRenderData.transform); |
} |
@@ -284,27 +284,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 +295,8 @@ public class DesktopCanvas { |
Matrix screenToImage = new Matrix(); |
mRenderData.transform.invert(screenToImage); |
- float[] screenVectors = {(float) mRenderData.screenWidth, (float) mRenderData.screenHeight}; |
+ PointF viewportCenter = getViewportScreenCenter(); |
+ float[] screenVectors = {viewportCenter.x, viewportCenter.y}; |
screenToImage.mapVectors(screenVectors); |
PointF letterboxPadding = getLetterboxPadding(); |
@@ -323,14 +304,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 +342,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 +353,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. |
// 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 || mViewportOffset.length() < EPSILON) { |
+ 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; |
} |
} |