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.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; | |
| 14 import android.graphics.Rect; | 13 import android.graphics.Rect; |
| 15 import android.graphics.Shader; | |
| 16 import android.os.Looper; | 14 import android.os.Looper; |
| 17 import android.os.SystemClock; | 15 import android.os.SystemClock; |
| 18 import android.text.InputType; | 16 import android.text.InputType; |
| 19 import android.util.AttributeSet; | 17 import android.util.AttributeSet; |
| 20 import android.view.MotionEvent; | 18 import android.view.MotionEvent; |
| 21 import android.view.SurfaceHolder; | 19 import android.view.SurfaceHolder; |
| 22 import android.view.SurfaceView; | 20 import android.view.SurfaceView; |
| 23 import android.view.inputmethod.EditorInfo; | 21 import android.view.inputmethod.EditorInfo; |
| 24 import android.view.inputmethod.InputConnection; | 22 import android.view.inputmethod.InputConnection; |
| 25 import android.view.inputmethod.InputMethodManager; | 23 import android.view.inputmethod.InputMethodManager; |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 53 // Flag to prevent multiple repaint requests from being backed up. Requests for repainting will | 51 // 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 | 52 // 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|. | 53 // thread, so the access should be synchronized on |mRenderData|. |
| 56 private boolean mRepaintPending; | 54 private boolean mRepaintPending; |
| 57 | 55 |
| 58 // Flag used to ensure that the SurfaceView is only painted between calls to surfaceCreated() | 56 // 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 | 57 // and surfaceDestroyed(). Accessed on main thread and display thread, so th is should be |
| 60 // synchronized on |mRenderData|. | 58 // synchronized on |mRenderData|. |
| 61 private boolean mSurfaceCreated = false; | 59 private boolean mSurfaceCreated = false; |
| 62 | 60 |
| 63 /** Helper class for displaying the long-press feedback animation. This clas s is thread-safe. */ | 61 private final Event.Raisable<EventParameters.Paint> mOnPaint = new Event.Rai sable<>(); |
|
Lambros
2016/06/01 23:51:36
mOnPaint is a bit misleading. Maybe mOnPaintAnimat
Hzj_jie
2016/06/02 02:21:37
From my opinion, the event name represents 'happen
| |
| 64 private static class FeedbackAnimator { | |
| 65 /** Total duration of the animation, in milliseconds. */ | |
| 66 private static final float TOTAL_DURATION_MS = 220; | |
| 67 | |
| 68 /** Start time of the animation, from {@link SystemClock#uptimeMillis()} . */ | |
| 69 private long mStartTimeInMs = 0; | |
| 70 | |
| 71 private boolean mRunning = false; | |
| 72 | |
| 73 /** Contains the size of the feedback animation for the most recent requ est. */ | |
| 74 private float mFeedbackSizeInPixels; | |
| 75 | |
| 76 /** Lock to allow multithreaded access to {@link #mStartTimeInMs} and {@ link #mRunning}. */ | |
| 77 private final Object mLock = new Object(); | |
| 78 | |
| 79 private Paint mPaint = new Paint(); | |
| 80 | |
| 81 public boolean isAnimationRunning() { | |
| 82 synchronized (mLock) { | |
| 83 return mRunning; | |
| 84 } | |
| 85 } | |
| 86 | |
| 87 /** | |
| 88 * Begins a new animation sequence. After calling this method, the calle r should | |
| 89 * call {@link #render(Canvas, float, float, float)} periodically whilst | |
| 90 * {@link #isAnimationRunning()} returns true. | |
| 91 */ | |
| 92 public void startAnimation(InputFeedbackType feedbackType) { | |
| 93 if (feedbackType == InputFeedbackType.NONE) { | |
| 94 return; | |
| 95 } | |
| 96 | |
| 97 synchronized (mLock) { | |
| 98 mRunning = true; | |
| 99 mStartTimeInMs = SystemClock.uptimeMillis(); | |
| 100 mFeedbackSizeInPixels = getInputFeedbackSizeInPixels(feedbackTyp e); | |
| 101 } | |
| 102 } | |
| 103 | |
| 104 public void render(Canvas canvas, float x, float y, float scale) { | |
| 105 // |progress| is 0 at the beginning, 1 at the end. | |
| 106 float progress; | |
| 107 float size; | |
| 108 synchronized (mLock) { | |
| 109 // |mStartTimeInMs| is set and accessed on different threads (he nce the lock). It | |
| 110 // is possible for |mStartTimeInMs| to be updated when an animat ion is in progress. | |
| 111 // When this occurs, |radius| will eventually be set to 0 and us ed to initialize | |
| 112 // RadialGradient which requires the radius to be > 0. This wil l result in a crash. | |
| 113 // In order to avoid this problem, we return early if the elapse d time is 0. | |
| 114 float elapsedTimeInMs = SystemClock.uptimeMillis() - mStartTimeI nMs; | |
| 115 if (elapsedTimeInMs < 1) { | |
| 116 return; | |
| 117 } | |
| 118 | |
| 119 progress = elapsedTimeInMs / TOTAL_DURATION_MS; | |
| 120 if (progress >= 1) { | |
| 121 mRunning = false; | |
| 122 return; | |
| 123 } | |
| 124 size = mFeedbackSizeInPixels / scale; | |
| 125 } | |
| 126 | |
| 127 // Animation grows from 0 to |size|, and goes from fully opaque to t ransparent for a | |
| 128 // seamless fading-out effect. The animation needs to have more than one color so it's | |
| 129 // visible over any background color. | |
| 130 float radius = size * progress; | |
| 131 int alpha = (int) ((1 - progress) * 0xff); | |
| 132 | |
| 133 int transparentBlack = Color.argb(0, 0, 0, 0); | |
| 134 int white = Color.argb(alpha, 0xff, 0xff, 0xff); | |
| 135 int black = Color.argb(alpha, 0, 0, 0); | |
| 136 mPaint.setShader(new RadialGradient(x, y, radius, | |
| 137 new int[] {transparentBlack, white, black, transparentBlack} , | |
| 138 new float[] {0.0f, 0.8f, 0.9f, 1.0f}, Shader.TileMode.CLAMP) ); | |
| 139 canvas.drawCircle(x, y, radius, mPaint); | |
| 140 } | |
| 141 | |
| 142 private float getInputFeedbackSizeInPixels(InputFeedbackType feedbackTyp e) { | |
| 143 switch (feedbackType) { | |
| 144 case SMALL_ANIMATION: | |
| 145 return 40.0f; | |
| 146 | |
| 147 case LARGE_ANIMATION: | |
| 148 return 160.0f; | |
| 149 | |
| 150 default: | |
| 151 // Unreachable, but required by Google Java style and findbu gs. | |
| 152 assert false : "Unreached"; | |
| 153 return 0.0f; | |
| 154 } | |
| 155 } | |
| 156 } | |
| 157 | |
| 158 private FeedbackAnimator mFeedbackAnimator = new FeedbackAnimator(); | |
| 159 | 62 |
| 160 // Variables to control animation by the TouchInputHandler. | 63 // Variables to control animation by the TouchInputHandler. |
| 161 | 64 |
| 162 /** Protects mInputAnimationRunning. */ | 65 /** Protects mInputAnimationRunning. */ |
| 163 private final Object mAnimationLock = new Object(); | 66 private final Object mAnimationLock = new Object(); |
| 164 | 67 |
| 165 /** Whether the TouchInputHandler has requested animation to be performed. * / | 68 /** Whether the TouchInputHandler has requested animation to be performed. * / |
| 166 private boolean mInputAnimationRunning = false; | 69 private boolean mInputAnimationRunning = false; |
| 167 | 70 |
| 168 public DesktopView(Context context, AttributeSet attributes) { | 71 public DesktopView(Context context, AttributeSet attributes) { |
| 169 super(context, attributes); | 72 super(context, attributes); |
| 170 | 73 |
| 171 // Give this view keyboard focus, allowing us to customize the soft keyb oard's settings. | 74 // Give this view keyboard focus, allowing us to customize the soft keyb oard's settings. |
| 172 setFocusableInTouchMode(true); | 75 setFocusableInTouchMode(true); |
| 173 | 76 |
| 174 mRenderData = new RenderData(); | 77 mRenderData = new RenderData(); |
| 175 mInputHandler = new TouchInputHandler(this, context, mRenderData); | 78 mInputHandler = new TouchInputHandler(this, context, mRenderData); |
| 176 | 79 |
| 177 mRepaintPending = false; | 80 mRepaintPending = false; |
| 178 | 81 |
| 179 getHolder().addCallback(this); | 82 getHolder().addCallback(this); |
| 180 } | 83 } |
| 181 | 84 |
| 85 public Event<EventParameters.Paint> onPaint() { | |
| 86 return mOnPaint; | |
| 87 } | |
| 88 | |
| 182 public void setDesktop(Desktop desktop) { | 89 public void setDesktop(Desktop desktop) { |
| 183 mDesktop = desktop; | 90 mDesktop = desktop; |
| 184 } | 91 } |
| 185 | 92 |
| 186 public void setClient(Client client) { | 93 public void setClient(Client client) { |
| 187 mClient = client; | 94 mClient = client; |
| 188 } | 95 } |
| 189 | 96 |
| 190 /** See {@link TouchInputHandler#onSoftInputMethodVisibilityChanged} for API details. */ | 97 /** See {@link TouchInputHandler#onSoftInputMethodVisibilityChanged} for API details. */ |
| 191 public void onSoftInputMethodVisibilityChanged(boolean inputMethodVisible, R ect bounds) { | 98 public void onSoftInputMethodVisibilityChanged(boolean inputMethodVisible, R ect bounds) { |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 256 return; | 163 return; |
| 257 } | 164 } |
| 258 canvas.setMatrix(mRenderData.transform); | 165 canvas.setMatrix(mRenderData.transform); |
| 259 drawCursor = mRenderData.drawCursor; | 166 drawCursor = mRenderData.drawCursor; |
| 260 cursorPosition = mRenderData.getCursorPosition(); | 167 cursorPosition = mRenderData.getCursorPosition(); |
| 261 } | 168 } |
| 262 | 169 |
| 263 canvas.drawColor(Color.BLACK); | 170 canvas.drawColor(Color.BLACK); |
| 264 canvas.drawBitmap(image, 0, 0, new Paint()); | 171 canvas.drawBitmap(image, 0, 0, new Paint()); |
| 265 | 172 |
| 266 // TODO(joedow): Replace the custom animation code with a standard Andro id implementation. | 173 float scaleFactor; |
| 267 boolean feedbackAnimationRunning = mFeedbackAnimator.isAnimationRunning( ); | 174 synchronized (mRenderData) { |
| 268 if (feedbackAnimationRunning) { | 175 scaleFactor = mRenderData.transform.mapRadius(1); |
| 269 float scaleFactor; | |
| 270 synchronized (mRenderData) { | |
| 271 scaleFactor = mRenderData.transform.mapRadius(1); | |
| 272 } | |
| 273 mFeedbackAnimator.render(canvas, cursorPosition.x, cursorPosition.y, scaleFactor); | |
| 274 } | 176 } |
| 177 mOnPaint.raise(new EventParameters.Paint(cursorPosition, canvas, scaleFa ctor)); | |
| 275 | 178 |
| 276 if (drawCursor) { | 179 if (drawCursor) { |
| 277 Bitmap cursorBitmap = mClient.getCursorBitmap(); | 180 Bitmap cursorBitmap = mClient.getCursorBitmap(); |
| 278 if (cursorBitmap != null) { | 181 if (cursorBitmap != null) { |
| 279 Point hotspot = mClient.getCursorHotspot(); | 182 Point hotspot = mClient.getCursorHotspot(); |
| 280 canvas.drawBitmap(cursorBitmap, cursorPosition.x - hotspot.x, | 183 canvas.drawBitmap(cursorBitmap, cursorPosition.x - hotspot.x, |
| 281 cursorPosition.y - hotspot.y, new Paint()); | 184 cursorPosition.y - hotspot.y, new Paint()); |
| 282 } | 185 } |
| 283 } | 186 } |
| 284 | 187 |
| 285 getHolder().unlockCanvasAndPost(canvas); | 188 getHolder().unlockCanvasAndPost(canvas); |
| 286 | 189 |
| 287 synchronized (mAnimationLock) { | 190 if (!mOnPaint.isEmpty()) { |
| 288 if (mInputAnimationRunning || feedbackAnimationRunning) { | 191 requestRepaint(); |
| 289 getHandler().postAtTime(new Runnable() { | 192 } else { |
| 290 @Override | 193 synchronized (mAnimationLock) { |
| 291 public void run() { | 194 if (mInputAnimationRunning) { |
| 292 processAnimation(); | 195 getHandler().postAtTime(new Runnable() { |
| 293 } | 196 @Override |
| 294 }, startTimeMs + 30); | 197 public void run() { |
| 198 processAnimation(); | |
| 199 } | |
| 200 }, startTimeMs + 30); | |
| 201 } | |
| 295 } | 202 } |
| 296 } | 203 } |
| 297 } | 204 } |
| 298 | 205 |
| 299 private void processAnimation() { | 206 private void processAnimation() { |
| 300 boolean running; | 207 boolean running; |
| 301 synchronized (mAnimationLock) { | 208 synchronized (mAnimationLock) { |
| 302 running = mInputAnimationRunning; | 209 running = mInputAnimationRunning; |
| 303 } | 210 } |
| 304 if (running) { | 211 if (running) { |
| 305 mInputHandler.processAnimation(); | 212 mInputHandler.processAnimation(); |
| 306 } | |
| 307 running |= mFeedbackAnimator.isAnimationRunning(); | |
| 308 if (running) { | |
| 309 requestRepaint(); | 213 requestRepaint(); |
| 310 } | 214 } |
| 311 } | 215 } |
| 312 | 216 |
| 313 /** | 217 /** |
| 314 * Called after the canvas is initially created, then after every subsequent resize, as when | 218 * Called after the canvas is initially created, then after every subsequent resize, as when |
| 315 * the display is rotated. | 219 * the display is rotated. |
| 316 */ | 220 */ |
| 317 @Override | 221 @Override |
| 318 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { | 222 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 372 return null; | 276 return null; |
| 373 } | 277 } |
| 374 | 278 |
| 375 /** Called whenever the user attempts to touch the canvas. */ | 279 /** Called whenever the user attempts to touch the canvas. */ |
| 376 @Override | 280 @Override |
| 377 public boolean onTouchEvent(MotionEvent event) { | 281 public boolean onTouchEvent(MotionEvent event) { |
| 378 return mInputHandler.onTouchEvent(event); | 282 return mInputHandler.onTouchEvent(event); |
| 379 } | 283 } |
| 380 | 284 |
| 381 @Override | 285 @Override |
| 382 public void showInputFeedback(InputFeedbackType feedbackToShow) { | 286 public void showInputFeedback(InputFeedbackType feedbackToShow, Point pos) { |
| 383 if (feedbackToShow != InputFeedbackType.NONE) { | 287 if (feedbackToShow != InputFeedbackType.NONE) { |
| 384 mFeedbackAnimator.startAnimation(feedbackToShow); | 288 FeedbackAnimator.startAnimation(this, pos, feedbackToShow); |
| 385 requestRepaint(); | 289 requestRepaint(); |
| 386 } | 290 } |
| 387 } | 291 } |
| 388 | 292 |
| 389 @Override | 293 @Override |
| 390 public void showActionBar() { | 294 public void showActionBar() { |
| 391 mDesktop.showActionBar(); | 295 mDesktop.showActionBar(); |
| 392 } | 296 } |
| 393 | 297 |
| 394 @Override | 298 @Override |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 437 | 341 |
| 438 default: | 342 default: |
| 439 // Unreachable, but required by Google Java style and findbugs. | 343 // Unreachable, but required by Google Java style and findbugs. |
| 440 assert false : "Unreached"; | 344 assert false : "Unreached"; |
| 441 } | 345 } |
| 442 | 346 |
| 443 // Ensure the cursor state is updated appropriately. | 347 // Ensure the cursor state is updated appropriately. |
| 444 requestRepaint(); | 348 requestRepaint(); |
| 445 } | 349 } |
| 446 } | 350 } |
| OLD | NEW |