diff options
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.java | 253 |
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; + } +} |