Bug 1404111 - Part 1 - Work around potential InputMethodManager bug when gaining focus. r?snorp draft
authorJan Henning <jh+bugzilla@buttercookie.de>
Tue, 17 Oct 2017 22:20:59 +0200
changeset 681811 31f674e799e17c9d32c742b02c4988860388c194
parent 681804 49da6f528da91b7d21332ca8c0fbc1f94a15c082
child 736246 39563cabb29bff1fb6b90c82955736ab70818083
push id84938
push usermozilla@buttercookie.de
push dateTue, 17 Oct 2017 20:45:03 +0000
reviewerssnorp
bugs1404111
milestone58.0a1
Bug 1404111 - Part 1 - Work around potential InputMethodManager bug when gaining focus. r?snorp MozReview-Commit-ID: 2YnsMCZYckA
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
@@ -23,31 +23,33 @@ import org.mozilla.gecko.util.GeckoBundl
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.app.Activity;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.res.Resources;
 import android.database.Cursor;
+import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
 
 public class GeckoView extends LayerView {
 
     private static final String DEFAULT_SHARED_PREFERENCES_FILE = "GeckoView";
     private static final String LOGTAG = "GeckoView";
 
     private static final boolean DEBUG = false;
 
@@ -304,16 +306,17 @@ public class GeckoView extends LayerView
      * @param delegate PermissionDelegate instance or null to use the default delegate.
      */
     public void setPermissionDelegate(final PermissionDelegate delegate) {
         mPermissionHandler.setListener(delegate, this);
     }
 
     private PromptDelegate mPromptDelegate;
     private InputConnectionListener mInputConnectionListener;
+    private boolean mIsResettingFocus;
 
     private GeckoViewSettings mSettings;
 
     protected String mChromeUri;
     protected int mScreenId = 0; // default to the primary screen
 
     @WrapForJNI(dispatchTo = "proxy")
     protected static final class Window extends JNIObject {
@@ -702,16 +705,55 @@ public class GeckoView extends LayerView
         mEventDispatcher.dispatch("GeckoView:SetActive", msg);
     }
 
     public GeckoViewSettings getSettings() {
         return mSettings;
     }
 
     @Override
+    public void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
+        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+
+        if (gainFocus && !mIsResettingFocus) {
+            ThreadUtils.postToUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    if (!isFocused()) {
+                        return;
+                    }
+
+                    final InputMethodManager imm = InputMethods.getInputMethodManager(getContext());
+                    // Bug 1404111:
+                    // Through View#onFocusChanged, the InputMethodManager queues up a checkFocus
+                    // call for the next spin of the message loop, so by posting this Runnable after
+                    // super#onFocusChanged, the IMM should have completed its focus change handling
+                    // at this point and we should be the active view for input handling.
+
+                    // If however onViewDetachedFromWindow for the previously active view gets
+                    // called *after* onFocusChanged, but *before* the focus change has been fully
+                    // processed by the IMM with the help of checkFocus, the IMM will lose track of
+                    // the currently active view, which means that we can't interact with the IME.
+                    if (!imm.isActive(GeckoView.this)) {
+                        // If that happens, we bring the IMM's internal state back into sync by
+                        // clearing and resetting our focus.
+                        mIsResettingFocus = true;
+                        clearFocus();
+                        // After calling clearFocus we might regain focus automatically, but we
+                        // explicitly request it again in case this doesn't happen.
+                        // If we've already got the focus back, this will then be a no-op anyway.
+                        requestFocus();
+                        mIsResettingFocus = false;
+                    }
+                }
+            });
+        }
+    }
+
+    @Override
     public Handler getHandler() {
         if (mInputConnectionListener != null) {
             return mInputConnectionListener.getHandler(super.getHandler());
         }
         return super.getHandler();
     }
 
     @Override