diff --git a/api/current.txt b/api/current.txt index 0857c8bb696ea2581fe0917082da2b651e8939b9..5906f996a77e5f10eeb3fdbf69a147ceee56204c 100644 --- a/api/current.txt +++ b/api/current.txt @@ -44641,6 +44641,7 @@ package android.view.inputmethod { ctor public BaseInputConnection(android.view.View, boolean); method public boolean beginBatchEdit(); method public boolean clearMetaKeyStates(int); + method public void closeConnection(); method public boolean commitCompletion(android.view.inputmethod.CompletionInfo); method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo); method public boolean commitText(java.lang.CharSequence, int); @@ -44808,6 +44809,7 @@ package android.view.inputmethod { public abstract interface InputConnection { method public abstract boolean beginBatchEdit(); method public abstract boolean clearMetaKeyStates(int); + method public abstract void closeConnection(); method public abstract boolean commitCompletion(android.view.inputmethod.CompletionInfo); method public abstract boolean commitCorrection(android.view.inputmethod.CorrectionInfo); method public abstract boolean commitText(java.lang.CharSequence, int); @@ -44840,6 +44842,7 @@ package android.view.inputmethod { ctor public InputConnectionWrapper(android.view.inputmethod.InputConnection, boolean); method public boolean beginBatchEdit(); method public boolean clearMetaKeyStates(int); + method public void closeConnection(); method public boolean commitCompletion(android.view.inputmethod.CompletionInfo); method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo); method public boolean commitText(java.lang.CharSequence, int); @@ -66433,3 +66436,4 @@ package org.xmlpull.v1.sax2 { } } + diff --git a/api/system-current.txt b/api/system-current.txt index 99a4ad3c448a60a19353e8c804359f44b0cf47c7..a3ed210d7d16aae35716ca9ede73f16aca30fe25 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -47370,6 +47370,7 @@ package android.view.inputmethod { ctor public BaseInputConnection(android.view.View, boolean); method public boolean beginBatchEdit(); method public boolean clearMetaKeyStates(int); + method public void closeConnection(); method public boolean commitCompletion(android.view.inputmethod.CompletionInfo); method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo); method public boolean commitText(java.lang.CharSequence, int); @@ -47537,6 +47538,7 @@ package android.view.inputmethod { public abstract interface InputConnection { method public abstract boolean beginBatchEdit(); method public abstract boolean clearMetaKeyStates(int); + method public abstract void closeConnection(); method public abstract boolean commitCompletion(android.view.inputmethod.CompletionInfo); method public abstract boolean commitCorrection(android.view.inputmethod.CorrectionInfo); method public abstract boolean commitText(java.lang.CharSequence, int); @@ -47569,6 +47571,7 @@ package android.view.inputmethod { ctor public InputConnectionWrapper(android.view.inputmethod.InputConnection, boolean); method public boolean beginBatchEdit(); method public boolean clearMetaKeyStates(int); + method public void closeConnection(); method public boolean commitCompletion(android.view.inputmethod.CompletionInfo); method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo); method public boolean commitText(java.lang.CharSequence, int); @@ -69498,3 +69501,4 @@ package org.xmlpull.v1.sax2 { } } + diff --git a/api/test-current.txt b/api/test-current.txt index 6cf047a2d411c9cfcc896210bee051631ca7af35..6a31de4dd635181962b9927fc7528379ca469a40 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -44715,6 +44715,7 @@ package android.view.inputmethod { ctor public BaseInputConnection(android.view.View, boolean); method public boolean beginBatchEdit(); method public boolean clearMetaKeyStates(int); + method public void closeConnection(); method public boolean commitCompletion(android.view.inputmethod.CompletionInfo); method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo); method public boolean commitText(java.lang.CharSequence, int); @@ -44882,6 +44883,7 @@ package android.view.inputmethod { public abstract interface InputConnection { method public abstract boolean beginBatchEdit(); method public abstract boolean clearMetaKeyStates(int); + method public abstract void closeConnection(); method public abstract boolean commitCompletion(android.view.inputmethod.CompletionInfo); method public abstract boolean commitCorrection(android.view.inputmethod.CorrectionInfo); method public abstract boolean commitText(java.lang.CharSequence, int); @@ -44914,6 +44916,7 @@ package android.view.inputmethod { ctor public InputConnectionWrapper(android.view.inputmethod.InputConnection, boolean); method public boolean beginBatchEdit(); method public boolean clearMetaKeyStates(int); + method public void closeConnection(); method public boolean commitCompletion(android.view.inputmethod.CompletionInfo); method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo); method public boolean commitText(java.lang.CharSequence, int); @@ -66507,3 +66510,4 @@ package org.xmlpull.v1.sax2 { } } + diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index a3c49c5a84570bbbd48f83b14b4cd3f67d901f5d..89dec2d22d7a74abf37d0833d72643ed0ef1ac6e 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -16,6 +16,7 @@ package android.view.inputmethod; +import android.annotation.CallSuper; import android.content.Context; import android.content.res.TypedArray; import android.os.Bundle; @@ -154,12 +155,11 @@ public class BaseInputConnection implements InputConnection { } /** - * Called when this InputConnection is no longer used by the InputMethodManager. - * - * @hide + * Default implementation calls {@link #finishComposingText()}. */ - public void reportFinish() { - // Intentionally empty + @CallSuper + public void closeConnection() { + finishComposingText(); } /** diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java index 8002a8e9ba14d434ce11399e47f5ec9203cfa2d6..9f664293476835b2eb8f6f54d0340deb1b3d092b 100644 --- a/core/java/android/view/inputmethod/InputConnection.java +++ b/core/java/android/view/inputmethod/InputConnection.java @@ -45,6 +45,8 @@ import android.view.KeyEvent; * was introduced in {@link android.os.Build.VERSION_CODES#N}.</li> * <li>{@link #getHandler()}}, which was introduced in * {@link android.os.Build.VERSION_CODES#N}.</li> + * <li>{@link #closeConnection()}}, which was introduced in + * {@link android.os.Build.VERSION_CODES#N}.</li> * </ul> * * <h3>Implementing an IME or an editor</h3> @@ -820,4 +822,18 @@ public interface InputConnection { * @return {@code null} to use the default {@link Handler}. */ public Handler getHandler(); + + /** + * Called by the system up to only once to notify that the system is about to invalidate + * connection between the input method and the application. + * + * <p><strong>Editor authors</strong>: You can clear all the nested batch edit right now and + * you no longer need to handle subsequent callbacks on this connection, including + * {@link #beginBatchEdit()}}. Note that although the system tries to call this method whenever + * possible, there may be a chance that this method is not called in some exceptional + * situations.</p> + * + * <p>Note: This does nothing when called from input methods.</p> + */ + public void closeConnection(); } diff --git a/core/java/android/view/inputmethod/InputConnectionInspector.java b/core/java/android/view/inputmethod/InputConnectionInspector.java index 46b2c3e0fbea8774b35a0713358accee63096ade..118a61f8d571c3d447b694971144bffd5dcd6d85 100644 --- a/core/java/android/view/inputmethod/InputConnectionInspector.java +++ b/core/java/android/view/inputmethod/InputConnectionInspector.java @@ -73,6 +73,11 @@ public final class InputConnectionInspector { * {@link android.os.Build.VERSION_CODES#N} and later. */ int GET_HANDLER = 1 << 5; + /** + * {@link InputConnection#closeConnection()}} is available in + * {@link android.os.Build.VERSION_CODES#N} and later. + */ + int CLOSE_CONNECTION = 1 << 6; } private static final Map<Class, Integer> sMissingMethodsMap = Collections.synchronizedMap( @@ -119,6 +124,9 @@ public final class InputConnectionInspector { if (!hasGetHandler(clazz)) { flags |= MissingMethodFlags.GET_HANDLER; } + if (!hasCloseConnection(clazz)) { + flags |= MissingMethodFlags.CLOSE_CONNECTION; + } sMissingMethodsMap.put(clazz, flags); return flags; } @@ -178,6 +186,15 @@ public final class InputConnectionInspector { } } + private static boolean hasCloseConnection(@NonNull final Class clazz) { + try { + final Method method = clazz.getMethod("closeConnection"); + return !Modifier.isAbstract(method.getModifiers()); + } catch (NoSuchMethodException e) { + return false; + } + } + public static String getMissingMethodFlagsAsString(@MissingMethodFlags final int flags) { final StringBuilder sb = new StringBuilder(); boolean isEmpty = true; @@ -219,6 +236,12 @@ public final class InputConnectionInspector { } sb.append("getHandler()"); } + if ((flags & MissingMethodFlags.CLOSE_CONNECTION) != 0) { + if (!isEmpty) { + sb.append(","); + } + sb.append("closeConnection()"); + } return sb.toString(); } } diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java index 381df49d8a69262e53b2a645e071cfe4b1bee3af..e743f62a5a0a6df97288951d4acdc5e6e1d7f51b 100644 --- a/core/java/android/view/inputmethod/InputConnectionWrapper.java +++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java @@ -261,4 +261,12 @@ public class InputConnectionWrapper implements InputConnection { public Handler getHandler() { return mTarget.getHandler(); } + + /** + * {@inheritDoc} + * @throws NullPointerException if the target is {@code null}. + */ + public void closeConnection() { + mTarget.closeConnection(); + } } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index f5908a50ec64b5009f92949ed7a58826a45687bb..687990124a5585f87f0f200f78736b2125b8fd73 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -544,7 +544,7 @@ public final class InputMethodManager { // reportFinish() will take effect. return; } - reportFinish(); + closeConnection(); } @Override diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 7cbe8de674920c758a3b8dbea9fcf056feabb77f..6e1dff9ea0defbc1324caae04fc979e4cd73ba5c 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -5937,6 +5937,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te public Handler getHandler() { return getTarget().getHandler(); } + + @Override + public void closeConnection() { + getTarget().closeConnection(); + } } /** diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index 6c1ebb43287b789423e2156b84998b554a6132ad..3a4afad3f3670a4e08c6030b80614e45abc9e09e 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -27,13 +27,12 @@ import android.os.Message; import android.os.RemoteException; import android.util.Log; import android.view.KeyEvent; -import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; - -import java.lang.ref.WeakReference; +import android.view.inputmethod.InputConnectionInspector; +import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags; public abstract class IInputConnectionWrapper extends IInputContext.Stub { static final String TAG = "IInputConnectionWrapper"; @@ -61,7 +60,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { private static final int DO_PERFORM_PRIVATE_COMMAND = 120; private static final int DO_CLEAR_META_KEY_STATES = 130; private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140; - private static final int DO_REPORT_FINISH = 150; + private static final int DO_CLOSE_CONNECTION = 150; @GuardedBy("mLock") @Nullable @@ -222,8 +221,8 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { seq, callback)); } - public void reportFinish() { - dispatchMessage(obtainMessage(DO_REPORT_FINISH)); + public void closeConnection() { + dispatchMessage(obtainMessage(DO_CLOSE_CONNECTION)); } void dispatchMessage(Message msg) { @@ -501,10 +500,10 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } return; } - case DO_REPORT_FINISH: { + case DO_CLOSE_CONNECTION: { // Note that we do not need to worry about race condition here, because 1) mFinished // is updated only inside this block, and 2) the code here is running on a Handler - // hence we assume multiple DO_REPORT_FINISH messages will not be handled at the + // hence we assume multiple DO_CLOSE_CONNECTION messages will not be handled at the // same time. if (isFinished()) { return; @@ -518,11 +517,10 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { if (ic == null) { return; } - ic.finishComposingText(); - // TODO: Make reportFinish() public method of InputConnection to remove this - // check. - if (ic instanceof BaseInputConnection) { - ((BaseInputConnection) ic).reportFinish(); + @MissingMethodFlags + final int missingMethods = InputConnectionInspector.getMissingMethodFlags(ic); + if ((missingMethods & MissingMethodFlags.CLOSE_CONNECTION) == 0) { + ic.closeConnection(); } } finally { synchronized (mLock) { diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index 85b8606f75003b2c51dd5a712ba0ea7d8b23a2a4..f9884d85d52cb7337e7cf1633545ce3efa6e735b 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -487,6 +487,10 @@ public class InputConnectionWrapper implements InputConnection { return null; } + public void closeConnection() { + // Nothing should happen when called from input method. + } + private boolean isMethodMissing(@MissingMethodFlags final int methodFlag) { return (mMissingMethods & methodFlag) == methodFlag; } diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java index a96b5a0d6ab12047651c70111375934da688db23..7f7528dd3de07073f5117181e6c82149e3b0119c 100644 --- a/core/java/com/android/internal/widget/EditableInputConnection.java +++ b/core/java/com/android/internal/widget/EditableInputConnection.java @@ -83,13 +83,9 @@ public class EditableInputConnection extends BaseInputConnection { return false; } - /** - * @hide - */ @Override - public void reportFinish() { - super.reportFinish(); - + public void closeConnection() { + super.closeConnection(); synchronized(this) { while (mBatchEditNesting > 0) { endBatchEdit();