Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a53146c5 authored by Christopher Tate's avatar Christopher Tate
Browse files

Drag/drop APIs and infrastructure

A View initiates a drag-and-drop operation (hereafter just called a "drag")
by calling its startDrag(ClipData) method.  Within the processing of that
call, two callbacks are made into the originating View.  The first is to
onMeasureDragThumbnail().  Similarly to the core onMeasure() method, this
callback must respond by calling setDragThumbnailDimension(width, height) to
declare the size of the drag thumbnail image that should be used.  Following
this, the View's onDrawDragThumbnail(canvas) method will be invoked to
actually produce the bits of the thumbnail image.

If all goes well, startDrag() will return 'true', and the drag is off and
running.  (The other arguments to startDrag() provide reconciliation between
the current finger position and where the thumbnail should be placed on
the screen relative to it.)

Potential receipients of the ClipData behind the drag are notified by a
new dispatch mechanism, roughly parallel to motion event dispatch.  The core
routine is the View's onDragEvent(event) callback, with the mechanics of
dispatch itself being routed through dispatchDragEvent(event) -- as in
the case of motion events, the dispatch logic is in ViewGroup, with leaf
View objects not needing to consider the dispatch flow.

Several different event 'actions' are delivered through this dispatch
mechanism:

ACTION_DRAG_STARTED: this event is propagated to every View in every window
(including windows created during the course of a drag).  It serves as a
global notification that a drag has started with a payload whose matching
ClipDescription is supplied with the event.  A View that is prepared to
consume the data described in this event should return 'true' from their
onDragEvent() method, and ideally will also make some visible on-screen
indication that they are a potential target of the drop.

ACTION_DRAG_ENTERED: this event is sent once when the drag point
enters the View's bounds.  It is an opportunity for the View to set up
feedback that they are the one who will see the drop if the finger goes
up now.

ACTION_DRAG_LOCATION: when the drag point is over a given View, that
View will receive a stream of DRAG_LOCATION events, providing an
opportunity for the View to show visual feedback tied to the drag point.

ACTION_DRAG_EXITED: like DRAG_ENTERED, but called when the drag point
leaves the View's bounds.  The View should undo any visuals meant to
emphasize their being the hovered-over target.

ACTION_DROP: when the drag ends at a given point, the View under that
point is sent this event, with the full ClipData of the payload.

ACTION_DRAG_ENDED: paralleling the DRAG_STARTED action, this is the global
broadcast that the drag has ended and all Views should return to their
normal visual state.  This happens after the DROP event.

Change-Id: Ia8d0fb1516bce8c735d87ffd101af0976d7e84b6
parent 07b88ea0
Loading
Loading
Loading
Loading
+276 −0
Original line number Diff line number Diff line
@@ -189612,6 +189612,217 @@
>
</field>
</class>
<class name="DragEvent"
 extends="java.lang.Object"
 abstract="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<implements name="android.os.Parcelable">
</implements>
<method name="describeContents"
 return="int"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="getAction"
 return="int"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="getClipData"
 return="android.content.ClipData"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="getClipDescription"
 return="android.content.ClipDescription"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="getX"
 return="float"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="getY"
 return="float"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="obtain"
 return="android.view.DragEvent"
 abstract="false"
 native="false"
 synchronized="false"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="obtain"
 return="android.view.DragEvent"
 abstract="false"
 native="false"
 synchronized="false"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="action" type="int">
</parameter>
<parameter name="x" type="float">
</parameter>
<parameter name="y" type="float">
</parameter>
<parameter name="description" type="android.content.ClipDescription">
</parameter>
<parameter name="data" type="android.content.ClipData">
</parameter>
</method>
<method name="recycle"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</method>
<method name="writeToParcel"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="dest" type="android.os.Parcel">
</parameter>
<parameter name="flags" type="int">
</parameter>
</method>
<field name="ACTION_DRAG_ENDED"
 type="int"
 transient="false"
 volatile="false"
 value="4"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="ACTION_DRAG_ENTERED"
 type="int"
 transient="false"
 volatile="false"
 value="5"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="ACTION_DRAG_EXITED"
 type="int"
 transient="false"
 volatile="false"
 value="6"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="ACTION_DRAG_LOCATION"
 type="int"
 transient="false"
 volatile="false"
 value="2"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="ACTION_DRAG_STARTED"
 type="int"
 transient="false"
 volatile="false"
 value="1"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="ACTION_DROP"
 type="int"
 transient="false"
 volatile="false"
 value="3"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="CREATOR"
 type="android.os.Parcelable.Creator"
 transient="false"
 volatile="false"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
