Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.chromoting; | 5 package org.chromium.chromoting; |
| 6 | 6 |
| 7 import android.graphics.Matrix; | 7 import android.graphics.Matrix; |
| 8 import android.graphics.PointF; | 8 import android.graphics.PointF; |
| 9 import android.graphics.Rect; | 9 import android.graphics.Rect; |
| 10 import android.graphics.RectF; | 10 import android.graphics.RectF; |
| 11 import android.os.SystemClock; | |
| 12 import android.view.animation.DecelerateInterpolator; | |
| 13 import android.view.animation.Interpolator; | |
| 11 | 14 |
| 12 /** | 15 /** |
| 13 * This class is responsible for transforming the desktop image matrix. | 16 * This class is responsible for transforming the desktop image matrix. |
| 14 */ | 17 */ |
| 15 public class DesktopCanvas { | 18 public class DesktopCanvas { |
| 16 /** | 19 /** Used for floating point comparisons. */ |
| 17 * Maximum allowed zoom level - see {@link #scaleAndRepositionImage()}. | 20 private static final float EPSILON = 0.0001f; |
| 18 */ | 21 |
| 22 /** Maximum allowed zoom level - see {@link #scaleAndRepositionImage()}. */ | |
| 19 private static final float MAX_ZOOM_FACTOR = 100.0f; | 23 private static final float MAX_ZOOM_FACTOR = 100.0f; |
| 20 | 24 |
| 21 /** | |
| 22 * Used to smoothly reduce the amount of padding while the user is zooming. | |
| 23 */ | |
| 24 private static final float PADDING_REDUCTION_FACTOR = 0.85f; | |
| 25 | |
| 26 private final RenderStub mRenderStub; | 25 private final RenderStub mRenderStub; |
| 27 private final RenderData mRenderData; | 26 private final RenderData mRenderData; |
| 28 | 27 |
| 29 /** | 28 /** |
| 30 * Represents the actual center of the viewport in image space. This value needs to be a pair | |
| 31 * of floats so the desktop image can be positioned with sub-pixel accuracy for smoother panning | |
| 32 * animations at high zoom levels. | |
| 33 */ | |
| 34 // TODO(joedow): See if we can collapse Viewport and Cursor position members . They were needed | |
| 35 // in the past due to how we calculated the center positions but may not be needed now. | |
| 36 private PointF mViewportPosition = new PointF(); | |
| 37 | |
| 38 /** | |
| 39 * Represents the desired center of the viewport in image space. This value may not represent | 29 * Represents the desired center of the viewport in image space. This value may not represent |
| 40 * the actual center of the viewport as adjustments are made to ensure as mu ch of the desktop is | 30 * the actual center of the viewport as adjustments are made to ensure as mu ch of the desktop is |
| 41 * visible as possible. This value needs to be a pair of floats so the desk top image can be | 31 * visible as possible. This value needs to be a pair of floats so the desk top image can be |
| 42 * positioned with sub-pixel accuracy for smoother panning animations at hig h zoom levels. | 32 * positioned with sub-pixel accuracy for smoother panning animations at hig h zoom levels. |
| 43 * Note: The internal cursor position may be placed outside of the image bou ndary, however the | |
| 44 * cursor position we inject on the host side is restricted to the actual im age dimensions. | |
| 45 */ | 33 */ |
| 46 private PointF mCursorPosition = new PointF(); | 34 private PointF mDesiredCenter = new PointF(); |
| 47 | 35 |
| 48 /** | 36 /** |
| 49 * Represents the amount of space, in pixels, used by System UI on each edge of the screen. | 37 * If System UI exists, this contains the area of the screen which is unobsc ured by it, |
| 38 * otherwise it is empty. | |
| 50 */ | 39 */ |
| 51 // TODO(joedow): Update usage of this member so it is a true Rect instead of a set of offsets. | 40 private Rect mSystemUiScreenRect = new Rect(); |
| 52 private Rect mSystemUiScreenSize = new Rect(); | |
| 53 | 41 |
| 54 /** | 42 /** |
| 55 * Represents the amount of padding, in screen pixels, added to each edge of the desktop image. | 43 * Represents the amount of space, in pixels, to shift the image under the v iewport. This value |
| 56 * This extra space allows the user to reveal portions of the desktop image which are obscured | 44 * is used to allow panning the image further than would be possible when us ing the normal |
| 57 * by System UI. | 45 * boundary values to account for System UI. This functionality ensures the user can view and |
| 46 * interact with any area of the remote image, even when System UI might oth erwise obscure it. | |
| 58 */ | 47 */ |
| 59 private RectF mVisibleImagePadding = new RectF(); | 48 private PointF mViewportOffset = new PointF(); |
| 60 | 49 |
| 61 /** | 50 /** |
| 62 * Tracks whether to adjust the viewport to account for System UI. If false, the viewport | 51 * Tracks whether to adjust the size of the viewport to account for System U I. If false, the |
| 63 * center is the center of the screen. If true, then System UI offsets will be used to | 52 * viewport center is mapped to the center of the screen. If true, then Sys tem UI sizes will be |
| 64 * adjust the position of the viewport to ensure the cursor is visible. | 53 * used to determine the center of the viewport. |
| 65 */ | 54 */ |
| 66 private boolean mAdjustViewportForSystemUi = false; | 55 private boolean mAdjustViewportSizeForSystemUi = false; |
| 67 | 56 |
| 68 /** | 57 /* Used to perform per-frame rendering tasks. */ |
| 69 * Represents the amount of space, in pixels, to adjust the cursor center alo ng the y-axis. | 58 private Event.ParameterCallback<Boolean, Void> mFrameRenderedCallback; |
| 70 */ | |
| 71 private float mCursorOffsetScreenY = 0.0f; | |
| 72 | 59 |
| 73 public DesktopCanvas(RenderStub renderStub, RenderData renderData) { | 60 public DesktopCanvas(RenderStub renderStub, RenderData renderData) { |
| 74 mRenderStub = renderStub; | 61 mRenderStub = renderStub; |
| 75 mRenderData = renderData; | 62 mRenderData = renderData; |
| 76 } | 63 } |
| 77 | 64 |
| 78 /** | 65 /** |
| 79 * Sets the desired center position of the viewport (a.k.a. the cursor posit ion) and ensures | 66 * Sets the desired center position of the viewport (a.k.a. the cursor posit ion). |
| 80 * the viewport is updated to include the cursor within it. | |
| 81 * | 67 * |
| 82 * @param newX The new x coordinate value for the desired center position. | 68 * @param newX The new x coordinate value for the desired center position. |
| 83 * @param newY The new y coordinate value for the desired center position. | 69 * @param newY The new y coordinate value for the desired center position. |
| 84 */ | 70 */ |
| 85 public void setCursorPosition(float newX, float newY) { | 71 public void setCursorPosition(float newX, float newY) { |
| 86 // First set the cursor position since its potential values are a supers et of the viewport. | 72 updateCursorPosition(newX, newY, getImageBounds()); |
| 87 mCursorPosition.set(newX, newY); | 73 } |
| 88 constrainPointToBounds(mCursorPosition, getImageBounds()); | |
| 89 | 74 |
| 90 // Now set the viewport position based on the cursor. | 75 /** |
| 91 mViewportPosition.set(mCursorPosition); | 76 * Sets the center of the viewport using an absolute position (in image coor dinates). |
| 92 constrainPointToBounds(mViewportPosition, getViewportBounds()); | 77 * |
| 78 * @param newX The new x coordinate value for the center position of the vie wport. | |
| 79 * @param newY The new y coordinate value for the center position of the vie wport. | |
| 80 */ | |
| 81 public void setViewportCenter(float newX, float newY) { | |
| 82 updateCursorPosition(newX, newY, getViewportBounds()); | |
| 83 } | |
| 93 | 84 |
| 94 repositionImage(); | 85 /** |
| 86 * Shifts the cursor by the passed in values (in image coordinates). | |
| 87 * | |
| 88 * @param deltaX The distance (in image coordinates) to move the cursor alon g the x-axis. | |
| 89 * @param deltaY The distance (in image coordinates) to move the cursor alon g the y-axis. | |
| 90 * @return A point representing the new cursor position. | |
| 91 */ | |
| 92 public PointF moveCursorPosition(float deltaX, float deltaY) { | |
| 93 updateCursorPosition( | |
| 94 mDesiredCenter.x + deltaX, mDesiredCenter.y + deltaY, getImageBo unds()); | |
| 95 return new PointF(mDesiredCenter.x, mDesiredCenter.y); | |
| 95 } | 96 } |
| 96 | 97 |
| 97 /** | 98 /** |
| 98 * Shifts the viewport by the passed in values (in image coordinates). | 99 * Shifts the viewport by the passed in values (in image coordinates). |
| 99 * | 100 * |
| 100 * @param deltaX The distance (in image coordinates) to move the viewport al ong the x-axis. | 101 * @param deltaX The distance (in image coordinates) to move the viewport al ong the x-axis. |
| 101 * @param deltaY The distance (in image coordinates) to move the viewport al ong the y-axis. | 102 * @param deltaY The distance (in image coordinates) to move the viewport al ong the y-axis. |
| 102 */ | 103 */ |
| 103 public void moveViewportCenter(float deltaX, float deltaY) { | 104 public void moveViewportCenter(float deltaX, float deltaY) { |
| 104 // Offset and adjust the viewport center position to fit the screen. | 105 updateCursorPosition( |
| 105 mViewportPosition.offset(deltaX, deltaY); | 106 mDesiredCenter.x + deltaX, mDesiredCenter.y + deltaY, getViewpor tBounds()); |
| 106 constrainPointToBounds(mViewportPosition, getViewportBounds()); | |
| 107 | |
| 108 // We don't need to constrain the cursor position as the viewport positi on is always within | |
| 109 // the bounds of the potential cursor positions. | |
| 110 mCursorPosition.set(mViewportPosition); | |
| 111 | |
| 112 repositionImage(); | |
| 113 } | 107 } |
| 114 | 108 |
| 115 /** | 109 /** |
| 116 * Shifts the cursor by the passed in values (in image coordinates) and adju sts the viewport. | |
| 117 * | |
| 118 * @param deltaX The distance (in image coordinates) to move the cursor alon g the x-axis. | |
| 119 * @param deltaY The distance (in image coordinates) to move the cursor alon g the y-axis. | |
| 120 * @return A point representing the new cursor position. | |
| 121 */ | |
| 122 public PointF moveCursorPosition(float deltaX, float deltaY) { | |
| 123 setCursorPosition(mCursorPosition.x + deltaX, mCursorPosition.y + deltaY ); | |
| 124 return new PointF(mCursorPosition.x, mCursorPosition.y); | |
| 125 } | |
| 126 | |
| 127 /** | |
| 128 * Handles System UI size and visibility changes. | 110 * Handles System UI size and visibility changes. |
| 129 * | 111 * |
| 130 * @param parameter The set of values defining the current System UI state. | 112 * @param parameter The set of values defining the current System UI state. |
| 131 */ | 113 */ |
| 132 public void onSystemUiVisibilityChanged(SystemUiVisibilityChangedEventParame ter parameter) { | 114 public void onSystemUiVisibilityChanged(SystemUiVisibilityChangedEventParame ter parameter) { |
| 133 if (parameter.softInputMethodVisible) { | 115 if (parameter.softInputMethodVisible) { |
| 134 mSystemUiScreenSize.set(parameter.left, parameter.top, | 116 mSystemUiScreenRect.set( |
| 135 mRenderData.screenWidth - parameter.right, | 117 parameter.left, parameter.top, parameter.right, parameter.bo ttom); |
| 136 mRenderData.screenHeight - parameter.bottom); | |
| 137 | 118 |
| 138 if (mAdjustViewportForSystemUi) { | 119 stopOffsetReductionAnimation(); |
| 139 // Adjust the cursor position to ensure it's visible when large System UI (1/3 or | 120 } else { |
| 140 // more of the total screen size) is displayed (typically the So ft Keyboard). | 121 mSystemUiScreenRect.setEmpty(); |
| 141 // Without this change, it is difficult for users to enter text into edit controls | 122 startOffsetReductionAnimation(); |
| 142 // which are located bottom of the screen and may not see the cu rsor at all. | 123 } |
| 143 if (mSystemUiScreenSize.bottom > (mRenderData.screenHeight / 3)) { | |
| 144 // Center the cursor within the viewable area (not obscured by System UI). | |
| 145 mCursorOffsetScreenY = (float) parameter.bottom / 2.0f; | |
| 146 } else { | |
| 147 mCursorOffsetScreenY = 0.0f; | |
| 148 } | |
| 149 | 124 |
| 150 // Apply the cursor offset. | 125 repositionImage(); |
| 151 setCursorPosition(mCursorPosition.x, mCursorPosition.y); | |
| 152 } | |
| 153 } else { | |
| 154 mCursorOffsetScreenY = 0.0f; | |
| 155 mSystemUiScreenSize.setEmpty(); | |
| 156 } | |
| 157 } | 126 } |
| 158 | 127 |
| 159 public void adjustViewportForSystemUi(boolean adjustViewportForSystemUi) { | 128 public void adjustViewportForSystemUi(boolean adjustViewportForSystemUi) { |
| 160 mAdjustViewportForSystemUi = adjustViewportForSystemUi; | 129 mAdjustViewportSizeForSystemUi = adjustViewportForSystemUi; |
| 130 | |
| 131 // The viewport center may have changed so reposition the image to refle ct the new value. | |
| 132 repositionImage(); | |
| 161 } | 133 } |
| 162 | 134 |
| 163 /** Resizes the image by zooming it such that the image is displayed without borders. */ | 135 /** Resizes the image by zooming it such that the image is displayed without borders. */ |
| 164 public void resizeImageToFitScreen() { | 136 public void resizeImageToFitScreen() { |
| 165 // Protect against being called before the image has been initialized. | 137 // Protect against being called before the image has been initialized. |
| 166 if (mRenderData.imageWidth == 0 || mRenderData.imageHeight == 0) { | 138 if (mRenderData.imageWidth == 0 || mRenderData.imageHeight == 0) { |
| 167 return; | 139 return; |
| 168 } | 140 } |
| 169 | 141 |
| 170 float widthRatio = (float) mRenderData.screenWidth / mRenderData.imageWi dth; | 142 float widthRatio = (float) mRenderData.screenWidth / mRenderData.imageWi dth; |
| 171 float heightRatio = (float) mRenderData.screenHeight / mRenderData.image Height; | 143 float heightRatio = (float) mRenderData.screenHeight / mRenderData.image Height; |
| 172 float screenToImageScale = Math.max(widthRatio, heightRatio); | 144 float screenToImageScale = Math.max(widthRatio, heightRatio); |
| 173 | 145 |
| 174 // If the image is smaller than the screen in either dimension, then we want to scale it | 146 // If the image is smaller than the screen in either dimension, then we want to scale it |
| 175 // up to fit both and fill the screen with the image of the remote deskt op. | 147 // up to fit both and fill the screen with the image of the remote deskt op. |
| 176 if (screenToImageScale > 1.0f) { | 148 if (screenToImageScale > 1.0f) { |
| 177 mRenderData.transform.setScale(screenToImageScale, screenToImageScal e); | 149 mRenderData.transform.setScale(screenToImageScale, screenToImageScal e); |
| 178 } | 150 } |
| 179 } | 151 } |
| 180 | 152 |
| 181 /** | 153 /** |
| 182 * Repositions the image by translating and zooming it, to keep the zoom lev el within sensible | 154 * Repositions the image by translating and zooming it, to keep the zoom lev el within sensible |
| 183 * limits. The minimum zoom level is chosen to avoid black space around all 4 sides. The | 155 * limits. The minimum zoom level is chosen to avoid letterboxing on all 4 s ides. The |
| 184 * maximum zoom level is set arbitrarily, so that the user can zoom out agai n in a reasonable | 156 * maximum zoom level is set arbitrarily, so that the user can zoom out agai n in a reasonable |
| 185 * time, and to prevent arithmetic overflow problems from displaying the ima ge. | 157 * time, and to prevent arithmetic overflow problems from displaying the ima ge. |
| 186 * | 158 * |
| 187 * @param scaleFactor The factor used to zoom the canvas in or out. | 159 * @param scaleFactor The factor used to zoom the canvas in or out. |
| 188 * @param px The center x coordinate for the scale action. | 160 * @param px The center x coordinate for the scale action. |
| 189 * @param py The center y coordinate for the scale action. | 161 * @param py The center y coordinate for the scale action. |
| 190 * @param centerOnCursor Determines whether the viewport will be translated to the desired | 162 * @param centerOnCursor Determines whether the viewport will be translated to the desired |
| 191 * center position before being adjusted to fit the sc reen boundaries. | 163 * center position before being adjusted to fit the sc reen boundaries. |
| 192 */ | 164 */ |
| 193 public void scaleAndRepositionImage( | 165 public void scaleAndRepositionImage( |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 210 mRenderData.transform.mapVectors(imageSize); | 182 mRenderData.transform.mapVectors(imageSize); |
| 211 | 183 |
| 212 if (imageSize[0] < mRenderData.screenWidth && imageSize[1] < mRenderData .screenHeight) { | 184 if (imageSize[0] < mRenderData.screenWidth && imageSize[1] < mRenderData .screenHeight) { |
| 213 // Displayed image is too small in both directions, so apply the min imum zoom | 185 // Displayed image is too small in both directions, so apply the min imum zoom |
| 214 // level needed to fit either the width or height. | 186 // level needed to fit either the width or height. |
| 215 float scale = Math.min((float) mRenderData.screenWidth / mRenderData .imageWidth, | 187 float scale = Math.min((float) mRenderData.screenWidth / mRenderData .imageWidth, |
| 216 (float) mRenderData.screenHeight / mRenderDat a.imageHeight); | 188 (float) mRenderData.screenHeight / mRenderDat a.imageHeight); |
| 217 mRenderData.transform.setScale(scale, scale); | 189 mRenderData.transform.setScale(scale, scale); |
| 218 } | 190 } |
| 219 | 191 |
| 220 // Trim the image padding if the user is zooming out. This prevents cas es where the image | |
| 221 // pops to the center when it reaches its minimum size. Note that we do not need to do | |
| 222 // anything when the user is zooming in as the canvas will expand and ab sorb the padding. | |
| 223 if (scaleFactor < 1.0f) { | |
| 224 mVisibleImagePadding.set(mVisibleImagePadding.left * PADDING_REDUCTI ON_FACTOR, | |
| 225 mVisibleImagePadding.top * PADDING_REDUCTION_FACTOR, | |
| 226 mVisibleImagePadding.right * PADDING_REDUCTION_FACTOR, | |
| 227 mVisibleImagePadding.bottom * PADDING_REDUCTION_FACTOR); | |
| 228 } | |
| 229 | |
| 230 if (centerOnCursor) { | 192 if (centerOnCursor) { |
| 231 setCursorPosition(mCursorPosition.x, mCursorPosition.y); | 193 setCursorPosition(mDesiredCenter.x, mDesiredCenter.y); |
| 232 } else { | 194 } else { |
| 233 // Find the new screen center (it was probably changed during the zo om operation) and | 195 // Find the new screen center (it probably changed during the zoom o peration) and update |
| 234 // update the viewport and cursor. | 196 // the viewport to smoothly track the zoom gesture. |
| 235 float[] mappedPoints = { | 197 float[] mappedPoints = {((float) mRenderData.screenWidth / 2) - mVie wportOffset.x, |
| 236 ((float) mRenderData.screenWidth / 2), ((float) mRenderData. screenHeight / 2)}; | 198 ((float) mRenderData.screenHeight / 2) - mViewportOffset.y}; |
| 237 Matrix screenToImage = new Matrix(); | 199 Matrix screenToImage = new Matrix(); |
| 238 mRenderData.transform.invert(screenToImage); | 200 mRenderData.transform.invert(screenToImage); |
| 239 screenToImage.mapPoints(mappedPoints); | 201 screenToImage.mapPoints(mappedPoints); |
| 240 // The cursor is mapped to the center of the viewport in this case. | 202 // The cursor is mapped to the center of the viewport in this case. |
| 241 setCursorPosition(mappedPoints[0], mappedPoints[1]); | 203 setViewportCenter(mappedPoints[0], mappedPoints[1]); |
| 242 } | 204 } |
| 243 } | 205 } |
| 244 | 206 |
| 245 /** | 207 /** |
| 208 * Sets the new cursor position, bounded by the given rect, and updates the image transform to | |
| 209 * reflect the new position. | |
| 210 */ | |
| 211 private void updateCursorPosition(float newX, float newY, RectF bounds) { | |
| 212 mDesiredCenter.set(newX, newY); | |
| 213 constrainPointToBounds(mDesiredCenter, bounds); | |
| 214 | |
| 215 calculateViewportOffset(newX - mDesiredCenter.x, newY - mDesiredCenter.y ); | |
| 216 | |
| 217 repositionImage(); | |
| 218 } | |
| 219 | |
| 220 /** | |
| 221 * Returns the height of the screen (in screen coordinates) for use in calcu lations involving | |
| 222 * viewport positioning. | |
| 223 */ | |
| 224 private float getAdjustedScreenHeight() { | |
| 225 float adjustedScreenHeight; | |
| 226 if (mAdjustViewportSizeForSystemUi && !mSystemUiScreenRect.isEmpty()) { | |
| 227 // Find the center point of the viewport on the screen. | |
| 228 adjustedScreenHeight = mSystemUiScreenRect.bottom; | |
| 229 } else { | |
| 230 adjustedScreenHeight = ((float) mRenderData.screenHeight); | |
| 231 } | |
| 232 | |
| 233 return adjustedScreenHeight; | |
| 234 } | |
| 235 | |
| 236 /** | |
| 237 * Returns the center position of the viewport (in screen coordinates) inclu ding adjustments. | |
|
Lambros
2016/10/25 19:26:31
Clarify 'adjustments' by referring to {@link #adju
joedow
2016/10/25 20:12:06
Done.
| |
| 238 */ | |
| 239 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.
| |
| 240 return new PointF((float) mRenderData.screenWidth / 2, getAdjustedScreen Height() / 2); | |
| 241 } | |
| 242 | |
| 243 /** | |
| 246 * Repositions the image by translating it (without affecting the zoom level ). | 244 * Repositions the image by translating it (without affecting the zoom level ). |
| 247 */ | 245 */ |
| 248 private void repositionImage() { | 246 private void repositionImage() { |
| 249 // Map the current viewport position to screen coordinates and adjust th e image position. | 247 PointF viewportPosition = new PointF(mDesiredCenter.x, mDesiredCenter.y) ; |
| 250 float[] viewportPosition = {mViewportPosition.x, mViewportPosition.y}; | 248 constrainPointToBounds(viewportPosition, getViewportBounds()); |
| 251 mRenderData.transform.mapPoints(viewportPosition); | 249 float[] viewportAdjustment = {viewportPosition.x, viewportPosition.y}; |
| 250 mRenderData.transform.mapPoints(viewportAdjustment); | |
| 252 | 251 |
| 253 float viewportTransX = ((float) mRenderData.screenWidth / 2) - viewportP osition[0]; | 252 // Adjust the viewport to include the overpan amount. |
| 254 float viewportTransY = | 253 viewportAdjustment[0] += mViewportOffset.x; |
| 255 ((float) mRenderData.screenHeight / 2) - viewportPosition[1] - m CursorOffsetScreenY; | 254 viewportAdjustment[1] += mViewportOffset.y; |
| 256 | 255 |
| 257 // Translate the image to move the viewport to the expected screen locat ion. | 256 // Translate the image to move the viewport to the expected screen locat ion. |
| 258 mRenderData.transform.postTranslate(viewportTransX, viewportTransY); | 257 PointF viewportCenter = getViewportCenter(); |
| 259 | 258 mRenderData.transform.postTranslate( |
| 260 updateVisibleImagePadding(); | 259 viewportCenter.x - viewportAdjustment[0], viewportCenter.y - vie wportAdjustment[1]); |
| 261 | 260 |
| 262 mRenderStub.setTransformation(mRenderData.transform); | 261 mRenderStub.setTransformation(mRenderData.transform); |
| 263 } | 262 } |
| 264 | 263 |
| 265 /** | 264 /** |
| 266 * Updates the given point such that it refers to a coordinate within the bo unds provided. | 265 * Updates the given point such that it refers to a coordinate within the bo unds provided. |
| 267 * | 266 * |
| 268 * @param point The point to adjust, the original object is modified. | 267 * @param point The point to adjust, the original object is modified. |
| 269 * @param bounds The bounds used to constrain the point. | 268 * @param bounds The bounds used to constrain the point. |
| 270 */ | 269 */ |
| 271 private void constrainPointToBounds(PointF point, RectF bounds) { | 270 private void constrainPointToBounds(PointF point, RectF bounds) { |
| 272 if (point.x < bounds.left) { | 271 if (point.x < bounds.left) { |
| 273 point.x = bounds.left; | 272 point.x = bounds.left; |
| 274 } else if (point.x > bounds.right) { | 273 } else if (point.x > bounds.right) { |
| 275 point.x = bounds.right; | 274 point.x = bounds.right; |
| 276 } | 275 } |
| 277 | 276 |
| 278 if (point.y < bounds.top) { | 277 if (point.y < bounds.top) { |
| 279 point.y = bounds.top; | 278 point.y = bounds.top; |
| 280 } else if (point.y > bounds.bottom) { | 279 } else if (point.y > bounds.bottom) { |
| 281 point.y = bounds.bottom; | 280 point.y = bounds.bottom; |
| 282 } | 281 } |
| 283 } | 282 } |
| 284 | 283 |
| 285 /** Returns a region which defines the set of valid cursor positions in imag e space. */ | 284 /** Returns a region which defines the set of valid cursor positions in imag e space. */ |
| 286 private RectF getImageBounds() { | 285 private RectF getImageBounds() { |
| 287 // The set of valid cursor positions includes any point on the image as well as the padding. | 286 return new RectF(0, 0, mRenderData.imageWidth, mRenderData.imageHeight); |
| 288 // Padding is additional space added to the image which is the larger va lue of: | |
| 289 // - Potential overlap of the System UI and image content | |
| 290 // - Actual amount of padding already being used | |
| 291 // | |
| 292 // By expanding the area, we allow the user to move the cursor 'under' t he System UI which | |
| 293 // pulls the content out from under it and allows it to be visible. Onc e the System UI has | |
| 294 // been dismissed or changes size, we use the actual padding value inste ad which prevents | |
| 295 // the desktop image from 'snapping' back to pre-System UI state. | |
| 296 RectF systemUiOverlap = getSystemUiOverlap(); | |
| 297 float[] padding = {Math.max(mVisibleImagePadding.left, systemUiOverlap.l eft), | |
| 298 Math.max(mVisibleImagePadding.top + mCursorOffsetScreenY, system UiOverlap.top), | |
| 299 Math.max(mVisibleImagePadding.right, systemUiOverlap.right), | |
| 300 Math.max(mVisibleImagePadding.bottom - mCursorOffsetScreenY, | |
| 301 systemUiOverlap.bottom)}; | |
| 302 Matrix screenToImage = new Matrix(); | |
| 303 mRenderData.transform.invert(screenToImage); | |
| 304 screenToImage.mapVectors(padding); | |
| 305 | |
| 306 return new RectF(-padding[0], -padding[1], mRenderData.imageWidth + padd ing[2], | |
| 307 mRenderData.imageHeight + padding[3]); | |
| 308 } | 287 } |
| 309 | 288 |
| 310 /** Returns a region which defines the set of valid viewport center values i n image space. */ | 289 /** Returns a region which defines the set of valid viewport center values i n image space. */ |
| 311 private RectF getViewportBounds() { | 290 private RectF getViewportBounds() { |
| 312 // The region of allowable viewport values is the imageBound rect, inset by the size of the | 291 // The region of allowable viewport values is the imageBound rect, inset by the size of the |
| 313 // viewport itself. This prevents over and under panning of the viewpor t while still | 292 // viewport itself. This prevents over and under panning of the viewpor t while still |
| 314 // allowing the user to see and interact with all pixels one the desktop image. | 293 // allowing the user to see and interact with all pixels one the desktop image. |
| 315 Matrix screenToImage = new Matrix(); | 294 Matrix screenToImage = new Matrix(); |
| 316 mRenderData.transform.invert(screenToImage); | 295 mRenderData.transform.invert(screenToImage); |
| 317 | 296 |
| 318 float[] screenVectors = {(float) mRenderData.screenWidth, (float) mRende rData.screenHeight}; | 297 PointF viewportCenter = getViewportCenter(); |
| 298 float[] screenVectors = {viewportCenter.x, viewportCenter.y}; | |
| 319 screenToImage.mapVectors(screenVectors); | 299 screenToImage.mapVectors(screenVectors); |
| 320 | 300 |
| 321 PointF letterboxPadding = getLetterboxPadding(); | 301 PointF letterboxPadding = getLetterboxPadding(); |
| 322 float[] letterboxPaddingVectors = {letterboxPadding.x, letterboxPadding. y}; | 302 float[] letterboxPaddingVectors = {letterboxPadding.x, letterboxPadding. y}; |
| 323 screenToImage.mapVectors(letterboxPaddingVectors); | 303 screenToImage.mapVectors(letterboxPaddingVectors); |
| 324 | 304 |
| 325 // screenCenter values are 1/2 of a particular screen dimension mapped t o image space. | 305 // screenCenter values are 1/2 of a particular screen dimension mapped t o image space. |
| 326 float screenCenterX = (screenVectors[0] / 2.0f) - letterboxPaddingVector s[0]; | 306 float screenCenterX = screenVectors[0] - letterboxPaddingVectors[0]; |
| 327 float screenCenterY = (screenVectors[1] / 2.0f) - letterboxPaddingVector s[1]; | 307 float screenCenterY = screenVectors[1] - letterboxPaddingVectors[1]; |
| 328 RectF imageBounds = getImageBounds(); | 308 RectF imageBounds = getImageBounds(); |
| 329 imageBounds.inset(screenCenterX, screenCenterY); | 309 imageBounds.inset(screenCenterX, screenCenterY); |
| 330 return imageBounds; | 310 return imageBounds; |
| 331 } | 311 } |
| 332 | 312 |
| 333 /** | 313 /** |
| 314 * Returns a region defining the maximum offset distance required to view th e entire image | |
| 315 * given the current amount of System UI overlapping it. | |
| 316 */ | |
| 317 private RectF getViewportOffsetBounds() { | |
| 318 // The allowable region is determined by: | |
| 319 // - Overlap of the System UI and image content | |
| 320 // - Current viewport offset | |
| 321 // | |
| 322 // The System UI overlap represents the maximum allowable offset, this i s used to bound the | |
| 323 // viewport movement in each direction. The current offset is used to p revent 'snapping' | |
| 324 // the image when the System UI overlap is reduced. | |
| 325 RectF viewportOffsetRect = new RectF(); | |
| 326 viewportOffsetRect.union(mViewportOffset.x, mViewportOffset.y); | |
| 327 | |
| 328 RectF systemUiOverlap = getSystemUiOverlap(); | |
| 329 return new RectF(Math.min(viewportOffsetRect.left, -systemUiOverlap.left ), | |
| 330 Math.min(viewportOffsetRect.top, -systemUiOverlap.top), | |
| 331 Math.max(viewportOffsetRect.right, systemUiOverlap.right), | |
| 332 Math.max(viewportOffsetRect.bottom, systemUiOverlap.bottom)); | |
| 333 } | |
| 334 | |
| 335 /** | |
| 334 * Provides the amount of padding needed to center the image content on the screen. | 336 * Provides the amount of padding needed to center the image content on the screen. |
| 335 */ | 337 */ |
| 336 private PointF getLetterboxPadding() { | 338 private PointF getLetterboxPadding() { |
| 337 float[] imageVectors = {mRenderData.imageWidth, mRenderData.imageHeight} ; | 339 float[] imageVectors = {mRenderData.imageWidth, mRenderData.imageHeight} ; |
| 338 mRenderData.transform.mapVectors(imageVectors); | 340 mRenderData.transform.mapVectors(imageVectors); |
| 339 | 341 |
| 340 // We want to letterbox when the image is smaller than the screen in a s pecific dimension. | 342 // We want to letterbox when the image is smaller than the screen in a s pecific dimension. |
| 341 // Since we center the image, split the difference so it is equally dist ributed. | 343 // Since we center the image, split the difference so it is equally dist ributed. |
| 342 float widthAdjust = | 344 float widthAdjust = Math.max(((float) mRenderData.screenWidth - imageVec tors[0]) / 2, 0); |
| 343 Math.max(((float) mRenderData.screenWidth - imageVectors[0]) / 2 .0f, 0.0f); | 345 float heightAdjust = Math.max((getAdjustedScreenHeight() - imageVectors[ 1]) / 2, 0); |
| 344 float heightAdjust = | |
| 345 Math.max(((float) mRenderData.screenHeight - imageVectors[1]) / 2.0f, 0.0f); | |
| 346 | 346 |
| 347 return new PointF(widthAdjust, heightAdjust); | 347 return new PointF(widthAdjust, heightAdjust); |
| 348 } | 348 } |
| 349 | 349 |
| 350 /** | 350 /** |
| 351 * Returns the amount of System UI along each edge of the screen which could overlap the remote | 351 * Returns the amount of System UI along each edge of the screen which could overlap the remote |
| 352 * desktop image below it. This is the maximum amount that could overlap, n ot the actual value. | 352 * desktop image below it. This is the maximum amount that could overlap, n ot the actual value. |
| 353 */ | 353 */ |
| 354 private RectF getSystemUiOverlap() { | 354 private RectF getSystemUiOverlap() { |
| 355 // letterBox padding represents the space added to the image to center i t on the screen. | 355 if (mSystemUiScreenRect.isEmpty()) { |
| 356 return new RectF(); | |
| 357 } | |
| 358 | |
| 359 // LetterBox padding represents the space added to the image to center i t on the screen. | |
|
Lambros
2016/10/25 19:26:31
nit: lower-case B.
joedow
2016/10/25 20:12:06
Done.
| |
| 356 // Since it does not contain any interactable UI, we ignore it when calc ulating the overlap | 360 // Since it does not contain any interactable UI, we ignore it when calc ulating the overlap |
| 357 // between the System UI and the remote desktop image. | 361 // between the System UI and the remote desktop image. |
| 358 // Note: Ignore negative padding (clamp to 0) since that means no overla p exists. | 362 // Note: Ignore negative padding (clamp to 0) since that means no overla p exists. |
| 363 float adjustedScreenHeight = getAdjustedScreenHeight(); | |
| 359 PointF letterboxPadding = getLetterboxPadding(); | 364 PointF letterboxPadding = getLetterboxPadding(); |
| 360 return new RectF(Math.max(mSystemUiScreenSize.left - letterboxPadding.x, 0.0f), | 365 return new RectF(Math.max(mSystemUiScreenRect.left - letterboxPadding.x, 0.0f), |
| 361 Math.max(mSystemUiScreenSize.top - letterboxPadding.y + mCursorO ffsetScreenY, 0.0f), | 366 Math.max(mSystemUiScreenRect.top - letterboxPadding.y, 0.0f), |
| 362 Math.max(mSystemUiScreenSize.right - letterboxPadding.x, 0.0f), | 367 Math.max(mRenderData.screenWidth - mSystemUiScreenRect.right - l etterboxPadding.x, |
| 363 Math.max(mSystemUiScreenSize.bottom - letterboxPadding.y - mCurs orOffsetScreenY, | 368 0.0f), |
| 369 Math.max(adjustedScreenHeight - mSystemUiScreenRect.bottom - let terboxPadding.y, | |
| 364 0.0f)); | 370 0.0f)); |
| 365 } | 371 } |
| 366 | 372 |
| 367 /** | 373 /** |
| 368 * Calculates the amount of padding visible on each edge of the desktop imag e. | 374 * Applies the given offset, as needed, to allow moving the image outside it s normal bounds. |
| 369 */ | 375 */ |
| 370 private void updateVisibleImagePadding() { | 376 private void calculateViewportOffset(float offsetX, float offsetY) { |
| 371 PointF letterboxPadding = getLetterboxPadding(); | 377 if (mSystemUiScreenRect.isEmpty()) { |
| 372 float[] imagePoints = {0.0f, 0.0f, mRenderData.imageWidth, mRenderData.i mageHeight}; | 378 // We only want to directly change the viewport offset when System U I is present. |
| 373 mRenderData.transform.mapPoints(imagePoints); | 379 return; |
| 380 } | |
| 374 | 381 |
| 375 mVisibleImagePadding.set(Math.max(imagePoints[0] - letterboxPadding.x, 0 .0f), | 382 float[] offsets = {offsetX, offsetY}; |
| 376 Math.max(imagePoints[1] - letterboxPadding.y, 0.0f), | 383 mRenderData.transform.mapVectors(offsets); |
| 377 Math.max(mRenderData.screenWidth - imagePoints[2] - letterboxPad ding.x, 0.0f), | 384 |
| 378 Math.max(mRenderData.screenHeight - imagePoints[3] - letterboxPa dding.y, 0.0f)); | 385 // Use a temporary variable here as {@link #getViewportOffsetBounds()} u ses the current |
| 386 // viewport offset as a maximum boundary. If we add the offset first, t he result ends up | |
| 387 // being unbounded. Thus we use a temporary object for the boundary cal culation. | |
| 388 PointF requestedOffset = | |
| 389 new PointF(mViewportOffset.x + offsets[0], mViewportOffset.y + o ffsets[1]); | |
| 390 constrainPointToBounds(requestedOffset, getViewportOffsetBounds()); | |
| 391 mViewportOffset.set(requestedOffset); | |
| 392 } | |
| 393 | |
| 394 /** | |
| 395 * Starts an animation to smoothly reduce the viewport offset. Does nothing if an animation is | |
| 396 * already running or the offset is already 0. | |
| 397 */ | |
| 398 private void startOffsetReductionAnimation() { | |
| 399 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!
| |
| 400 return; | |
| 401 } | |
| 402 | |
| 403 mFrameRenderedCallback = new Event.ParameterCallback<Boolean, Void>() { | |
| 404 private static final float DURATION_MS = 250.0f; | |
| 405 | |
| 406 private final Interpolator mInterpolator = new DecelerateInterpolato r(); | |
| 407 | |
| 408 private long mStartTime = 0; | |
| 409 private float mOriginalX = 0.0f; | |
| 410 private float mOriginalY = 0.0f; | |
| 411 | |
| 412 @Override | |
| 413 public Boolean run(Void p) { | |
| 414 if (mFrameRenderedCallback == null) { | |
| 415 return false; | |
| 416 } | |
| 417 | |
| 418 if (mStartTime == 0) { | |
| 419 mStartTime = SystemClock.elapsedRealtime(); | |
| 420 mOriginalX = mViewportOffset.x; | |
| 421 mOriginalY = mViewportOffset.y; | |
| 422 } | |
| 423 | |
| 424 float progress = (SystemClock.elapsedRealtime() - mStartTime) / DURATION_MS; | |
| 425 if (progress < 1.0f) { | |
| 426 float reductionFactor = 1.0f - mInterpolator.getInterpolatio n(progress); | |
| 427 mViewportOffset.set(mOriginalX * reductionFactor, mOriginalY * reductionFactor); | |
| 428 } else { | |
| 429 mViewportOffset.set(0.0f, 0.0f); | |
| 430 mFrameRenderedCallback = null; | |
| 431 } | |
| 432 | |
| 433 repositionImage(); | |
| 434 | |
| 435 return mFrameRenderedCallback != null; | |
| 436 } | |
| 437 }; | |
| 438 | |
| 439 mRenderStub.onCanvasRendered().addSelfRemovable(mFrameRenderedCallback); | |
| 440 } | |
| 441 | |
| 442 /** | |
| 443 * Stops an existing offset reduction animation. | |
| 444 */ | |
| 445 private void stopOffsetReductionAnimation() { | |
| 446 // Setting this value this null will prevent it from continuing to execu te. | |
| 447 mFrameRenderedCallback = null; | |
| 379 } | 448 } |
| 380 } | 449 } |
| OLD | NEW |