Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(309)

Side by Side Diff: remoting/android/java/src/org/chromium/chromoting/DesktopView.java

Issue 2023133002: Use Event to render feedback animations. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698