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 |