Bug 1464096 - 4. Move SessionTextInput.Delegate to GeckoSession.TextInputDelegate; r=me draft
authorJim Chen <nchen@mozilla.com>
Tue, 05 Jun 2018 17:49:01 -0400
changeset 804395 6de4b7ef1106e0a8457e2e62fc025acad9d40f0b
parent 804394 674f0c9115b99c7b6bdccf2c468ce067ae004ded
child 804396 b8344ac27299449346821e0b359c34a469b59736
push id112368
push userbmo:nchen@mozilla.com
push dateTue, 05 Jun 2018 21:49:45 +0000
reviewersme
bugs1464096
milestone62.0a1
Bug 1464096 - 4. Move SessionTextInput.Delegate to GeckoSession.TextInputDelegate; r=me I think it's more consumer-friendly to put the text input delegate in GeckoSession alongside all other delegates. MozReview-Commit-ID: 1UlBe27vplG
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoEditable.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoInputConnection.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionTextInput.java
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoEditable.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoEditable.java
@@ -1214,17 +1214,17 @@ import android.view.inputmethod.EditorIn
         switch (type) {
             case SessionTextInput.EditableListener.NOTIFY_IME_OF_FOCUS:
                 mFocusedChild = child;
                 mNeedSync = false;
                 mText.syncShadowText(/* listener */ null);
 
                 // Do not reset mIMEState here; see comments in notifyIMEContext
                 if (mIMEState != SessionTextInput.EditableListener.IME_STATE_DISABLED) {
-                    icRestartInput(SessionTextInput.Delegate.RESTART_REASON_FOCUS);
+                    icRestartInput(GeckoSession.TextInputDelegate.RESTART_REASON_FOCUS);
                 }
                 break;
 
             case SessionTextInput.EditableListener.NOTIFY_IME_OF_BLUR:
                 mFocusedChild = null;
                 break;
 
             case SessionTextInput.EditableListener.NOTIFY_IME_OPEN_VKB:
@@ -1243,17 +1243,17 @@ import android.view.inputmethod.EditorIn
                 final Object[] spans = text.getSpans(0, text.length(), Object.class);
                 for (final Object span : spans) {
                     if ((text.getSpanFlags(span) & Spanned.SPAN_COMPOSING) != 0) {
                         // Still have composition; no need to reset.
                         return; // Don't notify listener.
                     }
                 }
                 // No longer have composition; perform reset.
-                icRestartInput(SessionTextInput.Delegate.RESTART_REASON_CONTENT_CHANGE);
+                icRestartInput(GeckoSession.TextInputDelegate.RESTART_REASON_CONTENT_CHANGE);
                 return; // Don't notify listener.
             }
 
             default:
                 throw new IllegalArgumentException("Invalid notifyIME type: " + type);
         }
 
         if (mListener != null) {
@@ -1315,26 +1315,29 @@ import android.view.inputmethod.EditorIn
             mListener.notifyIMEContext(state, typeHint, modeHint, actionHint, flags);
         }
 
         // On focus, the notifyIMEContext call comes *before* the
         // notifyIME(NOTIFY_IME_OF_FOCUS) call, but we need to call restartInput during
         // notifyIME, so we skip restartInput here. On blur, the notifyIMEContext call
         // comes *after* the notifyIME(NOTIFY_IME_OF_BLUR) call, and we need to call
         // restartInput here.
-        if (oldState != SessionTextInput.EditableListener.IME_STATE_DISABLED) {
-            if (state == SessionTextInput.EditableListener.IME_STATE_DISABLED) {
-                icRestartInput(SessionTextInput.Delegate.RESTART_REASON_BLUR);
-            } else if (mFocusedChild != null) {
-                icRestartInput(SessionTextInput.Delegate.RESTART_REASON_CONTENT_CHANGE);
-            }
+
+        // In either case, there is nothing to do here if we were disabled before.
+        if (oldState == SessionTextInput.EditableListener.IME_STATE_DISABLED) {
+            return;
+        }
+        if (state == SessionTextInput.EditableListener.IME_STATE_DISABLED) {
+            icRestartInput(GeckoSession.TextInputDelegate.RESTART_REASON_BLUR);
+        } else if (mFocusedChild != null) {
+            icRestartInput(GeckoSession.TextInputDelegate.RESTART_REASON_CONTENT_CHANGE);
         }
     }
 
