Bug 1367079 - 1. Update TextSelection implementations for GeckoView; r=snorp draft
authorJim Chen <nchen@mozilla.com>
Fri, 15 Sep 2017 14:44:50 -0400
changeset 665606 cd2303f3eec796a43f3e23ee4f6d0f1a36a45639
parent 665605 cc7cb725f8b5eb1ab4f53bf285b6f1863f2f4d49
child 665613 c04cff0ef15bd042c00900b52f3c9825f4eb1e63
push id80118
push userbmo:nchen@mozilla.com
push dateFri, 15 Sep 2017 18:49:11 +0000
reviewerssnorp
bugs1367079
milestone57.0a1
Bug 1367079 - 1. Update TextSelection implementations for GeckoView; r=snorp Make TextSelection implementations not depend on GeckoApp. Instead, make them use GeckoView's EventDispatcher directly for communicating with Gecko. MozReview-Commit-ID: EygAt3D9HMI
mobile/android/base/java/org/mozilla/gecko/ActionBarTextSelection.java
mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
mobile/android/base/java/org/mozilla/gecko/text/FloatingActionModeCallback.java
mobile/android/base/java/org/mozilla/gecko/text/FloatingToolbarTextSelection.java
mobile/android/base/java/org/mozilla/gecko/text/TextSelection.java
--- a/mobile/android/base/java/org/mozilla/gecko/ActionBarTextSelection.java
+++ b/mobile/android/base/java/org/mozilla/gecko/ActionBarTextSelection.java
@@ -22,21 +22,21 @@ import android.view.Menu;
 import android.view.MenuItem;
 
 import java.util.Arrays;
 import java.util.Timer;
 import java.util.TimerTask;
 
 import android.util.Log;
 
-class ActionBarTextSelection implements TextSelection, BundleEventListener {
+public class ActionBarTextSelection implements TextSelection, BundleEventListener {
     private static final String LOGTAG = "GeckoTextSelection";
     private static final int SHUTDOWN_DELAY_MS = 250;
 
-    private final GeckoApp geckoApp;
+    private final GeckoView geckoView;
     private final ActionModePresenter presenter;
 
     private int selectionID; // Unique ID provided for each selection action.
 
     private GeckoBundle[] mCurrentItems;
 
     private TextSelectionActionModeCallback mCallback;
 
@@ -51,51 +51,42 @@ class ActionBarTextSelection implements 
                 public void run() {
                     endActionMode();
                 }
             });
         }
     };
     private ActionModeTimerTask mActionModeTimerTask;
 
-    ActionBarTextSelection(@NonNull final GeckoApp geckoApp,
-                           @Nullable final ActionModePresenter presenter) {
-        this.geckoApp = geckoApp;
+    public ActionBarTextSelection(@NonNull final GeckoView geckoView,
+                                  @Nullable final ActionModePresenter presenter) {
+        this.geckoView = geckoView;
         this.presenter = presenter;
     }
 
     @Override
     public void create() {
-        // Only register listeners if we have valid start/middle/end handles
-        if (geckoApp == null) {
-            Log.e(LOGTAG, "Failed to initialize text selection because at least one context is null");
-        } else {
-            geckoApp.getAppEventDispatcher().registerUiThreadListener(this,
-                    "TextSelection:ActionbarInit",
-                    "TextSelection:ActionbarStatus",
-                    "TextSelection:ActionbarUninit");
-        }
+        geckoView.getEventDispatcher().registerUiThreadListener(this,
+                "TextSelection:ActionbarInit",
+                "TextSelection:ActionbarStatus",
+                "TextSelection:ActionbarUninit");
     }
 
     @Override
     public boolean dismiss() {
         // We do not call endActionMode() here because this is already handled by the activity.
         return false;
     }
 
     @Override
     public void destroy() {
-        if (geckoApp == null) {
-            Log.e(LOGTAG, "Do not unregister TextSelection:* listeners since context is null");
-        } else {
-            geckoApp.getAppEventDispatcher().unregisterUiThreadListener(this,
-                    "TextSelection:ActionbarInit",
-                    "TextSelection:ActionbarStatus",
-                    "TextSelection:ActionbarUninit");
-        }
+        geckoView.getEventDispatcher().unregisterUiThreadListener(this,
+                "TextSelection:ActionbarInit",
+                "TextSelection:ActionbarStatus",
+                "TextSelection:ActionbarUninit");
     }
 
     @Override
     public void handleMessage(final String event, final GeckoBundle message,
                               final EventCallback callback) {
         if ("TextSelection:ActionbarInit".equals(event)) {
             // Init / Open the action bar. Note the current selectionID,
             // cancel any pending actionBar close.
@@ -179,17 +170,17 @@ class ActionBarTextSelection implements 
                 final GeckoBundle obj = mItems[i];
                 final MenuItem menuitem = menu.add(0, i, 0, obj.getString("label", ""));
                 final int actionEnum = obj.getBoolean("showAsAction")
                         ? MenuItem.SHOW_AS_ACTION_ALWAYS
                         : MenuItem.SHOW_AS_ACTION_NEVER;
                 menuitem.setShowAsAction(actionEnum);
 
                 final String iconString = obj.getString("icon", "");
-                ResourceDrawableUtils.getDrawable(geckoApp, iconString,
+                ResourceDrawableUtils.getDrawable(geckoView.getContext(), iconString,
                         new ResourceDrawableUtils.BitmapLoader() {
                     @Override
                     public void onBitmapFound(Drawable d) {
                         if (d != null) {
                             menuitem.setIcon(d);
                         }
                     }
                 });
@@ -203,24 +194,24 @@ class ActionBarTextSelection implements 
             return true;
         }
 
         @Override
         public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
             final GeckoBundle obj = mItems[item.getItemId()];
             final GeckoBundle data = new GeckoBundle(1);
             data.putString("id", obj.getString("id", ""));
-            geckoApp.getAppEventDispatcher().dispatch("TextSelection:Action", data);
+            geckoView.getEventDispatcher().dispatch("TextSelection:Action", data);
             return true;
         }
 
         // Called when the user exits the action mode
         @Override
         public void onDestroyActionMode(ActionMode mode) {
             mActionMode = null;
             mCallback = null;
 
             final GeckoBundle data = new GeckoBundle(1);
             data.putInt("selectionID", selectionID);
-            geckoApp.getAppEventDispatcher().dispatch("TextSelection:End", data);
+            geckoView.getEventDispatcher().dispatch("TextSelection:End", data);
         }
     }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -1236,21 +1236,17 @@ public abstract class GeckoApp extends G
             null);
 
         Tabs.getInstance().attachToContext(this, mLayerView, getAppEventDispatcher());
         Tabs.registerOnTabsChangedListener(this);
 
         // Use global layout state change to kick off additional initialization
         mMainLayout.getViewTreeObserver().addOnGlobalLayoutListener(this);
 
