summaryrefslogtreecommitdiff
path: root/core/java/android/inputmethodservice/InputMethodService.java
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2019-09-05 16:53:23 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2019-09-05 16:53:23 +0000
commitd191463bb0a528d3dc97a21b85ad83374b27c239 (patch)
tree425b31516b5d8b1530a6fcf90a9661d5878f08b4 /core/java/android/inputmethodservice/InputMethodService.java
parent3f275ca900aac74865a2f83eeda36a064661ff80 (diff)
parente199ca954dff7fdfb06e6280695d34a957ed5efc (diff)
Merge "DO NOT MERGE - Merge Android 10 into master"
Diffstat (limited to 'core/java/android/inputmethodservice/InputMethodService.java')
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java518
1 files changed, 362 insertions, 156 deletions
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 049e5af56019..82d4d1d10d7e 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -16,12 +16,14 @@
package android.inputmethodservice;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import android.annotation.AnyThread;
import android.annotation.CallSuper;
import android.annotation.DrawableRes;
import android.annotation.IntDef;
@@ -80,6 +82,12 @@ import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.IInputContentUriToken;
+import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
+import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
+import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -338,12 +346,20 @@ public class InputMethodService extends AbstractInputMethodService {
*/
public static final int IME_VISIBLE = 0x2;
+ /**
+ * @hide
+ * The IME is active and ready with views but set invisible.
+ * This flag cannot be combined with {@link #IME_VISIBLE}.
+ */
+ public static final int IME_INVISIBLE = 0x4;
+
// Min and max values for back disposition.
private static final int BACK_DISPOSITION_MIN = BACK_DISPOSITION_DEFAULT;
private static final int BACK_DISPOSITION_MAX = BACK_DISPOSITION_ADJUST_NOTHING;
InputMethodManager mImm;
-
+ private InputMethodPrivilegedOperations mPrivOps = new InputMethodPrivilegedOperations();
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
int mTheme = 0;
@@ -353,10 +369,19 @@ public class InputMethodService extends AbstractInputMethodService {
View mRootView;
SoftInputWindow mWindow;
boolean mInitialized;
- boolean mWindowCreated;
- boolean mWindowVisible;
- boolean mWindowWasVisible;
+ boolean mViewsCreated;
+ // IME views visibility.
+ boolean mDecorViewVisible;
+ boolean mDecorViewWasVisible;
boolean mInShowWindow;
+ // True if pre-rendering of IME views/window is supported.
+ boolean mCanPreRender;
+ // If IME is pre-rendered.
+ boolean mIsPreRendered;
+ // IME window visibility.
+ // Use (mDecorViewVisible && mWindowVisible) to check if IME is visible to the user.
+ boolean mWindowVisible;
+
ViewGroup mFullscreenArea;
FrameLayout mExtractFrame;
FrameLayout mCandidatesFrame;
@@ -372,19 +397,6 @@ public class InputMethodService extends AbstractInputMethodService {
InputConnection mStartedInputConnection;
EditorInfo mInputEditorInfo;
- /**
- * A token to keep tracking the last IPC that triggered
- * {@link #doStartInput(InputConnection, EditorInfo, boolean)}. If
- * {@link #doStartInput(InputConnection, EditorInfo, boolean)} was not caused by IPCs from
- * {@link com.android.server.InputMethodManagerService}, this needs to remain unchanged.
- *
- * <p>Some IPCs to {@link com.android.server.InputMethodManagerService} require this token to
- * disentangle event flows for various purposes such as better window animation and providing
- * fine-grained debugging information.</p>
- */
- @Nullable
- private IBinder mStartInputToken;
-
int mShowInputFlags;
boolean mShowInputRequested;
boolean mLastShowInputRequested;
@@ -411,13 +423,9 @@ public class InputMethodService extends AbstractInputMethodService {
@BackDispositionMode
int mBackDisposition;
- /**
- * {@code true} when the previous IME had non-empty inset at the bottom of the screen and we
- * have not shown our own window yet. In this situation, the previous inset continues to be
- * shown as an empty region until it is explicitly updated. Basically we can trigger the update
- * by calling 1) {@code mWindow.show()} or 2) {@link #clearInsetOfPreviousIme()}.
- */
- boolean mShouldClearInsetOfPreviousIme;
+ private Object mLock = new Object();
+ @GuardedBy("mLock")
+ private boolean mNotifyUserActionSent;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
final Insets mTmpInsets = new Insets();
@@ -460,13 +468,49 @@ public class InputMethodService extends AbstractInputMethodService {
public class InputMethodImpl extends AbstractInputMethodImpl {
/**
* {@inheritDoc}
+ * @hide
+ */
+ @MainThread
+ @Override
+ public final void initializeInternal(@NonNull IBinder token, int displayId,
+ IInputMethodPrivilegedOperations privilegedOperations) {
+ if (InputMethodPrivilegedOperationsRegistry.isRegistered(token)) {
+ Log.w(TAG, "The token has already registered, ignore this initialization.");
+ return;
+ }
+ mPrivOps.set(privilegedOperations);
+ InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps);
+ updateInputMethodDisplay(displayId);
+ attachToken(token);
+ }
+
+ /**
+ * {@inheritDoc}
*/
@MainThread
@Override
public void attachToken(IBinder token) {
- if (mToken == null) {
- mToken = token;
- mWindow.setToken(token);
+ if (mToken != null) {
+ throw new IllegalStateException(
+ "attachToken() must be called at most once. token=" + token);
+ }
+ mToken = token;
+ mWindow.setToken(token);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @MainThread
+ @Override
+ public void updateInputMethodDisplay(int displayId) {
+ // Update display for adding IME window to the right display.
+ if (displayId != DEFAULT_DISPLAY) {
+ // TODO(b/111364446) Need to address context lifecycle issue if need to re-create
+ // for update resources & configuration correctly when show soft input
+ // in non-default display.
+ updateDisplay(displayId);
}
}
@@ -482,9 +526,7 @@ public class InputMethodService extends AbstractInputMethodService {
mInputConnection = binding.getConnection();
if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding
+ " ic=" + mInputConnection);
- if (mImm != null && mToken != null) {
- mImm.reportFullscreenMode(mToken, mIsFullscreen);
- }
+ reportFullscreenMode();
initialize();
onBindInput();
}
@@ -530,16 +572,18 @@ public class InputMethodService extends AbstractInputMethodService {
*/
@MainThread
@Override
- public void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
+ public final void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
@NonNull EditorInfo editorInfo, boolean restarting,
- @NonNull IBinder startInputToken) {
- mStartInputToken = startInputToken;
+ @NonNull IBinder startInputToken, boolean shouldPreRenderIme) {
+ mPrivOps.reportStartInput(startInputToken);
+ mCanPreRender = shouldPreRenderIme;
+ if (DEBUG) Log.v(TAG, "Will Pre-render IME: " + mCanPreRender);
- // This needs to be dispatched to interface methods rather than doStartInput().
- // Otherwise IME developers who have overridden those interface methods will lose
- // notifications.
- super.dispatchStartInputWithToken(inputConnection, editorInfo, restarting,
- startInputToken);
+ if (restarting) {
+ restartInput(inputConnection, editorInfo);
+ } else {
+ startInput(inputConnection, editorInfo);
+ }
}
/**
@@ -549,15 +593,27 @@ public class InputMethodService extends AbstractInputMethodService {
@Override
public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
if (DEBUG) Log.v(TAG, "hideSoftInput()");
- boolean wasVis = isInputViewShown();
- mShowInputFlags = 0;
- mShowInputRequested = false;
- doHideWindow();
- clearInsetOfPreviousIme();
+ final boolean wasVisible = mIsPreRendered
+ ? mDecorViewVisible && mWindowVisible : isInputViewShown();
+ if (mIsPreRendered) {
+ if (DEBUG) {
+ Log.v(TAG, "Making IME window invisible");
+ }
+ setImeWindowStatus(IME_ACTIVE | IME_INVISIBLE, mBackDisposition);
+ applyVisibilityInInsetsConsumer(false /* setVisible */);
+ onPreRenderedWindowVisibilityChanged(false /* setVisible */);
+ } else {
+ mShowInputFlags = 0;
+ mShowInputRequested = false;
+ doHideWindow();
+ }
+ final boolean isVisible = mIsPreRendered
+ ? mDecorViewVisible && mWindowVisible : isInputViewShown();
+ final boolean visibilityChanged = isVisible != wasVisible;
if (resultReceiver != null) {
- resultReceiver.send(wasVis != isInputViewShown()
+ resultReceiver.send(visibilityChanged
? InputMethodManager.RESULT_HIDDEN
- : (wasVis ? InputMethodManager.RESULT_UNCHANGED_SHOWN
+ : (wasVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN
: InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
}
}
@@ -569,18 +625,28 @@ public class InputMethodService extends AbstractInputMethodService {
@Override
public void showSoftInput(int flags, ResultReceiver resultReceiver) {
if (DEBUG) Log.v(TAG, "showSoftInput()");
- boolean wasVis = isInputViewShown();
+ final boolean wasVisible = mIsPreRendered
+ ? mDecorViewVisible && mWindowVisible : isInputViewShown();
if (dispatchOnShowInputRequested(flags, false)) {
- showWindow(true);
+ if (mIsPreRendered) {
+ if (DEBUG) {
+ Log.v(TAG, "Making IME window visible");
+ }
+ applyVisibilityInInsetsConsumer(true /* setVisible */);
+ onPreRenderedWindowVisibilityChanged(true /* setVisible */);
+ } else {
+ showWindow(true);
+ }
}
- clearInsetOfPreviousIme();
// If user uses hard keyboard, IME button should always be shown.
- mImm.setImeWindowStatus(mToken, mStartInputToken,
- mapToImeWindowStatus(isInputViewShown()), mBackDisposition);
+ setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
+ final boolean isVisible = mIsPreRendered
+ ? mDecorViewVisible && mWindowVisible : isInputViewShown();
+ final boolean visibilityChanged = isVisible != wasVisible;
if (resultReceiver != null) {
- resultReceiver.send(wasVis != isInputViewShown()
+ resultReceiver.send(visibilityChanged
? InputMethodManager.RESULT_SHOWN
- : (wasVis ? InputMethodManager.RESULT_UNCHANGED_SHOWN
+ : (wasVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN
: InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
}
}
@@ -591,10 +657,19 @@ public class InputMethodService extends AbstractInputMethodService {
@MainThread
@Override
public void changeInputMethodSubtype(InputMethodSubtype subtype) {
- onCurrentInputMethodSubtypeChanged(subtype);
+ dispatchOnCurrentInputMethodSubtypeChanged(subtype);
}
}
+ private void notifyImeHidden() {
+ setImeWindowStatus(IME_ACTIVE | IME_INVISIBLE, mBackDisposition);
+ onPreRenderedWindowVisibilityChanged(false /* setVisible */);
+ }
+
+ private void setImeWindowStatus(int visibilityFlags, int backDisposition) {
+ mPrivOps.setImeWindowStatus(visibilityFlags, backDisposition);
+ }
+
/**
* Concrete implementation of
* {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides
@@ -693,6 +768,14 @@ public class InputMethodService extends AbstractInputMethodService {
}
InputMethodService.this.onUpdateCursorAnchorInfo(info);
}
+
+ /**
+ * Notify IME that window is hidden.
+ * @hide
+ */
+ public final void notifyImeHidden() {
+ InputMethodService.this.notifyImeHidden();
+ }
}
/**
@@ -914,9 +997,9 @@ public class InputMethodService extends AbstractInputMethodService {
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);
+ // TODO(b/111364446) Need to address context lifecycle issue if need to re-create
+ // for update resources & configuration correctly when show soft input
+ // in non-default display.
mInflater = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
@@ -949,7 +1032,7 @@ public class InputMethodService extends AbstractInputMethodService {
void initViews() {
mInitialized = false;
- mWindowCreated = false;
+ mViewsCreated = false;
mShowInputRequested = false;
mShowInputFlags = 0;
@@ -994,6 +1077,11 @@ public class InputMethodService extends AbstractInputMethodService {
mSettingsObserver.unregister();
mSettingsObserver = null;
}
+ if (mToken != null) {
+ // This is completely optional, but allows us to show more explicit error messages
+ // when IME developers are doing something unsupported.
+ InputMethodPrivilegedOperationsRegistry.remove(mToken);
+ }
}
/**
@@ -1018,7 +1106,7 @@ public class InputMethodService extends AbstractInputMethodService {
}
private void resetStateForNewConfiguration() {
- boolean visible = mWindowVisible;
+ boolean visible = mDecorViewVisible;
int showFlags = mShowInputFlags;
boolean showingInput = mShowInputRequested;
CompletionInfo[] completions = mCurCompletions;
@@ -1051,8 +1139,7 @@ public class InputMethodService extends AbstractInputMethodService {
}
// If user uses hard keyboard, IME button should always be shown.
boolean showing = onEvaluateInputViewShown();
- mImm.setImeWindowStatus(mToken, mStartInputToken,
- IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition);
+ setImeWindowStatus(IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition);
}
}
@@ -1102,8 +1189,7 @@ public class InputMethodService extends AbstractInputMethodService {
return;
}
mBackDisposition = disposition;
- mImm.setImeWindowStatus(mToken, mStartInputToken, mapToImeWindowStatus(isInputViewShown()),
- mBackDisposition);
+ setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
}
/**
@@ -1166,7 +1252,7 @@ public class InputMethodService extends AbstractInputMethodService {
* used input method and subtype.
*/
public final boolean switchToPreviousInputMethod() {
- return mImm.switchToPreviousInputMethodInternal(mToken);
+ return mPrivOps.switchToPreviousInputMethod();
}
/**
@@ -1178,7 +1264,7 @@ public class InputMethodService extends AbstractInputMethodService {
* input method and subtype.
*/
public final boolean switchToNextInputMethod(boolean onlyCurrentIme) {
- return mImm.switchToNextInputMethodInternal(mToken, onlyCurrentIme);
+ return mPrivOps.switchToNextInputMethod(onlyCurrentIme);
}
/**
@@ -1191,7 +1277,7 @@ public class InputMethodService extends AbstractInputMethodService {
* between IMEs and subtypes.
*/
public final boolean shouldOfferSwitchingToNextInputMethod() {
- return mImm.shouldOfferSwitchingToNextInputMethodInternal(mToken);
+ return mPrivOps.shouldOfferSwitchingToNextInputMethod();
}
public boolean getCurrentInputStarted() {
@@ -1201,7 +1287,11 @@ public class InputMethodService extends AbstractInputMethodService {
public EditorInfo getCurrentInputEditorInfo() {
return mInputEditorInfo;
}
-
+
+ private void reportFullscreenMode() {
+ mPrivOps.reportFullscreenMode(mIsFullscreen);
+ }
+
/**
* Re-evaluate whether the input method should be running in fullscreen
* mode, and update its UI if this has changed since the last time it
@@ -1216,9 +1306,7 @@ public class InputMethodService extends AbstractInputMethodService {
if (mIsFullscreen != isFullscreen || !mFullscreenApplied) {
changed = true;
mIsFullscreen = isFullscreen;
- if (mImm != null && mToken != null) {
- mImm.reportFullscreenMode(mToken, mIsFullscreen);
- }
+ reportFullscreenMode();
mFullscreenApplied = true;
initialize();
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
@@ -1352,7 +1440,7 @@ public class InputMethodService extends AbstractInputMethodService {
mExtractFrame.setVisibility(View.GONE);
}
updateCandidatesVisibility(mCandidatesVisibility == View.VISIBLE);
- if (mWindowWasVisible && mFullscreenArea.getVisibility() != vis) {
+ if (mDecorViewWasVisible && mFullscreenArea.getVisibility() != vis) {
int animRes = mThemeAttrs.getResourceId(vis == View.VISIBLE
? com.android.internal.R.styleable.InputMethodService_imeExtractEnterAnimation
: com.android.internal.R.styleable.InputMethodService_imeExtractExitAnimation,
@@ -1411,7 +1499,7 @@ public class InputMethodService extends AbstractInputMethodService {
*/
public void updateInputViewShown() {
boolean isShown = mShowInputRequested && onEvaluateInputViewShown();
- if (mIsInputViewShown != isShown && mWindowVisible) {
+ if (mIsInputViewShown != isShown && mDecorViewVisible) {
mIsInputViewShown = isShown;
mInputFrame.setVisibility(isShown ? View.VISIBLE : View.GONE);
if (mInputView == null) {
@@ -1430,14 +1518,14 @@ public class InputMethodService extends AbstractInputMethodService {
public boolean isShowInputRequested() {
return mShowInputRequested;
}
-
+
/**
* Return whether the soft input view is <em>currently</em> shown to the
* user. This is the state that was last determined and
* applied by {@link #updateInputViewShown()}.
*/
public boolean isInputViewShown() {
- return mIsInputViewShown && mWindowVisible;
+ return mCanPreRender ? mWindowVisible : mIsInputViewShown && mDecorViewVisible;
}
/**
@@ -1471,7 +1559,7 @@ public class InputMethodService extends AbstractInputMethodService {
*/
public void setCandidatesViewShown(boolean shown) {
updateCandidatesVisibility(shown);
- if (!mShowInputRequested && mWindowVisible != shown) {
+ if (!mShowInputRequested && mDecorViewVisible != shown) {
// If we are being asked to show the candidates view while the app
// has not asked for the input view to be shown, then we need
// to update whether the window is shown.
@@ -1507,12 +1595,12 @@ public class InputMethodService extends AbstractInputMethodService {
public void showStatusIcon(@DrawableRes int iconResId) {
mStatusIcon = iconResId;
- mImm.showStatusIconInternal(mToken, getPackageName(), iconResId);
+ mPrivOps.updateStatusIcon(getPackageName(), iconResId);
}
public void hideStatusIcon() {
mStatusIcon = 0;
- mImm.hideStatusIconInternal(mToken);
+ mPrivOps.updateStatusIcon(null, 0);
}
/**
@@ -1523,7 +1611,7 @@ public class InputMethodService extends AbstractInputMethodService {
* @param id Unique identifier of the new input method to start.
*/
public void switchInputMethod(String id) {
- mImm.setInputMethodInternal(mToken, id);
+ mPrivOps.setInputMethod(id);
}
/**
@@ -1535,7 +1623,7 @@ public class InputMethodService extends AbstractInputMethodService {
* @param subtype The new subtype of the new input method to be switched to.
*/
public final void switchInputMethod(String id, InputMethodSubtype subtype) {
- mImm.setInputMethodAndSubtypeInternal(mToken, id, subtype);
+ mPrivOps.setInputMethodAndSubtype(id, subtype);
}
public void setExtractView(View view) {
@@ -1776,7 +1864,8 @@ public class InputMethodService extends AbstractInputMethodService {
public void showWindow(boolean showInput) {
if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput
+ " mShowInputRequested=" + mShowInputRequested
- + " mWindowCreated=" + mWindowCreated
+ + " mViewsCreated=" + mViewsCreated
+ + " mDecorViewVisible=" + mDecorViewVisible
+ " mWindowVisible=" + mWindowVisible
+ " mInputStarted=" + mInputStarted
+ " mShowInputFlags=" + mShowInputFlags);
@@ -1786,18 +1875,55 @@ public class InputMethodService extends AbstractInputMethodService {
return;
}
- mWindowWasVisible = mWindowVisible;
+ mDecorViewWasVisible = mDecorViewVisible;
mInShowWindow = true;
- showWindowInner(showInput);
- mWindowWasVisible = true;
+ boolean isPreRenderedAndInvisible = mIsPreRendered && !mWindowVisible;
+ final int previousImeWindowStatus =
+ (mDecorViewVisible ? IME_ACTIVE : 0) | (isInputViewShown()
+ ? (isPreRenderedAndInvisible ? IME_INVISIBLE : IME_VISIBLE) : 0);
+ startViews(prepareWindow(showInput));
+ final int nextImeWindowStatus = mapToImeWindowStatus();
+ if (previousImeWindowStatus != nextImeWindowStatus) {
+ setImeWindowStatus(nextImeWindowStatus, mBackDisposition);
+ }
+
+ // compute visibility
+ onWindowShown();
+ mIsPreRendered = mCanPreRender;
+ if (mIsPreRendered) {
+ onPreRenderedWindowVisibilityChanged(true /* setVisible */);
+ } else {
+ // Pre-rendering not supported.
+ if (DEBUG) Log.d(TAG, "No pre-rendering supported");
+ mWindowVisible = true;
+ }
+
+ // request draw for the IME surface.
+ // When IME is not pre-rendered, this will actually show the IME.
+ if ((previousImeWindowStatus & IME_ACTIVE) == 0) {
+ if (DEBUG) Log.v(TAG, "showWindow: draw decorView!");
+ mWindow.show();
+ }
+ maybeNotifyPreRendered();
+ mDecorViewWasVisible = true;
mInShowWindow = false;
}
- void showWindowInner(boolean showInput) {
+ /**
+ * Notify {@link android.view.ImeInsetsSourceConsumer} if IME has been pre-rendered
+ * for current EditorInfo, when pre-rendering is enabled.
+ */
+ private void maybeNotifyPreRendered() {
+ if (!mCanPreRender || !mIsPreRendered) {
+ return;
+ }
+ mPrivOps.reportPreRendered(getCurrentInputEditorInfo());
+ }
+
+
+ private boolean prepareWindow(boolean showInput) {
boolean doShowInput = false;
- final int previousImeWindowStatus =
- (mWindowVisible ? IME_ACTIVE : 0) | (isInputViewShown() ? IME_VISIBLE : 0);
- mWindowVisible = true;
+ mDecorViewVisible = true;
if (!mShowInputRequested && mInputStarted && showInput) {
doShowInput = true;
mShowInputRequested = true;
@@ -1808,8 +1934,8 @@ public class InputMethodService extends AbstractInputMethodService {
updateFullscreenMode();
updateInputViewShown();
- if (!mWindowCreated) {
- mWindowCreated = true;
+ if (!mViewsCreated) {
+ mViewsCreated = true;
initialize();
if (DEBUG) Log.v(TAG, "CALL: onCreateCandidatesView");
View v = onCreateCandidatesView();
@@ -1818,6 +1944,10 @@ public class InputMethodService extends AbstractInputMethodService {
setCandidatesView(v);
}
}
+ return doShowInput;
+ }
+
+ private void startViews(boolean doShowInput) {
if (mShowInputRequested) {
if (!mInputViewStarted) {
if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
@@ -1829,50 +1959,58 @@ public class InputMethodService extends AbstractInputMethodService {
mCandidatesViewStarted = true;
onStartCandidatesView(mInputEditorInfo, false);
}
-
- if (doShowInput) {
- startExtractingText(false);
- }
+ if (doShowInput) startExtractingText(false);
+ }
- final int nextImeWindowStatus = mapToImeWindowStatus(isInputViewShown());
- if (previousImeWindowStatus != nextImeWindowStatus) {
- mImm.setImeWindowStatus(mToken, mStartInputToken, nextImeWindowStatus,
- mBackDisposition);
- }
- if ((previousImeWindowStatus & IME_ACTIVE) == 0) {
- if (DEBUG) Log.v(TAG, "showWindow: showing!");
+ private void onPreRenderedWindowVisibilityChanged(boolean setVisible) {
+ mWindowVisible = setVisible;
+ mShowInputFlags = setVisible ? mShowInputFlags : 0;
+ mShowInputRequested = setVisible;
+ mDecorViewVisible = setVisible;
+ if (setVisible) {
onWindowShown();
- mWindow.show();
- // Put here rather than in onWindowShown() in case people forget to call
- // super.onWindowShown().
- mShouldClearInsetOfPreviousIme = false;
}
}
- private void finishViews() {
+ /**
+ * Apply the IME visibility in {@link android.view.ImeInsetsSourceConsumer} when
+ * pre-rendering is enabled.
+ * @param setVisible {@code true} to make it visible, false to hide it.
+ */
+ private void applyVisibilityInInsetsConsumer(boolean setVisible) {
+ if (!mIsPreRendered) {
+ return;
+ }
+ mPrivOps.applyImeVisibility(setVisible);
+ }
+
+ private void finishViews(boolean finishingInput) {
if (mInputViewStarted) {
if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
- onFinishInputView(false);
+ onFinishInputView(finishingInput);
} else if (mCandidatesViewStarted) {
if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
- onFinishCandidatesView(false);
+ onFinishCandidatesView(finishingInput);
}
mInputViewStarted = false;
mCandidatesViewStarted = false;
}
private void doHideWindow() {
- mImm.setImeWindowStatus(mToken, mStartInputToken, 0, mBackDisposition);
+ setImeWindowStatus(0, mBackDisposition);
hideWindow();
}
public void hideWindow() {
- finishViews();
- if (mWindowVisible) {
+ if (DEBUG) Log.v(TAG, "CALL: hideWindow");
+ mIsPreRendered = false;
+ mWindowVisible = false;
+ finishViews(false /* finishingInput */);
+ if (mDecorViewVisible) {
mWindow.hide();
- mWindowVisible = false;
+ mDecorViewVisible = false;
onWindowHidden();
- mWindowWasVisible = false;
+ mDecorViewWasVisible = false;
}
updateFullscreenMode();
}
@@ -1895,19 +2033,6 @@ public class InputMethodService extends AbstractInputMethodService {
}
/**
- * Reset the inset occupied the previous IME when and only when
- * {@link #mShouldClearInsetOfPreviousIme} is {@code true}.
- */
- private void clearInsetOfPreviousIme() {
- if (DEBUG) Log.v(TAG, "clearInsetOfPreviousIme() "
- + " mShouldClearInsetOfPreviousIme=" + mShouldClearInsetOfPreviousIme);
- if (!mShouldClearInsetOfPreviousIme) return;
-
- mImm.clearLastInputMethodWindowForTransition(mToken);
- mShouldClearInsetOfPreviousIme = false;
- }
-
- /**
* Called when a new client has bound to the input method. This
* may be followed by a series of {@link #onStartInput(EditorInfo, boolean)}
* and {@link #onFinishInput()} calls as the user navigates through its
@@ -1945,15 +2070,8 @@ public class InputMethodService extends AbstractInputMethodService {
}
void doFinishInput() {
- if (mInputViewStarted) {
- if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
- onFinishInputView(true);
- } else if (mCandidatesViewStarted) {
- if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
- onFinishCandidatesView(true);
- }
- mInputViewStarted = false;
- mCandidatesViewStarted = false;
+ if (DEBUG) Log.v(TAG, "CALL: doFinishInput");
+ finishViews(true /* finishingInput */);
if (mInputStarted) {
if (DEBUG) Log.v(TAG, "CALL: onFinishInput");
onFinishInput();
@@ -1973,7 +2091,7 @@ public class InputMethodService extends AbstractInputMethodService {
initialize();
if (DEBUG) Log.v(TAG, "CALL: onStartInput");
onStartInput(attribute, restarting);
- if (mWindowVisible) {
+ if (mDecorViewVisible) {
if (mShowInputRequested) {
if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
mInputViewStarted = true;
@@ -1984,6 +2102,32 @@ public class InputMethodService extends AbstractInputMethodService {
mCandidatesViewStarted = true;
onStartCandidatesView(mInputEditorInfo, restarting);
}
+ } else if (mCanPreRender && mInputEditorInfo != null && mStartedInputConnection != null) {
+ // Pre-render IME views and window when real EditorInfo is available.
+ // pre-render IME window and keep it invisible.
+ if (DEBUG) Log.v(TAG, "Pre-Render IME for " + mInputEditorInfo.fieldName);
+ if (mInShowWindow) {
+ Log.w(TAG, "Re-entrance in to showWindow");
+ return;
+ }
+
+ mDecorViewWasVisible = mDecorViewVisible;
+ mInShowWindow = true;
+ startViews(prepareWindow(true /* showInput */));
+
+ // compute visibility
+ mIsPreRendered = true;
+ onPreRenderedWindowVisibilityChanged(false /* setVisible */);
+
+ // request draw for the IME surface.
+ // When IME is not pre-rendered, this will actually show the IME.
+ if (DEBUG) Log.v(TAG, "showWindow: draw decorView!");
+ mWindow.show();
+ maybeNotifyPreRendered();
+ mDecorViewWasVisible = true;
+ mInShowWindow = false;
+ } else {
+ mIsPreRendered = false;
}
}
@@ -2076,7 +2220,14 @@ public class InputMethodService extends AbstractInputMethodService {
* protocol, so applications with custom text editing written before this method appeared will
* not call to inform the IME of this interaction.
* @param focusChanged true if the user changed the focused view by this click.
+ * @see InputMethodManager#viewClicked(View)
+ * @deprecated The method may not be called for composite {@link View} that works as a giant
+ * "Canvas", which can host its own UI hierarchy and sub focus state.
+ * {@link android.webkit.WebView} is a good example. Application / IME developers
+ * should not rely on this method. If your goal is just being notified when an
+ * on-going input is interrupted, simply monitor {@link #onFinishInput()}.
*/
+ @Deprecated
public void onViewClicked(boolean focusChanged) {
// Intentionally empty
}
@@ -2114,7 +2265,7 @@ public class InputMethodService extends AbstractInputMethodService {
* @param flags Provides additional operating flags.
*/
public void requestHideSelf(int flags) {
- mImm.hideSoftInputFromInputMethodInternal(mToken, flags);
+ mPrivOps.hideMySoftInput(flags);
}
/**
@@ -2126,7 +2277,7 @@ public class InputMethodService extends AbstractInputMethodService {
* @param flags Provides additional operating flags.
*/
public final void requestShowSelf(int flags) {
- mImm.showSoftInputFromInputMethodInternal(mToken, flags);
+ mPrivOps.showMySoftInput(flags);
}
private boolean handleBack(boolean doIt) {
@@ -2135,7 +2286,7 @@ public class InputMethodService extends AbstractInputMethodService {
// consume the back key.
if (doIt) requestHideSelf(0);
return true;
- } else if (mWindowVisible) {
+ } else if (mDecorViewVisible) {
if (mCandidatesVisibility == View.VISIBLE) {
// If we are showing candidates even if no input area, then
// hide them.
@@ -2162,7 +2313,6 @@ public class InputMethodService extends AbstractInputMethodService {
return mExtractEditText;
}
-
/**
* Called back when a {@link KeyEvent} is forwarded from the target application.
*
@@ -2768,6 +2918,13 @@ public class InputMethodService extends AbstractInputMethodService {
}
}
+ private void dispatchOnCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype) {
+ synchronized (mLock) {
+ mNotifyUserActionSent = false;
+ }
+ onCurrentInputMethodSubtypeChanged(newSubtype);
+ }
+
// TODO: Handle the subtype change event
/**
* Called when the subtype was changed.
@@ -2786,18 +2943,22 @@ public class InputMethodService extends AbstractInputMethodService {
}
/**
- * @return The recommended height of the input method window.
- * An IME author can get the last input method's height as the recommended height
- * by calling this in
- * {@link android.inputmethodservice.InputMethodService#onStartInputView(EditorInfo, boolean)}.
- * If you don't need to use a predefined fixed height, you can avoid the window-resizing of IME
- * switching by using this value as a visible inset height. It's efficient for the smooth
- * transition between different IMEs. However, note that this may return 0 (or possibly
- * unexpectedly low height). You should thus avoid relying on the return value of this method
- * all the time. Please make sure to use a reasonable height for the IME.
+ * Aimed to return the previous input method's {@link Insets#contentTopInsets}, but its actual
+ * semantics has never been well defined.
+ *
+ * <p>Note that the previous document clearly mentioned that this method could return {@code 0}
+ * at any time for whatever reason. Now this method is just always returning {@code 0}.</p>
+ *
+ * @return on Android {@link android.os.Build.VERSION_CODES#Q} and later devices this method
+ * always returns {@code 0}
+ * @deprecated the actual behavior of this method has never been well defined. You cannot use
+ * this method in a reliable and predictable way
*/
+ @Deprecated
public int getInputMethodWindowRecommendedHeight() {
- return mImm.getInputMethodWindowVisibleHeight();
+ Log.w(TAG, "getInputMethodWindowRecommendedHeight() is deprecated and now always returns 0."
+ + " Do not use this method.");
+ return 0;
}
/**
@@ -2820,11 +2981,55 @@ public class InputMethodService extends AbstractInputMethodService {
if (getCurrentInputConnection() != inputConnection) {
return;
}
- mImm.exposeContent(mToken, inputContentInfo, getCurrentInputEditorInfo());
+ exposeContentInternal(inputContentInfo, getCurrentInputEditorInfo());
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @AnyThread
+ @Override
+ public final void notifyUserActionIfNecessary() {
+ synchronized (mLock) {
+ if (mNotifyUserActionSent) {
+ return;
+ }
+ mPrivOps.notifyUserAction();
+ mNotifyUserActionSent = true;
+ }
+ }
+
+ /**
+ * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
+ * permission to the content.
+ *
+ * <p>See {@link android.inputmethodservice.InputMethodService#exposeContent(InputContentInfo,
+ * InputConnection)} for details.</p>
+ *
+ * @param inputContentInfo Content to be temporarily exposed from the input method to the
+ * application.
+ * This cannot be {@code null}.
+ * @param editorInfo The editor that receives {@link InputContentInfo}.
+ */
+ private void exposeContentInternal(@NonNull InputContentInfo inputContentInfo,
+ @NonNull EditorInfo editorInfo) {
+ final Uri contentUri = inputContentInfo.getContentUri();
+ final IInputContentUriToken uriToken =
+ mPrivOps.createInputContentUriToken(contentUri, editorInfo.packageName);
+ if (uriToken == null) {
+ Log.e(TAG, "createInputContentAccessToken failed. contentUri=" + contentUri.toString()
+ + " packageName=" + editorInfo.packageName);
+ return;
+ }
+ inputContentInfo.setUriToken(uriToken);
}
- private static int mapToImeWindowStatus(boolean isInputViewShown) {
- return IME_ACTIVE | (isInputViewShown ? IME_VISIBLE : 0);
+ private int mapToImeWindowStatus() {
+ return IME_ACTIVE
+ | (isInputViewShown()
+ ? (mCanPreRender ? (mWindowVisible ? IME_VISIBLE : IME_INVISIBLE)
+ : IME_VISIBLE) : 0);
}
/**
@@ -2834,9 +3039,10 @@ public class InputMethodService extends AbstractInputMethodService {
@Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
final Printer p = new PrintWriterPrinter(fout);
p.println("Input method service state for " + this + ":");
- p.println(" mWindowCreated=" + mWindowCreated);
- p.println(" mWindowVisible=" + mWindowVisible
- + " mWindowWasVisible=" + mWindowWasVisible
+ p.println(" mViewsCreated=" + mViewsCreated);
+ p.println(" mDecorViewVisible=" + mDecorViewVisible
+ + " mDecorViewWasVisible=" + mDecorViewWasVisible
+ + " mWindowVisible=" + mWindowVisible
+ " mInShowWindow=" + mInShowWindow);
p.println(" Configuration=" + getResources().getConfiguration());
p.println(" mToken=" + mToken);
@@ -2846,7 +3052,6 @@ public class InputMethodService extends AbstractInputMethodService {
p.println(" mInputStarted=" + mInputStarted
+ " mInputViewStarted=" + mInputViewStarted
+ " mCandidatesViewStarted=" + mCandidatesViewStarted);
- p.println(" mStartInputToken=" + mStartInputToken);
if (mInputEditorInfo != null) {
p.println(" mInputEditorInfo:");
@@ -2857,6 +3062,8 @@ public class InputMethodService extends AbstractInputMethodService {
p.println(" mShowInputRequested=" + mShowInputRequested
+ " mLastShowInputRequested=" + mLastShowInputRequested
+ + " mCanPreRender=" + mCanPreRender
+ + " mIsPreRendered=" + mIsPreRendered
+ " mShowInputFlags=0x" + Integer.toHexString(mShowInputFlags));
p.println(" mCandidatesVisibility=" + mCandidatesVisibility
+ " mFullscreenApplied=" + mFullscreenApplied
@@ -2881,7 +3088,6 @@ public class InputMethodService extends AbstractInputMethodService {
+ " visibleTopInsets=" + mTmpInsets.visibleTopInsets
+ " touchableInsets=" + mTmpInsets.touchableInsets
+ " touchableRegion=" + mTmpInsets.touchableRegion);
- p.println(" mShouldClearInsetOfPreviousIme=" + mShouldClearInsetOfPreviousIme);
p.println(" mSettingsObserver=" + mSettingsObserver);
}
}