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") {
"HostInfo.java",
"HostListAdapter.java",
"HostListManager.java",
"InputModeChangedEventParameter.java",
"InputStrategyInterface.java",
"NavigationMenuAdapter.java",
"OAuthTokenConsumer.java",
......@@ -39,6 +40,7 @@ template("remoting_android_client_java_tmpl") {
"SessionAuthenticator.java",
"SessionConnector.java",
"SimulatedTouchInputStrategy.java",
"SoftInputMethodVisibilityChangedEventParameter.java",
"SwipePinchDetector.java",
"TapGestureDetector.java",
"ThirdPartyTokenFetcher.java",
......
......@@ -10,7 +10,6 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
......@@ -66,8 +65,11 @@ public class Desktop
/** 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;
/** The surface that displays the remote host's desktop feed. */
private DesktopView mRemoteHostDesktop;
private final Event.Raisable<SoftInputMethodVisibilityChangedEventParameter>
mOnSoftInputMethodVisibilityChanged = new Event.Raisable<>();
private final Event.Raisable<InputModeChangedEventParameter> mOnInputModeChanged =
new Event.Raisable<>();
private Client mClient;
......@@ -109,9 +111,8 @@ public class Desktop
mToolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(mToolbar);
mRemoteHostDesktop = (DesktopView) findViewById(R.id.desktop_view);
mRemoteHostDesktop.setDesktop(this);
mRemoteHostDesktop.setClient(mClient);
DesktopView remoteHostDesktop = (DesktopView) findViewById(R.id.desktop_view);
remoteHostDesktop.init(this, mClient);
mSwitchToCardboardDesktopActivity = false;
getSupportActionBar().setDisplayShowTitleEnabled(false);
......@@ -160,7 +161,7 @@ public class Desktop
}
});
} else {
mRemoteHostDesktop.setFitsSystemWindows(true);
remoteHostDesktop.setFitsSystemWindows(true);
}
}
......@@ -169,7 +170,8 @@ public class Desktop
super.onStart();
mActivityLifecycleListener.onActivityStarted(this);
mClient.enableVideoChannel(true);
mRemoteHostDesktop.attachRedrawCallback();
DesktopView desktopView = (DesktopView) findViewById(R.id.desktop_view);
desktopView.attachRedrawCallback();
mClient.getCapabilityManager().addListener(this);
}
......@@ -258,6 +260,15 @@ public class Desktop
return super.onCreateOptionsMenu(menu);
}
public Event<SoftInputMethodVisibilityChangedEventParameter>
onSoftInputMethodVisibilityChanged() {
return mOnSoftInputMethodVisibilityChanged;
}
public Event<InputModeChangedEventParameter> onInputModeChanged() {
return mOnInputModeChanged;
}
private InputMode getInitialInputModeValue() {
// Load the previously-selected input mode from Preferences.
// TODO(joedow): Evaluate and determine if we should use a different input mode based on
......@@ -297,7 +308,8 @@ public class Desktop
.putString(PREFERENCE_INPUT_MODE, mInputMode.name())
.apply();
mRemoteHostDesktop.changeInputMode(mInputMode, mHostTouchCapability);
mOnInputModeChanged.raise(
new InputModeChangedEventParameter(mInputMode, mHostTouchCapability));
}
@Override
......@@ -308,7 +320,8 @@ public class Desktop
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
......@@ -548,8 +561,9 @@ public class Desktop
// when the input method is changed so we want to send updates to the image canvas
// whenever they occur.
mSoftInputVisible = (bottom < mMaxBottomValue);
mRemoteHostDesktop.onSoftInputMethodVisibilityChanged(
mSoftInputVisible, new Rect(left, top, right, bottom));
mOnSoftInputMethodVisibilityChanged.raise(
new SoftInputMethodVisibilityChangedEventParameter(
mSoftInputVisible, left, top, right, bottom));
if (!mSoftInputVisible && mHideSystemUIOnSoftKeyboardDismiss) {
// Queue a task which will run after the current action (OSK dismiss) has
......
......@@ -10,7 +10,6 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Looper;
import android.os.SystemClock;
import android.text.InputType;
......@@ -82,21 +81,19 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface,
getHolder().addCallback(this);
}
public Event<PaintEventParameter> onPaint() {
return mOnPaint;
}
public void setDesktop(Desktop desktop) {
@Override
public void init(Desktop desktop, Client client) {
Preconditions.isNull(mDesktop);
Preconditions.isNull(mClient);
Preconditions.notNull(desktop);
Preconditions.notNull(client);
mDesktop = desktop;
}
public void setClient(Client client) {
mClient = client;
mInputHandler.init(desktop, client);
}
/** See {@link TouchInputHandler#onSoftInputMethodVisibilityChanged} for API details. */
public void onSoftInputMethodVisibilityChanged(boolean inputMethodVisible, Rect bounds) {
mInputHandler.onSoftInputMethodVisibilityChanged(inputMethodVisible, bounds);
public Event<PaintEventParameter> onPaint() {
return mOnPaint;
}
/** Request repainting of the desktop view. */
......@@ -316,35 +313,4 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface,
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;
import android.graphics.Point;
import org.chromium.chromoting.jni.Client;
/**
* Callback interface to allow the TouchInputHandler to request actions on the DesktopView.
*/
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. */
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 {
}
/**
* 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.
*/
public static final <T> T notNull(T ref) {
......@@ -34,4 +34,15 @@ public final class Preconditions {
}
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;
import android.view.ScaleGestureDetector;
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
* 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;
*/
public class TouchInputHandler implements TouchInputHandlerInterface {
private final DesktopViewInterface mViewer;
private final Context mContext;
private final RenderData mRenderData;
private final DesktopCanvas mDesktopCanvas;
private InputStrategyInterface mInputStrategy;
......@@ -180,6 +183,7 @@ public class TouchInputHandler implements TouchInputHandlerInterface {
public TouchInputHandler(DesktopViewInterface viewer, Context context, RenderData renderData) {
mViewer = viewer;
mContext = context;
mRenderData = renderData;
mDesktopCanvas = new DesktopCanvas(mViewer, mRenderData);
......@@ -256,20 +260,6 @@ public class TouchInputHandler implements TouchInputHandlerInterface {
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
public void processAnimation() {
boolean active = mCursorAnimationJob.processAnimation();
......@@ -281,7 +271,78 @@ public class TouchInputHandler implements TouchInputHandlerInterface {
}
@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
// current fling animation when the mode changes to prevent a wonky experience.
mCursorAnimationJob.abortAnimation();
......
......@@ -4,9 +4,10 @@
package org.chromium.chromoting;
import android.graphics.Rect;
import android.view.MotionEvent;
import org.chromium.chromoting.jni.Client;
/**
* 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
......@@ -21,6 +22,12 @@ public interface TouchInputHandlerInterface {
int BUTTON_MIDDLE = 2;
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.
*/
......@@ -38,21 +45,9 @@ public interface TouchInputHandlerInterface {
*/
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
* cancelled. After this method returns, the DesktopView will schedule a repaint.
*/
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