-        if (Versions.preMarshmallow) {
-            mTextSelection = new ActionBarTextSelection(this, getTextSelectPresenter());
-        } else {
-            mTextSelection = new FloatingToolbarTextSelection(this, mLayerView);
-        }
+        mTextSelection = TextSelection.Factory.create(mLayerView, getTextSelectPresenter());
         mTextSelection.create();
 
         final Bundle finalSavedInstanceState = savedInstanceState;
         ThreadUtils.postToBackgroundThread(new Runnable() {
             @Override
             public void run() {
                 // Determine whether we should restore tabs.
                 mLastSessionCrashed = updateCrashedState();
--- a/mobile/android/base/java/org/mozilla/gecko/text/FloatingActionModeCallback.java
+++ b/mobile/android/base/java/org/mozilla/gecko/text/FloatingActionModeCallback.java
@@ -49,17 +49,17 @@ public class FloatingActionModeCallback 
     }
 
     @Override
     public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
         final TextAction action = actions.get(item.getItemId());
 
         final GeckoBundle data = new GeckoBundle(1);
         data.putString("id", action.getId());
-        textSelection.geckoApp.getAppEventDispatcher().dispatch("TextSelection:Action", data);
+        textSelection.geckoView.getEventDispatcher().dispatch("TextSelection:Action", data);
 
         return true;
     }
 
     @Override
     public void onDestroyActionMode(ActionMode mode) {}
 
     @Override
--- a/mobile/android/base/java/org/mozilla/gecko/text/FloatingToolbarTextSelection.java
+++ b/mobile/android/base/java/org/mozilla/gecko/text/FloatingToolbarTextSelection.java
@@ -8,21 +8,20 @@ import android.annotation.TargetApi;
 import android.app.Activity;
 import android.graphics.Rect;
 import android.os.Build;
 import android.util.Log;
 import android.util.TypedValue;
 import android.view.ActionMode;
 
 import org.mozilla.gecko.EventDispatcher;
-import org.mozilla.gecko.GeckoApp;
-import org.mozilla.gecko.GeckoAppShell;
+import org.mozilla.gecko.GeckoView;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
-import org.mozilla.gecko.gfx.LayerView;
+import org.mozilla.gecko.util.ActivityUtils;
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import java.util.List;
 
 /**
@@ -31,33 +30,31 @@ import java.util.List;
 @TargetApi(Build.VERSION_CODES.M)
 public class FloatingToolbarTextSelection implements TextSelection, BundleEventListener {
     private static final String LOGTAG = "GeckoFloatTextSelection";
 
     // This is an additional offset we add to the height of the selection. This will avoid that the
     // floating toolbar overlays the bottom handle(s).
     private static final int HANDLES_OFFSET_DP = 20;
 
-    /* package */ final GeckoApp geckoApp;
-    private final LayerView layerView;
+    /* package */ final GeckoView geckoView;
     private final int[] locationInWindow;
     private final float handlesOffset;
 
     private ActionMode actionMode;
     private FloatingActionModeCallback actionModeCallback;
     private int selectionID;
     /* package-private */ Rect contentRect;
 
-    public FloatingToolbarTextSelection(GeckoApp geckoApp, LayerView layerView) {
-        this.geckoApp = geckoApp;
-        this.layerView = layerView;
+    public FloatingToolbarTextSelection(final GeckoView geckoView) {
+        this.geckoView = geckoView;
         this.locationInWindow = new int[2];
 
         this.handlesOffset = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                HANDLES_OFFSET_DP, geckoApp.getResources().getDisplayMetrics());
+                HANDLES_OFFSET_DP, geckoView.getContext().getResources().getDisplayMetrics());
     }
 
     @Override
     public boolean dismiss() {
         if (finishActionMode()) {
             endTextSelection();
             return true;
         }
@@ -67,39 +64,39 @@ public class FloatingToolbarTextSelectio
 
     private void endTextSelection() {
         if (selectionID == 0) {
             return;
         }
 
         final GeckoBundle data = new GeckoBundle(1);
         data.putInt("selectionID", selectionID);
-        geckoApp.getAppEventDispatcher().dispatch("TextSelection:End", data);
+        geckoView.getEventDispatcher().dispatch("TextSelection:End", data);
     }
 
     @Override
     public void create() {
         registerForEvents();
     }
 
     @Override
     public void destroy() {
         unregisterFromEvents();
     }
 
     private void registerForEvents() {
-        geckoApp.getAppEventDispatcher().registerUiThreadListener(this,
+        geckoView.getEventDispatcher().registerUiThreadListener(this,
                 "TextSelection:ActionbarInit",
                 "TextSelection:ActionbarStatus",
                 "TextSelection:ActionbarUninit",
                 "TextSelection:Visibility");
     }
 
     private void unregisterFromEvents() {
-        geckoApp.getAppEventDispatcher().unregisterUiThreadListener(this,
+        geckoView.getEventDispatcher().unregisterUiThreadListener(this,
                 "TextSelection:ActionbarInit",
                 "TextSelection:ActionbarStatus",
                 "TextSelection:ActionbarUninit",
                 "TextSelection:Visibility");
     }
 
     @Override // BundleEventListener
     public void handleMessage(final String event, final GeckoBundle message,
@@ -134,18 +131,23 @@ public class FloatingToolbarTextSelectio
 
     private void startActionMode(List<TextAction> actions) {
         if (actionMode != null) {
             actionModeCallback.updateActions(actions);
             actionMode.invalidate();
             return;
         }
 
+        final Activity activity =
+                ActivityUtils.getActivityFromContext(geckoView.getContext());
+        if (activity == null) {
+            return;
+        }
         actionModeCallback = new FloatingActionModeCallback(this, actions);
-        actionMode = geckoApp.startActionMode(actionModeCallback, ActionMode.TYPE_FLOATING);
+        actionMode = activity.startActionMode(actionModeCallback, ActionMode.TYPE_FLOATING);
     }
 
     private boolean finishActionMode() {
         if (actionMode != null) {
             actionMode.finish();
             actionMode = null;
             actionModeCallback = null;
             return true;
@@ -166,19 +168,19 @@ public class FloatingToolbarTextSelectio
     }
 
     private void updateRect(final GeckoBundle message) {
         final double x = message.getDouble("x");
         final double y = (int) message.getDouble("y");
         final double width = (int) message.getDouble("width");
         final double height = (int) message.getDouble("height");
 
-        final float toolbarOffset = layerView.getCurrentToolbarHeight();
-        final float zoomFactor = layerView.getZoomFactor();
-        layerView.getLocationInWindow(locationInWindow);
+        final float toolbarOffset = geckoView.getCurrentToolbarHeight();
+        final float zoomFactor = geckoView.getZoomFactor();
+        geckoView.getLocationInWindow(locationInWindow);
 
         contentRect = new Rect(
                 (int) (x * zoomFactor + locationInWindow[0]),
                 (int) (y * zoomFactor + locationInWindow[1] + toolbarOffset),
                 (int) ((x + width) * zoomFactor + locationInWindow[0]),
                 (int) ((y + height) * zoomFactor + locationInWindow[1] +
                        (height > 0 ? handlesOffset : 0)));
     }
--- a/mobile/android/base/java/org/mozilla/gecko/text/TextSelection.java
+++ b/mobile/android/base/java/org/mozilla/gecko/text/TextSelection.java
@@ -1,13 +1,32 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.text;
 
+import org.mozilla.gecko.ActionBarTextSelection;
+import org.mozilla.gecko.AppConstants.Versions;
+import org.mozilla.gecko.GeckoView;
+import org.mozilla.gecko.widget.ActionModePresenter;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
 public interface TextSelection {
     void create();
 
     boolean dismiss();
 
     void destroy();
+
+    static class Factory {
+        public static TextSelection create(@NonNull final GeckoView geckoView,
+                                           @Nullable final ActionModePresenter presenter) {
+            if (Versions.preMarshmallow) {
+                return new ActionBarTextSelection(geckoView, presenter);
+            } else {
+                return new FloatingToolbarTextSelection(geckoView);
+            }
+        }
+    }
 }