| /* |
| * Copyright (C) 2016 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 com.android.documentsui; |
| |
| import android.content.ClipData; |
| import android.graphics.drawable.Drawable; |
| import android.util.Log; |
| import android.view.DragEvent; |
| import android.view.View; |
| import android.view.View.OnDragListener; |
| |
| import androidx.annotation.VisibleForTesting; |
| |
| import com.android.documentsui.ItemDragListener.DragHost; |
| |
| import java.util.Timer; |
| import java.util.TimerTask; |
| |
| import javax.annotation.Nullable; |
| |
| /** |
| * An {@link OnDragListener} that adds support for "spring loading views". Use this when you want |
| * items to pop-open when user hovers on them during a drag n drop. |
| */ |
| public class ItemDragListener<H extends DragHost> implements OnDragListener { |
| |
| private static final String TAG = "ItemDragListener"; |
| |
| @VisibleForTesting |
| static final int DEFAULT_SPRING_TIMEOUT = 1500; |
| |
| protected final H mDragHost; |
| private final Timer mHoverTimer; |
| private final int mSpringTimeout; |
| |
| public ItemDragListener(H dragHost) { |
| this(dragHost, new Timer(), DEFAULT_SPRING_TIMEOUT); |
| } |
| |
| public ItemDragListener(H dragHost, int springTimeout) { |
| this(dragHost, new Timer(), springTimeout); |
| } |
| |
| @VisibleForTesting |
| protected ItemDragListener(H dragHost, Timer timer, int springTimeout) { |
| mDragHost = dragHost; |
| mHoverTimer = timer; |
| mSpringTimeout = springTimeout; |
| } |
| |
| @Override |
| public boolean onDrag(final View v, DragEvent event) { |
| switch (event.getAction()) { |
| case DragEvent.ACTION_DRAG_STARTED: |
| return mDragHost.canHandleDragEvent(v); |
| case DragEvent.ACTION_DRAG_ENTERED: |
| handleEnteredEvent(v, event); |
| return true; |
| case DragEvent.ACTION_DRAG_LOCATION: |
| handleLocationEvent(v, event.getX(), event.getY()); |
| return true; |
| case DragEvent.ACTION_DRAG_EXITED: |
| mDragHost.onDragExited(v); |
| handleExitedEndedEvent(v, event); |
| return true; |
| case DragEvent.ACTION_DRAG_ENDED: |
| mDragHost.onDragEnded(); |
| handleExitedEndedEvent(v, event); |
| return true; |
| case DragEvent.ACTION_DROP: |
| return handleDropEvent(v, event); |
| } |
| |
| return false; |
| } |
| |
| private void handleEnteredEvent(View v, DragEvent event) { |
| mDragHost.onDragEntered(v); |
| @Nullable TimerTask task = createOpenTask(v, event); |
| mDragHost.setDropTargetHighlight(v, true); |
| if (task == null) { |
| return; |
| } |
| v.setTag(R.id.drag_hovering_tag, task); |
| mHoverTimer.schedule(task, mSpringTimeout); |
| } |
| |
| private void handleLocationEvent(View v, float x, float y) { |
| Drawable background = v.getBackground(); |
| if (background != null) { |
| background.setHotspot(x, y); |
| } |
| } |
| |
| private void handleExitedEndedEvent(View v, DragEvent event) { |
| mDragHost.setDropTargetHighlight(v, false); |
| TimerTask task = (TimerTask) v.getTag(R.id.drag_hovering_tag); |
| if (task != null) { |
| task.cancel(); |
| } |
| } |
| |
| private boolean handleDropEvent(View v, DragEvent event) { |
| ClipData clipData = event.getClipData(); |
| if (clipData == null) { |
| Log.w(TAG, "Received invalid drop event with null clipdata. Ignoring."); |
| return false; |
| } |
| |
| return handleDropEventChecked(v, event); |
| } |
| |
| /** |
| * Sub-classes such as {@link DirectoryDragListener} can override this method and return null. |
| */ |
| public @Nullable TimerTask createOpenTask(final View v, DragEvent event) { |
| TimerTask task = new TimerTask() { |
| @Override |
| public void run() { |
| mDragHost.runOnUiThread(() -> { |
| mDragHost.onViewHovered(v); |
| }); |
| } |
| }; |
| return task; |
| } |
| |
| /** |
| * Handles a drop event. Override it if you want to do something on drop event. It's called when |
| * {@link DragEvent#ACTION_DROP} happens. ClipData in DragEvent is guaranteed not null. |
| * |
| * @param v The view where user drops. |
| * @param event the drag event. |
| * @return true if this event is consumed; false otherwise |
| */ |
| public boolean handleDropEventChecked(View v, DragEvent event) { |
| return false; // we didn't handle the drop |
| } |
| |
| /** |
| * An interface {@link ItemDragListener} uses to make some callbacks. |
| */ |
| public interface DragHost { |
| |
| /** |
| * Runs this runnable in main thread. |
| */ |
| void runOnUiThread(Runnable runnable); |
| |
| /** |
| * Highlights/unhighlights the view to visually indicate this view is being hovered. |
| * |
| * Called after {@link #onDragEntered(View)}, {@link #onDragExited(View)} |
| * or {@link #onDragEnded()}. |
| * |
| * @param v the view being hovered |
| * @param highlight true if highlight the view; false if unhighlight it |
| */ |
| void setDropTargetHighlight(View v, boolean highlight); |
| |
| /** |
| * Notifies hovering timeout has elapsed |
| * @param v the view being hovered |
| */ |
| void onViewHovered(View v); |
| |
| /** |
| * Notifies right away when drag shadow enters the view |
| * @param v the view which drop shadow just entered |
| */ |
| void onDragEntered(View v); |
| |
| /** |
| * Notifies right away when drag shadow exits the view |
| * @param v the view which drop shadow just exited |
| */ |
| void onDragExited(View v); |
| |
| /** |
| * Notifies when the drag and drop has ended. |
| */ |
| void onDragEnded(); |
| |
| /** |
| * Whether drag events can dispatch to the view. |
| * @param v the view being to receive drag events |
| */ |
| default boolean canHandleDragEvent(View v) { |
| return true; |
| } |
| } |
| } |