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 |