Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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.content.Context; | 7 import android.app.ActionBar; |
| 8 import android.app.Activity; | |
| 8 import android.graphics.Bitmap; | 9 import android.graphics.Bitmap; |
| 9 import android.graphics.Canvas; | 10 import android.graphics.Canvas; |
| 10 import android.graphics.Color; | 11 import android.graphics.Color; |
| 11 import android.graphics.Matrix; | 12 import android.graphics.Matrix; |
| 12 import android.graphics.Paint; | 13 import android.graphics.Paint; |
| 13 import android.os.Bundle; | 14 import android.os.Bundle; |
| 14 import android.os.Looper; | 15 import android.os.Looper; |
| 15 import android.util.Log; | 16 import android.util.Log; |
| 16 import android.view.GestureDetector; | 17 import android.view.GestureDetector; |
| 17 import android.view.MotionEvent; | 18 import android.view.MotionEvent; |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 45 */ | 46 */ |
| 46 private static final int BUTTON_UNDEFINED = 0; | 47 private static final int BUTTON_UNDEFINED = 0; |
| 47 private static final int BUTTON_LEFT = 1; | 48 private static final int BUTTON_LEFT = 1; |
| 48 private static final int BUTTON_RIGHT = 3; | 49 private static final int BUTTON_RIGHT = 3; |
| 49 | 50 |
| 50 /** Specifies one dimension of an image. */ | 51 /** Specifies one dimension of an image. */ |
| 51 private static enum Constraint { | 52 private static enum Constraint { |
| 52 UNDEFINED, WIDTH, HEIGHT | 53 UNDEFINED, WIDTH, HEIGHT |
| 53 } | 54 } |
| 54 | 55 |
| 56 private ActionBar mActionBar; | |
| 57 | |
| 55 private GestureDetector mScroller; | 58 private GestureDetector mScroller; |
| 56 private ScaleGestureDetector mZoomer; | 59 private ScaleGestureDetector mZoomer; |
| 57 | 60 |
| 58 /** Stores pan and zoom configuration and converts image coordinates to scre en coordinates. */ | 61 /** Stores pan and zoom configuration and converts image coordinates to scre en coordinates. */ |
| 59 private Matrix mTransform; | 62 private Matrix mTransform; |
| 60 | 63 |
| 61 private int mScreenWidth; | 64 private int mScreenWidth; |
| 62 private int mScreenHeight; | 65 private int mScreenHeight; |
| 63 | 66 |
| 64 /** Specifies the dimension by which the zoom level is being lower-bounded. */ | 67 /** Specifies the dimension by which the zoom level is being lower-bounded. */ |
| 65 private Constraint mConstraint; | 68 private Constraint mConstraint; |
|
Sergey Ulanov
2013/07/30 19:19:27
Should this be called mZoomConstraint?
solb
2013/07/30 21:30:54
Not really, since it also affects panning behavior
| |
| 66 | 69 |
| 70 /** Whether the dimension of constraint should be reckecked on the next aspe ct ratio change. */ | |
| 71 private boolean mRecheckConstraint; | |
| 72 | |
| 67 /** Whether the right edge of the image was visible on-screen during the las t render. */ | 73 /** Whether the right edge of the image was visible on-screen during the las t render. */ |
| 68 private boolean mRightUsedToBeOut; | 74 private boolean mRightUsedToBeOut; |
| 69 | 75 |
| 70 /** Whether the bottom edge of the image was visible on-screen during the la st render. */ | 76 /** Whether the bottom edge of the image was visible on-screen during the la st render. */ |
| 71 private boolean mBottomUsedToBeOut; | 77 private boolean mBottomUsedToBeOut; |
| 72 | 78 |
| 73 private int mMouseButton; | 79 private int mMouseButton; |
| 74 private boolean mMousePressed; | 80 private boolean mMousePressed; |
| 75 | 81 |
| 76 /** Whether the canvas needs to be redrawn. The update occurs when its size is next updated. */ | 82 public DesktopView(Activity context) { |
| 77 private boolean mCanvasNeedsRedraw; | 83 super(context); |
| 84 mActionBar = context.getActionBar(); | |
| 78 | 85 |
| 79 public DesktopView(Context context) { | |
| 80 super(context); | |
| 81 getHolder().addCallback(this); | 86 getHolder().addCallback(this); |
| 82 DesktopListener listener = new DesktopListener(); | 87 DesktopListener listener = new DesktopListener(); |
| 83 mScroller = new GestureDetector(context, listener); | 88 mScroller = new GestureDetector(context, listener); |
| 84 mZoomer = new ScaleGestureDetector(context, listener); | 89 mZoomer = new ScaleGestureDetector(context, listener); |
| 85 | 90 |
| 86 mTransform = new Matrix(); | 91 mTransform = new Matrix(); |
| 87 mScreenWidth = 0; | 92 mScreenWidth = 0; |
| 88 mScreenHeight = 0; | 93 mScreenHeight = 0; |
| 94 | |
| 89 mConstraint = Constraint.UNDEFINED; | 95 mConstraint = Constraint.UNDEFINED; |
| 96 mRecheckConstraint = false; | |
| 90 | 97 |
| 91 mRightUsedToBeOut = false; | 98 mRightUsedToBeOut = false; |
| 92 mBottomUsedToBeOut = false; | 99 mBottomUsedToBeOut = false; |
| 93 | 100 |
| 94 mMouseButton = BUTTON_UNDEFINED; | 101 mMouseButton = BUTTON_UNDEFINED; |
| 95 mMousePressed = false; | 102 mMousePressed = false; |
| 96 | |
| 97 mCanvasNeedsRedraw = false; | |
| 98 } | 103 } |
| 99 | 104 |
| 100 /** | 105 /** |
| 101 * Redraws the canvas. This should be done on a non-UI thread or it could | 106 * Redraws the canvas. This should be done on a non-UI thread or it could |
| 102 * cause the UI to lag. Specifically, it is currently invoked on the native | 107 * cause the UI to lag. Specifically, it is currently invoked on the native |
| 103 * graphics thread using a JNI. | 108 * graphics thread using a JNI. |
| 104 */ | 109 */ |
| 105 @Override | 110 @Override |
| 106 public void run() { | 111 public void run() { |
| 107 if (Looper.myLooper() == Looper.getMainLooper()) { | 112 if (Looper.myLooper() == Looper.getMainLooper()) { |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 120 // Screen coordinates of two defining points of the image. | 125 // Screen coordinates of two defining points of the image. |
| 121 float[] topleft = {0, 0}; | 126 float[] topleft = {0, 0}; |
| 122 mTransform.mapPoints(topleft); | 127 mTransform.mapPoints(topleft); |
| 123 float[] bottomright = {image.getWidth(), image.getHeight()}; | 128 float[] bottomright = {image.getWidth(), image.getHeight()}; |
| 124 mTransform.mapPoints(bottomright); | 129 mTransform.mapPoints(bottomright); |
| 125 | 130 |
| 126 // Whether to rescale and recenter the view. | 131 // Whether to rescale and recenter the view. |
| 127 boolean recenter = false; | 132 boolean recenter = false; |
| 128 | 133 |
| 129 if (mConstraint == Constraint.UNDEFINED) { | 134 if (mConstraint == Constraint.UNDEFINED) { |
| 130 mConstraint = image.getWidth()/image.getHeight() > mScreenWidth/ mScreenHeight ? | 135 mConstraint = (double)image.getWidth()/image.getHeight() > |
|
Sergey Ulanov
2013/07/30 19:19:27
mConstraint is calculated and used in the same met
solb
2013/07/30 21:30:54
Sometimes we need to keep the old value between re
| |
| 131 Constraint.WIDTH : Constraint.HEIGHT; | 136 (double)mScreenWidth/mScreenHeight ? Constraint.WIDTH : Constraint.HEIGHT; |
| 132 recenter = true; // We always rescale and recenter after a rota tion. | 137 recenter = true; // We always rescale and recenter after a rota tion. |
| 133 } | 138 } |
| 134 | 139 |
| 135 if (mConstraint == Constraint.WIDTH && | 140 if (mConstraint == Constraint.WIDTH && |
| 136 ((int)(bottomright[0] - topleft[0] + 0.5) < mScreenWidth || recenter)) { | 141 ((int)(bottomright[0] - topleft[0] + 0.5) < mScreenWidth || recenter)) { |
| 137 // The vertical edges of the image are flush against the device' s screen edges | 142 // The vertical edges of the image are flush against the device' s screen edges |
| 138 // when the entire host screen is visible, and the user has zoom ed out too far. | 143 // when the entire host screen is visible, and the user has zoom ed out too far. |
| 139 float imageMiddle = (float)image.getHeight() / 2; | 144 float imageMiddle = (float)image.getHeight() / 2; |
| 140 float screenMiddle = (float)mScreenHeight / 2; | 145 float screenMiddle = (float)mScreenHeight / 2; |
| 141 mTransform.setPolyToPoly( | 146 mTransform.setPolyToPoly( |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 168 if (leftEdgeOutOfBounds != rightEdgeOutOfBounds) { | 173 if (leftEdgeOutOfBounds != rightEdgeOutOfBounds) { |
| 169 if (leftEdgeOutOfBounds != mRightUsedToBeOut) { | 174 if (leftEdgeOutOfBounds != mRightUsedToBeOut) { |
| 170 // Make the left edge of the image flush with the left s creen edge. | 175 // Make the left edge of the image flush with the left s creen edge. |
| 171 values[Matrix.MTRANS_X] = 0; | 176 values[Matrix.MTRANS_X] = 0; |
| 172 } | 177 } |
| 173 else { | 178 else { |
| 174 // Make the right edge of the image flush with the right screen edge. | 179 // Make the right edge of the image flush with the right screen edge. |
| 175 values[Matrix.MTRANS_X] += mScreenWidth - bottomright[0] ; | 180 values[Matrix.MTRANS_X] += mScreenWidth - bottomright[0] ; |
| 176 } | 181 } |
| 177 } | 182 } |
| 178 else { | 183 else { |
|
Sergey Ulanov
2013/07/30 19:19:27
nit: else goes to the previous line.
solb
2013/07/30 21:30:54
Handling in https://codereview.chromium.org/212360
| |
| 179 // The else prevents this from being updated during the repo sitioning process, | 184 // The else prevents this from being updated during the repo sitioning process, |
| 180 // in which case the view would begin to oscillate. | 185 // in which case the view would begin to oscillate. |
| 181 mRightUsedToBeOut = rightEdgeOutOfBounds; | 186 mRightUsedToBeOut = rightEdgeOutOfBounds; |
| 182 } | 187 } |
| 183 | 188 |
| 184 // Prevent the user from scrolling past the top or bottom edge o f the image. | 189 // Prevent the user from scrolling past the top or bottom edge o f the image. |
| 185 if (topEdgeOutOfBounds != bottomEdgeOutOfBounds) { | 190 if (topEdgeOutOfBounds != bottomEdgeOutOfBounds) { |
| 186 if (topEdgeOutOfBounds != mBottomUsedToBeOut) { | 191 if (topEdgeOutOfBounds != mBottomUsedToBeOut) { |
| 187 // Make the top edge of the image flush with the top scr een edge. | 192 // Make the top edge of the image flush with the top scr een edge. |
| 188 values[Matrix.MTRANS_Y] = 0; | 193 values[Matrix.MTRANS_Y] = 0; |
| 189 } | 194 } |
| 190 else { | 195 else { |
| 191 // Make the bottom edge of the image flush with the bott om screen edge. | 196 // Make the bottom edge of the image flush with the bott om screen edge. |
| 192 values[Matrix.MTRANS_Y] += mScreenHeight - bottomright[1 ]; | 197 values[Matrix.MTRANS_Y] += mScreenHeight - bottomright[1 ]; |
| 193 } | 198 } |
| 194 } | 199 } |
| 195 else { | 200 else { |
|
Sergey Ulanov
2013/07/30 19:19:27
same here.
solb
2013/07/30 21:30:54
https://codereview.chromium.org/21236002
| |
| 196 // The else prevents this from being updated during the repo sitioning process, | 201 // The else prevents this from being updated during the repo sitioning process, |
| 197 // in which case the view would begin to oscillate. | 202 // in which case the view would begin to oscillate. |
| 198 mBottomUsedToBeOut = bottomEdgeOutOfBounds; | 203 mBottomUsedToBeOut = bottomEdgeOutOfBounds; |
| 199 } | 204 } |
| 200 | 205 |
| 201 mTransform.setValues(values); | 206 mTransform.setValues(values); |
| 202 } | 207 } |
| 203 | 208 |
| 204 canvas.setMatrix(mTransform); | 209 canvas.setMatrix(mTransform); |
| 205 } | 210 } |
| 206 | 211 |
| 207 canvas.drawColor(Color.BLACK); | 212 canvas.drawColor(Color.BLACK); |
| 208 canvas.drawBitmap(image, 0, 0, new Paint()); | 213 canvas.drawBitmap(image, 0, 0, new Paint()); |
| 209 getHolder().unlockCanvasAndPost(canvas); | 214 getHolder().unlockCanvasAndPost(canvas); |
| 210 } | 215 } |
| 211 | 216 |
| 212 /** Causes the canvas to be redrawn the next time our surface changes. */ | 217 /** |
| 213 public void requestCanvasRedraw() { | 218 * Causes the next canvas redraw to perform a check for which screen dimensi on more tightly |
| 214 mCanvasNeedsRedraw = true; | 219 * constrains the view of the image. This should be called between the time that a screen size |
| 220 * change is requested and the time it actually occurs. If it is not called in such a case, the | |
| 221 * screen will not be rearranged as aggressively (which is desirable when th e software keyboard | |
| 222 * appears in order to allow it to cover the image without forcing a resize) . | |
| 223 */ | |
| 224 public void requestRecheckConstrainingDimension() { | |
| 225 mRecheckConstraint = true; | |
|
Sergey Ulanov
2013/07/30 19:19:27
Can we reset mContraint here?
solb
2013/07/30 21:30:54
No, because we won't know the screen size until on
| |
| 215 } | 226 } |
| 216 | 227 |
| 217 /** | 228 /** |
| 218 * Called after the canvas is initially created, then after every | 229 * Called after the canvas is initially created, then after every |
| 219 * subsequent resize, as when the display is rotated. | 230 * subsequent resize, as when the display is rotated. |
| 220 */ | 231 */ |
| 221 @Override | 232 @Override |
| 222 public void surfaceChanged( | 233 public void surfaceChanged( |
| 223 SurfaceHolder holder, int format, int width, int height) { | 234 SurfaceHolder holder, int format, int width, int height) { |
| 235 mActionBar.hide(); | |
| 236 | |
| 224 synchronized (mTransform) { | 237 synchronized (mTransform) { |
| 225 mScreenWidth = width; | 238 mScreenWidth = width; |
| 226 mScreenHeight = height; | 239 mScreenHeight = height; |
| 227 mConstraint = Constraint.UNDEFINED; | 240 |
| 241 if (mRecheckConstraint) { | |
| 242 mConstraint = Constraint.UNDEFINED; | |
| 243 mRecheckConstraint = false; | |
| 244 } | |
| 228 } | 245 } |
| 229 | 246 |
| 230 if (mCanvasNeedsRedraw) { | 247 if (!JniInterface.redrawGraphics()) { |
| 231 JniInterface.redrawGraphics(); | 248 JniInterface.provideRedrawCallback(this); |
| 232 mCanvasNeedsRedraw = false; | |
| 233 } | 249 } |
| 234 } | 250 } |
| 235 | 251 |
| 236 /** Called when the canvas is first created. */ | 252 /** Called when the canvas is first created. */ |
| 237 @Override | 253 @Override |
| 238 public void surfaceCreated(SurfaceHolder holder) { | 254 public void surfaceCreated(SurfaceHolder holder) { |
| 239 Log.i("deskview", "DesktopView.surfaceCreated(...)"); | 255 Log.i("deskview", "DesktopView.surfaceCreated(...)"); |
| 240 JniInterface.provideRedrawCallback(this); | |
| 241 } | 256 } |
| 242 | 257 |
| 243 /** | 258 /** |
| 244 * Called when the canvas is finally destroyed. Marks the canvas as needing a redraw so that it | 259 * Called when the canvas is finally destroyed. Marks the canvas as needing a redraw so that it |
| 245 * will not be blank if the user later switches back to our window. | 260 * will not be blank if the user later switches back to our window. |
| 246 */ | 261 */ |
| 247 @Override | 262 @Override |
| 248 public void surfaceDestroyed(SurfaceHolder holder) { | 263 public void surfaceDestroyed(SurfaceHolder holder) { |
| 249 Log.i("deskview", "DesktopView.surfaceDestroyed(...)"); | 264 Log.i("deskview", "DesktopView.surfaceDestroyed(...)"); |
| 265 | |
| 266 // Stop this canvas from being redrawn. | |
| 250 JniInterface.provideRedrawCallback(null); | 267 JniInterface.provideRedrawCallback(null); |
| 251 | |
| 252 // Redraw the desktop as soon as the user switches back to this window. | |
| 253 mCanvasNeedsRedraw = true; | |
| 254 } | 268 } |
| 255 | 269 |
| 256 /** Called when a mouse action is made. */ | 270 /** Called when a mouse action is made. */ |
| 257 private void handleMouseMovement(float[] coordinates, int button, boolean pr essed) { | 271 private void handleMouseMovement(float[] coordinates, int button, boolean pr essed) { |
| 258 // Coordinates are relative to the canvas, but we need image coordinates . | 272 // Coordinates are relative to the canvas, but we need image coordinates . |
| 259 Matrix canvasToImage = new Matrix(); | 273 Matrix canvasToImage = new Matrix(); |
| 260 mTransform.invert(canvasToImage); | 274 mTransform.invert(canvasToImage); |
| 261 canvasToImage.mapPoints(coordinates); | 275 canvasToImage.mapPoints(coordinates); |
| 262 | 276 |
| 263 // Coordinates are now relative to the image, so transmit them to the ho st. | 277 // Coordinates are now relative to the image, so transmit them to the ho st. |
| 264 JniInterface.mouseAction((int)coordinates[0], (int)coordinates[1], butto n, pressed); | 278 JniInterface.mouseAction((int)coordinates[0], (int)coordinates[1], butto n, pressed); |
| 265 } | 279 } |
| 266 | 280 |
| 267 /** | 281 /** |
| 268 * Called whenever the user attempts to touch the canvas. Forwards such | 282 * Called whenever the user attempts to touch the canvas. Forwards such |
| 269 * events to the appropriate gesture detector until one accepts them. | 283 * events to the appropriate gesture detector until one accepts them. |
| 270 */ | 284 */ |
| 271 @Override | 285 @Override |
| 272 public boolean onTouchEvent(MotionEvent event) { | 286 public boolean onTouchEvent(MotionEvent event) { |
| 287 if (event.getPointerCount() == 3) { | |
| 288 mActionBar.show(); | |
| 289 } | |
| 290 | |
| 273 boolean handled = mScroller.onTouchEvent(event) || mZoomer.onTouchEvent( event); | 291 boolean handled = mScroller.onTouchEvent(event) || mZoomer.onTouchEvent( event); |
| 274 | 292 |
| 275 if (event.getPointerCount()==1) { | 293 if (event.getPointerCount() == 1) { |
|
Sergey Ulanov
2013/07/30 19:19:27
Do we still need to handle the event here when han
solb
2013/07/30 21:30:54
Unfortunately, in order to ensure the gesture dete
| |
| 276 float[] coordinates = {event.getRawX(), event.getY()}; | 294 float[] coordinates = {event.getRawX(), event.getY()}; |
| 277 | 295 |
| 278 switch (event.getActionMasked()) { | 296 switch (event.getActionMasked()) { |
| 279 case MotionEvent.ACTION_DOWN: | 297 case MotionEvent.ACTION_DOWN: |
| 280 Log.i("mouse", "Found a finger"); | 298 Log.i("mouse", "Found a finger"); |
| 281 mMouseButton = BUTTON_UNDEFINED; | 299 mMouseButton = BUTTON_UNDEFINED; |
| 282 mMousePressed = false; | 300 mMousePressed = false; |
| 283 break; | 301 break; |
| 284 | 302 |
| 285 case MotionEvent.ACTION_MOVE: | 303 case MotionEvent.ACTION_MOVE: |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 395 handleMouseMovement(coordinates, mMouseButton, false); | 413 handleMouseMovement(coordinates, mMouseButton, false); |
| 396 } | 414 } |
| 397 | 415 |
| 398 Log.i("mouse", "\tStarting right click"); | 416 Log.i("mouse", "\tStarting right click"); |
| 399 mMouseButton = BUTTON_RIGHT; | 417 mMouseButton = BUTTON_RIGHT; |
| 400 mMousePressed = true; | 418 mMousePressed = true; |
| 401 handleMouseMovement(coordinates, mMouseButton, mMousePressed); | 419 handleMouseMovement(coordinates, mMouseButton, mMousePressed); |
| 402 } | 420 } |
| 403 } | 421 } |
| 404 } | 422 } |
| OLD | NEW |