ItemDragListener.java revision 7f72a3c30ea89e2655ea6c453f8964bfedf4474b
1804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan/*
2804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * Copyright (C) 2016 The Android Open Source Project
3804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan *
4804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * Licensed under the Apache License, Version 2.0 (the "License");
5804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * you may not use this file except in compliance with the License.
6804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * You may obtain a copy of the License at
7804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan *
8804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan *      http://www.apache.org/licenses/LICENSE-2.0
9804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan *
10804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * Unless required by applicable law or agreed to in writing, software
11804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * distributed under the License is distributed on an "AS IS" BASIS,
12804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * See the License for the specific language governing permissions and
14804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * limitations under the License.
15804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan */
16804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
17804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanpackage com.android.documentsui;
18804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
19804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanimport android.content.ClipData;
2057facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tanimport android.graphics.drawable.Drawable;
21804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanimport android.util.Log;
22804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanimport android.view.DragEvent;
23804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanimport android.view.View;
24804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanimport android.view.View.OnDragListener;
25804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
26804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanimport com.android.documentsui.ItemDragListener.DragHost;
27804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanimport com.android.internal.annotations.VisibleForTesting;
28804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
29804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanimport java.util.Timer;
30804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanimport java.util.TimerTask;
31804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
325a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Linimport javax.annotation.Nullable;
335a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin
34804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan/**
35804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * An {@link OnDragListener} that adds support for "spring loading views". Use this when you want
36804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * items to pop-open when user hovers on them during a drag n drop.
37804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan */
38804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanpublic class ItemDragListener<H extends DragHost> implements OnDragListener {
39804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
40804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    private static final String TAG = "ItemDragListener";
41804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
42804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    @VisibleForTesting
437f72a3c30ea89e2655ea6c453f8964bfedf4474bBen Lin    static final int SPRING_TIMEOUT = 1500;
44804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
45804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    protected final H mDragHost;
46804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    private final Timer mHoverTimer;
47804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
48804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    public ItemDragListener(H dragHost) {
49804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        this(dragHost, new Timer());
50804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    }
51804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
52804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    @VisibleForTesting
53804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    protected ItemDragListener(H dragHost, Timer timer) {
54804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        mDragHost = dragHost;
55804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        mHoverTimer = timer;
56804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    }
57804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
58804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    @Override
59804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    public boolean onDrag(final View v, DragEvent event) {
60804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        switch (event.getAction()) {
61804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan            case DragEvent.ACTION_DRAG_STARTED:
62804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan                return true;
63804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan            case DragEvent.ACTION_DRAG_ENTERED:
645a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin                handleEnteredEvent(v, event);
65804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan                return true;
66804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan            case DragEvent.ACTION_DRAG_LOCATION:
6757facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan                handleLocationEvent(v, event.getX(), event.getY());
68804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan                return true;
69804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan            case DragEvent.ACTION_DRAG_EXITED:
70804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan            case DragEvent.ACTION_DRAG_ENDED:
71804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan                handleExitedEndedEvent(v);
72804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan                return true;
73804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan            case DragEvent.ACTION_DROP:
74804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan                return handleDropEvent(v, event);
75804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        }
76804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
77804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        return false;
78804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    }
79804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
805a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin    private void handleEnteredEvent(View v, DragEvent event) {
817f72a3c30ea89e2655ea6c453f8964bfedf4474bBen Lin        mDragHost.onDragEntered(v);
825a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin        @Nullable TimerTask task = createOpenTask(v, event);
835a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin        if (task == null) {
845a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin            return;
855a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin        }
86804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        mDragHost.setDropTargetHighlight(v, true);
87804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        v.setTag(R.id.drag_hovering_tag, task);
88a5588b65d55bc1b8b5ba943f8b660db26a7eac5cGarfield, Tan        mHoverTimer.schedule(task, SPRING_TIMEOUT);
89804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    }
90804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
9157facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan    private void handleLocationEvent(View v, float x, float y) {
9257facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan        Drawable background = v.getBackground();
9357facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan        if (background != null) {
9457facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan            background.setHotspot(x, y);
9557facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan        }
9657facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan    }
9757facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan
98804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    private void handleExitedEndedEvent(View v) {
99804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        mDragHost.setDropTargetHighlight(v, false);
100804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
101804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        TimerTask task = (TimerTask) v.getTag(R.id.drag_hovering_tag);
102804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        if (task != null) {
103804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan            task.cancel();
104804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        }
105804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    }
106804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
107804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    private boolean handleDropEvent(View v, DragEvent event) {
108804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        ClipData clipData = event.getClipData();
109804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        if (clipData == null) {
110804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan            Log.w(TAG, "Received invalid drop event with null clipdata. Ignoring.");
111804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan            return false;
112804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        }
113804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
114804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        return handleDropEventChecked(v, event);
115804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    }
116804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
1175a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin    /**
1185a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin     * Sub-classes such as {@link DirectoryDragListener} can override this method and return null.
1195a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin     */
1205a305b41ecd9c563c54bda9ff3c0d0f3739c5bdaBen Lin    public @Nullable TimerTask createOpenTask(final View v, DragEvent event) {
121804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        TimerTask task = new TimerTask() {
122804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan            @Override
123804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan            public void run() {
124804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan                mDragHost.runOnUiThread(() -> {
125804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan                    mDragHost.onViewHovered(v);
126804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan                });
127804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan            }
128804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        };
129804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        return task;
130804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    }
131804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
132804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    /**
133804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan     * Handles a drop event. Override it if you want to do something on drop event. It's called when
134804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan     * {@link DragEvent#ACTION_DROP} happens. ClipData in DragEvent is guaranteed not null.
135804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan     *
136804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan     * @param v The view where user drops.
137804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan     * @param event the drag event.
138804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan     * @return true if this event is consumed; false otherwise
139804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan     */
140804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    public boolean handleDropEventChecked(View v, DragEvent event) {
141804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        return false; // we didn't handle the drop
142804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    }
143804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
144804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    /**
145804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan     * An interface {@link ItemDragListener} uses to make some callbacks.
146804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan     */
147804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    public interface DragHost {
148804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
149804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        /**
150804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan         * Runs this runnable in main thread.
151804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan         */
152804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        void runOnUiThread(Runnable runnable);
153804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
154804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        /**
155804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan         * Highlights/unhighlights the view to visually indicate this view is being hovered.
156804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan         * @param v the view being hovered
157804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan         * @param highlight true if highlight the view; false if unhighlight it
158804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan         */
159804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        void setDropTargetHighlight(View v, boolean highlight);
160804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
161804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        /**
162804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan         * Notifies hovering timeout has elapsed
163804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan         * @param v the view being hovered
164804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan         */
165804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        void onViewHovered(View v);
1667f72a3c30ea89e2655ea6c453f8964bfedf4474bBen Lin
1677f72a3c30ea89e2655ea6c453f8964bfedf4474bBen Lin        /**
1687f72a3c30ea89e2655ea6c453f8964bfedf4474bBen Lin         * Notifies right away when drag shadow enters the view
1697f72a3c30ea89e2655ea6c453f8964bfedf4474bBen Lin         * @param v the view which drop shadow just entered
1707f72a3c30ea89e2655ea6c453f8964bfedf4474bBen Lin         */
1717f72a3c30ea89e2655ea6c453f8964bfedf4474bBen Lin        void onDragEntered(View v);
172804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    }
173804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan}
174