diff options
author | lambroslambrou@chromium.org <lambroslambrou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-13 17:21:21 +0000 |
---|---|---|
committer | lambroslambrou@chromium.org <lambroslambrou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-13 17:21:21 +0000 |
commit | eb07b7ecef22bc58fbad4cec4c90497487b661d7 (patch) | |
tree | 1ad11945f416dc07b1aed6f460bafb63c9d731ce /remoting | |
parent | 5e84c58eada069ea3d7c971d3e968387a44b0c8f (diff) | |
download | chromium_src-eb07b7ecef22bc58fbad4cec4c90497487b661d7.zip chromium_src-eb07b7ecef22bc58fbad4cec4c90497487b661d7.tar.gz chromium_src-eb07b7ecef22bc58fbad4cec4c90497487b661d7.tar.bz2 |
Android Chromoting: Show feedback animation on long-press.
This draws an animated blob underneath the cursor when a long-press
occurs, to give feedback to the user that they can start dragging
the remote mouse-cursor.
NOTRY=true
Review URL: https://codereview.chromium.org/66033011
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@234836 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
3 files changed, 99 insertions, 6 deletions
diff --git a/remoting/android/java/src/org/chromium/chromoting/DesktopView.java b/remoting/android/java/src/org/chromium/chromoting/DesktopView.java index 0118950..3a4f30c 100644 --- a/remoting/android/java/src/org/chromium/chromoting/DesktopView.java +++ b/remoting/android/java/src/org/chromium/chromoting/DesktopView.java @@ -12,7 +12,10 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Point; +import android.graphics.RadialGradient; +import android.graphics.Shader; import android.os.Looper; +import android.os.SystemClock; import android.text.InputType; import android.util.Log; import android.view.inputmethod.InputMethodManager; @@ -41,6 +44,68 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface, Ru // thread, so the access should be synchronized on |mRenderData|. private boolean mRepaintPending; + /** Helper class for displaying the long-press feedback animation. This class is thread-safe. */ + private static class FeedbackAnimator { + /** Total duration of the animation, in milliseconds. */ + private static final float TOTAL_DURATION_MS = 220; + + /** Start time of the animation, from {@link SystemClock#uptimeMillis()}. */ + private long mStartTime = 0; + + private boolean mRunning = false; + + /** Lock to allow multithreaded access to {@link #mStartTime} and {@link #mRunning}. */ + private Object mLock = new Object(); + + private Paint mPaint = new Paint(); + + public boolean isAnimationRunning() { + synchronized (mLock) { + return mRunning; + } + } + + /** + * Begins a new animation sequence. After calling this method, the caller should + * call {@link #render(Canvas, float, float, float)} periodically whilst + * {@link #isAnimationRunning()} returns true. + */ + public void startAnimation() { + synchronized (mLock) { + mRunning = true; + mStartTime = SystemClock.uptimeMillis(); + } + } + + public void render(Canvas canvas, float x, float y, float size) { + // |progress| is 0 at the beginning, 1 at the end. + float progress; + synchronized (mLock) { + progress = (SystemClock.uptimeMillis() - mStartTime) / TOTAL_DURATION_MS; + if (progress >= 1) { + mRunning = false; + return; + } + } + + // Animation grows from 0 to |size|, and goes from fully opaque to transparent for a + // seamless fading-out effect. The animation needs to have more than one color so it's + // visible over any background color. + float radius = size * progress; + int alpha = (int)((1 - progress) * 0xff); + + int transparentBlack = Color.argb(0, 0, 0, 0); + int white = Color.argb(alpha, 0xff, 0xff, 0xff); + int black = Color.argb(alpha, 0, 0, 0); + mPaint.setShader(new RadialGradient(x, y, radius, + new int[] {transparentBlack, white, black, transparentBlack}, + new float[] {0.0f, 0.8f, 0.9f, 1.0f}, Shader.TileMode.CLAMP)); + canvas.drawCircle(x, y, radius, mPaint); + } + } + + private FeedbackAnimator mFeedbackAnimator = new FeedbackAnimator(); + public DesktopView(Activity context) { super(context); @@ -80,6 +145,8 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface, Ru */ @Override public void run() { + long startTimeMs = SystemClock.uptimeMillis(); + if (Looper.myLooper() == Looper.getMainLooper()) { Log.w("deskview", "Canvas being redrawn on UI thread"); } @@ -109,23 +176,39 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface, Ru } Canvas canvas = getHolder().lockCanvas(); + int x, y; synchronized (mRenderData) { mRepaintPending = false; canvas.setMatrix(mRenderData.transform); + x = mRenderData.cursorPosition.x; + y = mRenderData.cursorPosition.y; } canvas.drawColor(Color.BLACK); canvas.drawBitmap(image, 0, 0, new Paint()); + + if (mFeedbackAnimator.isAnimationRunning()) { + float scaleFactor; + synchronized (mRenderData) { + scaleFactor = mRenderData.transform.mapRadius(1); + } + mFeedbackAnimator.render(canvas, x, y, 40 / scaleFactor); + + // Trigger a repaint request for the next frame of the animation. + getHandler().postAtTime(new Runnable() { + @Override + public void run() { + requestRepaint(); + } + }, startTimeMs + 30); + } + Bitmap cursorBitmap = JniInterface.getCursorBitmap(); if (cursorBitmap != null) { Point hotspot = JniInterface.getCursorHotspot(); - int bitmapX, bitmapY; - synchronized (mRenderData) { - bitmapX = mRenderData.cursorPosition.x - hotspot.x; - bitmapY = mRenderData.cursorPosition.y - hotspot.y; - } - canvas.drawBitmap(cursorBitmap, bitmapX, bitmapY, new Paint()); + canvas.drawBitmap(cursorBitmap, x - hotspot.x, y - hotspot.y, new Paint()); } + getHolder().unlockCanvasAndPost(canvas); } @@ -219,6 +302,12 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface, Ru } @Override + public void showLongPressFeedback() { + mFeedbackAnimator.startAnimation(); + requestRepaint(); + } + + @Override public void showActionBar() { mActionBar.show(); } diff --git a/remoting/android/java/src/org/chromium/chromoting/DesktopViewInterface.java b/remoting/android/java/src/org/chromium/chromoting/DesktopViewInterface.java index 7360aa8..18995f3 100644 --- a/remoting/android/java/src/org/chromium/chromoting/DesktopViewInterface.java +++ b/remoting/android/java/src/org/chromium/chromoting/DesktopViewInterface.java @@ -11,6 +11,9 @@ public interface DesktopViewInterface { /** Inject a mouse-move event, with optional button press/release. */ void injectMouseEvent(int x, int y, int button, boolean pressed); + /** Triggers a brief cursor animation to indicate a long-press event. */ + void showLongPressFeedback(); + /** Shows the action bar. */ void showActionBar(); diff --git a/remoting/android/java/src/org/chromium/chromoting/TrackingInputHandler.java b/remoting/android/java/src/org/chromium/chromoting/TrackingInputHandler.java index 4bddec5..05e22db 100644 --- a/remoting/android/java/src/org/chromium/chromoting/TrackingInputHandler.java +++ b/remoting/android/java/src/org/chromium/chromoting/TrackingInputHandler.java @@ -367,6 +367,7 @@ public class TrackingInputHandler implements TouchInputHandler { mHeldButton = mouseButtonFromPointerCount(pointerCount); if (mHeldButton != BUTTON_UNDEFINED) { injectButtonEvent(mHeldButton, true); + mViewer.showLongPressFeedback(); } } } |