summaryrefslogtreecommitdiffstats
path: root/mobile/android/base/java/org/mozilla/gecko/text
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /mobile/android/base/java/org/mozilla/gecko/text
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'mobile/android/base/java/org/mozilla/gecko/text')
-rw-r--r--mobile/android/base/java/org/mozilla/gecko/text/FloatingActionModeCallback.java69
-rw-r--r--mobile/android/base/java/org/mozilla/gecko/text/FloatingToolbarTextSelection.java206
-rw-r--r--mobile/android/base/java/org/mozilla/gecko/text/TextAction.java68
-rw-r--r--mobile/android/base/java/org/mozilla/gecko/text/TextSelection.java13
4 files changed, 356 insertions, 0 deletions
diff --git a/mobile/android/base/java/org/mozilla/gecko/text/FloatingActionModeCallback.java b/mobile/android/base/java/org/mozilla/gecko/text/FloatingActionModeCallback.java
new file mode 100644
index 000000000..07f17590d
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/text/FloatingActionModeCallback.java
@@ -0,0 +1,69 @@
+/* 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 android.annotation.TargetApi;
+import android.graphics.Rect;
+import android.os.Build;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+
+import org.mozilla.gecko.GeckoAppShell;
+
+import java.util.List;
+
+@TargetApi(Build.VERSION_CODES.M)
+public class FloatingActionModeCallback extends ActionMode.Callback2 {
+ private FloatingToolbarTextSelection textSelection;
+ private List<TextAction> actions;
+
+ public FloatingActionModeCallback(FloatingToolbarTextSelection textSelection, List<TextAction> actions) {
+ this.textSelection = textSelection;
+ this.actions = actions;
+ }
+
+ public void updateActions(List<TextAction> actions) {
+ this.actions = actions;
+ }
+
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ menu.clear();
+
+ for (int i = 0; i < actions.size(); i++) {
+ final TextAction action = actions.get(i);
+ menu.add(Menu.NONE, i, action.getFloatingOrder(), action.getLabel());
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ final TextAction action = actions.get(item.getItemId());
+
+ GeckoAppShell.notifyObservers("TextSelection:Action", action.getId());
+
+ return true;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {}
+
+ @Override
+ public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
+ final Rect contentRect = textSelection.contentRect;
+ if (contentRect != null) {
+ outRect.set(contentRect);
+ }
+ }
+}
diff --git a/mobile/android/base/java/org/mozilla/gecko/text/FloatingToolbarTextSelection.java b/mobile/android/base/java/org/mozilla/gecko/text/FloatingToolbarTextSelection.java
new file mode 100644
index 000000000..7a09624d4
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/text/FloatingToolbarTextSelection.java
@@ -0,0 +1,206 @@
+/* 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 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.json.JSONException;
+import org.json.JSONObject;
+
+import org.mozilla.gecko.EventDispatcher;
+import org.mozilla.gecko.GeckoApp;
+import org.mozilla.gecko.GeckoAppShell;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
+import org.mozilla.gecko.gfx.LayerView;
+import org.mozilla.gecko.util.GeckoEventListener;
+import org.mozilla.gecko.util.ThreadUtils;
+
+import java.util.List;
+
+import ch.boye.httpclientandroidlib.util.TextUtils;
+
+/**
+ * Floating toolbar for text selection actions. Only on Android 6+.
+ */
+@TargetApi(Build.VERSION_CODES.M)
+public class FloatingToolbarTextSelection implements TextSelection, GeckoEventListener {
+ 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;
+
+ private final Activity activity;
+ private final LayerView layerView;
+ private final int[] locationInWindow;
+ private final float handlesOffset;
+
+ private ActionMode actionMode;
+ private FloatingActionModeCallback actionModeCallback;
+ private String selectionID;
+ /* package-private */ Rect contentRect;
+
+ public FloatingToolbarTextSelection(Activity activity, LayerView layerView) {
+ this.activity = activity;
+ this.layerView = layerView;
+ this.locationInWindow = new int[2];
+
+ this.handlesOffset = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ HANDLES_OFFSET_DP, activity.getResources().getDisplayMetrics());
+ }
+
+ @Override
+ public boolean dismiss() {
+ if (finishActionMode()) {
+ endTextSelection();
+ return true;
+ }
+
+ return false;
+ }
+
+ private void endTextSelection() {
+ if (TextUtils.isEmpty(selectionID)) {
+ return;
+ }
+
+ final JSONObject args = new JSONObject();
+ try {
+ args.put("selectionID", selectionID);
+ } catch (JSONException e) {
+ Log.e(LOGTAG, "Error building JSON arguments for TextSelection:End", e);
+ return;
+ }
+
+ GeckoAppShell.notifyObservers("TextSelection:End", args.toString());
+ }
+
+ @Override
+ public void create() {
+ registerForEvents();
+ }
+
+ @Override
+ public void destroy() {
+ unregisterFromEvents();
+ }
+
+ private void registerForEvents() {
+ GeckoApp.getEventDispatcher().registerGeckoThreadListener(this,
+ "TextSelection:ActionbarInit",
+ "TextSelection:ActionbarStatus",
+ "TextSelection:ActionbarUninit",
+ "TextSelection:Update",
+ "TextSelection:Visibility");
+ }
+
+ private void unregisterFromEvents() {
+ GeckoApp.getEventDispatcher().unregisterGeckoThreadListener(this,
+ "TextSelection:ActionbarInit",
+ "TextSelection:ActionbarStatus",
+ "TextSelection:ActionbarUninit",
+ "TextSelection:Update",
+ "TextSelection:Visibility");
+ }
+
+ @Override
+ public void handleMessage(final String event, final JSONObject message) {
+ ThreadUtils.postToUiThread(new Runnable() {
+ @Override
+ public void run() {
+ handleOnMainThread(event, message);
+ }
+ });
+ }
+
+ private void handleOnMainThread(final String event, final JSONObject message) {
+ if ("TextSelection:ActionbarInit".equals(event)) {
+ Telemetry.sendUIEvent(TelemetryContract.Event.SHOW,
+ TelemetryContract.Method.CONTENT, "text_selection");
+
+ selectionID = message.optString("selectionID");
+ } else if ("TextSelection:ActionbarStatus".equals(event)) {
+ // Ensure async updates from SearchService for example are valid.
+ if (selectionID != message.optString("selectionID")) {
+ return;
+ }
+
+ updateRect(message);
+
+ if (!isRectVisible()) {
+ finishActionMode();
+ } else {
+ startActionMode(TextAction.fromEventMessage(message));
+ }
+ } else if ("TextSelection:ActionbarUninit".equals(event)) {
+ finishActionMode();
+ } else if ("TextSelection:Update".equals(event)) {
+ startActionMode(TextAction.fromEventMessage(message));
+ } else if ("TextSelection:Visibility".equals(event)) {
+ finishActionMode();
+ }
+ }
+
+ private void startActionMode(List<TextAction> actions) {
+ if (actionMode != null) {
+ actionModeCallback.updateActions(actions);
+ actionMode.invalidate();
+ return;
+ }
+
+ actionModeCallback = new FloatingActionModeCallback(this, actions);
+ actionMode = activity.startActionMode(actionModeCallback, ActionMode.TYPE_FLOATING);
+ }
+
+ private boolean finishActionMode() {
+ if (actionMode != null) {
+ actionMode.finish();
+ actionMode = null;
+ actionModeCallback = null;
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * If the content rect is a point (left == right and top == bottom) then this means that the
+ * content rect is not in the currently visible part.
+ */
+ private boolean isRectVisible() {
+ // There's another case of an empty rect where just left == right but not top == bottom.
+ // That's the rect for a collapsed selection. While technically this rect isn't visible too
+ // we are not interested in this case because we do not want to hide the toolbar.
+ return contentRect.left != contentRect.right || contentRect.top != contentRect.bottom;
+ }
+
+ private void updateRect(JSONObject message) {
+ try {
+ 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 zoomFactor = layerView.getZoomFactor();
+ layerView.getLocationInWindow(locationInWindow);
+
+ contentRect = new Rect(
+ (int) (x * zoomFactor + locationInWindow[0]),
+ (int) (y * zoomFactor + locationInWindow[1]),
+ (int) ((x + width) * zoomFactor + locationInWindow[0]),
+ (int) ((y + height) * zoomFactor + locationInWindow[1] +
+ (height > 0 ? handlesOffset : 0)));
+ } catch (JSONException e) {
+ Log.w(LOGTAG, "Could not calculate content rect", e);
+ }
+ }
+}
diff --git a/mobile/android/base/java/org/mozilla/gecko/text/TextAction.java b/mobile/android/base/java/org/mozilla/gecko/text/TextAction.java
new file mode 100644
index 000000000..9fcbce4a4
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/text/TextAction.java
@@ -0,0 +1,68 @@
+/* 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 android.util.Log;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Text selection action like "copy", "paste", ..
+ */
+public class TextAction {
+ private static final String LOGTAG = "GeckoTextAction";
+
+ private String id;
+ private String label;
+ private int order;
+ private int floatingOrder;
+
+ private TextAction() {}
+
+ public static List<TextAction> fromEventMessage(JSONObject message) {
+ final List<TextAction> actions = new ArrayList<>();
+
+ try {
+ final JSONArray array = message.getJSONArray("actions");
+
+ for (int i = 0; i < array.length(); i++) {
+ final JSONObject object = array.getJSONObject(i);
+
+ final TextAction action = new TextAction();
+ action.id = object.getString("id");
+ action.label = object.getString("label");
+ action.order = object.getInt("order");
+ action.floatingOrder = object.optInt("floatingOrder", i);
+
+ actions.add(action);
+ }
+ } catch (JSONException e) {
+ Log.w(LOGTAG, "Could not parse text actions", e);
+ }
+
+ return actions;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public int getOrder() {
+ return order;
+ }
+
+ public int getFloatingOrder() {
+ return floatingOrder;
+ }
+}
diff --git a/mobile/android/base/java/org/mozilla/gecko/text/TextSelection.java b/mobile/android/base/java/org/mozilla/gecko/text/TextSelection.java
new file mode 100644
index 000000000..29e8e43f5
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/text/TextSelection.java
@@ -0,0 +1,13 @@
+/* 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;
+
+public interface TextSelection {
+ void create();
+
+ boolean dismiss();
+
+ void destroy();
+}