ItemDragListener.java revision 57facaf76bc8d23b539518a342e1a126b51b64ce
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, Tanimport android.view.ViewConfiguration;
26804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
27804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanimport com.android.documentsui.ItemDragListener.DragHost;
28804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanimport com.android.internal.annotations.VisibleForTesting;
29804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
30804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanimport java.util.Timer;
31804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanimport java.util.TimerTask;
32804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
33804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan/**
34804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * An {@link OnDragListener} that adds support for "spring loading views". Use this when you want
35804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan * items to pop-open when user hovers on them during a drag n drop.
36804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan */
37804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tanpublic class ItemDragListener<H extends DragHost> implements OnDragListener {
38804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
39804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    private static final String TAG = "ItemDragListener";
40804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
41804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    @VisibleForTesting
42804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    static final int SPRING_TIMEOUT = ViewConfiguration.getLongPressTimeout();
43804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
44804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    protected final H mDragHost;
45804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    private final Timer mHoverTimer;
46804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
47804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    public ItemDragListener(H dragHost) {
48804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        this(dragHost, new Timer());
49804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    }
50804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
51804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    @VisibleForTesting
52804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    protected ItemDragListener(H dragHost, Timer timer) {
53804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        mDragHost = dragHost;
54804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        mHoverTimer = timer;
55804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    }
56804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
57804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    @Override
58804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    public boolean onDrag(final View v, DragEvent event) {
59804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        switch (event.getAction()) {
60804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan            case DragEvent.ACTION_DRAG_STARTED:
61804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan                return true;
62804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan            case DragEvent.ACTION_DRAG_ENTERED:
63804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan                handleEnteredEvent(v);
64804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan                return true;
65804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan            case DragEvent.ACTION_DRAG_LOCATION:
6657facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan                handleLocationEvent(v, event.getX(), event.getY());
67804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan                return true;
68804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan            case DragEvent.ACTION_DRAG_EXITED:
69804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan            case DragEvent.ACTION_DRAG_ENDED:
70804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan                handleExitedEndedEvent(v);
71804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan                return true;
72804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan            case DragEvent.ACTION_DROP:
73804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan                return handleDropEvent(v, event);
74804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        }
75804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
76804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        return false;
77804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    }
78804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
79804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    private void handleEnteredEvent(View v) {
80804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        mDragHost.setDropTargetHighlight(v, true);
81804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
82804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        TimerTask task = createOpenTask(v);
83804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        assert (task != null);
84804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        v.setTag(R.id.drag_hovering_tag, task);
85804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        mHoverTimer.schedule(task, ViewConfiguration.getLongPressTimeout());
86804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    }
87804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
8857facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan    private void handleLocationEvent(View v, float x, float y) {
8957facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan        Drawable background = v.getBackground();
9057facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan        if (background != null) {
9157facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan            background.setHotspot(x, y);
9257facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan        }
9357facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan    }
9457facaf76bc8d23b539518a342e1a126b51b64ceGarfield, Tan
95804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    private void handleExitedEndedEvent(View v) {
96804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        mDragHost.setDropTargetHighlight(v, false);
97804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
98804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        TimerTask task = (TimerTask) v.getTag(R.id.drag_hovering_tag);
99804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        if (task != null) {
100804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan            task.cancel();
101804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        }
102804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    }
103804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
104804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    private boolean handleDropEvent(View v, DragEvent event) {
105804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        ClipData clipData = event.getClipData();
106804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        if (clipData == null) {
107804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan            Log.w(TAG, "Received invalid drop event with null clipdata. Ignoring.");
108804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan            return false;
109804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        }
110804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
111804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        return handleDropEventChecked(v, event);
112804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    }
113804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
114804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    @VisibleForTesting
115804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    TimerTask createOpenTask(final View v) {
116804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        TimerTask task = new TimerTask() {
117804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan            @Override
118804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan            public void run() {
119804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan                mDragHost.runOnUiThread(() -> {
120804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan                    mDragHost.onViewHovered(v);
121804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan                });
122804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan            }
123804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        };
124804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        return task;
125804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    }
126804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
127804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    /**
128804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan     * Handles a drop event. Override it if you want to do something on drop event. It's called when
129804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan     * {@link DragEvent#ACTION_DROP} happens. ClipData in DragEvent is guaranteed not null.
130804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan     *
131804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan     * @param v The view where user drops.
132804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan     * @param event the drag event.
133804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan     * @return true if this event is consumed; false otherwise
134804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan     */
135804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    public boolean handleDropEventChecked(View v, DragEvent event) {
136804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        return false; // we didn't handle the drop
137804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    }
138804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
139804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    /**
140804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan     * An interface {@link ItemDragListener} uses to make some callbacks.
141804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan     */
142804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    public interface DragHost {
143804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
144804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        /**
145804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan         * Runs this runnable in main thread.
146804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan         */
147804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        void runOnUiThread(Runnable runnable);
148804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
149804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        /**
150804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan         * Highlights/unhighlights the view to visually indicate this view is being hovered.
151804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan         * @param v the view being hovered
152804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan         * @param highlight true if highlight the view; false if unhighlight it
153804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan         */
154804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        void setDropTargetHighlight(View v, boolean highlight);
155804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan
156804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        /**
157804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan         * Notifies hovering timeout has elapsed
158804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan         * @param v the view being hovered
159804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan         */
160804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan        void onViewHovered(View v);
161804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan    }
162804133e4ca98ffa168cd547793054b594cf6d9ccGarfield, Tan}
163