mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-18 23:15:38 +00:00
Bug 1325155 - 2. Convert text selection events to bundle events; r=sebastian
Convert the "TextSelection:*" events to use BundleEventListener / GeckoBundle, in both ActionBarTextSelection.java and FloatingToolbarTextSelection.java. UI events are used because the listeners require the UI thread. The "TextSelection:Update" listeners are removed because the event is no longer sent by ActionBarHandler.
This commit is contained in:
parent
34ae4d687c
commit
c3a02d7e8e
@ -8,7 +8,9 @@ import org.mozilla.gecko.menu.GeckoMenu;
|
||||
import org.mozilla.gecko.menu.GeckoMenuItem;
|
||||
import org.mozilla.gecko.util.ResourceDrawableUtils;
|
||||
import org.mozilla.gecko.text.TextSelection;
|
||||
import org.mozilla.gecko.util.GeckoEventListener;
|
||||
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 org.mozilla.gecko.ActionModeCompat.Callback;
|
||||
|
||||
@ -16,16 +18,16 @@ import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
class ActionBarTextSelection implements TextSelection, GeckoEventListener {
|
||||
class ActionBarTextSelection implements TextSelection, BundleEventListener {
|
||||
private static final String LOGTAG = "GeckoTextSelection";
|
||||
private static final int SHUTDOWN_DELAY_MS = 250;
|
||||
|
||||
@ -33,9 +35,9 @@ class ActionBarTextSelection implements TextSelection, GeckoEventListener {
|
||||
|
||||
private boolean mDraggingHandles;
|
||||
|
||||
private String selectionID; // Unique ID provided for each selection action.
|
||||
private int selectionID; // Unique ID provided for each selection action.
|
||||
|
||||
private String mCurrentItems;
|
||||
private GeckoBundle[] mCurrentItems;
|
||||
|
||||
private TextSelectionActionModeCallback mCallback;
|
||||
|
||||
@ -65,11 +67,10 @@ class ActionBarTextSelection implements TextSelection, GeckoEventListener {
|
||||
if (context == null) {
|
||||
Log.e(LOGTAG, "Failed to initialize text selection because at least one context is null");
|
||||
} else {
|
||||
GeckoApp.getEventDispatcher().registerGeckoThreadListener(this,
|
||||
"TextSelection:ActionbarInit",
|
||||
"TextSelection:ActionbarStatus",
|
||||
"TextSelection:ActionbarUninit",
|
||||
"TextSelection:Update");
|
||||
GeckoApp.getEventDispatcher().registerUiThreadListener(this,
|
||||
"TextSelection:ActionbarInit",
|
||||
"TextSelection:ActionbarStatus",
|
||||
"TextSelection:ActionbarUninit");
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,66 +85,51 @@ class ActionBarTextSelection implements TextSelection, GeckoEventListener {
|
||||
if (context == null) {
|
||||
Log.e(LOGTAG, "Do not unregister TextSelection:* listeners since context is null");
|
||||
} else {
|
||||
GeckoApp.getEventDispatcher().unregisterGeckoThreadListener(this,
|
||||
GeckoApp.getEventDispatcher().unregisterUiThreadListener(this,
|
||||
"TextSelection:ActionbarInit",
|
||||
"TextSelection:ActionbarStatus",
|
||||
"TextSelection:ActionbarUninit",
|
||||
"TextSelection:Update");
|
||||
"TextSelection:ActionbarUninit");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(final String event, final JSONObject message) {
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (event.equals("TextSelection:Update")) {
|
||||
if (mActionModeTimerTask != null)
|
||||
mActionModeTimerTask.cancel();
|
||||
showActionMode(message.getJSONArray("actions"));
|
||||
} else if (event.equals("TextSelection:ActionbarInit")) {
|
||||
// Init / Open the action bar. Note the current selectionID,
|
||||
// cancel any pending actionBar close.
|
||||
Telemetry.sendUIEvent(TelemetryContract.Event.SHOW,
|
||||
TelemetryContract.Method.CONTENT, "text_selection");
|
||||
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.
|
||||
Telemetry.sendUIEvent(TelemetryContract.Event.SHOW,
|
||||
TelemetryContract.Method.CONTENT, "text_selection");
|
||||
|
||||
selectionID = message.getString("selectionID");
|
||||
mCurrentItems = null;
|
||||
if (mActionModeTimerTask != null) {
|
||||
mActionModeTimerTask.cancel();
|
||||
}
|
||||
|
||||
} else if (event.equals("TextSelection:ActionbarStatus")) {
|
||||
// Ensure async updates from SearchService for example are valid.
|
||||
if (selectionID != message.optString("selectionID")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the actionBar actions as provided by Gecko.
|
||||
showActionMode(message.getJSONArray("actions"));
|
||||
|
||||
} else if (event.equals("TextSelection:ActionbarUninit")) {
|
||||
// Uninit the actionbar. Schedule a cancellable close
|
||||
// action to avoid UI jank. (During SelectionAll for ex).
|
||||
mCurrentItems = null;
|
||||
mActionModeTimerTask = new ActionModeTimerTask();
|
||||
mActionModeTimer.schedule(mActionModeTimerTask, SHUTDOWN_DELAY_MS);
|
||||
}
|
||||
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOGTAG, "JSON exception", e);
|
||||
}
|
||||
selectionID = message.getInt("selectionID");
|
||||
mCurrentItems = null;
|
||||
if (mActionModeTimerTask != null) {
|
||||
mActionModeTimerTask.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
} else if ("TextSelection:ActionbarStatus".equals(event)) {
|
||||
// Ensure async updates from SearchService for example are valid.
|
||||
if (selectionID != message.getInt("selectionID")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the actionBar actions as provided by Gecko.
|
||||
showActionMode(message.getBundleArray("actions"));
|
||||
|
||||
} else if ("TextSelection:ActionbarUninit".equals(event)) {
|
||||
// Uninit the actionbar. Schedule a cancellable close
|
||||
// action to avoid UI jank. (During SelectionAll for ex).
|
||||
mCurrentItems = null;
|
||||
mActionModeTimerTask = new ActionModeTimerTask();
|
||||
mActionModeTimer.schedule(mActionModeTimerTask, SHUTDOWN_DELAY_MS);
|
||||
}
|
||||
}
|
||||
|
||||
private void showActionMode(final JSONArray items) {
|
||||
String itemsString = items.toString();
|
||||
if (itemsString.equals(mCurrentItems)) {
|
||||
private void showActionMode(final GeckoBundle[] items) {
|
||||
if (Arrays.equals(items, mCurrentItems)) {
|
||||
return;
|
||||
}
|
||||
mCurrentItems = itemsString;
|
||||
mCurrentItems = items;
|
||||
|
||||
if (mCallback != null) {
|
||||
mCallback.updateItems(items);
|
||||
@ -167,14 +153,14 @@ class ActionBarTextSelection implements TextSelection, GeckoEventListener {
|
||||
}
|
||||
|
||||
private class TextSelectionActionModeCallback implements Callback {
|
||||
private JSONArray mItems;
|
||||
private GeckoBundle[] mItems;
|
||||
private ActionModeCompat mActionMode;
|
||||
|
||||
public TextSelectionActionModeCallback(JSONArray items) {
|
||||
public TextSelectionActionModeCallback(final GeckoBundle[] items) {
|
||||
mItems = items;
|
||||
}
|
||||
|
||||
public void updateItems(JSONArray items) {
|
||||
public void updateItems(final GeckoBundle[] items) {
|
||||
mItems = items;
|
||||
if (mActionMode != null) {
|
||||
mActionMode.invalidate();
|
||||
@ -189,32 +175,31 @@ class ActionBarTextSelection implements TextSelection, GeckoEventListener {
|
||||
|
||||
@Override
|
||||
public boolean onPrepareActionMode(final ActionModeCompat mode, final GeckoMenu menu) {
|
||||
// Android would normally expect us to only update the state of menu items here
|
||||
// To make the js-java interaction a bit simpler, we just wipe out the menu here and recreate all
|
||||
// the javascript menu items in onPrepare instead. This will be called any time invalidate() is called on the
|
||||
// action mode.
|
||||
// Android would normally expect us to only update the state of menu items
|
||||
// here To make the js-java interaction a bit simpler, we just wipe out the
|
||||
// menu here and recreate all the javascript menu items in onPrepare instead.
|
||||
// This will be called any time invalidate() is called on the action mode.
|
||||
menu.clear();
|
||||
|
||||
int length = mItems.length();
|
||||
final int length = mItems.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
try {
|
||||
final JSONObject obj = mItems.getJSONObject(i);
|
||||
final GeckoMenuItem menuitem = (GeckoMenuItem) menu.add(0, i, 0, obj.optString("label"));
|
||||
final int actionEnum = obj.optBoolean("showAsAction") ? GeckoMenuItem.SHOW_AS_ACTION_ALWAYS : GeckoMenuItem.SHOW_AS_ACTION_NEVER;
|
||||
menuitem.setShowAsAction(actionEnum, R.attr.menuItemActionModeStyle);
|
||||
final GeckoBundle obj = mItems[i];
|
||||
final GeckoMenuItem menuitem = (GeckoMenuItem)
|
||||
menu.add(0, i, 0, obj.getString("label"));
|
||||
final int actionEnum = obj.getBoolean("showAsAction") ?
|
||||
GeckoMenuItem.SHOW_AS_ACTION_ALWAYS : GeckoMenuItem.SHOW_AS_ACTION_NEVER;
|
||||
menuitem.setShowAsAction(actionEnum, R.attr.menuItemActionModeStyle);
|
||||
|
||||
final String iconString = obj.optString("icon");
|
||||
ResourceDrawableUtils.getDrawable(context, iconString, new ResourceDrawableUtils.BitmapLoader() {
|
||||
@Override
|
||||
public void onBitmapFound(Drawable d) {
|
||||
if (d != null) {
|
||||
menuitem.setIcon(d);
|
||||
}
|
||||
final String iconString = obj.getString("icon");
|
||||
ResourceDrawableUtils.getDrawable(context, iconString,
|
||||
new ResourceDrawableUtils.BitmapLoader() {
|
||||
@Override
|
||||
public void onBitmapFound(Drawable d) {
|
||||
if (d != null) {
|
||||
menuitem.setIcon(d);
|
||||
}
|
||||
});
|
||||
} catch (Exception ex) {
|
||||
Log.i(LOGTAG, "Exception building menu", ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -227,14 +212,9 @@ class ActionBarTextSelection implements TextSelection, GeckoEventListener {
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionModeCompat mode, MenuItem item) {
|
||||
try {
|
||||
final JSONObject obj = mItems.getJSONObject(item.getItemId());
|
||||
GeckoAppShell.notifyObservers("TextSelection:Action", obj.optString("id"));
|
||||
return true;
|
||||
} catch (Exception ex) {
|
||||
Log.i(LOGTAG, "Exception calling action", ex);
|
||||
}
|
||||
return false;
|
||||
final GeckoBundle obj = mItems[item.getItemId()];
|
||||
GeckoAppShell.notifyObservers("TextSelection:Action", obj.getString("id"));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Called when the user exits the action mode
|
||||
|
@ -21,18 +21,18 @@ 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.BundleEventListener;
|
||||
import org.mozilla.gecko.util.EventCallback;
|
||||
import org.mozilla.gecko.util.GeckoBundle;
|
||||
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 {
|
||||
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
|
||||
@ -46,7 +46,7 @@ public class FloatingToolbarTextSelection implements TextSelection, GeckoEventLi
|
||||
|
||||
private ActionMode actionMode;
|
||||
private FloatingActionModeCallback actionModeCallback;
|
||||
private String selectionID;
|
||||
private int selectionID;
|
||||
/* package-private */ Rect contentRect;
|
||||
|
||||
public FloatingToolbarTextSelection(Activity activity, LayerView layerView) {
|
||||
@ -69,7 +69,7 @@ public class FloatingToolbarTextSelection implements TextSelection, GeckoEventLi
|
||||
}
|
||||
|
||||
private void endTextSelection() {
|
||||
if (TextUtils.isEmpty(selectionID)) {
|
||||
if (selectionID == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -95,42 +95,33 @@ public class FloatingToolbarTextSelection implements TextSelection, GeckoEventLi
|
||||
}
|
||||
|
||||
private void registerForEvents() {
|
||||
GeckoApp.getEventDispatcher().registerGeckoThreadListener(this,
|
||||
GeckoApp.getEventDispatcher().registerUiThreadListener(this,
|
||||
"TextSelection:ActionbarInit",
|
||||
"TextSelection:ActionbarStatus",
|
||||
"TextSelection:ActionbarUninit",
|
||||
"TextSelection:Update",
|
||||
"TextSelection:Visibility");
|
||||
}
|
||||
|
||||
private void unregisterFromEvents() {
|
||||
GeckoApp.getEventDispatcher().unregisterGeckoThreadListener(this,
|
||||
GeckoApp.getEventDispatcher().unregisterUiThreadListener(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) {
|
||||
@Override // BundleEventListener
|
||||
public void handleMessage(final String event, final GeckoBundle message,
|
||||
final EventCallback callback) {
|
||||
if ("TextSelection:ActionbarInit".equals(event)) {
|
||||
Telemetry.sendUIEvent(TelemetryContract.Event.SHOW,
|
||||
TelemetryContract.Method.CONTENT, "text_selection");
|
||||
|
||||
selectionID = message.optString("selectionID");
|
||||
selectionID = message.getInt("selectionID");
|
||||
|
||||
} else if ("TextSelection:ActionbarStatus".equals(event)) {
|
||||
// Ensure async updates from SearchService for example are valid.
|
||||
if (selectionID != message.optString("selectionID")) {
|
||||
if (selectionID != message.getInt("selectionID")) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -141,10 +132,10 @@ public class FloatingToolbarTextSelection implements TextSelection, GeckoEventLi
|
||||
} 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();
|
||||
}
|
||||
@ -183,24 +174,20 @@ public class FloatingToolbarTextSelection implements TextSelection, GeckoEventLi
|
||||
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");
|
||||
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 zoomFactor = layerView.getZoomFactor();
|
||||
layerView.getLocationInWindow(locationInWindow);
|
||||
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);
|
||||
}
|
||||
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)));
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import org.mozilla.gecko.util.GeckoBundle;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ -26,25 +28,20 @@ public class TextAction {
|
||||
|
||||
private TextAction() {}
|
||||
|
||||
public static List<TextAction> fromEventMessage(JSONObject message) {
|
||||
public static List<TextAction> fromEventMessage(final GeckoBundle message) {
|
||||
final List<TextAction> actions = new ArrayList<>();
|
||||
final GeckoBundle[] array = message.getBundleArray("actions");
|
||||
|
||||
try {
|
||||
final JSONArray array = message.getJSONArray("actions");
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
final GeckoBundle object = array[i];
|
||||
final TextAction action = new TextAction();
|
||||
|
||||
for (int i = 0; i < array.length(); i++) {
|
||||
final JSONObject object = array.getJSONObject(i);
|
||||
action.id = object.getString("id");
|
||||
action.label = object.getString("label");
|
||||
action.order = object.getInt("order");
|
||||
action.floatingOrder = object.getInt("floatingOrder", 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);
|
||||
actions.add(action);
|
||||
}
|
||||
|
||||
return actions;
|
||||
|
@ -143,7 +143,7 @@ var ActionBarHandler = {
|
||||
this._boundingClientRect = boundingClientRect;
|
||||
|
||||
// Open the ActionBar, send it's actions list.
|
||||
Messaging.sendRequest({
|
||||
WindowEventDispatcher.sendRequest({
|
||||
type: "TextSelection:ActionbarInit",
|
||||
selectionID: this._selectionID,
|
||||
});
|
||||
@ -156,7 +156,7 @@ var ActionBarHandler = {
|
||||
* Called when content is scrolled and handles are hidden.
|
||||
*/
|
||||
_updateVisibility: function() {
|
||||
Messaging.sendRequest({
|
||||
WindowEventDispatcher.sendRequest({
|
||||
type: "TextSelection:Visibility",
|
||||
selectionID: this._selectionID,
|
||||
});
|
||||
@ -207,7 +207,7 @@ var ActionBarHandler = {
|
||||
}
|
||||
|
||||
// Close the ActionBar.
|
||||
Messaging.sendRequest({
|
||||
WindowEventDispatcher.sendRequest({
|
||||
type: "TextSelection:ActionbarUninit",
|
||||
});
|
||||
|
||||
@ -266,7 +266,7 @@ var ActionBarHandler = {
|
||||
});
|
||||
|
||||
if (sendAlways || !actionsMatch) {
|
||||
Messaging.sendRequest({
|
||||
WindowEventDispatcher.sendRequest({
|
||||
type: "TextSelection:ActionbarStatus",
|
||||
selectionID: this._selectionID,
|
||||
actions: actions,
|
||||
|
Loading…
x
Reference in New Issue
Block a user