</class>
<class name="FocusFinder"
 extends="java.lang.Object"
 abstract="false"
@@ -198568,6 +198779,19 @@
<parameter name="hint" type="int">
</parameter>
</method>
<method name="dispatchDragEvent"
 return="boolean"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="event" type="android.view.DragEvent">
</parameter>
</method>
<method name="dispatchDraw"
 return="void"
 abstract="false"
@@ -200533,6 +200757,19 @@
<parameter name="hint" type="int">
</parameter>
</method>
<method name="onDragEvent"
 return="boolean"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="protected"
>
<parameter name="event" type="android.view.DragEvent">
</parameter>
</method>
<method name="onDraw"
 return="void"
 abstract="false"
@@ -200546,6 +200783,19 @@
<parameter name="canvas" type="android.graphics.Canvas">
</parameter>
</method>
<method name="onDrawDragThumbnail"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="protected"
>
<parameter name="canvas" type="android.graphics.Canvas">
</parameter>
</method>
<method name="onDrawScrollBars"
 return="void"
 abstract="false"
@@ -200739,6 +200989,17 @@
<parameter name="heightMeasureSpec" type="int">
</parameter>
</method>
<method name="onMeasureDragThumbnail"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="protected"
>
</method>
<method name="onRestoreInstanceState"
 return="void"
 abstract="false"
@@ -201404,6 +201665,21 @@
<parameter name="contentDescription" type="java.lang.CharSequence">
</parameter>
</method>
<method name="setDragThumbnailDimension"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="true"
 deprecated="not deprecated"
 visibility="protected"
>
<parameter name="width" type="int">
</parameter>
<parameter name="height" type="int">
</parameter>
</method>
<method name="setDrawingCacheBackgroundColor"
 return="void"
 abstract="false"
+12 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.content.res;

import android.content.pm.ApplicationInfo;
import android.graphics.Canvas;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
import android.util.DisplayMetrics;
@@ -362,6 +363,17 @@ public class CompatibilityInfo {
            rect.scale(applicationInvertedScale);
        }

        /**
         * Translate a Point in screen coordinates into the app window's coordinates.
         */
        public void translatePointInScreenToAppWindow(PointF point) {
            final float scale = applicationInvertedScale;
            if (scale != 1.0f) {
                point.x *= scale;
                point.y *= scale;
            }
        }

        /**
         * Translate the location of the sub window.
         * @param params
+19 −0
Original line number Diff line number Diff line
/*
** Copyright 2010, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License"); 
** you may not use this file except in compliance with the License. 
** You may obtain a copy of the License at 
**
**     http://www.apache.org/licenses/LICENSE-2.0 
**
** Unless required by applicable law or agreed to in writing, software 
** distributed under the License is distributed on an "AS IS" BASIS, 
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
** See the License for the specific language governing permissions and 
** limitations under the License.
*/

package android.view;

parcelable DragEvent;
+186 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.view;

import android.content.ClipData;
import android.content.ClipDescription;
import android.os.Parcel;
import android.os.Parcelable;

/** !!! TODO: real docs */
public class DragEvent implements Parcelable {
    private static final boolean TRACK_RECYCLED_LOCATION = false;

    int mAction;
    float mX, mY;
    ClipDescription mClipDescription;
    ClipData mClipData;

    private DragEvent mNext;
    private RuntimeException mRecycledLocation;
    private boolean mRecycled;

    private static final int MAX_RECYCLED = 10;
    private static final Object gRecyclerLock = new Object();
    private static int gRecyclerUsed = 0;
    private static DragEvent gRecyclerTop = null;

