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