diff options
-rw-r--r-- | remoting/android/java/AndroidManifest.xml | 5 | ||||
-rw-r--r-- | remoting/android/java/src/org/chromium/chromoting/Chromoting.java | 87 | ||||
-rw-r--r-- | remoting/android/java/src/org/chromium/chromoting/Desktop.java | 6 | ||||
-rw-r--r-- | remoting/android/java/src/org/chromium/chromoting/DesktopView.java | 25 | ||||
-rw-r--r-- | remoting/client/jni/android_keymap.cc | 7 | ||||
-rw-r--r-- | remoting/client/jni/chromoting_jni_instance.cc | 3 | ||||
-rw-r--r-- | remoting/client/jni/chromoting_jni_runtime.cc | 21 | ||||
-rw-r--r-- | remoting/client/jni/chromoting_jni_runtime.h | 4 | ||||
-rw-r--r-- | remoting/resources/menu/chromoting_actionbar.xml | 4 | ||||
-rw-r--r-- | remoting/resources/strings.xml | 2 |
10 files changed, 124 insertions, 40 deletions
diff --git a/remoting/android/java/AndroidManifest.xml b/remoting/android/java/AndroidManifest.xml index ff27b57..c43fa1d 100644 --- a/remoting/android/java/AndroidManifest.xml +++ b/remoting/android/java/AndroidManifest.xml @@ -1,12 +1,13 @@ <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.chromium.chromoting" - android:versionCode="9" - android:versionName="0.09"> + android:versionCode="10" + android:versionName="0.10"> <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="14"/> <uses-permission android:name="android.permission.GET_ACCOUNTS"/> <uses-permission android:name="android.permission.INTERNET"/> + <uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/> <uses-permission android:name="android.permission.USE_CREDENTIALS"/> <application android:label="@string/app_name" android:icon="@drawable/chromoting128"> diff --git a/remoting/android/java/src/org/chromium/chromoting/Chromoting.java b/remoting/android/java/src/org/chromium/chromoting/Chromoting.java index cfcbf49..e322550 100644 --- a/remoting/android/java/src/org/chromium/chromoting/Chromoting.java +++ b/remoting/android/java/src/org/chromium/chromoting/Chromoting.java @@ -69,6 +69,9 @@ public class Chromoting extends Activity { /** List of hosts. */ private JSONArray mHosts; + /** Refresh button. */ + private MenuItem mRefreshButton; + /** Account switcher. */ private MenuItem mAccountSwitcher; @@ -139,8 +142,21 @@ public class Chromoting extends Activity { @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.chromoting_actionbar, menu); + mRefreshButton = menu.findItem(R.id.actionbar_directoryrefresh); mAccountSwitcher = menu.findItem(R.id.actionbar_accountswitcher); - if (mAccount != null) { + + Account[] usableAccounts = AccountManager.get(this).getAccountsByType(ACCOUNT_TYPE); + if (usableAccounts.length == 1 && mAccount.equals(usableAccounts[0])) { + // If we're using the only available account, don't offer account switching. + // (If there are *no* accounts available, clicking this allows you to add a new one.) + mAccountSwitcher.setEnabled(false); + } + + if (mAccount == null) { + // If no account has been chosen, don't allow the user to refresh the listing. + mRefreshButton.setEnabled(false); + } else { + // If the user has picked an account, show its name directly on the account switcher. mAccountSwitcher.setTitle(mAccount.name); } @@ -150,17 +166,25 @@ public class Chromoting extends Activity { /** Called whenever an action bar button is pressed. */ @Override public boolean onOptionsItemSelected(MenuItem item) { - // The only button is the account switcher, so defer to the android accounts system now. - AccountManager.get(this).getAuthTokenByFeatures( - ACCOUNT_TYPE, - TOKEN_SCOPE, - null, - this, - null, - null, - new HostListDirectoryGrabber(this), - mNetwork - ); + if (item == mAccountSwitcher) { + // The account switcher triggers a listing of all available accounts. + AccountManager.get(this).getAuthTokenByFeatures( + ACCOUNT_TYPE, + TOKEN_SCOPE, + null, + this, + null, + null, + new HostListDirectoryGrabber(this), + mNetwork + ); + } + else { + // The refresh button simply makes use of the currently-chosen account. + AccountManager.get(this).getAuthToken(mAccount, TOKEN_SCOPE, null, this, + new HostListDirectoryGrabber(this), mNetwork); + } + return true; } @@ -235,23 +259,31 @@ public class Chromoting extends Activity { } else if (ex instanceof AuthenticatorException) { explanation = getString(R.string.error_no_accounts); } else if (ex instanceof IOException) { - if (!mAlreadyTried) { // This was our first connection attempt. - Log.w("auth", "Unable to authenticate with (expired?) token"); + if (!mAlreadyTried) { + // This was our first connection attempt. - // Ask system to renew the auth token in case it expired. - AccountManager authenticator = AccountManager.get(mUi); synchronized (mUi) { - authenticator.invalidateAuthToken(mAccount.type, mToken); - mToken = null; - Log.i("auth", "Requesting auth token renewal"); - authenticator.getAuthToken( - mAccount, TOKEN_SCOPE, null, mUi, this, mNetwork); + if (mAccount != null) { + // We got an account, but couldn't log into it. We'll retry in case + // the system's cached authentication token had already expired. + AccountManager authenticator = AccountManager.get(mUi); + mAlreadyTried = true; + + Log.w("auth", "Requesting renewal of rejected auth token"); + authenticator.invalidateAuthToken(mAccount.type, mToken); + mToken = null; + authenticator.getAuthToken( + mAccount, TOKEN_SCOPE, null, mUi, this, mNetwork); + + // We're not in an error state *yet*. + return; + } } - // We're not in an error state *yet*. - mAlreadyTried = true; - return; - } else { // Authentication truly failed. + // We didn't even get an account, so the auth server is likely unreachable. + explanation = getString(R.string.error_bad_connection); + } else { + // Authentication truly failed. Log.e("auth", "Fresh auth token was also rejected"); explanation = getString(R.string.error_auth_failed); } @@ -287,7 +319,10 @@ public class Chromoting extends Activity { @Override public void run() { synchronized (mUi) { - mAccountSwitcher.setTitle(mAccount.name); + mRefreshButton.setEnabled(mAccount != null); + if (mAccount != null) { + mAccountSwitcher.setTitle(mAccount.name); + } } if (mHosts == null) { diff --git a/remoting/android/java/src/org/chromium/chromoting/Desktop.java b/remoting/android/java/src/org/chromium/chromoting/Desktop.java index 3d59961..8a9917d 100644 --- a/remoting/android/java/src/org/chromium/chromoting/Desktop.java +++ b/remoting/android/java/src/org/chromium/chromoting/Desktop.java @@ -96,12 +96,6 @@ public class Desktop extends Activity { default: // We try to send all other key codes to the host directly. JniInterface.keyboardAction(event.getKeyCode(), depressed); - - if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER || - event.getKeyCode() == KeyEvent.KEYCODE_NUMPAD_ENTER) { - // We stop this key from propagating to prevent the keyboard from closing. - return true; - } } return super.dispatchKeyEvent(event); diff --git a/remoting/android/java/src/org/chromium/chromoting/DesktopView.java b/remoting/android/java/src/org/chromium/chromoting/DesktopView.java index 1e18f91..b1eac42 100644 --- a/remoting/android/java/src/org/chromium/chromoting/DesktopView.java +++ b/remoting/android/java/src/org/chromium/chromoting/DesktopView.java @@ -13,12 +13,15 @@ import android.graphics.Matrix; import android.graphics.Paint; import android.os.Bundle; import android.os.Looper; +import android.text.InputType; import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.SurfaceHolder; import android.view.SurfaceView; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; import org.chromium.chromoting.jni.JniInterface; @@ -81,6 +84,10 @@ public class DesktopView extends SurfaceView implements Runnable, SurfaceHolder. public DesktopView(Activity context) { super(context); + + // Give this view keyboard focus, allowing us to customize the soft keyboard's settings. + setFocusableInTouchMode(true); + mActionBar = context.getActionBar(); getHolder().addCallback(this); @@ -265,6 +272,24 @@ public class DesktopView extends SurfaceView implements Runnable, SurfaceHolder. JniInterface.provideRedrawCallback(null); } + /** Called when a software keyboard is requested, and specifies its options. */ + @Override + public InputConnection onCreateInputConnection(EditorInfo outAttrs) { + // Disables rich input support and instead requests simple key events. + outAttrs.inputType = InputType.TYPE_NULL; + + // Prevents most third-party IMEs from ignoring our Activity's adjustResize preference. + outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_FULLSCREEN; + + // Ensures that keyboards will not decide to hide the remote desktop on small displays. + outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI; + + // Stops software keyboards from closing as soon as the enter key is pressed. + outAttrs.imeOptions |= EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION; + + return null; + } + /** Called when a mouse action is made. */ private void handleMouseMovement(float x, float y, int button, boolean pressed) { float[] coordinates = {x, y}; diff --git a/remoting/client/jni/android_keymap.cc b/remoting/client/jni/android_keymap.cc index 743e15e..45d67c4 100644 --- a/remoting/client/jni/android_keymap.cc +++ b/remoting/client/jni/android_keymap.cc @@ -199,12 +199,15 @@ const uint32 usb_keycodes[] = { 0x0700b7, // NUMPAD_RIGHT_PAREN }; -} +} // namespace namespace remoting { uint32 AndroidKeycodeToUsbKeycode(size_t android) { - DCHECK_LT(android, sizeof (usb_keycodes) / sizeof (uint32)); + if (android >= sizeof (usb_keycodes) / sizeof (uint32)) { + LOG(WARNING) << "Attempted to decode out-of-range Android keycode"; + return 0; + } return usb_keycodes[android]; } diff --git a/remoting/client/jni/chromoting_jni_instance.cc b/remoting/client/jni/chromoting_jni_instance.cc index 3e11b2fa..b68aab9 100644 --- a/remoting/client/jni/chromoting_jni_instance.cc +++ b/remoting/client/jni/chromoting_jni_instance.cc @@ -132,8 +132,7 @@ void ChromotingJniInstance::PerformKeyboardAction(int key_code, bool key_down) { action.set_usb_keycode(usb_code); action.set_pressed(key_down); connection_->input_stub()->InjectKeyEvent(action); - } - else { + } else { LOG(WARNING) << "Ignoring unknown keycode: " << key_code; } } diff --git a/remoting/client/jni/chromoting_jni_runtime.cc b/remoting/client/jni/chromoting_jni_runtime.cc index 8fa9018..8d51a83 100644 --- a/remoting/client/jni/chromoting_jni_runtime.cc +++ b/remoting/client/jni/chromoting_jni_runtime.cc @@ -7,6 +7,7 @@ #include "base/android/base_jni_registrar.h" #include "base/android/jni_android.h" #include "base/memory/singleton.h" +#include "base/synchronization/waitable_event.h" #include "media/base/yuv_convert.h" #include "net/android/net_jni_registrar.h" #include "remoting/base/url_request_context.h" @@ -68,8 +69,19 @@ ChromotingJniRuntime::~ChromotingJniRuntime() { JNIEnv* env = base::android::AttachCurrentThread(); env->DeleteGlobalRef(class_); - // TODO(solb): Detach all threads from JVM here. - // crbug.com/259594 + + base::WaitableEvent done_event(false, false); + network_task_runner_->PostTask(FROM_HERE, base::Bind( + &ChromotingJniRuntime::DetachFromVmAndSignal, + base::Unretained(this), + &done_event)); + done_event.Wait(); + display_task_runner_->PostTask(FROM_HERE, base::Bind( + &ChromotingJniRuntime::DetachFromVmAndSignal, + base::Unretained(this), + &done_event)); + done_event.Wait(); + base::android::DetachFromVM(); } void ChromotingJniRuntime::ConnectToHost(const char* username, @@ -178,4 +190,9 @@ void ChromotingJniRuntime::RedrawCanvas() { env->GetStaticMethodID(class_, "redrawGraphicsInternal", "()V")); } +void ChromotingJniRuntime::DetachFromVmAndSignal(base::WaitableEvent* waiter) { + base::android::DetachFromVM(); + waiter->Signal(); +} + } // namespace remoting diff --git a/remoting/client/jni/chromoting_jni_runtime.h b/remoting/client/jni/chromoting_jni_runtime.h index 1a6952b..93f933f 100644 --- a/remoting/client/jni/chromoting_jni_runtime.h +++ b/remoting/client/jni/chromoting_jni_runtime.h @@ -6,6 +6,7 @@ #define REMOTING_CLIENT_JNI_CHROMOTING_JNI_RUNTIME_H_ #include <jni.h> +#include <string> #include "base/at_exit.h" #include "net/url_request/url_request_context_getter.h" @@ -94,6 +95,9 @@ class ChromotingJniRuntime { // cross-thread calls on the class. virtual ~ChromotingJniRuntime(); + // Detaches JVM from the current thread, then signals. Doesn't own |waiter|. + void DetachFromVmAndSignal(base::WaitableEvent* waiter); + // Reference to the Java class into which we make JNI calls. jclass class_; diff --git a/remoting/resources/menu/chromoting_actionbar.xml b/remoting/resources/menu/chromoting_actionbar.xml index 1507dd4..e602a36 100644 --- a/remoting/resources/menu/chromoting_actionbar.xml +++ b/remoting/resources/menu/chromoting_actionbar.xml @@ -1,6 +1,10 @@ <?xml version="1.0" encoding="utf-8"?> <!--Action bar buttons for the Android app's host listing--> <menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/actionbar_directoryrefresh" + android:title="@string/actionbar_directoryrefresh" + android:icon="@android:drawable/ic_popup_sync" + android:showAsAction="ifRoom"/> <item android:id="@+id/actionbar_accountswitcher" android:title="@string/actionbar_accountswitcher" android:icon="@android:drawable/ic_menu_more" diff --git a/remoting/resources/strings.xml b/remoting/resources/strings.xml index 949a2aa..a2c2095 100644 --- a/remoting/resources/strings.xml +++ b/remoting/resources/strings.xml @@ -18,6 +18,7 @@ <string name="pin_entry_cancel">Cancel</string> <!--Action bar buttons--> + <string name="actionbar_directoryrefresh">Refresh</string> <string name="actionbar_accountswitcher">Accounts</string> <string name="actionbar_hide">Hide</string> <string name="actionbar_keyboard">Keyboard</string> @@ -31,6 +32,7 @@ <string name="error_auth_canceled">Authentication prompt canceled by user</string> <string name="error_no_accounts">Device not linked to any Google accounts</string> <string name="error_auth_failed">Authentication with specified account failed</string> + <string name="error_bad_connection">No network connection</string> <string name="error_cataloging_hosts">Unable to display host list</string> <string name="error_displaying_host">Unable to display host entry</string> <string name="error_unexpected_response">Account has no remote desktop hosts registered</string> |