    /**
     * action constants for DragEvent dispatch
     */
    public static final int ACTION_DRAG_STARTED = 1;
    public static final int ACTION_DRAG_LOCATION = 2;
    public static final int ACTION_DROP = 3;
    public static final int ACTION_DRAG_ENDED = 4;
    public static final int ACTION_DRAG_ENTERED = 5;
    public static final int ACTION_DRAG_EXITED = 6;

    /* hide the constructor behind package scope */
    DragEvent() {
    }

    public static DragEvent obtain() {
        return DragEvent.obtain(0, 0f, 0f, null, null);
    }

    public static DragEvent obtain(int action, float x, float y,
            ClipDescription description, ClipData data) {
        final DragEvent ev;
        synchronized (gRecyclerLock) {
            if (gRecyclerTop == null) {
                return new DragEvent();
            }
            ev = gRecyclerTop;
            gRecyclerTop = ev.mNext;
            gRecyclerUsed -= 1;
        }
        ev.mRecycledLocation = null;
        ev.mRecycled = false;
        ev.mNext = null;

        ev.mAction = action;
        ev.mX = x;
        ev.mY = y;
        ev.mClipDescription = description;
        ev.mClipData = data;

        return ev;
    }

    public int getAction() {
        return mAction;
    }

    public float getX() {
        return mX;
    }

    public float getY() {
        return mY;
    }

    public ClipData getClipData() {
        return mClipData;
    }

    public ClipDescription getClipDescription() {
        return mClipDescription;
    }

    /**
     * Recycle the DragEvent, to be re-used by a later caller.  After calling
     * this function you must never touch the event again.
     */
    public final void recycle() {
        // Ensure recycle is only called once!
        if (TRACK_RECYCLED_LOCATION) {
            if (mRecycledLocation != null) {
                throw new RuntimeException(toString() + " recycled twice!", mRecycledLocation);
            }
            mRecycledLocation = new RuntimeException("Last recycled here");
        } else {
            if (mRecycled) {
                throw new RuntimeException(toString() + " recycled twice!");
            }
            mRecycled = true;
        }

        mClipData = null;
        mClipDescription = null;

        synchronized (gRecyclerLock) {
            if (gRecyclerUsed < MAX_RECYCLED) {
                gRecyclerUsed++;
                mNext = gRecyclerTop;
                gRecyclerTop = this;
            }
        }
    }

    @Override
    public String toString() {
        return "DragEvent{" + Integer.toHexString(System.identityHashCode(this))
        + " action=" + mAction + " @ (" + mX + ", " + mY + ") desc=" + mClipDescription
        + " data=" + mClipData
        + "}";
    }

    /* Parcelable interface */

    public int describeContents() {
        return 0;
    }

    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(mAction);
        dest.writeFloat(mX);
        dest.writeFloat(mY);
        if (mClipData == null) {
            dest.writeInt(0);
        } else {
            dest.writeInt(1);
            mClipData.writeToParcel(dest, flags);
        }
        if (mClipDescription == null) {
            dest.writeInt(0);
        } else {
            dest.writeInt(1);
            mClipDescription.writeToParcel(dest, flags);
        }
    }

    public static final Parcelable.Creator<DragEvent> CREATOR =
        new Parcelable.Creator<DragEvent>() {
        public DragEvent createFromParcel(Parcel in) {
            DragEvent event = DragEvent.obtain();
            event.mAction = in.readInt();
            event.mX = in.readFloat();
            event.mY = in.readFloat();
            if (in.readInt() != 0) {
                event.mClipData = ClipData.CREATOR.createFromParcel(in);
            }
            if (in.readInt() != 0) {
                event.mClipDescription = ClipDescription.CREATOR.createFromParcel(in);
            }
            return event;
        }

        public DragEvent[] newArray(int size) {
            return new DragEvent[size];
        }
    };
}
+8 −0
Original line number Diff line number Diff line
@@ -17,10 +17,13 @@

package android.view;

import android.content.ClipData;
import android.content.ClipDescription;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.view.DragEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;

@@ -64,4 +67,9 @@ oneway interface IWindow {
    
    void dispatchWallpaperCommand(String action, int x, int y,
            int z, in Bundle extras, boolean sync);

    /**
     * Drag/drop events
     */
     void dispatchDragEvent(in DragEvent event);
}
Loading