pt 1 in reviving the android build (copied from pm 28a1, wish me luck)

This commit is contained in:
wuggy 2026-04-08 15:43:25 -07:00
commit d7788a6d6d
4249 changed files with 468189 additions and 0 deletions

View file

@ -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);
}
}
}

View file

@ -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);
}
}
}

View file

@ -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;
}
}

View file

@ -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();
}