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 |