-    private void icRestartInput(@SessionTextInput.Delegate.RestartReason final int reason) {
+    private void icRestartInput(@GeckoSession.TextInputDelegate.RestartReason final int reason) {
         if (DEBUG) {
             assertOnIcThread();
         }
 
         ThreadUtils.postToUiThread(new Runnable() {
             @Override
             public void run() {
                 mSoftInputReentrancyGuard.incrementAndGet();
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoInputConnection.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoInputConnection.java
@@ -255,17 +255,17 @@ import java.lang.reflect.Proxy;
     }
 
     @Override // SessionTextInput.InputConnectionClient
     public View getView() {
         return mView;
     }
 
     @NonNull
-    /* package */ SessionTextInput.Delegate getInputDelegate() {
+    /* package */ GeckoSession.TextInputDelegate getInputDelegate() {
         return mSession.getTextInput().getDelegate();
     }
 
     @Override // SessionTextInput.EditableListener
     public void onTextChange() {
         final Editable editable = getEditable();
         if (mUpdateRequest == null || editable == null) {
             return;
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
@@ -41,16 +41,19 @@ import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
 import android.support.annotation.NonNull;
 import android.support.annotation.StringDef;
 import android.util.Base64;
 import android.util.Log;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
 
 public class GeckoSession extends LayerSession
                           implements Parcelable {
     private static final String LOGTAG = "GeckoSession";
     private static final boolean DEBUG = false;
 
     // Type of changes given to onWindowChanged.
     // Window has been cleared due to the session being closed.
@@ -2913,9 +2916,99 @@ public class GeckoSession extends LayerS
          * @param uri The URI of the content requesting the permission.
          * @param video List of video sources, or null if not requesting video.
          * @param audio List of audio sources, or null if not requesting audio.
          * @param callback Callback interface.
          */
         void onMediaPermissionRequest(GeckoSession session, String uri, MediaSource[] video,
                                       MediaSource[] audio, MediaCallback callback);
     }
+
+    /**
+     * Interface that SessionTextInput uses for performing operations such as opening and closing
+     * the software keyboard. If the delegate is not set, these operations are forwarded to the
+     * system {@link android.view.inputmethod.InputMethodManager} automatically.
+     */
+    public interface TextInputDelegate {
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef({RESTART_REASON_FOCUS, RESTART_REASON_BLUR, RESTART_REASON_CONTENT_CHANGE})
+        @interface RestartReason {}
+        /** Restarting input due to an input field gaining focus. */
+        int RESTART_REASON_FOCUS = 0;
+        /** Restarting input due to an input field losing focus. */
+        int RESTART_REASON_BLUR = 1;
+        /**
+         * Restarting input due to the content of the input field changing. For example, the
+         * input field type may have changed, or the current composition may have been committed
+         * outside of the input method.
+         */
+        int RESTART_REASON_CONTENT_CHANGE = 2;
+
+        /**
+         * Reset the input method, and discard any existing states such as the current composition
+         * or current autocompletion. Because the current focused editor may have changed, as
+         * part of the reset, a custom input method would normally call {@link
+         * SessionTextInput#onCreateInputConnection} to update its knowledge of the focused editor.
+         * Note that {@code restartInput} should be used to detect changes in focus, rather than
+         * {@link #showSoftInput} or {@link #hideSoftInput}, because focus changes are not always
+         * accompanied by requests to show or hide the soft input. This method is always called,
+         * even in viewless mode.
+         *
+         * @param session Session instance.
+         * @param reason Reason for the reset.
+         */
+        void restartInput(@NonNull GeckoSession session, @RestartReason int reason);
+
+        /**
+         * Display the soft input. May be called consecutively, even if the soft input is
+         * already shown. This method is always called, even in viewless mode.
+         *
+         * @param session Session instance.
+         * @see #hideSoftInput
+         * */
+        void showSoftInput(@NonNull GeckoSession session);
+
+        /**
+         * Hide the soft input. May be called consecutively, even if the soft input is
+         * already hidden. This method is always called, even in viewless mode.
+         *
+         * @param session Session instance.
+         * @see #showSoftInput
+         * */
+        void hideSoftInput(@NonNull GeckoSession session);
+
+        /**
+         * Update the soft input on the current selection. This method is <i>not</i> called
+         * in viewless mode.
+         *
+         * @param session Session instance.
+         * @param selStart Start offset of the selection.
+         * @param selEnd End offset of the selection.
+         * @param compositionStart Composition start offset, or -1 if there is no composition.
+         * @param compositionEnd Composition end offset, or -1 if there is no composition.
+         */
+        void updateSelection(@NonNull GeckoSession session, int selStart, int selEnd,
+                             int compositionStart, int compositionEnd);
+
+        /**
+         * Update the soft input on the current extracted text, as requested through
+         * {@link android.view.inputmethod.InputConnection#getExtractedText}.
+         * Consequently, this method is <i>not</i> called in viewless mode.
+         *
+         * @param session Session instance.
+         * @param request The extract text request.
+         * @param text The extracted text.
+         */
+        void updateExtractedText(@NonNull GeckoSession session,
+                                 @NonNull ExtractedTextRequest request,
+                                 @NonNull ExtractedText text);
+
+        /**
+         * Update the cursor-anchor information as requested through
+         * {@link android.view.inputmethod.InputConnection#requestCursorUpdates}.
+         * Consequently, this method is <i>not</i> called in viewless mode.
+         *
+         * @param session Session instance.
+         * @param info Cursor-anchor information.
+         */
+        void updateCursorAnchorInfo(@NonNull GeckoSession session, @NonNull CursorAnchorInfo info);
+    }
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionTextInput.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/SessionTextInput.java
@@ -13,138 +13,44 @@ import org.mozilla.gecko.NativeQueue;
 import org.mozilla.gecko.util.ActivityUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.annotation.TargetApi;
 import android.app.Activity;
 import android.content.Context;
 import android.graphics.RectF;
 import android.os.Handler;
-import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.text.Editable;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.inputmethod.CursorAnchorInfo;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
 /**
  * {@code SessionTextInput} handles text input for {@code GeckoSession} through key events or input
  * methods. It is typically used to implement certain methods in {@link android.view.View}
  * such as {@link android.view.View#onCreateInputConnection}, by forwarding such calls to
  * corresponding methods in {@code SessionTextInput}.
  * <p>
  * For full functionality, {@code SessionTextInput} requires a {@link android.view.View} to be set
  * first through {@link #setView}. When a {@link android.view.View} is not set or set to null,
  * {@code SessionTextInput} will operate in a reduced functionality mode. See {@link
- * #onCreateInputConnection} and methods in {@link Delegate} for changes in behavior in this
- * viewless mode.
+ * #onCreateInputConnection} and methods in {@link GeckoSession.TextInputDelegate} for changes in
+ * behavior in this viewless mode.
  */
 public final class SessionTextInput {
     /* package */ static final String LOGTAG = "GeckoSessionTextInput";
 
-    /**
-     * Interface that SessionTextInput uses for performing operations such as opening and closing
-     * the software keyboard. If the delegate is not set, these operations are forwarded to the
-     * system {@link android.view.inputmethod.InputMethodManager} automatically.
-     */
-    public interface Delegate {
-        @Retention(RetentionPolicy.SOURCE)
-        @IntDef({RESTART_REASON_FOCUS, RESTART_REASON_BLUR, RESTART_REASON_CONTENT_CHANGE})
-        @interface RestartReason {}
-        /** Restarting input due to an input field gaining focus. */
-        int RESTART_REASON_FOCUS = 0;
-        /** Restarting input due to an input field losing focus. */
-        int RESTART_REASON_BLUR = 1;
-        /**
-         * Restarting input due to the content of the input field changing. For example, the
-         * input field type may have changed, or the current composition may have been committed
-         * outside of the input method.
-         */
-        int RESTART_REASON_CONTENT_CHANGE = 2;
-
-        /**
-         * Reset the input method, and discard any existing states such as the current composition
-         * or current autocompletion. Because the current focused editor may have changed, as
-         * part of the reset, a custom input method would normally call {@link
-         * #onCreateInputConnection} to update its knowledge of the focused editor. Note that
-         * {@code restartInput} should be used to detect changes in focus, rather than {@link
-         * #showSoftInput} or {@link #hideSoftInput}, because focus changes are not always
-         * accompanied by requests to show or hide the soft input. This method is always called,
-         * even in viewless mode.
-         *
-         * @param session Session instance.
-         * @param reason Reason for the reset.
-         */
-        void restartInput(@NonNull GeckoSession session, @RestartReason int reason);
-
-        /**
-         * Display the soft input. May be called consecutively, even if the soft input is
-         * already shown. This method is always called, even in viewless mode.
-         *
-         * @param session Session instance.
-         * @see #hideSoftInput
-         * */
-        void showSoftInput(@NonNull GeckoSession session);
-
-        /**
-         * Hide the soft input. May be called consecutively, even if the soft input is
-         * already hidden. This method is always called, even in viewless mode.
-         *
-         * @param session Session instance.
-         * @see #showSoftInput
-         * */
-        void hideSoftInput(@NonNull GeckoSession session);
-
-        /**
-         * Update the soft input on the current selection. This method is <i>not</i> called
-         * in viewless mode.
-         *
-         * @param session Session instance.
-         * @param selStart Start offset of the selection.
-         * @param selEnd End offset of the selection.
-         * @param compositionStart Composition start offset, or -1 if there is no composition.
-         * @param compositionEnd Composition end offset, or -1 if there is no composition.
-         */
-        void updateSelection(@NonNull GeckoSession session, int selStart, int selEnd,
-                             int compositionStart, int compositionEnd);
-
-        /**
-         * Update the soft input on the current extracted text, as requested through
-         * {@link android.view.inputmethod.InputConnection#getExtractedText}.
-         * Consequently, this method is <i>not</i> called in viewless mode.
-         *
-         * @param session Session instance.
-         * @param request The extract text request.
-         * @param text The extracted text.
-         */
-        void updateExtractedText(@NonNull GeckoSession session,
-                                 @NonNull ExtractedTextRequest request,
-                                 @NonNull ExtractedText text);
-
-        /**
-         * Update the cursor-anchor information as requested through
-         * {@link android.view.inputmethod.InputConnection#requestCursorUpdates}.
-         * Consequently, this method is <i>not</i> called in viewless mode.
-         *
-         * @param session Session instance.
-         * @param info Cursor-anchor information.
-         */
-        void updateCursorAnchorInfo(@NonNull GeckoSession session, @NonNull CursorAnchorInfo info);
-    }
-
     // Interface to access GeckoInputConnection from SessionTextInput.
     /* package */ interface InputConnectionClient {
         View getView();
         Handler getHandler(Handler defHandler);
         InputConnection onCreateInputConnection(EditorInfo attrs);
     }
 
     // Interface to access GeckoEditable from GeckoInputConnection.
@@ -191,17 +97,17 @@ public final class SessionTextInput {
         void notifyIMEContext(int state, String typeHint, String modeHint,
                               String actionHint, int flag);
         void onSelectionChange();
         void onTextChange();
         void onDefaultKeyEvent(KeyEvent event);
         void updateCompositionRects(final RectF[] aRects);
     }
 
-    private static final class DefaultDelegate implements Delegate {
+    private static final class DefaultDelegate implements GeckoSession.TextInputDelegate {
         public static final DefaultDelegate INSTANCE = new DefaultDelegate();
 
         private InputMethodManager getInputMethodManager(@Nullable final View view) {
             if (view == null) {
                 return null;
             }
             return (InputMethodManager) view.getContext()
                                             .getSystemService(Context.INPUT_METHOD_SERVICE);
@@ -313,17 +219,17 @@ public final class SessionTextInput {
         }
     }
 
     private final GeckoSession mSession;
     private final NativeQueue mQueue;
     private final GeckoEditable mEditable;
     private final GeckoEditableChild mEditableChild;
     private InputConnectionClient mInputConnection;
-    private Delegate mDelegate;
+    private GeckoSession.TextInputDelegate mDelegate;
 
     /* package */ SessionTextInput(final @NonNull GeckoSession session,
                                    final @NonNull NativeQueue queue) {
         mSession = session;
         mQueue = queue;
         mEditable = new GeckoEditable(session);
         mEditableChild = new GeckoEditableChild(mEditable);
         mEditable.setDefaultEditableChild(mEditableChild);
@@ -476,28 +382,28 @@ public final class SessionTextInput {
                                  final @NonNull KeyEvent event) {
         ThreadUtils.assertOnUiThread();
         return mEditable.onKeyMultiple(getView(), keyCode, repeatCount, event);
     }
 
     /**
      * Set the current text input delegate.
      *
-     * @param delegate Delegate instance or null to restore to default.
+     * @param delegate TextInputDelegate instance or null to restore to default.
      */
-    public void setDelegate(@Nullable final Delegate delegate) {
+    public void setDelegate(@Nullable final GeckoSession.TextInputDelegate delegate) {
         ThreadUtils.assertOnUiThread();
         mDelegate = delegate;
     }
 
     /**
      * Get the current text input delegate.
      *
-     * @return Delegate instance or a default instance if no delegate has been set.
+     * @return TextInputDelegate instance or a default instance if no delegate has been set.
      */
-    public Delegate getDelegate() {
+    public GeckoSession.TextInputDelegate getDelegate() {
         ThreadUtils.assertOnUiThread();
         if (mDelegate == null) {
             mDelegate = DefaultDelegate.INSTANCE;
         }
         return mDelegate;
     }
 }