summaryrefslogtreecommitdiff
path: root/core/java/android/inputmethodservice/InputMethodService.java
diff options
context:
space:
mode:
authorYohei Yukawa <yukawa@google.com>2015-12-21 13:30:44 -0800
committerYohei Yukawa <yukawa@google.com>2015-12-21 13:30:44 -0800
commit7b739a802cb7d97460a0bf8a1e9774efe96fa552 (patch)
tree587bac6cfbfe687aae4a3ed563163ef0b2c56327 /core/java/android/inputmethodservice/InputMethodService.java
parentfc5151106fd3484484e3cf98254e1674dfa5be1e (diff)
Stop lying about hardware keyboard presence.
Here is a brief history of "Use Physical Keyboard", or "Show input method", or SHOW_IME_WITH_HARD_KEYBOARD. In Honeycomb, we introduced a mechanism which allows users to temporary disconnect the physical keyboard connection from a UI [1]. That state was temporary and non-persistent, but it was implemented to have system-wide effect. Whenever the user turned on that temporarily disconnected state, not only IMEs but also other processes would be notified that Configuration#keyboard was changed to KEYBOARD_NOKEYS. The UI to do that was available only in tablet UI at that point and nothing changed in phones. In JellyBean, we decided to port that UI to phone, and the "Use Physical Keyboard" toggle switch found its new home on the IME switcher dialog [2]. In Lollipop, we revised the wording for the toggle switch on the IME switcher dialog. Now it's labeled as "Show input method" [3]. In Lollipop, we also decided to make the settings of "Show input method" to be persistent as @hide entry in the secure settings [4]. [1] 2992ea782fa61780d8e0de7a36a2a84622f8694b I136087ca4f726d0068d5983d7d3686787ba60c55 [2] cb598b11ea2c012bd15e7e7f44a3006f752c0b81 (Ica768083f95c33dc1e494a28ba7d8b6eb989b0ef) [3] be11f3a5cea67c68a08c8da9ce85b3418501060a (I9c8a7eb98b5277f1d09cc19fa7402e9b4cf51d92) [4] b64e56aad3ffb267aec44308f57641a4c91caa83 (I2237ded850a0d4ab43ca441d0b7df13e0958e630) Now, we have hard time making everyone happy. Having an easy way to bring software keyboard back is probably still a valid use case, but after several wording changes it is no longer obvious why onConfigurationChanged() event is sent to all the processes as a consequence of tapping that toggle. In order to address above confusions, this CL removes the concept of "temporarily disconnected physical keyboard" but tries to implement "Show input method" functionality in InputMethodManagerService and InputMethodService to keep the affordance that has been offered in the IME switcher dialog. As long as the IME overrides InputMethodService#onEvaluateInputViewShown() as follows, the behavior of "Show input method" toggle remains the same except for processes including IMEs no longer receive onConfigurationChanged() event with faked Configuration#keyboard value. @Override public boolean onEvaluateInputViewShown() { // If the default implementation returns true, recommend honoring // it. if (super.onEvaluateInputViewShown()) { return true; } // Implement the logic that is specific to your IME here. return myOnEvaluateInputViewShown(); } Bug: 22517687 Change-Id: Id4d332e3909590c68345e10e1f2e18650efb2eb7
Diffstat (limited to 'core/java/android/inputmethodservice/InputMethodService.java')
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java128
1 files changed, 120 insertions, 8 deletions
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 8ab8991c7a42..f642f08df76e 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -19,16 +19,22 @@ package android.inputmethodservice;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import android.annotation.CallSuper;
import android.annotation.DrawableRes;
+import android.annotation.IntDef;
+import android.annotation.MainThread;
import android.app.ActivityManager;
import android.app.Dialog;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.database.ContentObserver;
import android.graphics.Rect;
import android.graphics.Region;
+import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
import android.os.IBinder;
import android.os.ResultReceiver;
import android.os.SystemClock;
@@ -68,6 +74,8 @@ import android.widget.LinearLayout;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* InputMethodService provides a standard implementation of an InputMethod,
@@ -634,6 +642,97 @@ public class InputMethodService extends AbstractInputMethodService {
}
/**
+ * A {@link ContentObserver} to monitor {@link Settings.Secure#SHOW_IME_WITH_HARD_KEYBOARD}.
+ *
+ * <p>Note that {@link Settings.Secure#SHOW_IME_WITH_HARD_KEYBOARD} is not a public API.
+ * Basically this functionality still needs to be considered as implementation details.</p>
+ */
+ @MainThread
+ private static final class SettingsObserver extends ContentObserver {
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ ShowImeWithHardKeyboardType.UNKNOWN,
+ ShowImeWithHardKeyboardType.FALSE,
+ ShowImeWithHardKeyboardType.TRUE,
+ })
+ private @interface ShowImeWithHardKeyboardType {
+ int UNKNOWN = 0;
+ int FALSE = 1;
+ int TRUE = 2;
+ }
+ @ShowImeWithHardKeyboardType
+ private int mShowImeWithHardKeyboard = ShowImeWithHardKeyboardType.UNKNOWN;
+
+ private final InputMethodService mService;
+
+ private SettingsObserver(InputMethodService service) {
+ super(new Handler(service.getMainLooper()));
+ mService = service;
+ }
+
+ /**
+ * A factory method that internally enforces two-phase initialization to make sure that the
+ * object reference will not be escaped until the object is properly constructed.
+ *
+ * <p>NOTE: Currently {@link SettingsObserver} is accessed only from main thread. Hence
+ * this enforcement of two-phase initialization may be unnecessary at the moment.</p>
+ *
+ * @param service {@link InputMethodService} that needs to receive the callback.
+ * @return {@link SettingsObserver} that is already registered to
+ * {@link android.content.ContentResolver}. The caller must call
+ * {@link SettingsObserver#unregister()}.
+ */
+ public static SettingsObserver createAndRegister(InputMethodService service) {
+ final SettingsObserver observer = new SettingsObserver(service);
+ // The observer is properly constructed. Let's start accepting the event.
+ service.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD),
+ false, observer);
+ return observer;
+ }
+
+ void unregister() {
+ mService.getContentResolver().unregisterContentObserver(this);
+ }
+
+ private boolean shouldShowImeWithHardKeyboard() {
+ // Lazily initialize as needed.
+ if (mShowImeWithHardKeyboard == ShowImeWithHardKeyboardType.UNKNOWN) {
+ mShowImeWithHardKeyboard = Settings.Secure.getInt(mService.getContentResolver(),
+ Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0 ?
+ ShowImeWithHardKeyboardType.TRUE : ShowImeWithHardKeyboardType.FALSE;
+ }
+ switch (mShowImeWithHardKeyboard) {
+ case ShowImeWithHardKeyboardType.TRUE:
+ return true;
+ case ShowImeWithHardKeyboardType.FALSE:
+ return false;
+ default:
+ Log.e(TAG, "Unexpected mShowImeWithHardKeyboard=" + mShowImeWithHardKeyboard);
+ return false;
+ }
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ final Uri showImeWithHardKeyboardUri =
+ Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
+ if (showImeWithHardKeyboardUri.equals(uri)) {
+ mShowImeWithHardKeyboard = Settings.Secure.getInt(mService.getContentResolver(),
+ Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0 ?
+ ShowImeWithHardKeyboardType.TRUE : ShowImeWithHardKeyboardType.FALSE;
+ mService.updateInputViewShown();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "SettingsObserver{mShowImeWithHardKeyboard=" + mShowImeWithHardKeyboard + "}";
+ }
+ }
+ private SettingsObserver mSettingsObserver;
+
+ /**
* You can call this to customize the theme used by your IME's window.
* This theme should typically be one that derives from
* {@link android.R.style#Theme_InputMethod}, which is the default theme
@@ -682,6 +781,7 @@ public class InputMethodService extends AbstractInputMethodService {
super.setTheme(mTheme);
super.onCreate();
mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
+ mSettingsObserver = SettingsObserver.createAndRegister(this);
// If the previous IME has occupied non-empty inset in the screen, we need to decide whether
// we continue to use the same size of the inset or update it
mShouldClearInsetOfPreviousIme = (mImm.getInputMethodWindowVisibleHeight() > 0);
@@ -764,6 +864,10 @@ public class InputMethodService extends AbstractInputMethodService {
mWindow.getWindow().setWindowAnimations(0);
mWindow.dismiss();
}
+ if (mSettingsObserver != null) {
+ mSettingsObserver.unregister();
+ mSettingsObserver = null;
+ }
}
/**
@@ -1140,21 +1244,28 @@ public class InputMethodService extends AbstractInputMethodService {
public boolean isInputViewShown() {
return mIsInputViewShown && mWindowVisible;
}
-
+
/**
- * Override this to control when the soft input area should be shown to
- * the user. The default implementation only shows the input view when
- * there is no hard keyboard or the keyboard is hidden. If you change what
- * this returns, you will need to call {@link #updateInputViewShown()}
- * yourself whenever the returned value may have changed to have it
- * re-evaluated and applied.
+ * Override this to control when the soft input area should be shown to the user. The default
+ * implementation returns {@code false} when there is no hard keyboard or the keyboard is hidden
+ * unless the user shows an intention to use software keyboard. If you change what this
+ * returns, you will need to call {@link #updateInputViewShown()} yourself whenever the returned
+ * value may have changed to have it re-evaluated and applied.
+ *
+ * <p>When you override this method, it is recommended to call
+ * {@code super.onEvaluateInputViewShown()} and return {@code true} when {@code true} is
+ * returned.</p>
*/
+ @CallSuper
public boolean onEvaluateInputViewShown() {
+ if (mSettingsObserver.shouldShowImeWithHardKeyboard()) {
+ return true;
+ }
Configuration config = getResources().getConfiguration();
return config.keyboard == Configuration.KEYBOARD_NOKEYS
|| config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES;
}
-
+
/**
* Controls the visibility of the candidates display area. By default
* it is hidden.
@@ -2483,5 +2594,6 @@ public class InputMethodService extends AbstractInputMethodService {
+ " touchableInsets=" + mTmpInsets.touchableInsets
+ " touchableRegion=" + mTmpInsets.touchableRegion);
p.println(" mShouldClearInsetOfPreviousIme=" + mShouldClearInsetOfPreviousIme);
+ p.println(" mSettingsObserver=" + mSettingsObserver);
}
}