tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

GeckoDragAndDrop.java (8178B)


      1 /* -*- Mode: Java; c-basic-offset: 2; tab-width: 20; indent-tabs-mode: nil; -*- */
      2 /* vim: set ts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 package org.mozilla.gecko;
      8 
      9 import android.content.ClipData;
     10 import android.content.ClipDescription;
     11 import android.graphics.Bitmap;
     12 import android.graphics.Canvas;
     13 import android.graphics.Point;
     14 import android.text.TextUtils;
     15 import android.util.Log;
     16 import android.view.DragEvent;
     17 import android.view.View;
     18 import androidx.annotation.NonNull;
     19 import androidx.annotation.Nullable;
     20 import org.mozilla.gecko.annotation.WrapForJNI;
     21 
     22 public class GeckoDragAndDrop {
     23  private static final String LOGTAG = "GeckoDragAndDrop";
     24  private static final boolean DEBUG = false;
     25 
     26  /** The drag/drop data is nsITransferable and stored into nsDragService. */
     27  private static final String MIMETYPE_NATIVE = "application/x-moz-draganddrop";
     28 
     29  private static final String[] sSupportedMimeType = {
     30    MIMETYPE_NATIVE, ClipDescription.MIMETYPE_TEXT_HTML, ClipDescription.MIMETYPE_TEXT_PLAIN
     31  };
     32 
     33  private static ClipData sDragClipData;
     34  private static float sX;
     35  private static float sY;
     36  private static boolean mEndingSession;
     37 
     38  private static class DrawDragImage extends View.DragShadowBuilder {
     39    private final Bitmap mBitmap;
     40 
     41    public DrawDragImage(final Bitmap bitmap) {
     42      if (bitmap != null && bitmap.getWidth() > 0 && bitmap.getHeight() > 0) {
     43        mBitmap = bitmap;
     44        return;
     45      }
     46      mBitmap = null;
     47    }
     48 
     49    @Override
     50    public void onProvideShadowMetrics(final Point outShadowSize, final Point outShadowTouchPoint) {
     51      if (mBitmap == null) {
     52        super.onProvideShadowMetrics(outShadowSize, outShadowTouchPoint);
     53        if (outShadowSize.x <= 0 || outShadowSize.y <= 0) {
     54          // startDragAndDrop might throw an IllegalStateException exception if the shadow size is
     55          // zero or negative.
     56          outShadowSize.set(1, 1);
     57        }
     58        if (outShadowTouchPoint.x < 0 || outShadowTouchPoint.y < 0) {
     59          // startDragAndDrop might throw an IllegalStateException exception if the touch point is
     60          // negative.
     61          outShadowTouchPoint.set(0, 0);
     62        }
     63        return;
     64      }
     65      outShadowSize.set(mBitmap.getWidth(), mBitmap.getHeight());
     66    }
     67 
     68    @Override
     69    public void onDrawShadow(final Canvas canvas) {
     70      if (mBitmap == null) {
     71        super.onDrawShadow(canvas);
     72        return;
     73      }
     74      canvas.drawBitmap(mBitmap, 0.0f, 0.0f, null);
     75    }
     76  }
     77 
     78  @WrapForJNI
     79  public static class DropData {
     80    public final String mimeType;
     81    public final String text;
     82 
     83    @WrapForJNI(skip = true)
     84    public DropData() {
     85      this.mimeType = MIMETYPE_NATIVE;
     86      this.text = null;
     87    }
     88 
     89    @WrapForJNI(skip = true)
     90    public DropData(final String mimeType) {
     91      this.mimeType = mimeType;
     92      this.text = "";
     93    }
     94 
     95    @WrapForJNI(skip = true)
     96    public DropData(final String mimeType, final String text) {
     97      this.mimeType = mimeType;
     98      this.text = text;
     99    }
    100  }
    101 
    102  public static void startDragAndDrop(final View view, final Bitmap bitmap) {
    103    view.startDragAndDrop(sDragClipData, new DrawDragImage(bitmap), null, View.DRAG_FLAG_GLOBAL);
    104    sDragClipData = null;
    105  }
    106 
    107  public static void updateDragImage(final View view, final Bitmap bitmap) {
    108    view.updateDragShadow(new DrawDragImage(bitmap));
    109  }
    110 
    111  public static boolean onDragEvent(@NonNull final DragEvent event) {
    112    if (DEBUG) {
    113      final StringBuilder sb = new StringBuilder("onDragEvent: action=");
    114      sb.append(event.getAction())
    115          .append(", x=")
    116          .append(event.getX())
    117          .append(", y=")
    118          .append(event.getY());
    119      Log.d(LOGTAG, sb.toString());
    120    }
    121 
    122    switch (event.getAction()) {
    123      case DragEvent.ACTION_DRAG_STARTED:
    124        mEndingSession = false;
    125        sX = event.getX();
    126        sY = event.getY();
    127        break;
    128      case DragEvent.ACTION_DRAG_LOCATION:
    129        sX = event.getX();
    130        sY = event.getY();
    131        break;
    132      case DragEvent.ACTION_DROP:
    133        sX = event.getX();
    134        sY = event.getY();
    135        break;
    136      case DragEvent.ACTION_DRAG_ENDED:
    137        mEndingSession = true;
    138        return true;
    139      default:
    140        break;
    141    }
    142    if (mEndingSession) {
    143      return false;
    144    }
    145    return true;
    146  }
    147 
    148  public static float getLocationX() {
    149    return sX;
    150  }
    151 
    152  public static float getLocationY() {
    153    return sY;
    154  }
    155 
    156  /**
    157   * Create drop data by DragEvent. This ClipData will be stored into nsDragService as
    158   * nsITransferable. If this type has MIMETYPE_NATIVE, this is already stored into nsDragService.
    159   * So do nothing.
    160   *
    161   * @param event A DragEvent
    162   * @return DropData that is from ClipData. If null, no data that we can convert to Gecko's type.
    163   */
    164  public static DropData createDropData(final DragEvent event) {
    165    final ClipDescription description = event.getClipDescription();
    166 
    167    if (event.getAction() == DragEvent.ACTION_DRAG_ENTERED) {
    168      // Android API cannot get real dragging item until drop event. So we set MIME type only.
    169      for (final String mimeType : sSupportedMimeType) {
    170        if (description.hasMimeType(mimeType)) {
    171          return new DropData(mimeType);
    172        }
    173      }
    174      return null;
    175    }
    176 
    177    if (event.getAction() != DragEvent.ACTION_DROP) {
    178      return null;
    179    }
    180 
    181    final ClipData clip = event.getClipData();
    182    if (clip == null || clip.getItemCount() == 0) {
    183      return null;
    184    }
    185 
    186    if (description.hasMimeType(MIMETYPE_NATIVE)) {
    187      if (DEBUG) {
    188        Log.d(LOGTAG, "Drop data is native nsITransferable. Do nothing");
    189      }
    190      return new DropData();
    191    }
    192    if (description.hasMimeType(ClipDescription.MIMETYPE_TEXT_HTML)) {
    193      final CharSequence data = clip.getItemAt(0).getHtmlText();
    194      if (data == null) {
    195        return null;
    196      }
    197      if (DEBUG) {
    198        Log.d(LOGTAG, "Drop data is text/html");
    199      }
    200      return new DropData(ClipDescription.MIMETYPE_TEXT_HTML, data.toString());
    201    }
    202 
    203    final CharSequence text = clip.getItemAt(0).coerceToText(GeckoAppShell.getApplicationContext());
    204    if (!TextUtils.isEmpty(text)) {
    205      if (DEBUG) {
    206        Log.d(LOGTAG, "Drop data is text/plain");
    207      }
    208      return new DropData(ClipDescription.MIMETYPE_TEXT_PLAIN, text.toString());
    209    }
    210    return null;
    211  }
    212 
    213  private static void setDragClipData(final ClipData clipData) {
    214    sDragClipData = clipData;
    215  }
    216 
    217  private static @Nullable ClipData getDragClipData() {
    218    return sDragClipData;
    219  }
    220 
    221  /**
    222   * Set drag item before calling View.startDragAndDrop. This is set from nsITransferable, so it
    223   * marks as native data.
    224   */
    225  @WrapForJNI
    226  private static void setDragData(final CharSequence text, final String htmlText) {
    227    if (TextUtils.isEmpty(text)) {
    228      final ClipDescription description =
    229          new ClipDescription("drag item", new String[] {MIMETYPE_NATIVE});
    230      final ClipData.Item item = new ClipData.Item("");
    231      final ClipData clipData = new ClipData(description, item);
    232      setDragClipData(clipData);
    233      return;
    234    }
    235 
    236    if (TextUtils.isEmpty(htmlText)) {
    237      final ClipDescription description =
    238          new ClipDescription(
    239              "drag item", new String[] {MIMETYPE_NATIVE, ClipDescription.MIMETYPE_TEXT_PLAIN});
    240      final ClipData.Item item = new ClipData.Item(text);
    241      final ClipData clipData = new ClipData(description, item);
    242      setDragClipData(clipData);
    243      return;
    244    }
    245 
    246    final ClipDescription description =
    247        new ClipDescription(
    248            "drag item",
    249            new String[] {
    250              MIMETYPE_NATIVE,
    251              ClipDescription.MIMETYPE_TEXT_HTML,
    252              ClipDescription.MIMETYPE_TEXT_PLAIN
    253            });
    254    final ClipData.Item item = new ClipData.Item(text, htmlText);
    255    final ClipData clipData = new ClipData(description, item);
    256    setDragClipData(clipData);
    257    return;
    258  }
    259 
    260  @WrapForJNI
    261  private static void endDragSession() {
    262    mEndingSession = true;
    263  }
    264 }