1ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch// Copyright 2013 The Chromium Authors. All rights reserved. 2ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch// Use of this source code is governed by a BSD-style license that can be 3ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch// found in the LICENSE file. 4ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 5ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochpackage org.chromium.chromoting; 6ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 70f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)import android.content.Context; 8ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport android.graphics.Bitmap; 9ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport android.graphics.Canvas; 10ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport android.graphics.Color; 11ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport android.graphics.Paint; 12d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)import android.graphics.Point; 13f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)import android.graphics.RadialGradient; 14f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)import android.graphics.Shader; 15ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport android.os.Looper; 16f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)import android.os.SystemClock; 17ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochimport android.text.InputType; 185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.util.AttributeSet; 19ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport android.util.Log; 20ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport android.view.MotionEvent; 21ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport android.view.SurfaceHolder; 22ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport android.view.SurfaceView; 23ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochimport android.view.inputmethod.EditorInfo; 24ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochimport android.view.inputmethod.InputConnection; 25a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)import android.view.inputmethod.InputMethodManager; 26ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 27ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport org.chromium.chromoting.jni.JniInterface; 28ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 29ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch/** 30ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch * The user interface for viewing and interacting with a specific remote host. 31ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch * It provides a canvas onto which the video feed is rendered, handles 32ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch * multitouch pan and zoom gestures, and collects and forwards input events. 33ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch */ 34ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch/** GUI element that holds the drawing canvas. */ 35a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)public class DesktopView extends SurfaceView implements DesktopViewInterface, 361e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) SurfaceHolder.Callback { 371e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) private RenderData mRenderData; 381e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) private TouchInputHandler mInputHandler; 395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) /** The parent Desktop activity. */ 415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) private Desktop mDesktop; 42a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 431e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // Flag to prevent multiple repaint requests from being backed up. Requests for repainting will 441e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // be dropped if this is already set to true. This is used by the main thread and the painting 451e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // thread, so the access should be synchronized on |mRenderData|. 461e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) private boolean mRepaintPending; 47a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 48a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // Flag used to ensure that the SurfaceView is only painted between calls to surfaceCreated() 49a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // and surfaceDestroyed(). Accessed on main thread and display thread, so this should be 50a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // synchronized on |mRenderData|. 51a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) private boolean mSurfaceCreated = false; 52a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 53f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) /** Helper class for displaying the long-press feedback animation. This class is thread-safe. */ 54f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) private static class FeedbackAnimator { 55f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) /** Total duration of the animation, in milliseconds. */ 56f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) private static final float TOTAL_DURATION_MS = 220; 57f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 58f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) /** Start time of the animation, from {@link SystemClock#uptimeMillis()}. */ 59f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) private long mStartTime = 0; 60f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 61f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) private boolean mRunning = false; 62f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 63f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) /** Lock to allow multithreaded access to {@link #mStartTime} and {@link #mRunning}. */ 64f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) private Object mLock = new Object(); 65f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 66f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) private Paint mPaint = new Paint(); 67f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 68f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) public boolean isAnimationRunning() { 69f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) synchronized (mLock) { 70f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return mRunning; 71f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 72f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 73f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 74f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) /** 75f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * Begins a new animation sequence. After calling this method, the caller should 76f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * call {@link #render(Canvas, float, float, float)} periodically whilst 77f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * {@link #isAnimationRunning()} returns true. 78f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) */ 79f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) public void startAnimation() { 80f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) synchronized (mLock) { 81f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mRunning = true; 82f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mStartTime = SystemClock.uptimeMillis(); 83f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 84f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 85f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 86f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) public void render(Canvas canvas, float x, float y, float size) { 87f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // |progress| is 0 at the beginning, 1 at the end. 88f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) float progress; 89f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) synchronized (mLock) { 90f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) progress = (SystemClock.uptimeMillis() - mStartTime) / TOTAL_DURATION_MS; 91f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (progress >= 1) { 92f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mRunning = false; 93f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return; 94f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 95f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 96f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 97f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Animation grows from 0 to |size|, and goes from fully opaque to transparent for a 98f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // seamless fading-out effect. The animation needs to have more than one color so it's 99f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // visible over any background color. 100f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) float radius = size * progress; 1011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci int alpha = (int) ((1 - progress) * 0xff); 102f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 103f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) int transparentBlack = Color.argb(0, 0, 0, 0); 104f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) int white = Color.argb(alpha, 0xff, 0xff, 0xff); 105f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) int black = Color.argb(alpha, 0, 0, 0); 106f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mPaint.setShader(new RadialGradient(x, y, radius, 107f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) new int[] {transparentBlack, white, black, transparentBlack}, 108f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) new float[] {0.0f, 0.8f, 0.9f, 1.0f}, Shader.TileMode.CLAMP)); 109f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) canvas.drawCircle(x, y, radius, mPaint); 110f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 111f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 112f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 113f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) private FeedbackAnimator mFeedbackAnimator = new FeedbackAnimator(); 114f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 115f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Variables to control animation by the TouchInputHandler. 116f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 117f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) /** Protects mInputAnimationRunning. */ 118f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) private Object mAnimationLock = new Object(); 119f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 120f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) /** Whether the TouchInputHandler has requested animation to be performed. */ 121f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) private boolean mInputAnimationRunning = false; 122f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public DesktopView(Context context, AttributeSet attributes) { 1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) super(context, attributes); 125ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch 126ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch // Give this view keyboard focus, allowing us to customize the soft keyboard's settings. 127ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch setFocusableInTouchMode(true); 128ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch 1291e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mRenderData = new RenderData(); 1301e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mInputHandler = new TrackingInputHandler(this, context, mRenderData); 1311e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mRepaintPending = false; 132a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 133ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch getHolder().addCallback(this); 1341e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 135ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public void setDesktop(Desktop desktop) { 1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) mDesktop = desktop; 1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 140f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) /** Request repainting of the desktop view. */ 1411e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) void requestRepaint() { 1421e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) synchronized (mRenderData) { 1431e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (mRepaintPending) { 1441e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return; 1451e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 1461e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mRepaintPending = true; 1471e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 1481e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) JniInterface.redrawGraphics(); 1491e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 150ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 1511e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) /** Called whenever the screen configuration is changed. */ 1521e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) public void onScreenConfigurationChanged() { 1531e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mInputHandler.onScreenConfigurationChanged(); 154ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch } 155ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 156ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch /** 157ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch * Redraws the canvas. This should be done on a non-UI thread or it could 158ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch * cause the UI to lag. Specifically, it is currently invoked on the native 159ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch * graphics thread using a JNI. 160ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch */ 161a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) public void paint() { 162f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) long startTimeMs = SystemClock.uptimeMillis(); 163f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 164ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch if (Looper.myLooper() == Looper.getMainLooper()) { 165ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch Log.w("deskview", "Canvas being redrawn on UI thread"); 166ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch } 167ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 1684e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) Bitmap image = JniInterface.getVideoFrame(); 169f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (image == null) { 170f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // This can happen if the client is connected, but a complete video frame has not yet 171f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // been decoded. 172f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return; 173f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 174ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 1751e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) int width = image.getWidth(); 1761e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) int height = image.getHeight(); 1771e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) boolean sizeChanged = false; 1781e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) synchronized (mRenderData) { 1791e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (mRenderData.imageWidth != width || mRenderData.imageHeight != height) { 1801e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // TODO(lambroslambrou): Move this code into a sizeChanged() callback, to be 1811e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // triggered from JniInterface (on the display thread) when the remote screen size 1821e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // changes. 1831e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mRenderData.imageWidth = width; 1841e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mRenderData.imageHeight = height; 1851e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) sizeChanged = true; 186ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch } 1871e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 1881e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (sizeChanged) { 1891e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mInputHandler.onHostSizeChanged(width, height); 1901e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 191ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 192a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) Canvas canvas; 193f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) int x, y; 1941e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) synchronized (mRenderData) { 1951e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mRepaintPending = false; 196a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // Don't try to lock the canvas before it is ready, as the implementation of 197a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // lockCanvas() may throttle these calls to a slow rate in order to avoid consuming CPU. 198a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // Note that a successful call to lockCanvas() will prevent the framework from 199a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // destroying the Surface until it is unlocked. 200a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (!mSurfaceCreated) { 201a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) return; 202a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) } 203a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) canvas = getHolder().lockCanvas(); 204a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (canvas == null) { 205a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) return; 206a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) } 2071e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) canvas.setMatrix(mRenderData.transform); 208f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) x = mRenderData.cursorPosition.x; 209f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) y = mRenderData.cursorPosition.y; 210ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch } 211ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 212ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch canvas.drawColor(Color.BLACK); 213ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch canvas.drawBitmap(image, 0, 0, new Paint()); 214f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 215f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) boolean feedbackAnimationRunning = mFeedbackAnimator.isAnimationRunning(); 216f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (feedbackAnimationRunning) { 217f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) float scaleFactor; 218f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) synchronized (mRenderData) { 219f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) scaleFactor = mRenderData.transform.mapRadius(1); 220f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 221f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mFeedbackAnimator.render(canvas, x, y, 40 / scaleFactor); 222f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 223f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 224d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) Bitmap cursorBitmap = JniInterface.getCursorBitmap(); 225d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) if (cursorBitmap != null) { 226d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) Point hotspot = JniInterface.getCursorHotspot(); 227f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) canvas.drawBitmap(cursorBitmap, x - hotspot.x, y - hotspot.y, new Paint()); 228d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) } 229f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 230ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch getHolder().unlockCanvasAndPost(canvas); 231f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 232f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) synchronized (mAnimationLock) { 233f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (mInputAnimationRunning || feedbackAnimationRunning) { 234f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) getHandler().postAtTime(new Runnable() { 235f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) @Override 236f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) public void run() { 237f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) processAnimation(); 238f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 239f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) }, startTimeMs + 30); 240f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 241f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) }; 242f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 243f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 244f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) private void processAnimation() { 245f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) boolean running; 246f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) synchronized (mAnimationLock) { 247f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) running = mInputAnimationRunning; 248f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 249f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (running) { 250f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mInputHandler.processAnimation(); 251f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 252f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) running |= mFeedbackAnimator.isAnimationRunning(); 253f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (running) { 254f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) requestRepaint(); 255f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 256ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch } 257ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 258a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) /** 2591e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * Called after the canvas is initially created, then after every subsequent resize, as when 2601e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * the display is rotated. 261ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch */ 262ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch @Override 2631e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 2641e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) synchronized (mRenderData) { 2651e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mRenderData.screenWidth = width; 2661e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mRenderData.screenHeight = height; 267ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch } 268ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 269a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) JniInterface.provideRedrawCallback(new Runnable() { 270a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) @Override 271a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) public void run() { 272a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) paint(); 273a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) } 274a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) }); 2751e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mInputHandler.onClientSizeChanged(width, height); 276a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) requestRepaint(); 277ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch } 278ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 279ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch /** Called when the canvas is first created. */ 280ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch @Override 281ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch public void surfaceCreated(SurfaceHolder holder) { 282a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) synchronized (mRenderData) { 283a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) mSurfaceCreated = true; 284a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) } 285ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch } 286ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 287558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch /** 288558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch * Called when the canvas is finally destroyed. Marks the canvas as needing a redraw so that it 289558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch * will not be blank if the user later switches back to our window. 290558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch */ 291ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch @Override 292ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch public void surfaceDestroyed(SurfaceHolder holder) { 293a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) // Stop this canvas from being redrawn. 294a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) JniInterface.provideRedrawCallback(null); 295a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 296a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) synchronized (mRenderData) { 297a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) mSurfaceCreated = false; 298a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) } 299ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch } 300ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 301ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch /** Called when a software keyboard is requested, and specifies its options. */ 302ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch @Override 303ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch public InputConnection onCreateInputConnection(EditorInfo outAttrs) { 304ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch // Disables rich input support and instead requests simple key events. 305ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch outAttrs.inputType = InputType.TYPE_NULL; 306ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch 307ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch // Prevents most third-party IMEs from ignoring our Activity's adjustResize preference. 308ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_FULLSCREEN; 309ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch 310ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch // Ensures that keyboards will not decide to hide the remote desktop on small displays. 311ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI; 312ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch 313ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch // Stops software keyboards from closing as soon as the enter key is pressed. 314ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch outAttrs.imeOptions |= EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION; 315ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch 316ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch return null; 317ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch } 318ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch 3191e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) /** Called whenever the user attempts to touch the canvas. */ 320ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch @Override 321ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch public boolean onTouchEvent(MotionEvent event) { 3221e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return mInputHandler.onTouchEvent(event); 323ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch } 324ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 3251e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) @Override 3261e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) public void injectMouseEvent(int x, int y, int button, boolean pressed) { 3271e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) boolean cursorMoved = false; 3281e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) synchronized (mRenderData) { 3291e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // Test if the cursor actually moved, which requires repainting the cursor. This 3301e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // requires that the TouchInputHandler doesn't mutate |mRenderData.cursorPosition| 3311e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // directly. 3321e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (x != mRenderData.cursorPosition.x) { 3331e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mRenderData.cursorPosition.x = x; 3341e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) cursorMoved = true; 335ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch } 3361e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (y != mRenderData.cursorPosition.y) { 3371e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mRenderData.cursorPosition.y = y; 3381e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) cursorMoved = true; 339ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch } 340ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch } 341ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 3421e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (button == TouchInputHandler.BUTTON_UNDEFINED && !cursorMoved) { 3431e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // No need to inject anything or repaint. 3441e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return; 345a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch } 346a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 347effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch JniInterface.sendMouseEvent(x, y, button, pressed); 3481e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (cursorMoved) { 3491e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // TODO(lambroslambrou): Optimize this by only repainting the affected areas. 3501e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) requestRepaint(); 351ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch } 3521e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 353ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch 3541e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) @Override 355f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) public void injectMouseWheelDeltaEvent(int deltaX, int deltaY) { 356effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch JniInterface.sendMouseWheelEvent(deltaX, deltaY); 357f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 358f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 359f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) @Override 360f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) public void showLongPressFeedback() { 361f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mFeedbackAnimator.startAnimation(); 362f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) requestRepaint(); 363f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 364f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 365f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) @Override 3661e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) public void showActionBar() { 3675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) mDesktop.showActionBar(); 3681e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 369a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 3701e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) @Override 3711e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) public void showKeyboard() { 3720f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) InputMethodManager inputManager = 3731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 3740f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) inputManager.showSoftInput(this, 0); 3751e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 376a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 3771e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) @Override 3781e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) public void transformationChanged() { 3791e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) requestRepaint(); 380ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch } 381f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 382f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) @Override 383f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) public void setAnimationEnabled(boolean enabled) { 384f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) synchronized (mAnimationLock) { 385f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (enabled && !mInputAnimationRunning) { 386f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) requestRepaint(); 387f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 388f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mInputAnimationRunning = enabled; 389f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 390f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 391ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch} 392