summaryrefslogtreecommitdiffstats
path: root/remoting/android
diff options
context:
space:
mode:
authorjoedow <joedow@chromium.org>2015-12-02 13:40:33 -0800
committerCommit bot <commit-bot@chromium.org>2015-12-02 21:41:13 +0000
commitcf895fda588d81c9d1b4fa8ab572750da2fbaae7 (patch)
tree8ea3b88179536b6789e8f6f59056949c47bc690c /remoting/android
parentb023bc744eaa8b821ba69269c832c646ddc54dd4 (diff)
downloadchromium_src-cf895fda588d81c9d1b4fa8ab572750da2fbaae7.zip
chromium_src-cf895fda588d81c9d1b4fa8ab572750da2fbaae7.tar.gz
chromium_src-cf895fda588d81c9d1b4fa8ab572750da2fbaae7.tar.bz2
Adding UX for toggling Touch Input Mode.
This change is needed to allow toggling the input mode via the UX. I have tested it but am leaving it disabled until my next changelist which will add the new input modes. Included in this change is: - CapabilityManager updated to allow other classes to listen for change events. - Fixed a problem with the CapabilityManager where it would continue to return capabilities for the previous host until a new connection was made. - Added state information to the Desktop Activity to track whether the remote host supports touch injection and the currently selected input mode. - InputMode changes are persisted between sessions using SharedPreferences - Added a function which will handle creating a new InputStrategy when the user toggles input modes. There are two interesting pieces of logic in this change. - Desktop needs to know both the current InputMode and the host capabilities and it will wait to tell the DesktopView which InputStrategy to use until it knows both - The Host Capabilities are not known until after creation time so I updated CapabilityManager to use callbacks to signal changes to the negotiated capabilities. BUG=454549 Review URL: https://codereview.chromium.org/1490453002 Cr-Commit-Position: refs/heads/master@{#362798}
Diffstat (limited to 'remoting/android')
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/Capabilities.java1
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/CapabilityManager.java73
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/ChromotingUtil.java18
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/Desktop.java105
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/DesktopView.java31
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/jni/JniInterface.java1
6 files changed, 217 insertions, 12 deletions
diff --git a/remoting/android/java/src/org/chromium/chromoting/Capabilities.java b/remoting/android/java/src/org/chromium/chromoting/Capabilities.java
index cff3d9e..294a242 100644
--- a/remoting/android/java/src/org/chromium/chromoting/Capabilities.java
+++ b/remoting/android/java/src/org/chromium/chromoting/Capabilities.java
@@ -12,4 +12,5 @@ package org.chromium.chromoting;
*/
public class Capabilities {
public static final String CAST_CAPABILITY = "casting";
+ public static final String TOUCH_CAPABILITY = "touchEvents";
}
diff --git a/remoting/android/java/src/org/chromium/chromoting/CapabilityManager.java b/remoting/android/java/src/org/chromium/chromoting/CapabilityManager.java
index 939817d..e332e83 100644
--- a/remoting/android/java/src/org/chromium/chromoting/CapabilityManager.java
+++ b/remoting/android/java/src/org/chromium/chromoting/CapabilityManager.java
@@ -27,6 +27,27 @@ import java.util.List;
* different capabilities at different stages of the application.
*/
public class CapabilityManager {
+ /** Used to allow objects to receive notifications when the host capabilites are received. */
+ public interface CapabilitiesChangedListener {
+ void onCapabilitiesChanged(List<String> newCapabilities);
+ }
+
+ /** Tracks whether the remote host supports a capability. */
+ public enum HostCapability {
+ UNKNOWN,
+ SUPPORTED,
+ UNSUPPORTED;
+
+ public boolean isSet() {
+ return this != UNKNOWN;
+ }
+
+ public boolean isSupported() {
+ assert isSet();
+ return this == SUPPORTED;
+ }
+ }
+
private static final String TAG = "Chromoting";
/** Lazily-initialized singleton object that can be used from different Activities. */
@@ -44,11 +65,17 @@ public class CapabilityManager {
/** List of extensions to the client based on capabilities negotiated with the host. */
private List<ClientExtension> mClientExtensions;
+ /** Maintains a list of listeners to notify when host capabilities are received. */
+ private List<CapabilitiesChangedListener> mCapabilitiesChangedListeners;
+
private CapabilityManager() {
mLocalCapabilities = new ArrayList<String>();
mClientExtensions = new ArrayList<ClientExtension>();
mLocalCapabilities.add(Capabilities.CAST_CAPABILITY);
+ mLocalCapabilities.add(Capabilities.TOUCH_CAPABILITY);
+
+ mCapabilitiesChangedListeners = new ArrayList<CapabilitiesChangedListener>();
}
/**
@@ -64,6 +91,13 @@ public class CapabilityManager {
}
/**
+ * Cleans up host specific state when the connection has been terminated.
+ */
+ public void onHostDisconnect() {
+ mNegotiatedCapabilities = null;
+ }
+
+ /**
* Returns a space-separated list (required by host) of the capabilities supported by
* this client.
*/
@@ -72,6 +106,30 @@ public class CapabilityManager {
}
/**
+ * Registers the given listener object so it is notified when host capabilities are negotiated.
+ */
+ public void addListener(CapabilitiesChangedListener listener) {
+ assert !mCapabilitiesChangedListeners.contains(listener);
+ mCapabilitiesChangedListeners.add(listener);
+
+ // If we have already received the host capabilities before this listener was registered,
+ // then fire the event for this listener immediately.
+ if (mNegotiatedCapabilities != null) {
+ // Clone the capabilities list passed to the caller to prevent them from mutating it.
+ listener.onCapabilitiesChanged(new ArrayList<>(mNegotiatedCapabilities));
+ }
+ }
+
+ /**
+ * Removes the given listener object from the list of change listeners.
+ */
+ public void removeListener(CapabilitiesChangedListener listener) {
+ assert mCapabilitiesChangedListeners.contains(listener);
+
+ mCapabilitiesChangedListeners.remove(listener);
+ }
+
+ /**
* Returns the ActivityLifecycleListener associated with the specified capability, if
* |capability| is enabled and such a listener exists.
*
@@ -97,10 +155,8 @@ public class CapabilityManager {
}
/**
- * Receives the capabilities negotiated between client and host and creates the appropriate
- * extension handlers.
- *
- * Currently only the CAST_CAPABILITY exists, so that is the only extension constructed.
+ * Receives the capabilities negotiated between client and host, creates the appropriate
+ * extension handlers, and notifies registered listeners of the change.
*/
public void setNegotiatedCapabilities(String capabilities) {
mNegotiatedCapabilities = Arrays.asList(capabilities.split(" "));
@@ -108,6 +164,15 @@ public class CapabilityManager {
if (isCapabilityEnabled(Capabilities.CAST_CAPABILITY)) {
mClientExtensions.add(maybeCreateCastExtensionHandler());
}
+
+ // Clone the list of listeners to prevent problems if the callback calls back into this
+ // object and removes itself from the list of listeners.
+ List<CapabilitiesChangedListener> listeners =
+ new ArrayList<>(mCapabilitiesChangedListeners);
+ for (CapabilitiesChangedListener listener : listeners) {
+ // Clone the capabilities list passed to the caller to prevent them from mutating it.
+ listener.onCapabilitiesChanged(new ArrayList<>(mNegotiatedCapabilities));
+ }
}
/**
diff --git a/remoting/android/java/src/org/chromium/chromoting/ChromotingUtil.java b/remoting/android/java/src/org/chromium/chromoting/ChromotingUtil.java
index 64e0008..0f26ea7 100644
--- a/remoting/android/java/src/org/chromium/chromoting/ChromotingUtil.java
+++ b/remoting/android/java/src/org/chromium/chromoting/ChromotingUtil.java
@@ -10,6 +10,7 @@ import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.util.TypedValue;
import android.view.Menu;
+import android.view.MenuItem;
import org.chromium.base.ApiCompatibilityUtils;
@@ -27,10 +28,19 @@ public abstract class ChromotingUtil {
int color = getColorAttribute(context, R.attr.colorControlNormal);
int items = menu.size();
for (int i = 0; i < items; i++) {
- Drawable icon = menu.getItem(i).getIcon();
- if (icon != null) {
- icon.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN);
- }
+ ChromotingUtil.tintMenuIcon(menu.getItem(i), color);
+ }
+ }
+
+ /**
+ * Sets a color filter on the specified MenuItem.
+ * @param menuItem MenuItem to tint.
+ * @param color Color to set on the menuItem.
+ */
+ public static void tintMenuIcon(MenuItem menuItem, int color) {
+ Drawable icon = menuItem.getIcon();
+ if (icon != null) {
+ icon.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN);
}
}
diff --git a/remoting/android/java/src/org/chromium/chromoting/Desktop.java b/remoting/android/java/src/org/chromium/chromoting/Desktop.java
index e7b2628..99ef605 100644
--- a/remoting/android/java/src/org/chromium/chromoting/Desktop.java
+++ b/remoting/android/java/src/org/chromium/chromoting/Desktop.java
@@ -33,19 +33,36 @@ import org.chromium.chromoting.help.HelpContext;
import org.chromium.chromoting.help.HelpSingleton;
import org.chromium.chromoting.jni.JniInterface;
+import java.util.List;
import java.util.Set;
import java.util.TreeSet;
/**
* A simple screen that does nothing except display a DesktopView and notify it of rotations.
*/
-public class Desktop extends AppCompatActivity implements View.OnSystemUiVisibilityChangeListener {
+public class Desktop
+ extends AppCompatActivity implements View.OnSystemUiVisibilityChangeListener,
+ CapabilityManager.CapabilitiesChangedListener {
+ /** Used to set/store the selected input mode. */
+ public enum InputMode {
+ UNKNOWN,
+ TRACKPAD,
+ TOUCH;
+
+ public boolean isSet() {
+ return this != UNKNOWN;
+ }
+ }
+
/**
* Preference used for displaying an interestitial dialog only when the user first accesses the
* Cardboard function.
*/
private static final String PREFERENCE_CARDBOARD_DIALOG_SEEN = "cardboard_dialog_seen";
+ /** Preference used to track the last input mode selected by the user. */
+ private static final String PREFERENCE_INPUT_MODE = "input_mode";
+
/** 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;
@@ -69,6 +86,13 @@ public class Desktop extends AppCompatActivity implements View.OnSystemUiVisibil
/** The Toolbar instance backing our SupportActionBar. */
private Toolbar mToolbar;
+ /** Tracks the current input mode (e.g. trackpad/touch). */
+ private InputMode mInputMode = InputMode.UNKNOWN;
+
+ /** Indicates whether the remote host supports touch injection. */
+ private CapabilityManager.HostCapability mHostTouchCapability =
+ CapabilityManager.HostCapability.UNKNOWN;
+
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -101,6 +125,8 @@ public class Desktop extends AppCompatActivity implements View.OnSystemUiVisibil
this, Capabilities.CAST_CAPABILITY);
mActivityLifecycleListener.onActivityCreated(this, savedInstanceState);
+ mInputMode = getInitialInputModeValue();
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
attachKeyboardVisibilityListener();
@@ -136,6 +162,7 @@ public class Desktop extends AppCompatActivity implements View.OnSystemUiVisibil
mActivityLifecycleListener.onActivityStarted(this);
JniInterface.enableVideoChannel(true);
mRemoteHostDesktop.attachRedrawCallback();
+ CapabilityManager.getInstance().addListener(this);
}
@Override
@@ -158,6 +185,7 @@ public class Desktop extends AppCompatActivity implements View.OnSystemUiVisibil
@Override
protected void onStop() {
+ CapabilityManager.getInstance().removeListener(this);
mActivityLifecycleListener.onActivityStopped(this);
super.onStop();
if (mSwitchToCardboardDesktopActivity) {
@@ -219,9 +247,80 @@ public class Desktop extends AppCompatActivity implements View.OnSystemUiVisibil
ChromotingUtil.tintMenuIcons(this, menu);
+ // Wait to set the input mode until after the default tinting has been applied.
+ setInputMode(mInputMode);
+
return super.onCreateOptionsMenu(menu);
}
+ 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
+ // a device characteristic such as screen size.
+ InputMode inputMode = InputMode.TRACKPAD;
+ String previousInputMode =
+ getPreferences(MODE_PRIVATE)
+ .getString(PREFERENCE_INPUT_MODE, inputMode.name());
+
+ try {
+ inputMode = InputMode.valueOf(previousInputMode);
+ } catch (IllegalArgumentException ex) {
+ // Invalid or unexpected value was found, just use the default mode.
+ }
+
+ return inputMode;
+ }
+
+ private void toggleInputMode() {
+ if (mInputMode == InputMode.TRACKPAD) {
+ setInputMode(InputMode.TOUCH);
+ } else if (mInputMode == InputMode.TOUCH) {
+ setInputMode(InputMode.TRACKPAD);
+ }
+ }
+
+ private void setInputMode(InputMode inputMode) {
+ String titleText;
+ int inputModeItemColorId;
+ if (inputMode == InputMode.TRACKPAD) {
+ titleText = getString(R.string.select_trackpad_mode);
+ inputModeItemColorId = R.attr.colorControlNormal;
+ } else if (inputMode == InputMode.TOUCH) {
+ titleText = getString(R.string.select_touch_mode);
+ inputModeItemColorId = R.attr.colorControlActivated;
+ } else {
+ assert false : "Unreached";
+ return;
+ }
+
+ mInputMode = inputMode;
+ getPreferences(MODE_PRIVATE)
+ .edit()
+ .putString(PREFERENCE_INPUT_MODE, mInputMode.name())
+ .apply();
+
+ Menu menu = mToolbar.getMenu();
+ MenuItem inputModeMenuItem = menu.findItem(R.id.actionbar_input_mode);
+ if (inputModeMenuItem != null) {
+ inputModeMenuItem.setTitle(titleText);
+ int color = ChromotingUtil.getColorAttribute(this, inputModeItemColorId);
+ ChromotingUtil.tintMenuIcon(inputModeMenuItem, color);
+ }
+
+ mRemoteHostDesktop.changeInputMode(mInputMode, mHostTouchCapability);
+ }
+
+ @Override
+ public void onCapabilitiesChanged(List<String> newCapabilities) {
+ if (newCapabilities.contains(Capabilities.TOUCH_CAPABILITY)) {
+ mHostTouchCapability = CapabilityManager.HostCapability.SUPPORTED;
+ } else {
+ mHostTouchCapability = CapabilityManager.HostCapability.UNSUPPORTED;
+ }
+
+ mRemoteHostDesktop.changeInputMode(mInputMode, mHostTouchCapability);
+ }
+
// Any time an onTouchListener is attached, a lint warning about filtering touch events is
// generated. Since the function below is only used to listen to, not intercept, the events,
// the lint warning can be safely suppressed.
@@ -378,6 +477,10 @@ public class Desktop extends AppCompatActivity implements View.OnSystemUiVisibil
onCardboardItemSelected();
return true;
}
+ if (id == R.id.actionbar_input_mode) {
+ toggleInputMode();
+ return true;
+ }
if (id == R.id.actionbar_keyboard) {
((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE)).toggleSoftInput(0, 0);
return true;
diff --git a/remoting/android/java/src/org/chromium/chromoting/DesktopView.java b/remoting/android/java/src/org/chromium/chromoting/DesktopView.java
index 466012b..d2499be 100644
--- a/remoting/android/java/src/org/chromium/chromoting/DesktopView.java
+++ b/remoting/android/java/src/org/chromium/chromoting/DesktopView.java
@@ -159,9 +159,6 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface,
mRenderData = new RenderData();
mInputHandler = new TouchInputHandler(this, context, mRenderData);
- // TODO(joedow): Move this into a method which will choose the correct input strategy
- // based on host support and current toggle setting in the UI.
- mInputHandler.setInputStrategy(new TrackpadInputStrategy(this, mRenderData));
mRepaintPending = false;
@@ -392,4 +389,32 @@ 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) {
+ // In order to set the correct input strategy, we need to know the current input mode and
+ // the host input capabilities.
+ if (!inputMode.isSet() || !hostTouchCapability.isSet()) {
+ return;
+ }
+
+ switch (inputMode) {
+ case TRACKPAD:
+ mInputHandler.setInputStrategy(new TrackpadInputStrategy(this, mRenderData));
+ break;
+
+ case TOUCH:
+ if (hostTouchCapability.isSupported()) {
+ // TODO(joedow): Set the touch input strategy.
+ } else {
+ // TODO(joedow): Set the simulated touch input strategy.
+ }
+ break;
+
+ default:
+ // Unreachable, but required by Google Java style and findbugs.
+ assert false : "Unreached";
+ }
+ }
}
diff --git a/remoting/android/java/src/org/chromium/chromoting/jni/JniInterface.java b/remoting/android/java/src/org/chromium/chromoting/jni/JniInterface.java
index 69dbdba..288fd78 100644
--- a/remoting/android/java/src/org/chromium/chromoting/jni/JniInterface.java
+++ b/remoting/android/java/src/org/chromium/chromoting/jni/JniInterface.java
@@ -210,6 +210,7 @@ public class JniInterface {
nativeDisconnect();
sConnectionListener = null;
sConnected = false;
+ sCapabilityManager.onHostDisconnect();
// Drop the reference to free the Bitmap for GC.
synchronized (sFrameLock) {