| 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.content.Context; |
| 8 import android.graphics.Bitmap; | 8 import android.graphics.Bitmap; |
| 9 import android.graphics.Canvas; | 9 import android.graphics.Canvas; |
| 10 import android.graphics.Color; | 10 import android.graphics.Color; |
| 11 import android.graphics.Paint; | 11 import android.graphics.Paint; |
| 12 import android.graphics.Point; | 12 import android.graphics.Point; |
| 13 import android.graphics.RadialGradient; | 13 import android.graphics.RadialGradient; |
| 14 import android.graphics.Rect; | 14 import android.graphics.Rect; |
| 15 import android.graphics.Shader; | 15 import android.graphics.Shader; |
| 16 import android.os.Looper; | 16 import android.os.Looper; |
| 17 import android.os.SystemClock; | 17 import android.os.SystemClock; |
| 18 import android.text.InputType; | 18 import android.text.InputType; |
| 19 import android.util.AttributeSet; | 19 import android.util.AttributeSet; |
| 20 import android.view.MotionEvent; | 20 import android.view.MotionEvent; |
| 21 import android.view.SurfaceHolder; | 21 import android.view.SurfaceHolder; |
| 22 import android.view.SurfaceView; | 22 import android.view.SurfaceView; |
| 23 import android.view.inputmethod.EditorInfo; | 23 import android.view.inputmethod.EditorInfo; |
| 24 import android.view.inputmethod.InputConnection; | 24 import android.view.inputmethod.InputConnection; |
| 25 import android.view.inputmethod.InputMethodManager; | 25 import android.view.inputmethod.InputMethodManager; |
| 26 | 26 |
| 27 import org.chromium.base.Log; | 27 import org.chromium.base.Log; |
| 28 import org.chromium.chromoting.jni.Client; | 28 import org.chromium.chromoting.jni.JniInterface; |
| 29 | 29 |
| 30 /** | 30 /** |
| 31 * The user interface for viewing and interacting with a specific remote host. | 31 * The user interface for viewing and interacting with a specific remote host. |
| 32 * It provides a canvas onto which the video feed is rendered, handles | 32 * It provides a canvas onto which the video feed is rendered, handles |
| 33 * multitouch pan and zoom gestures, and collects and forwards input events. | 33 * multitouch pan and zoom gestures, and collects and forwards input events. |
| 34 */ | 34 */ |
| 35 /** GUI element that holds the drawing canvas. */ | 35 /** GUI element that holds the drawing canvas. */ |
| 36 public class DesktopView extends SurfaceView implements DesktopViewInterface, | 36 public class DesktopView extends SurfaceView implements DesktopViewInterface, |
| 37 SurfaceHolder.Callback { | 37 SurfaceHolder.Callback { |
| 38 /** Used to define the animation feedback shown when a user touches the scre
en. */ | 38 /** Used to define the animation feedback shown when a user touches the scre
en. */ |
| 39 public enum InputFeedbackType { NONE, SMALL_ANIMATION, LARGE_ANIMATION } | 39 public enum InputFeedbackType { NONE, SMALL_ANIMATION, LARGE_ANIMATION } |
| 40 | 40 |
| 41 private static final String TAG = "Chromoting"; | 41 private static final String TAG = "Chromoting"; |
| 42 | 42 |
| 43 private final RenderData mRenderData; | 43 private final RenderData mRenderData; |
| 44 private final TouchInputHandlerInterface mInputHandler; | 44 private final TouchInputHandlerInterface mInputHandler; |
| 45 | 45 |
| 46 /** The parent Desktop activity. */ | 46 /** The parent Desktop activity. */ |
| 47 private Desktop mDesktop; | 47 private Desktop mDesktop; |
| 48 | 48 |
| 49 /** The Client connection, used to inject input and fetch the video frames.
*/ | |
| 50 private Client mClient; | |
| 51 | |
| 52 | |
| 53 // Flag to prevent multiple repaint requests from being backed up. Requests
for repainting will | 49 // Flag to prevent multiple repaint requests from being backed up. Requests
for repainting will |
| 54 // be dropped if this is already set to true. This is used by the main threa
d and the painting | 50 // be dropped if this is already set to true. This is used by the main threa
d and the painting |
| 55 // thread, so the access should be synchronized on |mRenderData|. | 51 // thread, so the access should be synchronized on |mRenderData|. |
| 56 private boolean mRepaintPending; | 52 private boolean mRepaintPending; |
| 57 | 53 |
| 58 // Flag used to ensure that the SurfaceView is only painted between calls to
surfaceCreated() | 54 // Flag used to ensure that the SurfaceView is only painted between calls to
surfaceCreated() |
| 59 // and surfaceDestroyed(). Accessed on main thread and display thread, so th
is should be | 55 // and surfaceDestroyed(). Accessed on main thread and display thread, so th
is should be |
| 60 // synchronized on |mRenderData|. | 56 // synchronized on |mRenderData|. |
| 61 private boolean mSurfaceCreated = false; | 57 private boolean mSurfaceCreated = false; |
| 62 | 58 |
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 176 | 172 |
| 177 mRepaintPending = false; | 173 mRepaintPending = false; |
| 178 | 174 |
| 179 getHolder().addCallback(this); | 175 getHolder().addCallback(this); |
| 180 } | 176 } |
| 181 | 177 |
| 182 public void setDesktop(Desktop desktop) { | 178 public void setDesktop(Desktop desktop) { |
| 183 mDesktop = desktop; | 179 mDesktop = desktop; |
| 184 } | 180 } |
| 185 | 181 |
| 186 public void setClient(Client client) { | |
| 187 mClient = client; | |
| 188 } | |
| 189 | |
| 190 /** See {@link TouchInputHandler#onSoftInputMethodVisibilityChanged} for API
details. */ | 182 /** See {@link TouchInputHandler#onSoftInputMethodVisibilityChanged} for API
details. */ |
| 191 public void onSoftInputMethodVisibilityChanged(boolean inputMethodVisible, R
ect bounds) { | 183 public void onSoftInputMethodVisibilityChanged(boolean inputMethodVisible, R
ect bounds) { |
| 192 mInputHandler.onSoftInputMethodVisibilityChanged(inputMethodVisible, bou
nds); | 184 mInputHandler.onSoftInputMethodVisibilityChanged(inputMethodVisible, bou
nds); |
| 193 } | 185 } |
| 194 | 186 |
| 195 /** Request repainting of the desktop view. */ | 187 /** Request repainting of the desktop view. */ |
| 196 void requestRepaint() { | 188 void requestRepaint() { |
| 197 synchronized (mRenderData) { | 189 synchronized (mRenderData) { |
| 198 if (mRepaintPending) { | 190 if (mRepaintPending) { |
| 199 return; | 191 return; |
| 200 } | 192 } |
| 201 mRepaintPending = true; | 193 mRepaintPending = true; |
| 202 } | 194 } |
| 203 mClient.redrawGraphics(); | 195 JniInterface.redrawGraphics(); |
| 204 } | 196 } |
| 205 | 197 |
| 206 /** | 198 /** |
| 207 * Redraws the canvas. This should be done on a non-UI thread or it could | 199 * Redraws the canvas. This should be done on a non-UI thread or it could |
| 208 * cause the UI to lag. Specifically, it is currently invoked on the native | 200 * cause the UI to lag. Specifically, it is currently invoked on the native |
| 209 * graphics thread using a JNI. | 201 * graphics thread using a JNI. |
| 210 */ | 202 */ |
| 211 public void paint() { | 203 public void paint() { |
| 212 long startTimeMs = SystemClock.uptimeMillis(); | 204 long startTimeMs = SystemClock.uptimeMillis(); |
| 213 | 205 |
| 214 if (Looper.myLooper() == Looper.getMainLooper()) { | 206 if (Looper.myLooper() == Looper.getMainLooper()) { |
| 215 Log.w(TAG, "Canvas being redrawn on UI thread"); | 207 Log.w(TAG, "Canvas being redrawn on UI thread"); |
| 216 } | 208 } |
| 217 | 209 |
| 218 Bitmap image = mClient.getVideoFrame(); | 210 Bitmap image = JniInterface.getVideoFrame(); |
| 219 if (image == null) { | 211 if (image == null) { |
| 220 // This can happen if the client is connected, but a complete video
frame has not yet | 212 // This can happen if the client is connected, but a complete video
frame has not yet |
| 221 // been decoded. | 213 // been decoded. |
| 222 return; | 214 return; |
| 223 } | 215 } |
| 224 | 216 |
| 225 int width = image.getWidth(); | 217 int width = image.getWidth(); |
| 226 int height = image.getHeight(); | 218 int height = image.getHeight(); |
| 227 boolean sizeChanged = false; | 219 boolean sizeChanged = false; |
| 228 synchronized (mRenderData) { | 220 synchronized (mRenderData) { |
| 229 if (mRenderData.imageWidth != width || mRenderData.imageHeight != he
ight) { | 221 if (mRenderData.imageWidth != width || mRenderData.imageHeight != he
ight) { |
| 230 // TODO(lambroslambrou): Move this code into a sizeChanged() cal
lback, to be | 222 // TODO(lambroslambrou): Move this code into a sizeChanged() cal
lback, to be |
| 231 // triggered from native code (on the display thread) when the r
emote screen size | 223 // triggered from JniInterface (on the display thread) when the
remote screen size |
| 232 // changes. | 224 // changes. |
| 233 mRenderData.imageWidth = width; | 225 mRenderData.imageWidth = width; |
| 234 mRenderData.imageHeight = height; | 226 mRenderData.imageHeight = height; |
| 235 sizeChanged = true; | 227 sizeChanged = true; |
| 236 } | 228 } |
| 237 } | 229 } |
| 238 if (sizeChanged) { | 230 if (sizeChanged) { |
| 239 mInputHandler.onHostSizeChanged(width, height); | 231 mInputHandler.onHostSizeChanged(width, height); |
| 240 } | 232 } |
| 241 | 233 |
| (...skipping 25 matching lines...) Expand all Loading... |
| 267 boolean feedbackAnimationRunning = mFeedbackAnimator.isAnimationRunning(
); | 259 boolean feedbackAnimationRunning = mFeedbackAnimator.isAnimationRunning(
); |
| 268 if (feedbackAnimationRunning) { | 260 if (feedbackAnimationRunning) { |
| 269 float scaleFactor; | 261 float scaleFactor; |
| 270 synchronized (mRenderData) { | 262 synchronized (mRenderData) { |
| 271 scaleFactor = mRenderData.transform.mapRadius(1); | 263 scaleFactor = mRenderData.transform.mapRadius(1); |
| 272 } | 264 } |
| 273 mFeedbackAnimator.render(canvas, cursorPosition.x, cursorPosition.y,
scaleFactor); | 265 mFeedbackAnimator.render(canvas, cursorPosition.x, cursorPosition.y,
scaleFactor); |
| 274 } | 266 } |
| 275 | 267 |
| 276 if (drawCursor) { | 268 if (drawCursor) { |
| 277 Bitmap cursorBitmap = mClient.getCursorBitmap(); | 269 Bitmap cursorBitmap = JniInterface.getCursorBitmap(); |
| 278 if (cursorBitmap != null) { | 270 if (cursorBitmap != null) { |
| 279 Point hotspot = mClient.getCursorHotspot(); | 271 Point hotspot = JniInterface.getCursorHotspot(); |
| 280 canvas.drawBitmap(cursorBitmap, cursorPosition.x - hotspot.x, | 272 canvas.drawBitmap(cursorBitmap, cursorPosition.x - hotspot.x, |
| 281 cursorPosition.y - hotspot.y, new Paint()); | 273 cursorPosition.y - hotspot.y, new Paint()); |
| 282 } | 274 } |
| 283 } | 275 } |
| 284 | 276 |
| 285 getHolder().unlockCanvasAndPost(canvas); | 277 getHolder().unlockCanvasAndPost(canvas); |
| 286 | 278 |
| 287 synchronized (mAnimationLock) { | 279 synchronized (mAnimationLock) { |
| 288 if (mInputAnimationRunning || feedbackAnimationRunning) { | 280 if (mInputAnimationRunning || feedbackAnimationRunning) { |
| 289 getHandler().postAtTime(new Runnable() { | 281 getHandler().postAtTime(new Runnable() { |
| (...skipping 30 matching lines...) Expand all Loading... |
| 320 mRenderData.screenWidth = width; | 312 mRenderData.screenWidth = width; |
| 321 mRenderData.screenHeight = height; | 313 mRenderData.screenHeight = height; |
| 322 } | 314 } |
| 323 | 315 |
| 324 attachRedrawCallback(); | 316 attachRedrawCallback(); |
| 325 mInputHandler.onClientSizeChanged(width, height); | 317 mInputHandler.onClientSizeChanged(width, height); |
| 326 requestRepaint(); | 318 requestRepaint(); |
| 327 } | 319 } |
| 328 | 320 |
| 329 public void attachRedrawCallback() { | 321 public void attachRedrawCallback() { |
| 330 mClient.provideRedrawCallback(new Runnable() { | 322 JniInterface.provideRedrawCallback(new Runnable() { |
| 331 @Override | 323 @Override |
| 332 public void run() { | 324 public void run() { |
| 333 paint(); | 325 paint(); |
| 334 } | 326 } |
| 335 }); | 327 }); |
| 336 } | 328 } |
| 337 | 329 |
| 338 /** Called when the canvas is first created. */ | 330 /** Called when the canvas is first created. */ |
| 339 @Override | 331 @Override |
| 340 public void surfaceCreated(SurfaceHolder holder) { | 332 public void surfaceCreated(SurfaceHolder holder) { |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 416 /** Updates the current InputStrategy used by the TouchInputHandler. */ | 408 /** Updates the current InputStrategy used by the TouchInputHandler. */ |
| 417 public void changeInputMode( | 409 public void changeInputMode( |
| 418 Desktop.InputMode inputMode, CapabilityManager.HostCapability hostTo
uchCapability) { | 410 Desktop.InputMode inputMode, CapabilityManager.HostCapability hostTo
uchCapability) { |
| 419 // We need both input mode and host input capabilities to select the inp
ut strategy. | 411 // We need both input mode and host input capabilities to select the inp
ut strategy. |
| 420 if (!inputMode.isSet() || !hostTouchCapability.isSet()) { | 412 if (!inputMode.isSet() || !hostTouchCapability.isSet()) { |
| 421 return; | 413 return; |
| 422 } | 414 } |
| 423 | 415 |
| 424 switch (inputMode) { | 416 switch (inputMode) { |
| 425 case TRACKPAD: | 417 case TRACKPAD: |
| 426 mInputHandler.setInputStrategy(new TrackpadInputStrategy(mRender
Data, mClient)); | 418 mInputHandler.setInputStrategy(new TrackpadInputStrategy(mRender
Data)); |
| 427 break; | 419 break; |
| 428 | 420 |
| 429 case TOUCH: | 421 case TOUCH: |
| 430 if (hostTouchCapability.isSupported()) { | 422 if (hostTouchCapability.isSupported()) { |
| 431 mInputHandler.setInputStrategy(new TouchInputStrategy(mRende
rData, mClient)); | 423 mInputHandler.setInputStrategy(new TouchInputStrategy(mRende
rData)); |
| 432 } else { | 424 } else { |
| 433 mInputHandler.setInputStrategy( | 425 mInputHandler.setInputStrategy( |
| 434 new SimulatedTouchInputStrategy(mRenderData, mClient
, getContext())); | 426 new SimulatedTouchInputStrategy(mRenderData, getCont
ext())); |
| 435 } | 427 } |
| 436 break; | 428 break; |
| 437 | 429 |
| 438 default: | 430 default: |
| 439 // Unreachable, but required by Google Java style and findbugs. | 431 // Unreachable, but required by Google Java style and findbugs. |
| 440 assert false : "Unreached"; | 432 assert false : "Unreached"; |
| 441 } | 433 } |
| 442 | 434 |
| 443 // Ensure the cursor state is updated appropriately. | 435 // Ensure the cursor state is updated appropriately. |
| 444 requestRepaint(); | 436 requestRepaint(); |
| 445 } | 437 } |
| 446 } | 438 } |
| OLD | NEW |