Commit 96f227fa authored by zijiehe's avatar zijiehe Committed by Commit bot

[Chromoting] Decouple DesktopView and TouchInputHandler

This change decouples DesktopView(Interface) and TouchInputHandler(Interface) by
removing routing functions
DesktopViewInterface.onSoftInputMethodVisibilityChanged and
DesktopViewInterface.setInputStrategy. After this change, TouchInputHandler
takes responsibility to listen to onInputModeChanged and
onSoftInputMethodVisibilityChanged events of Desktop activity directly.

This is part remoting desktop Android client refactor work, a design doc is @
https://goo.gl/MA6zjx.

BUG=615277

Review-Url: https://codereview.chromium.org/2035303002
Cr-Commit-Position: refs/heads/master@{#398192}
parent 3bf9b9c2
...@@ -30,6 +30,7 @@ template("remoting_android_client_java_tmpl") { ...@@ -30,6 +30,7 @@ template("remoting_android_client_java_tmpl") {
"HostInfo.java", "HostInfo.java",
"HostListAdapter.java", "HostListAdapter.java",
"HostListManager.java", "HostListManager.java",
"InputModeChangedEventParameter.java",
"InputStrategyInterface.java", "InputStrategyInterface.java",
"NavigationMenuAdapter.java", "NavigationMenuAdapter.java",
"OAuthTokenConsumer.java", "OAuthTokenConsumer.java",
...@@ -39,6 +40,7 @@ template("remoting_android_client_java_tmpl") { ...@@ -39,6 +40,7 @@ template("remoting_android_client_java_tmpl") {
"SessionAuthenticator.java", "SessionAuthenticator.java",
"SessionConnector.java", "SessionConnector.java",
"SimulatedTouchInputStrategy.java", "SimulatedTouchInputStrategy.java",
"SoftInputMethodVisibilityChangedEventParameter.java",
"SwipePinchDetector.java", "SwipePinchDetector.java",
"TapGestureDetector.java", "TapGestureDetector.java",
"ThirdPartyTokenFetcher.java", "ThirdPartyTokenFetcher.java",
......
...@@ -10,7 +10,6 @@ import android.content.Intent; ...@@ -10,7 +10,6 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Rect;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
...@@ -66,8 +65,11 @@ public class Desktop ...@@ -66,8 +65,11 @@ public class Desktop
/** The amount of time to wait to hide the Actionbar after user input is seen. */ /** The amount of time to wait to hide the Actionbar after user input is seen. */
private static final int ACTIONBAR_AUTO_HIDE_DELAY_MS = 3000; private static final int ACTIONBAR_AUTO_HIDE_DELAY_MS = 3000;
/** The surface that displays the remote host's desktop feed. */ private final Event.Raisable<SoftInputMethodVisibilityChangedEventParameter>
private DesktopView mRemoteHostDesktop; mOnSoftInputMethodVisibilityChanged = new Event.Raisable<>();
private final Event.Raisable<InputModeChangedEventParameter> mOnInputModeChanged =
new Event.Raisable<>();
private Client mClient; private Client mClient;
...@@ -109,9 +111,8 @@ public class Desktop ...@@ -109,9 +111,8 @@ public class Desktop
mToolbar = (Toolbar) findViewById(R.id.toolbar); mToolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(mToolbar); setSupportActionBar(mToolbar);
mRemoteHostDesktop = (DesktopView) findViewById(R.id.desktop_view); DesktopView remoteHostDesktop = (DesktopView) findViewById(R.id.desktop_view);
mRemoteHostDesktop.setDesktop(this); remoteHostDesktop.init(this, mClient);
mRemoteHostDesktop.setClient(mClient);
mSwitchToCardboardDesktopActivity = false; mSwitchToCardboardDesktopActivity = false;
getSupportActionBar().setDisplayShowTitleEnabled(false); getSupportActionBar().setDisplayShowTitleEnabled(false);
...@@ -160,7 +161,7 @@ public class Desktop ...@@ -160,7 +161,7 @@ public class Desktop
} }
}); });
} else { } else {
mRemoteHostDesktop.setFitsSystemWindows(true); remoteHostDesktop.setFitsSystemWindows(true);
} }
} }
...@@ -169,7 +170,8 @@ public class Desktop ...@@ -169,7 +170,8 @@ public class Desktop
super.onStart(); super.onStart();
mActivityLifecycleListener.onActivityStarted(this); mActivityLifecycleListener.onActivityStarted(this);
mClient.enableVideoChannel(true); mClient.enableVideoChannel(true);
mRemoteHostDesktop.attachRedrawCallback(); DesktopView desktopView = (DesktopView) findViewById(R.id.desktop_view);
desktopView.attachRedrawCallback();
mClient.getCapabilityManager().addListener(this); mClient.getCapabilityManager().addListener(this);
} }
...@@ -258,6 +260,15 @@ public class Desktop ...@@ -258,6 +260,15 @@ public class Desktop
return super.onCreateOptionsMenu(menu); return super.onCreateOptionsMenu(menu);
} }
public Event<SoftInputMethodVisibilityChangedEventParameter>
onSoftInputMethodVisibilityChanged() {
return mOnSoftInputMethodVisibilityChanged;
}
public Event<InputModeChangedEventParameter> onInputModeChanged() {
return mOnInputModeChanged;
}
private InputMode getInitialInputModeValue() { private InputMode getInitialInputModeValue() {
// Load the previously-selected input mode from Preferences. // Load the previously-selected input mode from Preferences.
// TODO(joedow): Evaluate and determine if we should use a different input mode based on // TODO(joedow): Evaluate and determine if we should use a different input mode based on
...@@ -297,7 +308,8 @@ public class Desktop ...@@ -297,7 +308,8 @@ public class Desktop
.putString(PREFERENCE_INPUT_MODE, mInputMode.name()) .putString(PREFERENCE_INPUT_MODE, mInputMode.name())
.apply(); .apply();
mRemoteHostDesktop.changeInputMode(mInputMode, mHostTouchCapability); mOnInputModeChanged.raise(
new InputModeChangedEventParameter(mInputMode, mHostTouchCapability));
} }
@Override @Override
...@@ -308,7 +320,8 @@ public class Desktop ...@@ -308,7 +320,8 @@ public class Desktop
mHostTouchCapability = CapabilityManager.HostCapability.UNSUPPORTED; mHostTouchCapability = CapabilityManager.HostCapability.UNSUPPORTED;
} }
mRemoteHostDesktop.changeInputMode(mInputMode, mHostTouchCapability); mOnInputModeChanged.raise(
new InputModeChangedEventParameter(mInputMode, mHostTouchCapability));
} }
// Any time an onTouchListener is attached, a lint warning about filtering touch events is // Any time an onTouchListener is attached, a lint warning about filtering touch events is
...@@ -548,8 +561,9 @@ public class Desktop ...@@ -548,8 +561,9 @@ public class Desktop
// when the input method is changed so we want to send updates to the image canvas // when the input method is changed so we want to send updates to the image canvas
// whenever they occur. // whenever they occur.
mSoftInputVisible = (bottom < mMaxBottomValue); mSoftInputVisible = (bottom < mMaxBottomValue);
mRemoteHostDesktop.onSoftInputMethodVisibilityChanged( mOnSoftInputMethodVisibilityChanged.raise(
mSoftInputVisible, new Rect(left, top, right, bottom)); new SoftInputMethodVisibilityChangedEventParameter(
mSoftInputVisible, left, top, right, bottom));
if (!mSoftInputVisible && mHideSystemUIOnSoftKeyboardDismiss) { if (!mSoftInputVisible && mHideSystemUIOnSoftKeyboardDismiss) {
// Queue a task which will run after the current action (OSK dismiss) has // Queue a task which will run after the current action (OSK dismiss) has
......
...@@ -10,7 +10,6 @@ import android.graphics.Canvas; ...@@ -10,7 +10,6 @@ import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Point; import android.graphics.Point;
import android.graphics.Rect;
import android.os.Looper; import android.os.Looper;
import android.os.SystemClock; import android.os.SystemClock;
import android.text.InputType; import android.text.InputType;
...@@ -82,21 +81,19 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface, ...@@ -82,21 +81,19 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface,
getHolder().addCallback(this); getHolder().addCallback(this);
} }
public Event<PaintEventParameter> onPaint() { @Override
return mOnPaint; public void init(Desktop desktop, Client client) {
} Preconditions.isNull(mDesktop);
Preconditions.isNull(mClient);
public void setDesktop(Desktop desktop) { Preconditions.notNull(desktop);
Preconditions.notNull(client);
mDesktop = desktop; mDesktop = desktop;
}
public void setClient(Client client) {
mClient = client; mClient = client;
mInputHandler.init(desktop, client);
} }
/** See {@link TouchInputHandler#onSoftInputMethodVisibilityChanged} for API details. */ public Event<PaintEventParameter> onPaint() {
public void onSoftInputMethodVisibilityChanged(boolean inputMethodVisible, Rect bounds) { return mOnPaint;
mInputHandler.onSoftInputMethodVisibilityChanged(inputMethodVisible, bounds);
} }
/** Request repainting of the desktop view. */ /** Request repainting of the desktop view. */
...@@ -316,35 +313,4 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface, ...@@ -316,35 +313,4 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface,
mInputAnimationRunning = enabled; mInputAnimationRunning = enabled;
} }
} }
/** Updates the current InputStrategy used by the TouchInputHandler. */
public void changeInputMode(
Desktop.InputMode inputMode, CapabilityManager.HostCapability hostTouchCapability) {
// We need both input mode and host input capabilities to select the input strategy.
if (!inputMode.isSet() || !hostTouchCapability.isSet()) {
return;
}
switch (inputMode) {
case TRACKPAD:
mInputHandler.setInputStrategy(new TrackpadInputStrategy(mRenderData, mClient));
break;
case TOUCH:
if (hostTouchCapability.isSupported()) {
mInputHandler.setInputStrategy(new TouchInputStrategy(mRenderData, mClient));
} else {
mInputHandler.setInputStrategy(
new SimulatedTouchInputStrategy(mRenderData, mClient, getContext()));
}
break;
default:
// Unreachable, but required by Google Java style and findbugs.
assert false : "Unreached";
}
// Ensure the cursor state is updated appropriately.
requestRepaint();
}
} }
...@@ -6,10 +6,18 @@ package org.chromium.chromoting; ...@@ -6,10 +6,18 @@ package org.chromium.chromoting;
import android.graphics.Point; import android.graphics.Point;
import org.chromium.chromoting.jni.Client;
/** /**
* Callback interface to allow the TouchInputHandler to request actions on the DesktopView. * Callback interface to allow the TouchInputHandler to request actions on the DesktopView.
*/ */
public interface DesktopViewInterface { public interface DesktopViewInterface {
/**
* Initializes the instance. Implementations can assume this function will be called exactly
* once after constructor but before other functions.
*/
void init(Desktop desktop, Client client);
/** Triggers a brief animation to indicate the existence and location of an input event. */ /** Triggers a brief animation to indicate the existence and location of an input event. */
void showInputFeedback(DesktopView.InputFeedbackType feedbackToShow, Point pos); void showInputFeedback(DesktopView.InputFeedbackType feedbackToShow, Point pos);
......
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chromoting;
/** The parameter for an InputModeChanged event. */
public final class InputModeChangedEventParameter {
public final Desktop.InputMode inputMode;
public final CapabilityManager.HostCapability hostCapability;
public InputModeChangedEventParameter(Desktop.InputMode inputMode,
CapabilityManager.HostCapability hostCapability) {
this.inputMode = inputMode;
this.hostCapability = hostCapability;
}
}
...@@ -25,7 +25,7 @@ public final class Preconditions { ...@@ -25,7 +25,7 @@ public final class Preconditions {
} }
/** /**
* Checks whether input |ref| is not a null reference, and return its value. Throws * Checks whether input |ref| is not a null reference, and returns its value. Throws
* {@link NullPointerException} if |ref| is null. * {@link NullPointerException} if |ref| is null.
*/ */
public static final <T> T notNull(T ref) { public static final <T> T notNull(T ref) {
...@@ -34,4 +34,15 @@ public final class Preconditions { ...@@ -34,4 +34,15 @@ public final class Preconditions {
} }
return ref; return ref;
} }
/**
* Checks whether input |ref| is a null reference, and returns its value. Throws
* {@link IllegalArgumentException} if |ref| is not null.
*/
public static final <T> T isNull(T ref) {
if (ref != null) {
throw new IllegalArgumentException();
}
return ref;
}
} }
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chromoting;
/**
* The parameter for an OnSoftInputMethodVisibilityChanged event.
*
* {@link android.graphics.Rect} is mutable, so this class owns four integers to represent the
* rectangle of new layout.
*/
public final class SoftInputMethodVisibilityChangedEventParameter {
public final boolean visible;
public final int left;
public final int top;
public final int right;
public final int bottom;
public SoftInputMethodVisibilityChangedEventParameter(boolean visible,
int left,
int top,
int right,
int bottom) {
this.visible = visible;
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
}
...@@ -14,6 +14,8 @@ import android.view.MotionEvent; ...@@ -14,6 +14,8 @@ import android.view.MotionEvent;
import android.view.ScaleGestureDetector; import android.view.ScaleGestureDetector;
import android.view.ViewConfiguration; import android.view.ViewConfiguration;
import org.chromium.chromoting.jni.Client;
/** /**
* This class is responsible for handling Touch input from the user. Touch events which manipulate * This class is responsible for handling Touch input from the user. Touch events which manipulate
* the local canvas are handled in this class and any input which should be sent to the remote host * the local canvas are handled in this class and any input which should be sent to the remote host
...@@ -21,6 +23,7 @@ import android.view.ViewConfiguration; ...@@ -21,6 +23,7 @@ import android.view.ViewConfiguration;
*/ */
public class TouchInputHandler implements TouchInputHandlerInterface { public class TouchInputHandler implements TouchInputHandlerInterface {
private final DesktopViewInterface mViewer; private final DesktopViewInterface mViewer;
private final Context mContext;
private final RenderData mRenderData; private final RenderData mRenderData;
private final DesktopCanvas mDesktopCanvas; private final DesktopCanvas mDesktopCanvas;
private InputStrategyInterface mInputStrategy; private InputStrategyInterface mInputStrategy;
...@@ -180,6 +183,7 @@ public class TouchInputHandler implements TouchInputHandlerInterface { ...@@ -180,6 +183,7 @@ public class TouchInputHandler implements TouchInputHandlerInterface {
public TouchInputHandler(DesktopViewInterface viewer, Context context, RenderData renderData) { public TouchInputHandler(DesktopViewInterface viewer, Context context, RenderData renderData) {
mViewer = viewer; mViewer = viewer;
mContext = context;
mRenderData = renderData; mRenderData = renderData;
mDesktopCanvas = new DesktopCanvas(mViewer, mRenderData); mDesktopCanvas = new DesktopCanvas(mViewer, mRenderData);
...@@ -256,20 +260,6 @@ public class TouchInputHandler implements TouchInputHandlerInterface { ...@@ -256,20 +260,6 @@ public class TouchInputHandler implements TouchInputHandlerInterface {
mDesktopCanvas.resizeImageToFitScreen(); mDesktopCanvas.resizeImageToFitScreen();
} }
@Override
public void onSoftInputMethodVisibilityChanged(boolean inputMethodVisible, Rect bounds) {
synchronized (mRenderData) {
if (inputMethodVisible) {
mDesktopCanvas.setInputMethodOffsetValues(mRenderData.screenWidth - bounds.right,
mRenderData.screenHeight - bounds.bottom);
} else {
mDesktopCanvas.setInputMethodOffsetValues(0, 0);
}
}
mDesktopCanvas.repositionImage(true);
}
@Override @Override
public void processAnimation() { public void processAnimation() {
boolean active = mCursorAnimationJob.processAnimation(); boolean active = mCursorAnimationJob.processAnimation();
...@@ -281,7 +271,78 @@ public class TouchInputHandler implements TouchInputHandlerInterface { ...@@ -281,7 +271,78 @@ public class TouchInputHandler implements TouchInputHandlerInterface {
} }
@Override @Override
public void setInputStrategy(InputStrategyInterface inputStrategy) { public void init(Desktop desktop, final Client client) {
Preconditions.notNull(client);
desktop.onInputModeChanged().add(
new Event.ParameterRunnable<InputModeChangedEventParameter>() {
@Override
public void run(InputModeChangedEventParameter parameter) {
handleInputModeChanged(parameter, client);
}
});
desktop.onSoftInputMethodVisibilityChanged().add(
new Event.ParameterRunnable<SoftInputMethodVisibilityChangedEventParameter>() {
@Override
public void run(SoftInputMethodVisibilityChangedEventParameter parameter) {
handleSoftInputMethodVisibilityChanged(parameter);
}
});
}
private void handleInputModeChanged(InputModeChangedEventParameter parameter,
Client client) {
final Desktop.InputMode inputMode = parameter.inputMode;
final CapabilityManager.HostCapability hostTouchCapability =
parameter.hostCapability;
// We need both input mode and host input capabilities to select the input
// strategy.
if (!inputMode.isSet() || !hostTouchCapability.isSet()) {
return;
}
switch (inputMode) {
case TRACKPAD:
setInputStrategy(new TrackpadInputStrategy(mRenderData, client));
break;
case TOUCH:
if (hostTouchCapability.isSupported()) {
setInputStrategy(new TouchInputStrategy(mRenderData, client));
} else {
setInputStrategy(new SimulatedTouchInputStrategy(
mRenderData, client, mContext));
}
break;
default:
// Unreachable, but required by Google Java style and findbugs.
assert false : "Unreached";
}
// Ensure the cursor state is updated appropriately.
// TODO (zijiehe): Move repaint control out of DesktopView.
if (mViewer instanceof DesktopView) {
((DesktopView) mViewer).requestRepaint();
}
}
private void handleSoftInputMethodVisibilityChanged(
SoftInputMethodVisibilityChangedEventParameter parameter) {
synchronized (mRenderData) {
if (parameter.visible) {
mDesktopCanvas.setInputMethodOffsetValues(
mRenderData.screenWidth - parameter.right,
mRenderData.screenHeight - parameter.bottom);
} else {
mDesktopCanvas.setInputMethodOffsetValues(0, 0);
}
}
mDesktopCanvas.repositionImage(true);
}
private void setInputStrategy(InputStrategyInterface inputStrategy) {
// Since the rules for flinging differ between input modes, we want to stop running the // Since the rules for flinging differ between input modes, we want to stop running the
// current fling animation when the mode changes to prevent a wonky experience. // current fling animation when the mode changes to prevent a wonky experience.
mCursorAnimationJob.abortAnimation(); mCursorAnimationJob.abortAnimation();
......
...@@ -4,9 +4,10 @@ ...@@ -4,9 +4,10 @@
package org.chromium.chromoting; package org.chromium.chromoting;
import android.graphics.Rect;
import android.view.MotionEvent; import android.view.MotionEvent;
import org.chromium.chromoting.jni.Client;
/** /**
* This interface allows multiple styles of touchscreen UI to be implemented and dynamically * This interface allows multiple styles of touchscreen UI to be implemented and dynamically
* switched. The DesktopView passes the low-level touchscreen events and other events via this * switched. The DesktopView passes the low-level touchscreen events and other events via this
...@@ -21,6 +22,12 @@ public interface TouchInputHandlerInterface { ...@@ -21,6 +22,12 @@ public interface TouchInputHandlerInterface {
int BUTTON_MIDDLE = 2; int BUTTON_MIDDLE = 2;
int BUTTON_RIGHT = 3; int BUTTON_RIGHT = 3;
/**
* Initializes the instance. Implementations can assume this function will be called exactly
* once after constructor but before other functions.
*/
void init(Desktop desktop, Client client);
/** /**
* Processes a touch event. This should be called by the View in its onTouchEvent() handler. * Processes a touch event. This should be called by the View in its onTouchEvent() handler.
*/ */
...@@ -38,21 +45,9 @@ public interface TouchInputHandlerInterface { ...@@ -38,21 +45,9 @@ public interface TouchInputHandlerInterface {
*/ */
void onHostSizeChanged(int width, int height); void onHostSizeChanged(int width, int height);
/**
* Called when the visibility of the soft input method has changed.
* The innerBounds parameter describes the amount of space used by SystemUI along each edge of
* the screen. The status bar is typically shown along the top, soft input UI is generally
* shown at the bottom. The navigation bar is shown along the bottom for tablets and along the
* right side for phones in landscape mode (it shown at the bottom in portrait mode).
*/
void onSoftInputMethodVisibilityChanged(boolean inputMethodVisible, Rect innerBounds);
/** /**
* Whilst an animation is in progress, this method is called repeatedly until the animation is * Whilst an animation is in progress, this method is called repeatedly until the animation is
* cancelled. After this method returns, the DesktopView will schedule a repaint. * cancelled. After this method returns, the DesktopView will schedule a repaint.
*/ */
void processAnimation(); void processAnimation();
/** Sets the underlying strategy to use when translating and forwarding local touch input. */
void setInputStrategy(InputStrategyInterface inputStrategy);
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment