summaryrefslogtreecommitdiffstats
path: root/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoDragAndDrop.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoDragAndDrop.java')
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoDragAndDrop.java253
1 files changed, 253 insertions, 0 deletions
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoDragAndDrop.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoDragAndDrop.java
new file mode 100644
index 0000000000..9c1473d4e7
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoDragAndDrop.java
@@ -0,0 +1,253 @@
+/* -*- Mode: Java; c-basic-offset: 2; tab-width: 20; indent-tabs-mode: nil; -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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;
+
+import android.annotation.TargetApi;
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Point;
+import android.os.Build;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.DragEvent;
+import android.view.View;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import org.mozilla.gecko.annotation.WrapForJNI;
+
+@TargetApi(Build.VERSION_CODES.N)
+public class GeckoDragAndDrop {
+ private static final String LOGTAG = "GeckoDragAndDrop";
+ private static final boolean DEBUG = false;
+
+ /** The drag/drop data is nsITransferable and stored into nsDragService. */
+ private static final String MIMETYPE_NATIVE = "application/x-moz-draganddrop";
+
+ private static final String[] sSupportedMimeType = {
+ MIMETYPE_NATIVE, ClipDescription.MIMETYPE_TEXT_HTML, ClipDescription.MIMETYPE_TEXT_PLAIN
+ };
+
+ private static ClipData sDragClipData;
+ private static float sX;
+ private static float sY;
+ private static boolean mEndingSession;
+
+ private static class DrawDragImage extends View.DragShadowBuilder {
+ private final Bitmap mBitmap;
+
+ public DrawDragImage(final Bitmap bitmap) {
+ mBitmap = bitmap;
+ }
+
+ @Override
+ public void onProvideShadowMetrics(final Point outShadowSize, final Point outShadowTouchPoint) {
+ if (mBitmap == null) {
+ super.onProvideShadowMetrics(outShadowSize, outShadowTouchPoint);
+ return;
+ }
+ outShadowSize.set(mBitmap.getWidth(), mBitmap.getHeight());
+ }
+
+ @Override
+ public void onDrawShadow(final Canvas canvas) {
+ if (mBitmap == null) {
+ super.onDrawShadow(canvas);
+ return;
+ }
+ canvas.drawBitmap(mBitmap, 0.0f, 0.0f, null);
+ }
+ }
+
+ @WrapForJNI
+ public static class DropData {
+ public final String mimeType;
+ public final String text;
+
+ @WrapForJNI(skip = true)
+ public DropData() {
+ this.mimeType = MIMETYPE_NATIVE;
+ this.text = null;
+ }
+
+ @WrapForJNI(skip = true)
+ public DropData(final String mimeType) {
+ this.mimeType = mimeType;
+ this.text = "";
+ }
+
+ @WrapForJNI(skip = true)
+ public DropData(final String mimeType, final String text) {
+ this.mimeType = mimeType;
+ this.text = text;
+ }
+ }
+
+ public static void startDragAndDrop(final View view, final Bitmap bitmap) {
+ view.startDragAndDrop(sDragClipData, new DrawDragImage(bitmap), null, View.DRAG_FLAG_GLOBAL);
+ sDragClipData = null;
+ }
+
+ public static void updateDragImage(final View view, final Bitmap bitmap) {
+ view.updateDragShadow(new DrawDragImage(bitmap));
+ }
+
+ public static boolean onDragEvent(@NonNull final DragEvent event) {
+ if (DEBUG) {
+ final StringBuilder sb = new StringBuilder("onDragEvent: action=");
+ sb.append(event.getAction())
+ .append(", x=")
+ .append(event.getX())
+ .append(", y=")
+ .append(event.getY());
+ Log.d(LOGTAG, sb.toString());
+ }
+
+ switch (event.getAction()) {
+ case DragEvent.ACTION_DRAG_STARTED:
+ mEndingSession = false;
+ sX = event.getX();
+ sY = event.getY();
+ break;
+ case DragEvent.ACTION_DRAG_LOCATION:
+ sX = event.getX();
+ sY = event.getY();
+ break;
+ case DragEvent.ACTION_DROP:
+ sX = event.getX();
+ sY = event.getY();
+ break;
+ case DragEvent.ACTION_DRAG_ENDED:
+ mEndingSession = true;
+ return true;
+ default:
+ break;
+ }
+ if (mEndingSession) {
+ return false;
+ }
+ return true;
+ }
+
+ public static float getLocationX() {
+ return sX;
+ }
+
+ public static float getLocationY() {
+ return sY;
+ }
+
+ /**
+ * Create drop data by DragEvent. This ClipData will be stored into nsDragService as
+ * nsITransferable. If this type has MIMETYPE_NATIVE, this is already stored into nsDragService.
+ * So do nothing.
+ *
+ * @param event A DragEvent
+ * @return DropData that is from ClipData. If null, no data that we can convert to Gecko's type.
+ */
+ public static DropData createDropData(final DragEvent event) {
+ final ClipDescription description = event.getClipDescription();
+
+ if (event.getAction() == DragEvent.ACTION_DRAG_ENTERED) {
+ // Android API cannot get real dragging item until drop event. So we set MIME type only.
+ for (final String mimeType : sSupportedMimeType) {
+ if (description.hasMimeType(mimeType)) {
+ return new DropData(mimeType);
+ }
+ }
+ return null;
+ }
+
+ if (event.getAction() != DragEvent.ACTION_DROP) {
+ return null;
+ }
+
+ final ClipData clip = event.getClipData();
+ if (clip == null || clip.getItemCount() == 0) {
+ return null;
+ }
+
+ if (description.hasMimeType(MIMETYPE_NATIVE)) {
+ if (DEBUG) {
+ Log.d(LOGTAG, "Drop data is native nsITransferable. Do nothing");
+ }
+ return new DropData();
+ }
+ if (description.hasMimeType(ClipDescription.MIMETYPE_TEXT_HTML)) {
+ final CharSequence data = clip.getItemAt(0).getHtmlText();
+ if (data == null) {
+ return null;
+ }
+ if (DEBUG) {
+ Log.d(LOGTAG, "Drop data is text/html");
+ }
+ return new DropData(ClipDescription.MIMETYPE_TEXT_HTML, data.toString());
+ }
+
+ final CharSequence text = clip.getItemAt(0).coerceToText(GeckoAppShell.getApplicationContext());
+ if (!TextUtils.isEmpty(text)) {
+ if (DEBUG) {
+ Log.d(LOGTAG, "Drop data is text/plain");
+ }
+ return new DropData(ClipDescription.MIMETYPE_TEXT_PLAIN, text.toString());
+ }
+ return null;
+ }
+
+ private static void setDragClipData(final ClipData clipData) {
+ sDragClipData = clipData;
+ }
+
+ private static @Nullable ClipData getDragClipData() {
+ return sDragClipData;
+ }
+
+ /**
+ * Set drag item before calling View.startDragAndDrop. This is set from nsITransferable, so it
+ * marks as native data.
+ */
+ @WrapForJNI
+ private static void setDragData(final CharSequence text, final String htmlText) {
+ if (TextUtils.isEmpty(text)) {
+ final ClipDescription description =
+ new ClipDescription("drag item", new String[] {MIMETYPE_NATIVE});
+ final ClipData.Item item = new ClipData.Item("");
+ final ClipData clipData = new ClipData(description, item);
+ setDragClipData(clipData);
+ return;
+ }
+
+ if (TextUtils.isEmpty(htmlText)) {
+ final ClipDescription description =
+ new ClipDescription(
+ "drag item", new String[] {MIMETYPE_NATIVE, ClipDescription.MIMETYPE_TEXT_PLAIN});
+ final ClipData.Item item = new ClipData.Item(text);
+ final ClipData clipData = new ClipData(description, item);
+ setDragClipData(clipData);
+ return;
+ }
+
+ final ClipDescription description =
+ new ClipDescription(
+ "drag item",
+ new String[] {
+ MIMETYPE_NATIVE,
+ ClipDescription.MIMETYPE_TEXT_HTML,
+ ClipDescription.MIMETYPE_TEXT_PLAIN
+ });
+ final ClipData.Item item = new ClipData.Item(text, htmlText);
+ final ClipData clipData = new ClipData(description, item);
+ setDragClipData(clipData);
+ return;
+ }
+
+ @WrapForJNI
+ private static void endDragSession() {
+ mEndingSession = true;
+ }
+}