summaryrefslogtreecommitdiffstats
path: root/remoting/android
diff options
context:
space:
mode:
Diffstat (limited to 'remoting/android')
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/CapabilityManager.java24
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/Chromoting.java27
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/Desktop.java53
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/DesktopView.java28
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/SessionAuthenticator.java16
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/SessionConnector.java11
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/SimulatedTouchInputStrategy.java12
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/TouchInputStrategy.java13
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/TrackpadInputStrategy.java12
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/cardboard/CardboardRenderer.java16
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/cardboard/Cursor.java13
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/cardboard/Desktop.java11
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/cardboard/DesktopActivity.java22
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/jni/Client.java376
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/jni/JniInterface.java309
-rw-r--r--remoting/android/javatests/src/org/chromium/chromoting/TouchInputStrategyTest.java5
16 files changed, 568 insertions, 380 deletions
diff --git a/remoting/android/java/src/org/chromium/chromoting/CapabilityManager.java b/remoting/android/java/src/org/chromium/chromoting/CapabilityManager.java
index e332e83..0a551bf4e 100644
--- a/remoting/android/java/src/org/chromium/chromoting/CapabilityManager.java
+++ b/remoting/android/java/src/org/chromium/chromoting/CapabilityManager.java
@@ -21,10 +21,6 @@ import java.util.List;
* The CapabilityManager mirrors how the Chromoting host handles extension messages. For each
* incoming extension message, runs through a list of HostExtensionSession objects, giving each one
* a chance to handle the message.
- *
- * The CapabilityManager is a singleton class so we can manage client extensions on an application
- * level. The singleton object may be used from multiple Activities, thus allowing it to support
- * different capabilities at different stages of the application.
*/
public class CapabilityManager {
/** Used to allow objects to receive notifications when the host capabilites are received. */
@@ -50,12 +46,6 @@ public class CapabilityManager {
private static final String TAG = "Chromoting";
- /** Lazily-initialized singleton object that can be used from different Activities. */
- private static CapabilityManager sInstance;
-
- /** Protects access to |sInstance|. */
- private static final Object sInstanceLock = new Object();
-
/** List of all capabilities that are supported by the application. */
private List<String> mLocalCapabilities;
@@ -68,7 +58,7 @@ public class CapabilityManager {
/** Maintains a list of listeners to notify when host capabilities are received. */
private List<CapabilitiesChangedListener> mCapabilitiesChangedListeners;
- private CapabilityManager() {
+ public CapabilityManager() {
mLocalCapabilities = new ArrayList<String>();
mClientExtensions = new ArrayList<ClientExtension>();
@@ -79,18 +69,6 @@ public class CapabilityManager {
}
/**
- * Returns the singleton object. Thread-safe.
- */
- public static CapabilityManager getInstance() {
- synchronized (sInstanceLock) {
- if (sInstance == null) {
- sInstance = new CapabilityManager();
- }
- return sInstance;
- }
- }
-
- /**
* Cleans up host specific state when the connection has been terminated.
*/
public void onHostDisconnect() {
diff --git a/remoting/android/java/src/org/chromium/chromoting/Chromoting.java b/remoting/android/java/src/org/chromium/chromoting/Chromoting.java
index a10a17f..f53926c 100644
--- a/remoting/android/java/src/org/chromium/chromoting/Chromoting.java
+++ b/remoting/android/java/src/org/chromium/chromoting/Chromoting.java
@@ -36,6 +36,7 @@ import org.chromium.chromoting.accountswitcher.AccountSwitcher;
import org.chromium.chromoting.accountswitcher.AccountSwitcherFactory;
import org.chromium.chromoting.help.HelpContext;
import org.chromium.chromoting.help.HelpSingleton;
+import org.chromium.chromoting.jni.Client;
import org.chromium.chromoting.jni.ConnectionListener;
import org.chromium.chromoting.jni.JniInterface;
@@ -117,6 +118,9 @@ public class Chromoting extends AppCompatActivity implements ConnectionListener,
private AccountSwitcher mAccountSwitcher;
+ /** The currently-connected Client, if any. */
+ private Client mClient;
+
/** Shows a warning explaining that a Google account is required, then closes the activity. */
private void showNoAccountsDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
@@ -349,8 +353,14 @@ public class Chromoting extends AppCompatActivity implements ConnectionListener,
@Override
public void onDestroy() {
super.onDestroy();
- JniInterface.disconnectFromHost();
mAccountSwitcher.destroy();
+
+ // TODO(lambroslambrou): Determine whether we really need to tear down the connection here,
+ // so we can remove this code.
+ if (mClient != null) {
+ mClient.destroy();
+ mClient = null;
+ }
}
/** Called when a child Activity exits and sends a result back to this Activity. */
@@ -444,6 +454,11 @@ public class Chromoting extends AppCompatActivity implements ConnectionListener,
}
private void connectToHost(HostInfo host) {
+ if (mClient != null) {
+ mClient.destroy();
+ }
+
+ mClient = new Client();
mProgressIndicator = ProgressDialog.show(
this,
host.name,
@@ -453,11 +468,15 @@ public class Chromoting extends AppCompatActivity implements ConnectionListener,
new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
- JniInterface.disconnectFromHost();
+ if (mClient != null) {
+ mClient.destroy();
+ mClient = null;
+ }
}
});
- SessionConnector connector = new SessionConnector(this, this, mHostListLoader);
- mAuthenticator = new SessionAuthenticator(this, host);
+
+ SessionConnector connector = new SessionConnector(mClient, this, this, mHostListLoader);
+ mAuthenticator = new SessionAuthenticator(this, mClient, host);
connector.connectToHost(mAccount, mToken, host, mAuthenticator,
getPreferences(MODE_PRIVATE).getString(PREFERENCE_EXPERIMENTAL_FLAGS, ""));
}
diff --git a/remoting/android/java/src/org/chromium/chromoting/Desktop.java b/remoting/android/java/src/org/chromium/chromoting/Desktop.java
index 93e54b1..f9f3c73 100644
--- a/remoting/android/java/src/org/chromium/chromoting/Desktop.java
+++ b/remoting/android/java/src/org/chromium/chromoting/Desktop.java
@@ -31,7 +31,7 @@ import android.view.inputmethod.InputMethodManager;
import org.chromium.chromoting.cardboard.DesktopActivity;
import org.chromium.chromoting.help.HelpContext;
import org.chromium.chromoting.help.HelpSingleton;
-import org.chromium.chromoting.jni.JniInterface;
+import org.chromium.chromoting.jni.Client;
import java.util.List;
import java.util.Set;
@@ -69,6 +69,8 @@ public class Desktop
/** The surface that displays the remote host's desktop feed. */
private DesktopView mRemoteHostDesktop;
+ private Client mClient;
+
/** Set of pressed keys for which we've sent TextEvent. */
private Set<Integer> mPressedTextKeys = new TreeSet<Integer>();
@@ -102,11 +104,14 @@ public class Desktop
super.onCreate(savedInstanceState);
setContentView(R.layout.desktop);
+ mClient = Client.getInstance();
+
mToolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(mToolbar);
mRemoteHostDesktop = (DesktopView) findViewById(R.id.desktop_view);
mRemoteHostDesktop.setDesktop(this);
+ mRemoteHostDesktop.setClient(mClient);
mSwitchToCardboardDesktopActivity = false;
getSupportActionBar().setDisplayShowTitleEnabled(false);
@@ -124,7 +129,7 @@ public class Desktop
View decorView = getWindow().getDecorView();
decorView.setOnSystemUiVisibilityChangeListener(this);
- mActivityLifecycleListener = CapabilityManager.getInstance().onActivityAcceptingListener(
+ mActivityLifecycleListener = mClient.getCapabilityManager().onActivityAcceptingListener(
this, Capabilities.CAST_CAPABILITY);
mActivityLifecycleListener.onActivityCreated(this, savedInstanceState);
@@ -163,9 +168,9 @@ public class Desktop
protected void onStart() {
super.onStart();
mActivityLifecycleListener.onActivityStarted(this);
- JniInterface.enableVideoChannel(true);
+ mClient.enableVideoChannel(true);
mRemoteHostDesktop.attachRedrawCallback();
- CapabilityManager.getInstance().addListener(this);
+ mClient.getCapabilityManager().addListener(this);
}
@Override
@@ -173,7 +178,7 @@ public class Desktop
if (isFinishing()) mActivityLifecycleListener.onActivityPaused(this);
super.onPause();
if (!mSwitchToCardboardDesktopActivity) {
- JniInterface.enableVideoChannel(false);
+ mClient.enableVideoChannel(false);
}
stopActionBarAutoHideTimer();
}
@@ -182,19 +187,19 @@ public class Desktop
public void onResume() {
super.onResume();
mActivityLifecycleListener.onActivityResumed(this);
- JniInterface.enableVideoChannel(true);
+ mClient.enableVideoChannel(true);
startActionBarAutoHideTimer();
}
@Override
protected void onStop() {
- CapabilityManager.getInstance().removeListener(this);
+ mClient.getCapabilityManager().removeListener(this);
mActivityLifecycleListener.onActivityStopped(this);
super.onStop();
if (mSwitchToCardboardDesktopActivity) {
mSwitchToCardboardDesktopActivity = false;
} else {
- JniInterface.enableVideoChannel(false);
+ mClient.enableVideoChannel(false);
}
}
@@ -489,7 +494,7 @@ public class Desktop
return true;
}
if (id == R.id.actionbar_disconnect || id == android.R.id.home) {
- JniInterface.disconnectFromHost();
+ mClient.destroy();
return true;
}
if (id == R.id.actionbar_send_ctrl_alt_del) {
@@ -499,10 +504,10 @@ public class Desktop
KeyEvent.KEYCODE_FORWARD_DEL,
};
for (int key : keys) {
- JniInterface.sendKeyEvent(0, key, true);
+ mClient.sendKeyEvent(0, key, true);
}
for (int key : keys) {
- JniInterface.sendKeyEvent(0, key, false);
+ mClient.sendKeyEvent(0, key, false);
}
return true;
}
@@ -609,7 +614,7 @@ public class Desktop
// Dispatch the back button to the system to handle navigation
if (keyCode == KeyEvent.KEYCODE_BACK) {
- JniInterface.disconnectFromHost();
+ mClient.destroy();
return super.dispatchKeyEvent(event);
}
@@ -621,7 +626,7 @@ public class Desktop
// the keyboard layout selected on the client doesn't affect the key
// codes sent to the host.
if (event.getDeviceId() != KeyCharacterMap.VIRTUAL_KEYBOARD) {
- return JniInterface.sendKeyEvent(event.getScanCode(), 0, pressed);
+ return mClient.sendKeyEvent(event.getScanCode(), 0, pressed);
}
// Events received from software keyboards generate TextEvent in two
@@ -632,7 +637,7 @@ public class Desktop
// correspond to what user sees on the screen, while physical keyboard
// acts as if it is connected to the remote host.
if (event.getAction() == KeyEvent.ACTION_MULTIPLE) {
- JniInterface.sendTextEvent(event.getCharacters());
+ mClient.sendTextEvent(event.getCharacters());
return true;
}
@@ -646,7 +651,7 @@ public class Desktop
if (pressed && unicode != 0 && no_modifiers) {
mPressedTextKeys.add(keyCode);
int[] codePoints = { unicode };
- JniInterface.sendTextEvent(new String(codePoints, 0, 1));
+ mClient.sendTextEvent(new String(codePoints, 0, 1));
return true;
}
@@ -661,28 +666,28 @@ public class Desktop
// third-party keyboards that may still generate these events. See
// https://source.android.com/devices/input/keyboard-devices.html#legacy-unsupported-keys
case KeyEvent.KEYCODE_AT:
- JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed);
- JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_2, pressed);
+ mClient.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed);
+ mClient.sendKeyEvent(0, KeyEvent.KEYCODE_2, pressed);
return true;
case KeyEvent.KEYCODE_POUND:
- JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed);
- JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_3, pressed);
+ mClient.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed);
+ mClient.sendKeyEvent(0, KeyEvent.KEYCODE_3, pressed);
return true;
case KeyEvent.KEYCODE_STAR:
- JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed);
- JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_8, pressed);
+ mClient.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed);
+ mClient.sendKeyEvent(0, KeyEvent.KEYCODE_8, pressed);
return true;
case KeyEvent.KEYCODE_PLUS:
- JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed);
- JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_EQUALS, pressed);
+ mClient.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed);
+ mClient.sendKeyEvent(0, KeyEvent.KEYCODE_EQUALS, pressed);
return true;
default:
// We try to send all other key codes to the host directly.
- return JniInterface.sendKeyEvent(0, keyCode, pressed);
+ return mClient.sendKeyEvent(0, keyCode, pressed);
}
}
}
diff --git a/remoting/android/java/src/org/chromium/chromoting/DesktopView.java b/remoting/android/java/src/org/chromium/chromoting/DesktopView.java
index 0c2330b..e79065d3 100644
--- a/remoting/android/java/src/org/chromium/chromoting/DesktopView.java
+++ b/remoting/android/java/src/org/chromium/chromoting/DesktopView.java
@@ -25,7 +25,7 @@ import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import org.chromium.base.Log;
-import org.chromium.chromoting.jni.JniInterface;
+import org.chromium.chromoting.jni.Client;
/**
* The user interface for viewing and interacting with a specific remote host.
@@ -46,6 +46,10 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface,
/** The parent Desktop activity. */
private Desktop mDesktop;
+ /** The Client connection, used to inject input and fetch the video frames. */
+ private Client mClient;
+
+
// Flag to prevent multiple repaint requests from being backed up. Requests for repainting will
// be dropped if this is already set to true. This is used by the main thread and the painting
// thread, so the access should be synchronized on |mRenderData|.
@@ -179,6 +183,10 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface,
mDesktop = desktop;
}
+ public void setClient(Client client) {
+ mClient = client;
+ }
+
/** See {@link TouchInputHandler#onSoftInputMethodVisibilityChanged} for API details. */
public void onSoftInputMethodVisibilityChanged(boolean inputMethodVisible, Rect bounds) {
mInputHandler.onSoftInputMethodVisibilityChanged(inputMethodVisible, bounds);
@@ -192,7 +200,7 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface,
}
mRepaintPending = true;
}
- JniInterface.redrawGraphics();
+ mClient.redrawGraphics();
}
/**
@@ -207,7 +215,7 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface,
Log.w(TAG, "Canvas being redrawn on UI thread");
}
- Bitmap image = JniInterface.getVideoFrame();
+ Bitmap image = mClient.getVideoFrame();
if (image == null) {
// This can happen if the client is connected, but a complete video frame has not yet
// been decoded.
@@ -220,7 +228,7 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface,
synchronized (mRenderData) {
if (mRenderData.imageWidth != width || mRenderData.imageHeight != height) {
// TODO(lambroslambrou): Move this code into a sizeChanged() callback, to be
- // triggered from JniInterface (on the display thread) when the remote screen size
+ // triggered from native code (on the display thread) when the remote screen size
// changes.
mRenderData.imageWidth = width;
mRenderData.imageHeight = height;
@@ -266,9 +274,9 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface,
}
if (drawCursor) {
- Bitmap cursorBitmap = JniInterface.getCursorBitmap();
+ Bitmap cursorBitmap = mClient.getCursorBitmap();
if (cursorBitmap != null) {
- Point hotspot = JniInterface.getCursorHotspot();
+ Point hotspot = mClient.getCursorHotspot();
canvas.drawBitmap(cursorBitmap, cursorPosition.x - hotspot.x,
cursorPosition.y - hotspot.y, new Paint());
}
@@ -319,7 +327,7 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface,
}
public void attachRedrawCallback() {
- JniInterface.provideRedrawCallback(new Runnable() {
+ mClient.provideRedrawCallback(new Runnable() {
@Override
public void run() {
paint();
@@ -415,15 +423,15 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface,
switch (inputMode) {
case TRACKPAD:
- mInputHandler.setInputStrategy(new TrackpadInputStrategy(mRenderData));
+ mInputHandler.setInputStrategy(new TrackpadInputStrategy(mRenderData, mClient));
break;
case TOUCH:
if (hostTouchCapability.isSupported()) {
- mInputHandler.setInputStrategy(new TouchInputStrategy(mRenderData));
+ mInputHandler.setInputStrategy(new TouchInputStrategy(mRenderData, mClient));
} else {
mInputHandler.setInputStrategy(
- new SimulatedTouchInputStrategy(mRenderData, getContext()));
+ new SimulatedTouchInputStrategy(mRenderData, mClient, getContext()));
}
break;
diff --git a/remoting/android/java/src/org/chromium/chromoting/SessionAuthenticator.java b/remoting/android/java/src/org/chromium/chromoting/SessionAuthenticator.java
index a18c2f4..ce50a04 100644
--- a/remoting/android/java/src/org/chromium/chromoting/SessionAuthenticator.java
+++ b/remoting/android/java/src/org/chromium/chromoting/SessionAuthenticator.java
@@ -16,7 +16,7 @@ import android.widget.CheckBox;
import android.widget.TextView;
import android.widget.Toast;
-import org.chromium.chromoting.jni.JniInterface;
+import org.chromium.chromoting.jni.Client;
/**
* This class performs the user-interaction needed to authenticate the session connection. This
@@ -29,14 +29,18 @@ public class SessionAuthenticator {
*/
private Chromoting mApplicationContext;
+ /** Client connection being authenticated. */
+ private final Client mClient;
+
/** Provides the tokenUrlPatterns for this host during fetchThirdPartyTokens(). */
private HostInfo mHost;
/** Object for fetching OAuth2 access tokens from third party authorization servers. */
private ThirdPartyTokenFetcher mTokenFetcher;
- public SessionAuthenticator(Chromoting context, HostInfo host) {
+ public SessionAuthenticator(Chromoting context, Client client, HostInfo host) {
mApplicationContext = context;
+ mClient = client;
mHost = host;
}
@@ -62,8 +66,8 @@ public class SessionAuthenticator {
R.string.connect_button, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- if (JniInterface.isConnected()) {
- JniInterface.handleAuthenticationResponse(
+ if (mClient.isConnected()) {
+ mClient.handleAuthenticationResponse(
String.valueOf(pinTextView.getText()),
pinCheckBox.isChecked(), Build.MODEL);
} else {
@@ -78,7 +82,7 @@ public class SessionAuthenticator {
R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- JniInterface.disconnectFromHost();
+ mClient.destroy();
}
});
@@ -138,7 +142,7 @@ public class SessionAuthenticator {
// authenticate itself with the host using spake.
String sharedSecret = accessToken;
- JniInterface.onThirdPartyTokenFetched(token, sharedSecret);
+ mClient.onThirdPartyTokenFetched(token, sharedSecret);
}
};
mTokenFetcher = new ThirdPartyTokenFetcher(mApplicationContext, mHost.getTokenUrlPatterns(),
diff --git a/remoting/android/java/src/org/chromium/chromoting/SessionConnector.java b/remoting/android/java/src/org/chromium/chromoting/SessionConnector.java
index 4fa9522..38e4634 100644
--- a/remoting/android/java/src/org/chromium/chromoting/SessionConnector.java
+++ b/remoting/android/java/src/org/chromium/chromoting/SessionConnector.java
@@ -4,15 +4,15 @@
package org.chromium.chromoting;
+import org.chromium.chromoting.jni.Client;
import org.chromium.chromoting.jni.ConnectionListener;
-import org.chromium.chromoting.jni.JniInterface;
/**
* This class manages making a connection to a host, with logic for reloading the host list and
* retrying the connection in the case of a stale host JID.
*/
-public class SessionConnector implements ConnectionListener,
- HostListLoader.Callback {
+public class SessionConnector implements ConnectionListener, HostListLoader.Callback {
+ private Client mClient;
private ConnectionListener mConnectionListener;
private HostListLoader.Callback mHostListCallback;
private HostListLoader mHostListLoader;
@@ -38,8 +38,9 @@ public class SessionConnector implements ConnectionListener,
* @param hostListCallback Object to be notified whenever the host list is reloaded.
* @param hostListLoader The object used for reloading the host list.
*/
- public SessionConnector(ConnectionListener connectionListener,
+ public SessionConnector(Client client, ConnectionListener connectionListener,
HostListLoader.Callback hostListCallback, HostListLoader hostListLoader) {
+ mClient = client;
mConnectionListener = connectionListener;
mHostListCallback = hostListCallback;
mHostListLoader = hostListLoader;
@@ -65,7 +66,7 @@ public class SessionConnector implements ConnectionListener,
}
private void doConnect() {
- JniInterface.connectToHost(mAccountName, mAuthToken, mHost.jabberId, mHost.id,
+ mClient.connectToHost(mAccountName, mAuthToken, mHost.jabberId, mHost.id,
mHost.publicKey, mAuthenticator, mFlags, this);
}
diff --git a/remoting/android/java/src/org/chromium/chromoting/SimulatedTouchInputStrategy.java b/remoting/android/java/src/org/chromium/chromoting/SimulatedTouchInputStrategy.java
index 2c3ded2..ccc0dbd 100644
--- a/remoting/android/java/src/org/chromium/chromoting/SimulatedTouchInputStrategy.java
+++ b/remoting/android/java/src/org/chromium/chromoting/SimulatedTouchInputStrategy.java
@@ -10,7 +10,7 @@ import android.os.SystemClock;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
-import org.chromium.chromoting.jni.JniInterface;
+import org.chromium.chromoting.jni.Client;
/**
* This class receives local touch events and translates them into the appropriate mouse based
@@ -22,6 +22,7 @@ public class SimulatedTouchInputStrategy implements InputStrategyInterface {
private static final float DOUBLE_TAP_SLOP_SCALE_FACTOR = 0.25f;
private final RenderData mRenderData;
+ private final Client mClient;
/**
* Stores the time of the most recent left button single tap processed.
@@ -48,8 +49,9 @@ public class SimulatedTouchInputStrategy implements InputStrategyInterface {
/** Mouse-button currently held down, or BUTTON_UNDEFINED otherwise. */
private int mHeldButton = TouchInputHandlerInterface.BUTTON_UNDEFINED;
- public SimulatedTouchInputStrategy(RenderData renderData, Context context) {
+ public SimulatedTouchInputStrategy(RenderData renderData, Client client, Context context) {
mRenderData = renderData;
+ mClient = client;
ViewConfiguration config = ViewConfiguration.get(context);
mDoubleTapDurationInMs = config.getDoubleTapTimeout();
@@ -121,7 +123,7 @@ public class SimulatedTouchInputStrategy implements InputStrategyInterface {
@Override
public void onScroll(float distanceX, float distanceY) {
- JniInterface.sendMouseWheelEvent((int) -distanceX, (int) -distanceY);
+ mClient.sendMouseWheelEvent((int) -distanceX, (int) -distanceY);
}
@Override
@@ -135,7 +137,7 @@ public class SimulatedTouchInputStrategy implements InputStrategyInterface {
@Override
public void injectCursorMoveEvent(int x, int y) {
- JniInterface.sendMouseEvent(x, y, TouchInputHandlerInterface.BUTTON_UNDEFINED, false);
+ mClient.sendMouseEvent(x, y, TouchInputHandlerInterface.BUTTON_UNDEFINED, false);
}
@Override
@@ -180,6 +182,6 @@ public class SimulatedTouchInputStrategy implements InputStrategyInterface {
}
private void injectMouseButtonEvent(int button, boolean pressed, Point tapPoint) {
- JniInterface.sendMouseEvent(tapPoint.x, tapPoint.y, button, pressed);
+ mClient.sendMouseEvent(tapPoint.x, tapPoint.y, button, pressed);
}
}
diff --git a/remoting/android/java/src/org/chromium/chromoting/TouchInputStrategy.java b/remoting/android/java/src/org/chromium/chromoting/TouchInputStrategy.java
index 488760b..19d70e3 100644
--- a/remoting/android/java/src/org/chromium/chromoting/TouchInputStrategy.java
+++ b/remoting/android/java/src/org/chromium/chromoting/TouchInputStrategy.java
@@ -8,7 +8,7 @@ import android.graphics.Matrix;
import android.view.MotionEvent;
import org.chromium.base.VisibleForTesting;
-import org.chromium.chromoting.jni.JniInterface;
+import org.chromium.chromoting.jni.Client;
import org.chromium.chromoting.jni.TouchEventData;
import java.util.ArrayList;
@@ -37,15 +37,15 @@ public class TouchInputStrategy implements InputStrategyInterface {
/**
* This class provides the default implementation for injecting remote events.
*/
- private static class DefaultInputInjector implements RemoteInputInjector {
+ private class DefaultInputInjector implements RemoteInputInjector {
@Override
public void injectMouseEvent(int x, int y, int button, boolean buttonDown) {
- JniInterface.sendMouseEvent(x, y, button, buttonDown);
+ mClient.sendMouseEvent(x, y, button, buttonDown);
}
@Override
public void injectTouchEvent(TouchEventData.EventType eventType, TouchEventData[] data) {
- JniInterface.sendTouchEvent(eventType, data);
+ mClient.sendTouchEvent(eventType, data);
}
}
@@ -77,10 +77,13 @@ public class TouchInputStrategy implements InputStrategyInterface {
private final RenderData mRenderData;
+ private final Client mClient;
+
private RemoteInputInjector mRemoteInputInjector;
- public TouchInputStrategy(RenderData renderData) {
+ public TouchInputStrategy(RenderData renderData, Client client) {
mRenderData = renderData;
+ mClient = client;
mRemoteInputInjector = new DefaultInputInjector();
mQueuedEvents = new LinkedList<MotionEvent>();
diff --git a/remoting/android/java/src/org/chromium/chromoting/TrackpadInputStrategy.java b/remoting/android/java/src/org/chromium/chromoting/TrackpadInputStrategy.java
index 326eff9..c9dcf4b 100644
--- a/remoting/android/java/src/org/chromium/chromoting/TrackpadInputStrategy.java
+++ b/remoting/android/java/src/org/chromium/chromoting/TrackpadInputStrategy.java
@@ -7,7 +7,7 @@ package org.chromium.chromoting;
import android.graphics.Point;
import android.view.MotionEvent;
-import org.chromium.chromoting.jni.JniInterface;
+import org.chromium.chromoting.jni.Client;
/**
* Defines a set of behavior and methods to simulate trackpad behavior when responding to
@@ -16,12 +16,14 @@ import org.chromium.chromoting.jni.JniInterface;
*/
public class TrackpadInputStrategy implements InputStrategyInterface {
private final RenderData mRenderData;
+ private final Client mClient;
/** Mouse-button currently held down, or BUTTON_UNDEFINED otherwise. */
private int mHeldButton = TouchInputHandlerInterface.BUTTON_UNDEFINED;
- public TrackpadInputStrategy(RenderData renderData) {
+ public TrackpadInputStrategy(RenderData renderData, Client client) {
mRenderData = renderData;
+ mClient = client;
synchronized (mRenderData) {
mRenderData.drawCursor = true;
@@ -44,7 +46,7 @@ public class TrackpadInputStrategy implements InputStrategyInterface {
@Override
public void onScroll(float distanceX, float distanceY) {
- JniInterface.sendMouseWheelEvent((int) -distanceX, (int) -distanceY);
+ mClient.sendMouseWheelEvent((int) -distanceX, (int) -distanceY);
}
@Override
@@ -58,7 +60,7 @@ public class TrackpadInputStrategy implements InputStrategyInterface {
@Override
public void injectCursorMoveEvent(int x, int y) {
- JniInterface.sendMouseEvent(x, y, TouchInputHandlerInterface.BUTTON_UNDEFINED, false);
+ mClient.sendMouseEvent(x, y, TouchInputHandlerInterface.BUTTON_UNDEFINED, false);
}
@Override
@@ -81,6 +83,6 @@ public class TrackpadInputStrategy implements InputStrategyInterface {
synchronized (mRenderData) {
cursorPosition = mRenderData.getCursorPosition();
}
- JniInterface.sendMouseEvent(cursorPosition.x, cursorPosition.y, button, pressed);
+ mClient.sendMouseEvent(cursorPosition.x, cursorPosition.y, button, pressed);
}
}
diff --git a/remoting/android/java/src/org/chromium/chromoting/cardboard/CardboardRenderer.java b/remoting/android/java/src/org/chromium/chromoting/cardboard/CardboardRenderer.java
index 843edd1..e8a05f0 100644
--- a/remoting/android/java/src/org/chromium/chromoting/cardboard/CardboardRenderer.java
+++ b/remoting/android/java/src/org/chromium/chromoting/cardboard/CardboardRenderer.java
@@ -15,7 +15,7 @@ import com.google.vrtoolkit.cardboard.Eye;
import com.google.vrtoolkit.cardboard.HeadTransform;
import com.google.vrtoolkit.cardboard.Viewport;
-import org.chromium.chromoting.jni.JniInterface;
+import org.chromium.chromoting.jni.Client;
import javax.microedition.khronos.egl.EGLConfig;
@@ -65,6 +65,7 @@ public class CardboardRenderer implements CardboardView.StereoRenderer {
private static final float EPSILON = 1e-5f;
private final Activity mActivity;
+ private final Client mClient;
private float mCameraPosition;
@@ -103,8 +104,9 @@ public class CardboardRenderer implements CardboardView.StereoRenderer {
// Flag to indicate whether to show menu bar.
private boolean mMenuBarVisible;
- public CardboardRenderer(Activity activity) {
+ public CardboardRenderer(Activity activity, Client client) {
mActivity = activity;
+ mClient = client;
mCameraPosition = 0.0f;
mCameraMatrix = new float[16];
@@ -122,7 +124,7 @@ public class CardboardRenderer implements CardboardView.StereoRenderer {
private void initializeRedrawCallback() {
mActivity.runOnUiThread(new Runnable() {
public void run() {
- JniInterface.provideRedrawCallback(new Runnable() {
+ mClient.provideRedrawCallback(new Runnable() {
@Override
public void run() {
mDesktop.reloadTexture();
@@ -130,7 +132,7 @@ public class CardboardRenderer implements CardboardView.StereoRenderer {
}
});
- JniInterface.redrawGraphics();
+ mClient.redrawGraphics();
}
});
}
@@ -146,10 +148,10 @@ public class CardboardRenderer implements CardboardView.StereoRenderer {
// Enable depth testing.
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
- mDesktop = new Desktop();
+ mDesktop = new Desktop(mClient);
mMenuBar = new MenuBar(mActivity);
mPhotosphere = new Photosphere(mActivity);
- mCursor = new Cursor();
+ mCursor = new Cursor(mClient);
initializeRedrawCallback();
}
@@ -429,4 +431,4 @@ public class CardboardRenderer implements CardboardView.StereoRenderer {
return Math.abs(phi) > FARAWAY_ANGLE_RATIO * Math.abs(theta);
}
-} \ No newline at end of file
+}
diff --git a/remoting/android/java/src/org/chromium/chromoting/cardboard/Cursor.java b/remoting/android/java/src/org/chromium/chromoting/cardboard/Cursor.java
index 34403d6..534728a 100644
--- a/remoting/android/java/src/org/chromium/chromoting/cardboard/Cursor.java
+++ b/remoting/android/java/src/org/chromium/chromoting/cardboard/Cursor.java
@@ -13,7 +13,7 @@ import android.graphics.PointF;
import android.opengl.GLES20;
import org.chromium.chromoting.TouchInputHandler;
-import org.chromium.chromoting.jni.JniInterface;
+import org.chromium.chromoting.jni.Client;
import java.nio.FloatBuffer;
@@ -52,6 +52,8 @@ public class Cursor {
// Threshold to determine whether to send the mouse move event.
private static final float CURSOR_MOVE_THRESHOLD = 1.0f;
+ private final Client mClient;
+
private FloatBuffer mPositionCoordinates;
private int mVertexShaderHandle;
@@ -76,7 +78,8 @@ public class Cursor {
private PointF mCursorPosition;
- public Cursor() {
+ public Cursor(Client client) {
+ mClient = client;
mHalfFrameSize = new PointF(0.0f, 0.0f);
mCursorPosition = new PointF(0.0f, 0.0f);
@@ -120,7 +123,7 @@ public class Cursor {
*/
public void moveTo(PointF position) {
if (moveCursor(position)) {
- JniInterface.sendMouseEvent((int) position.x, (int) position.y,
+ mClient.sendMouseEvent((int) position.x, (int) position.y,
TouchInputHandler.BUTTON_UNDEFINED, false);
}
mCursorPosition = position;
@@ -137,7 +140,7 @@ public class Cursor {
}
}
- Bitmap cursorBitmap = JniInterface.getCursorBitmap();
+ Bitmap cursorBitmap = mClient.getCursorBitmap();
if (cursorBitmap == mCursorBitmap) {
// Case when cursor image has not changed.
@@ -148,7 +151,7 @@ public class Cursor {
}
mCursorBitmap = cursorBitmap;
- updatePosition(desktop, mCursorBitmap, JniInterface.getCursorHotspot());
+ updatePosition(desktop, mCursorBitmap, mClient.getCursorHotspot());
TextureHelper.linkTexture(mTextureDataHandle, cursorBitmap);
diff --git a/remoting/android/java/src/org/chromium/chromoting/cardboard/Desktop.java b/remoting/android/java/src/org/chromium/chromoting/cardboard/Desktop.java
index ded464b..d987f6b 100644
--- a/remoting/android/java/src/org/chromium/chromoting/cardboard/Desktop.java
+++ b/remoting/android/java/src/org/chromium/chromoting/cardboard/Desktop.java
@@ -11,7 +11,7 @@ import android.graphics.Bitmap;
import android.graphics.Point;
import android.opengl.GLES20;
-import org.chromium.chromoting.jni.JniInterface;
+import org.chromium.chromoting.jni.Client;
import java.nio.FloatBuffer;
@@ -61,6 +61,8 @@ public class Desktop {
// Number of vertices passed to glDrawArrays().
private static final int VERTICES_NUMBER = 6;
+ private final Client mClient;
+
private int mVertexShaderHandle;
private int mFragmentShaderHandle;
private int mProgramHandle;
@@ -87,7 +89,8 @@ public class Desktop {
// Lock to allow multithreaded access to mReloadTexture.
private final Object mReloadTextureLock = new Object();
- public Desktop() {
+ public Desktop(Client client) {
+ mClient = client;
mVertexShaderHandle =
ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER);
mFragmentShaderHandle =
@@ -220,7 +223,7 @@ public class Desktop {
}
// TODO(shichengfeng): Record the time desktop drawing takes.
- Bitmap bitmap = JniInterface.getVideoFrame();
+ Bitmap bitmap = mClient.getVideoFrame();
if (bitmap == null) {
// This can happen if the client is connected, but a complete video frame has not yet
@@ -244,4 +247,4 @@ public class Desktop {
mReloadTexture = true;
}
}
-} \ No newline at end of file
+}
diff --git a/remoting/android/java/src/org/chromium/chromoting/cardboard/DesktopActivity.java b/remoting/android/java/src/org/chromium/chromoting/cardboard/DesktopActivity.java
index 1f37082..7800b4e 100644
--- a/remoting/android/java/src/org/chromium/chromoting/cardboard/DesktopActivity.java
+++ b/remoting/android/java/src/org/chromium/chromoting/cardboard/DesktopActivity.java
@@ -16,7 +16,7 @@ import com.google.vrtoolkit.cardboard.CardboardView;
import org.chromium.chromoting.R;
import org.chromium.chromoting.TouchInputHandler;
-import org.chromium.chromoting.jni.JniInterface;
+import org.chromium.chromoting.jni.Client;
import java.util.ArrayList;
@@ -28,6 +28,7 @@ public class DesktopActivity extends CardboardActivity {
// desktop activity.
private boolean mSwitchToDesktopActivity;
+ private Client mClient;
private CardboardRenderer mRenderer;
private SpeechRecognizer mSpeechRecognizer;
@@ -38,9 +39,12 @@ public class DesktopActivity extends CardboardActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.cardboard_desktop);
+
+ mClient = Client.getInstance();
+
mSwitchToDesktopActivity = false;
CardboardView cardboardView = (CardboardView) findViewById(R.id.cardboard_view);
- mRenderer = new CardboardRenderer(this);
+ mRenderer = new CardboardRenderer(this, mClient);
mIsListening = false;
// Associate a CardboardView.StereoRenderer with cardboard view.
@@ -75,9 +79,9 @@ public class DesktopActivity extends CardboardActivity {
} else {
if (mRenderer.isLookingAtDesktop()) {
PointF coordinates = mRenderer.getMouseCoordinates();
- JniInterface.sendMouseEvent((int) coordinates.x, (int) coordinates.y,
+ mClient.sendMouseEvent((int) coordinates.x, (int) coordinates.y,
TouchInputHandler.BUTTON_LEFT, true);
- JniInterface.sendMouseEvent((int) coordinates.x, (int) coordinates.y,
+ mClient.sendMouseEvent((int) coordinates.x, (int) coordinates.y,
TouchInputHandler.BUTTON_LEFT, false);
} else {
if (mRenderer.isLookingFarawayFromDesktop()) {
@@ -92,14 +96,14 @@ public class DesktopActivity extends CardboardActivity {
@Override
protected void onStart() {
super.onStart();
- JniInterface.enableVideoChannel(true);
+ mClient.enableVideoChannel(true);
}
@Override
protected void onPause() {
super.onPause();
if (!mSwitchToDesktopActivity) {
- JniInterface.enableVideoChannel(false);
+ mClient.enableVideoChannel(false);
}
if (mSpeechRecognizer != null) {
mSpeechRecognizer.stopListening();
@@ -109,7 +113,7 @@ public class DesktopActivity extends CardboardActivity {
@Override
protected void onResume() {
super.onResume();
- JniInterface.enableVideoChannel(true);
+ mClient.enableVideoChannel(true);
}
@Override
@@ -118,7 +122,7 @@ public class DesktopActivity extends CardboardActivity {
if (mSwitchToDesktopActivity) {
mSwitchToDesktopActivity = false;
} else {
- JniInterface.enableVideoChannel(false);
+ mClient.enableVideoChannel(false);
}
if (mSpeechRecognizer != null) {
mSpeechRecognizer.stopListening();
@@ -186,7 +190,7 @@ public class DesktopActivity extends CardboardActivity {
ArrayList<String> data =
results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
if (!data.isEmpty()) {
- JniInterface.sendTextEvent(data.get(0));
+ mClient.sendTextEvent(data.get(0));
}
}
diff --git a/remoting/android/java/src/org/chromium/chromoting/jni/Client.java b/remoting/android/java/src/org/chromium/chromoting/jni/Client.java
index 6c67a8c..451a5d4 100644
--- a/remoting/android/java/src/org/chromium/chromoting/jni/Client.java
+++ b/remoting/android/java/src/org/chromium/chromoting/jni/Client.java
@@ -4,7 +4,18 @@
package org.chromium.chromoting.jni;
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.os.Looper;
+
+import org.chromium.base.Log;
import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.SuppressFBWarnings;
+import org.chromium.chromoting.CapabilityManager;
+import org.chromium.chromoting.SessionAuthenticator;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
/**
* Class to manage a client connection to the host. This class controls the lifetime of the
@@ -14,18 +25,379 @@ import org.chromium.base.annotations.JNINamespace;
*/
@JNINamespace("remoting")
public class Client {
+ private static final String TAG = "Chromoting";
+
// Pointer to the C++ object, cast to a |long|.
private long mNativeJniClient;
- public void init() {
+ // The global Client instance (may be null). This needs to be a global singleton so that the
+ // Client can be passed between Activities.
+ private static Client sClient;
+
+ // Called on the UI thread.
+ public Client() {
+ if (sClient != null) {
+ throw new RuntimeException("Client instance already created.");
+ }
+
+ sClient = this;
mNativeJniClient = nativeInit();
}
private native long nativeInit();
+ // Called on the UI thread. Suppress FindBugs warning, since |sClient| is only used on the
+ // UI thread.
+ @SuppressFBWarnings("LI_LAZY_INIT_STATIC")
public void destroy() {
- nativeDestroy(mNativeJniClient);
+ if (sClient != null) {
+ disconnectFromHost();
+ nativeDestroy(mNativeJniClient);
+ sClient = null;
+ }
}
private native void nativeDestroy(long nativeJniClient);
+
+ /** Returns the current Client instance, or null. */
+ public static Client getInstance() {
+ return sClient;
+ }
+
+ /** Used for authentication-related UX during connection. Accessed on the UI thread. */
+ private SessionAuthenticator mAuthenticator;
+
+ /** Whether the native code is attempting a connection. Accessed on the UI thread. */
+ private boolean mConnected;
+
+ /** Notified upon successful connection or disconnection. Accessed on the UI thread. */
+ private ConnectionListener mConnectionListener;
+
+ /**
+ * Callback invoked on the graphics thread to repaint the desktop. Accessed on the UI and
+ * graphics threads.
+ */
+ private Runnable mRedrawCallback;
+
+ /** Bitmap holding a copy of the latest video frame. Accessed on the UI and graphics threads. */
+ private Bitmap mFrameBitmap;
+
+ /** Protects access to {@link mFrameBitmap}. */
+ private final Object mFrameLock = new Object();
+
+ /** Position of cursor hot-spot. Accessed on the graphics thread. */
+ private Point mCursorHotspot = new Point();
+
+ /** Bitmap holding the cursor shape. Accessed on the graphics thread. */
+ private Bitmap mCursorBitmap;
+
+ /** Capability Manager through which capabilities and extensions are handled. */
+ private CapabilityManager mCapabilityManager = new CapabilityManager();
+
+ public CapabilityManager getCapabilityManager() {
+ return mCapabilityManager;
+ }
+
+ /** Returns whether the client is connected. */
+ public boolean isConnected() {
+ return mConnected;
+ }
+
+ /** Attempts to form a connection to the user-selected host. Called on the UI thread. */
+ public void connectToHost(String username, String authToken, String hostJid,
+ String hostId, String hostPubkey, SessionAuthenticator authenticator, String flags,
+ ConnectionListener listener) {
+ disconnectFromHost();
+
+ mConnectionListener = listener;
+ mAuthenticator = authenticator;
+ JniInterface.nativeConnect(username, authToken, hostJid, hostId, hostPubkey,
+ mAuthenticator.getPairingId(hostId), mAuthenticator.getPairingSecret(hostId),
+ mCapabilityManager.getLocalCapabilities(), flags);
+ mConnected = true;
+ }
+
+ /** Severs the connection and cleans up. Called on the UI thread. */
+ public void disconnectFromHost() {
+ if (!mConnected) {
+ return;
+ }
+
+ mConnectionListener.onConnectionState(
+ ConnectionListener.State.CLOSED, ConnectionListener.Error.OK);
+
+ disconnectFromHostWithoutNotification();
+ }
+
+ /** Same as disconnectFromHost() but without notifying the ConnectionListener. */
+ private void disconnectFromHostWithoutNotification() {
+ if (!mConnected) {
+ return;
+ }
+
+ JniInterface.nativeDisconnect();
+ mConnectionListener = null;
+ mConnected = false;
+ mCapabilityManager.onHostDisconnect();
+
+ // Drop the reference to free the Bitmap for GC.
+ synchronized (mFrameLock) {
+ mFrameBitmap = null;
+ }
+ }
+
+ /** Called by native code whenever the connection status changes. Called on the UI thread. */
+ void onConnectionState(int stateCode, int errorCode) {
+ ConnectionListener.State state = ConnectionListener.State.fromValue(stateCode);
+ ConnectionListener.Error error = ConnectionListener.Error.fromValue(errorCode);
+ mConnectionListener.onConnectionState(state, error);
+ if (state == ConnectionListener.State.FAILED || state == ConnectionListener.State.CLOSED) {
+ // Disconnect from the host here, otherwise the next time connectToHost() is called,
+ // it will try to disconnect, triggering an incorrect status notification.
+
+ // TODO(lambroslambrou): Connection state notifications for separate sessions should
+ // go to separate Client instances. Once this is true, we can remove this line and
+ // simplify the disconnectFromHost() code.
+ disconnectFromHostWithoutNotification();
+ }
+ }
+
+ /**
+ * Called by JniInterface (from native code) to prompt the user to enter a PIN. Called on the
+ * UI thread.
+ */
+ void displayAuthenticationPrompt(boolean pairingSupported) {
+ mAuthenticator.displayAuthenticationPrompt(pairingSupported);
+ }
+
+ /**
+ * Called by the SessionAuthenticator after the user enters a PIN.
+ * @param pin The entered PIN.
+ * @param createPair Whether to create a new pairing for this client.
+ * @param deviceName The device name to appear in the pairing registry. Only used if createPair
+ * is true.
+ */
+ public void handleAuthenticationResponse(
+ String pin, boolean createPair, String deviceName) {
+ assert mConnected;
+ JniInterface.nativeAuthenticationResponse(pin, createPair, deviceName);
+ }
+
+ /**
+ * Called by JniInterface (from native code), to save newly-received pairing credentials to
+ * permanent storage. Called on the UI thread.
+ */
+ void commitPairingCredentials(String host, String id, String secret) {
+ mAuthenticator.commitPairingCredentials(host, id, secret);
+ }
+
+ /**
+ * Moves the mouse cursor, possibly while clicking the specified (nonnegative) button. Called
+ * on the UI thread.
+ */
+ public void sendMouseEvent(int x, int y, int whichButton, boolean buttonDown) {
+ if (!mConnected) {
+ return;
+ }
+
+ JniInterface.nativeSendMouseEvent(x, y, whichButton, buttonDown);
+ }
+
+ /** Injects a mouse-wheel event with delta values. Called on the UI thread. */
+ public void sendMouseWheelEvent(int deltaX, int deltaY) {
+ if (!mConnected) {
+ return;
+ }
+
+ JniInterface.nativeSendMouseWheelEvent(deltaX, deltaY);
+ }
+
+ /**
+ * Presses or releases the specified key. Called on the UI thread. If scanCode is not zero then
+ * keyCode is ignored.
+ */
+ public boolean sendKeyEvent(int scanCode, int keyCode, boolean keyDown) {
+ if (!mConnected) {
+ return false;
+ }
+
+ return JniInterface.nativeSendKeyEvent(scanCode, keyCode, keyDown);
+ }
+
+ /** Sends TextEvent to the host. Called on the UI thread. */
+ public void sendTextEvent(String text) {
+ if (!mConnected) {
+ return;
+ }
+
+ JniInterface.nativeSendTextEvent(text);
+ }
+
+ /** Sends an array of TouchEvents to the host. Called on the UI thread. */
+ public void sendTouchEvent(TouchEventData.EventType eventType, TouchEventData[] data) {
+ if (!mConnected) {
+ return;
+ }
+
+ JniInterface.nativeSendTouchEvent(eventType.value(), data);
+ }
+
+ /**
+ * Enables or disables the video channel. Called on the UI thread in response to Activity
+ * lifecycle events.
+ */
+ public void enableVideoChannel(boolean enable) {
+ if (!mConnected) {
+ return;
+ }
+
+ JniInterface.nativeEnableVideoChannel(enable);
+ }
+
+ /**
+ * Sets the redraw callback to the provided functor. Provide a value of null whenever the
+ * window is no longer visible so that we don't continue to draw onto it. Called on the UI
+ * thread.
+ */
+ public void provideRedrawCallback(Runnable redrawCallback) {
+ mRedrawCallback = redrawCallback;
+ }
+
+ /** Forces the native graphics thread to redraw to the canvas. Called on the UI thread. */
+ public boolean redrawGraphics() {
+ if (!mConnected || mRedrawCallback == null) return false;
+
+ JniInterface.nativeScheduleRedraw();
+ return true;
+ }
+
+ /**
+ * Called by JniInterface to perform the redrawing callback requested by
+ * {@link #redrawGraphics}. This is a no-op if the window isn't visible (the callback is null).
+ * Called on the graphics thread.
+ */
+ void redrawGraphicsInternal() {
+ Runnable callback = mRedrawCallback;
+ if (callback != null) {
+ callback.run();
+ }
+ }
+
+ /**
+ * Returns a bitmap of the latest video frame. Called on the native graphics thread when
+ * DesktopView is repainted.
+ */
+ public Bitmap getVideoFrame() {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ Log.w(TAG, "Canvas being redrawn on UI thread");
+ }
+
+ synchronized (mFrameLock) {
+ return mFrameBitmap;
+ }
+ }
+
+ /**
+ * Called by JniInterface (from native code) to set a new video frame. Called on the native
+ * graphics thread when a new frame is allocated.
+ */
+ void setVideoFrame(Bitmap bitmap) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ Log.w(TAG, "Video frame updated on UI thread");
+ }
+
+ synchronized (mFrameLock) {
+ mFrameBitmap = bitmap;
+ }
+ }
+
+ /**
+ * Creates a new Bitmap to hold video frame pixels. Called by JniInterface (from native code),
+ * and the returned Bitmap is referenced by native code which writes the decoded frame pixels
+ * to it.
+ */
+ static Bitmap newBitmap(int width, int height) {
+ return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ }
+
+ /**
+ * Called by JniInterface (from native code) to update the cursor shape. This is called on the
+ * graphics thread when receiving a new cursor shape from the host.
+ */
+ void updateCursorShape(
+ int width, int height, int hotspotX, int hotspotY, ByteBuffer buffer) {
+ mCursorHotspot = new Point(hotspotX, hotspotY);
+
+ int[] data = new int[width * height];
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+ buffer.asIntBuffer().get(data, 0, data.length);
+ mCursorBitmap = Bitmap.createBitmap(data, width, height, Bitmap.Config.ARGB_8888);
+ }
+
+ /** Position of cursor hotspot within cursor image. Called on the graphics thread. */
+ public Point getCursorHotspot() {
+ return mCursorHotspot;
+ }
+
+ /** Returns the current cursor shape. Called on the graphics thread. */
+ public Bitmap getCursorBitmap() {
+ return mCursorBitmap;
+ }
+
+ //
+ // Third Party Authentication
+ //
+
+ /**
+ * Called by JniInterface (from native code), to pop up a third party login page to fetch the
+ * token required for authentication.
+ */
+ void fetchThirdPartyToken(String tokenUrl, String clientId, String scope) {
+ mAuthenticator.fetchThirdPartyToken(tokenUrl, clientId, scope);
+ }
+
+ /**
+ * Called by the SessionAuthenticator to pass the |token| and |sharedSecret| to native code to
+ * continue authentication.
+ */
+ public void onThirdPartyTokenFetched(String token, String sharedSecret) {
+ if (!mConnected) {
+ return;
+ }
+
+ JniInterface.nativeOnThirdPartyTokenFetched(token, sharedSecret);
+ }
+
+ //
+ // Host and Client Capabilities
+ //
+
+ /**
+ * Called by JniInterface (from native code) to set the list of negotiated capabilities between
+ * host and client. Called on the UI thread.
+ */
+ void setCapabilities(String capabilities) {
+ mCapabilityManager.setNegotiatedCapabilities(capabilities);
+ }
+
+ //
+ // Extension Message Handling
+ //
+
+ /**
+ * Called by JniInterface (from native code), to pass on the deconstructed ExtensionMessage to
+ * the app. Called on the UI thread.
+ */
+ void handleExtensionMessage(String type, String data) {
+ mCapabilityManager.onExtensionMessage(type, data);
+ }
+
+ /** Sends an extension message to the Chromoting host. Called on the UI thread. */
+ public void sendExtensionMessage(String type, String data) {
+ if (!mConnected) {
+ return;
+ }
+
+ JniInterface.nativeSendExtensionMessage(type, data);
+ }
}
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 0238702..92a1447 100644
--- a/remoting/android/java/src/org/chromium/chromoting/jni/JniInterface.java
+++ b/remoting/android/java/src/org/chromium/chromoting/jni/JniInterface.java
@@ -6,18 +6,12 @@ package org.chromium.chromoting.jni;
import android.content.Context;
import android.graphics.Bitmap;
-import android.graphics.Point;
-import android.os.Looper;
import org.chromium.base.ContextUtils;
-import org.chromium.base.Log;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
-import org.chromium.chromoting.CapabilityManager;
-import org.chromium.chromoting.SessionAuthenticator;
import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
/**
* Initializes the Chromium remoting library, and provides JNI calls into it.
@@ -25,47 +19,12 @@ import java.nio.ByteOrder;
*/
@JNINamespace("remoting")
public class JniInterface {
- private static final String TAG = "Chromoting";
-
/*
* Library-loading state machine.
*/
/** Whether the library has been loaded. Accessed on the UI thread. */
private static boolean sLoaded = false;
- /** Used for authentication-related UX during connection. Accessed on the UI thread. */
- private static SessionAuthenticator sAuthenticator;
-
- /*
- * Connection-initiating state machine.
- */
- /** Whether the native code is attempting a connection. Accessed on the UI thread. */
- private static boolean sConnected = false;
-
- /** Notified upon successful connection or disconnection. Accessed on the UI thread. */
- private static ConnectionListener sConnectionListener = null;
-
- /**
- * Callback invoked on the graphics thread to repaint the desktop. Accessed on the UI and
- * graphics threads.
- */
- private static Runnable sRedrawCallback = null;
-
- /** Bitmap holding a copy of the latest video frame. Accessed on the UI and graphics threads. */
- private static Bitmap sFrameBitmap = null;
-
- /** Protects access to sFrameBitmap. */
- private static final Object sFrameLock = new Object();
-
- /** Position of cursor hot-spot. Accessed on the graphics thread. */
- private static Point sCursorHotspot = new Point();
-
- /** Bitmap holding the cursor shape. Accessed on the graphics thread. */
- private static Bitmap sCursorBitmap = null;
-
- /** Capability Manager through which capabilities and extensions are handled. */
- private static CapabilityManager sCapabilityManager = CapabilityManager.getInstance();
-
/**
* To be called once from the main Activity. Loads and initializes the native code.
* Called on the UI thread.
@@ -90,203 +49,65 @@ public class JniInterface {
public static native String nativeGetClientId();
public static native String nativeGetClientSecret();
- /** Returns whether the client is connected. */
- public static boolean isConnected() {
- return sConnected;
- }
-
- /** Attempts to form a connection to the user-selected host. Called on the UI thread. */
- public static void connectToHost(String username, String authToken, String hostJid,
- String hostId, String hostPubkey, SessionAuthenticator authenticator, String flags,
- ConnectionListener listener) {
- disconnectFromHost();
-
- sConnectionListener = listener;
- sAuthenticator = authenticator;
- nativeConnect(username, authToken, hostJid, hostId, hostPubkey,
- sAuthenticator.getPairingId(hostId), sAuthenticator.getPairingSecret(hostId),
- sCapabilityManager.getLocalCapabilities(), flags);
- sConnected = true;
- }
-
/** Performs the native portion of the connection. */
- private static native void nativeConnect(String username, String authToken, String hostJid,
+ static native void nativeConnect(String username, String authToken, String hostJid,
String hostId, String hostPubkey, String pairId, String pairSecret,
String capabilities, String flags);
- /** Severs the connection and cleans up. Called on the UI thread. */
- public static void disconnectFromHost() {
- if (!sConnected) {
- return;
- }
-
- sConnectionListener.onConnectionState(
- ConnectionListener.State.CLOSED, ConnectionListener.Error.OK);
-
- disconnectFromHostWithoutNotification();
- }
-
- /** Same as disconnectFromHost() but without notifying the ConnectionListener. */
- private static void disconnectFromHostWithoutNotification() {
- if (!sConnected) {
- return;
- }
-
- nativeDisconnect();
- sConnectionListener = null;
- sConnected = false;
- sCapabilityManager.onHostDisconnect();
-
- // Drop the reference to free the Bitmap for GC.
- synchronized (sFrameLock) {
- sFrameBitmap = null;
- }
- }
-
/** Performs the native portion of the cleanup. */
- private static native void nativeDisconnect();
+ static native void nativeDisconnect();
/** Called by native code whenever the connection status changes. Called on the UI thread. */
@CalledByNative
private static void onConnectionState(int stateCode, int errorCode) {
- ConnectionListener.State state = ConnectionListener.State.fromValue(stateCode);
- ConnectionListener.Error error = ConnectionListener.Error.fromValue(errorCode);
- sConnectionListener.onConnectionState(state, error);
- if (state == ConnectionListener.State.FAILED || state == ConnectionListener.State.CLOSED) {
- // Disconnect from the host here, otherwise the next time connectToHost() is called,
- // it will try to disconnect, triggering an incorrect status notification.
- disconnectFromHostWithoutNotification();
+ if (Client.getInstance() != null) {
+ Client.getInstance().onConnectionState(stateCode, errorCode);
}
}
/** Prompts the user to enter a PIN. Called on the UI thread. */
@CalledByNative
private static void displayAuthenticationPrompt(boolean pairingSupported) {
- sAuthenticator.displayAuthenticationPrompt(pairingSupported);
- }
-
- /**
- * Performs the native response to the user's PIN.
- * @param pin The entered PIN.
- * @param createPair Whether to create a new pairing for this client.
- * @param deviceName The device name to appear in the pairing registry. Only used if createPair
- * is true.
- */
- public static void handleAuthenticationResponse(
- String pin, boolean createPair, String deviceName) {
- assert sConnected;
- nativeAuthenticationResponse(pin, createPair, deviceName);
+ if (Client.getInstance() != null) {
+ Client.getInstance().displayAuthenticationPrompt(pairingSupported);
+ }
}
- /** Native implementation of handleAuthenticationResponse(). */
- private static native void nativeAuthenticationResponse(
+ /** Native implementation of Client.handleAuthenticationResponse(). */
+ static native void nativeAuthenticationResponse(
String pin, boolean createPair, String deviceName);
/** Saves newly-received pairing credentials to permanent storage. Called on the UI thread. */
@CalledByNative
private static void commitPairingCredentials(String host, String id, String secret) {
- sAuthenticator.commitPairingCredentials(host, id, secret);
- }
-
- /**
- * Moves the mouse cursor, possibly while clicking the specified (nonnegative) button. Called
- * on the UI thread.
- */
- public static void sendMouseEvent(int x, int y, int whichButton, boolean buttonDown) {
- if (!sConnected) {
- return;
+ if (Client.getInstance() != null) {
+ Client.getInstance().commitPairingCredentials(host, id, secret);
}
-
- nativeSendMouseEvent(x, y, whichButton, buttonDown);
}
/** Passes mouse information to the native handling code. */
- private static native void nativeSendMouseEvent(
+ static native void nativeSendMouseEvent(
int x, int y, int whichButton, boolean buttonDown);
- /** Injects a mouse-wheel event with delta values. Called on the UI thread. */
- public static void sendMouseWheelEvent(int deltaX, int deltaY) {
- if (!sConnected) {
- return;
- }
-
- nativeSendMouseWheelEvent(deltaX, deltaY);
- }
-
/** Passes mouse-wheel information to the native handling code. */
- private static native void nativeSendMouseWheelEvent(int deltaX, int deltaY);
-
- /**
- * Presses or releases the specified (nonnegative) key. Called on the UI thread. If scanCode
- * is not zero then keyCode is ignored.
- */
- public static boolean sendKeyEvent(int scanCode, int keyCode, boolean keyDown) {
- if (!sConnected) {
- return false;
- }
-
- return nativeSendKeyEvent(scanCode, keyCode, keyDown);
- }
+ static native void nativeSendMouseWheelEvent(int deltaX, int deltaY);
/**
* Passes key press information to the native handling code.
*/
- private static native boolean nativeSendKeyEvent(int scanCode, int keyCode, boolean keyDown);
-
- /** Sends TextEvent to the host. Called on the UI thread. */
- public static void sendTextEvent(String text) {
- if (!sConnected) {
- return;
- }
-
- nativeSendTextEvent(text);
- }
+ static native boolean nativeSendKeyEvent(int scanCode, int keyCode, boolean keyDown);
/** Passes text event information to the native handling code. */
- private static native void nativeSendTextEvent(String text);
-
- /** Sends an array of TouchEvents to the host. Called on the UI thread. */
- public static void sendTouchEvent(TouchEventData.EventType eventType, TouchEventData[] data) {
- nativeSendTouchEvent(eventType.value(), data);
- }
+ static native void nativeSendTextEvent(String text);
/** Passes touch event information to the native handling code. */
- private static native void nativeSendTouchEvent(int eventType, TouchEventData[] data);
+ static native void nativeSendTouchEvent(int eventType, TouchEventData[] data);
- /**
- * Enables or disables the video channel. Called on the UI thread in response to Activity
- * lifecycle events.
- */
- public static void enableVideoChannel(boolean enable) {
- if (!sConnected) {
- return;
- }
-
- nativeEnableVideoChannel(enable);
- }
-
- /** Native implementation of enableVideoChannel() */
- private static native void nativeEnableVideoChannel(boolean enable);
-
- /**
- * Sets the redraw callback to the provided functor. Provide a value of null whenever the
- * window is no longer visible so that we don't continue to draw onto it. Called on the UI
- * thread.
- */
- public static void provideRedrawCallback(Runnable redrawCallback) {
- sRedrawCallback = redrawCallback;
- }
-
- /** Forces the native graphics thread to redraw to the canvas. Called on the UI thread. */
- public static boolean redrawGraphics() {
- if (!sConnected || sRedrawCallback == null) return false;
-
- nativeScheduleRedraw();
- return true;
- }
+ /** Native implementation of Client.enableVideoChannel() */
+ static native void nativeEnableVideoChannel(boolean enable);
/** Schedules a redraw on the native graphics thread. */
- private static native void nativeScheduleRedraw();
+ static native void nativeScheduleRedraw();
/**
* Performs the redrawing callback. This is a no-op if the window isn't visible. Called on the
@@ -294,23 +115,9 @@ public class JniInterface {
*/
@CalledByNative
private static void redrawGraphicsInternal() {
- Runnable callback = sRedrawCallback;
- if (callback != null) {
- callback.run();
- }
- }
-
- /**
- * Returns a bitmap of the latest video frame. Called on the native graphics thread when
- * DesktopView is repainted.
- */
- public static Bitmap getVideoFrame() {
- if (Looper.myLooper() == Looper.getMainLooper()) {
- Log.w(TAG, "Canvas being redrawn on UI thread");
- }
-
- synchronized (sFrameLock) {
- return sFrameBitmap;
+ Client client = Client.getInstance();
+ if (client != null) {
+ client.redrawGraphicsInternal();
}
}
@@ -319,12 +126,9 @@ public class JniInterface {
*/
@CalledByNative
private static void setVideoFrame(Bitmap bitmap) {
- if (Looper.myLooper() == Looper.getMainLooper()) {
- Log.w(TAG, "Video frame updated on UI thread");
- }
-
- synchronized (sFrameLock) {
- sFrameBitmap = bitmap;
+ Client client = Client.getInstance();
+ if (client != null) {
+ client.setVideoFrame(bitmap);
}
}
@@ -334,7 +138,7 @@ public class JniInterface {
*/
@CalledByNative
private static Bitmap newBitmap(int width, int height) {
- return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ return Client.newBitmap(width, height);
}
/**
@@ -342,24 +146,12 @@ public class JniInterface {
* shape from the host.
*/
@CalledByNative
- public static void updateCursorShape(
+ private static void updateCursorShape(
int width, int height, int hotspotX, int hotspotY, ByteBuffer buffer) {
- sCursorHotspot = new Point(hotspotX, hotspotY);
-
- int[] data = new int[width * height];
- buffer.order(ByteOrder.LITTLE_ENDIAN);
- buffer.asIntBuffer().get(data, 0, data.length);
- sCursorBitmap = Bitmap.createBitmap(data, width, height, Bitmap.Config.ARGB_8888);
- }
-
- /** Position of cursor hotspot within cursor image. Called on the graphics thread. */
- public static Point getCursorHotspot() {
- return sCursorHotspot;
- }
-
- /** Returns the current cursor shape. Called on the graphics thread. */
- public static Bitmap getCursorBitmap() {
- return sCursorBitmap;
+ Client client = Client.getInstance();
+ if (client != null) {
+ client.updateCursorShape(width, height, hotspotX, hotspotY, buffer);
+ }
}
//
@@ -368,23 +160,14 @@ public class JniInterface {
/** Pops up a third party login page to fetch the token required for authentication. */
@CalledByNative
- public static void fetchThirdPartyToken(String tokenUrl, String clientId, String scope) {
- sAuthenticator.fetchThirdPartyToken(tokenUrl, clientId, scope);
- }
-
- /**
- * Notify the native code to continue authentication with the |token| and the |sharedSecret|.
- */
- public static void onThirdPartyTokenFetched(String token, String sharedSecret) {
- if (!sConnected) {
- return;
+ private static void fetchThirdPartyToken(String tokenUrl, String clientId, String scope) {
+ if (Client.getInstance() != null) {
+ Client.getInstance().fetchThirdPartyToken(tokenUrl, clientId, scope);
}
-
- nativeOnThirdPartyTokenFetched(token, sharedSecret);
}
/** Passes authentication data to the native handling code. */
- private static native void nativeOnThirdPartyTokenFetched(String token, String sharedSecret);
+ static native void nativeOnThirdPartyTokenFetched(String token, String sharedSecret);
//
// Host and Client Capabilities
@@ -392,8 +175,10 @@ public class JniInterface {
/** Set the list of negotiated capabilities between host and client. Called on the UI thread. */
@CalledByNative
- public static void setCapabilities(String capabilities) {
- sCapabilityManager.setNegotiatedCapabilities(capabilities);
+ private static void setCapabilities(String capabilities) {
+ if (Client.getInstance() != null) {
+ Client.getInstance().setCapabilities(capabilities);
+ }
}
//
@@ -402,18 +187,12 @@ public class JniInterface {
/** Passes on the deconstructed ExtensionMessage to the app. Called on the UI thread. */
@CalledByNative
- public static void handleExtensionMessage(String type, String data) {
- sCapabilityManager.onExtensionMessage(type, data);
- }
-
- /** Sends an extension message to the Chromoting host. Called on the UI thread. */
- public static void sendExtensionMessage(String type, String data) {
- if (!sConnected) {
- return;
+ private static void handleExtensionMessage(String type, String data) {
+ if (Client.getInstance() != null) {
+ Client.getInstance().handleExtensionMessage(type, data);
}
-
- nativeSendExtensionMessage(type, data);
}
- private static native void nativeSendExtensionMessage(String type, String data);
+ /** Passes extension message to the native code. */
+ static native void nativeSendExtensionMessage(String type, String data);
}
diff --git a/remoting/android/javatests/src/org/chromium/chromoting/TouchInputStrategyTest.java b/remoting/android/javatests/src/org/chromium/chromoting/TouchInputStrategyTest.java
index c7774f7..3dea369 100644
--- a/remoting/android/javatests/src/org/chromium/chromoting/TouchInputStrategyTest.java
+++ b/remoting/android/javatests/src/org/chromium/chromoting/TouchInputStrategyTest.java
@@ -197,7 +197,10 @@ public class TouchInputStrategyTest extends InstrumentationTestCase {
public void setUp() {
mRenderData = new RenderData();
mRemoteInputInjector = new MockRemoteInputInjector();
- mInputStrategy = new TouchInputStrategy(mRenderData);
+
+ // TODO(lambroslambrou): Provide a mock Client implementation that doesn't call out to JNI,
+ // and mock the Client methods instead of using MockRemoteInputInjector here.
+ mInputStrategy = new TouchInputStrategy(mRenderData, null);
mInputStrategy.setRemoteInputInjectorForTest(mRemoteInputInjector);
mEventGenerator = new TouchEventGenerator();