blob: 9994de5f30cf805f71bd77054653fc9fd00ed103 [file] [log] [blame]
Ben Linc5e3e8e2016-07-13 18:16:36 -07001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Riddle Hsuded3dc52018-05-17 21:27:07 +080017package com.android.documentsui;
Ben Linc5e3e8e2016-07-13 18:16:36 -070018
Ben Linc5e3e8e2016-07-13 18:16:36 -070019import android.graphics.Point;
KOUSHIK PANUGANTI6ca7acc2018-04-17 16:00:10 -070020import androidx.annotation.VisibleForTesting;
Ben Linc5e3e8e2016-07-13 18:16:36 -070021import android.view.DragEvent;
22import android.view.View;
23import android.view.View.OnDragListener;
Riddle Hsuded3dc52018-05-17 21:27:07 +080024import android.widget.AbsListView;
Ben Linc5e3e8e2016-07-13 18:16:36 -070025
Ben Linc5e3e8e2016-07-13 18:16:36 -070026import com.android.documentsui.ItemDragListener.DragHost;
Riddle Hsu0c375982018-06-21 22:06:43 +080027import com.android.documentsui.ViewAutoScroller.ScrollHost;
28import com.android.documentsui.ViewAutoScroller.ScrollerCallbacks;
Ben Linc5e3e8e2016-07-13 18:16:36 -070029
30import java.util.function.BooleanSupplier;
Riddle Hsuded3dc52018-05-17 21:27:07 +080031import java.util.function.IntConsumer;
Ben Linc5e3e8e2016-07-13 18:16:36 -070032import java.util.function.IntSupplier;
Garfield, Tan61f564b2016-08-16 13:36:15 -070033import java.util.function.Predicate;
Ben Linc5e3e8e2016-07-13 18:16:36 -070034
35import javax.annotation.Nullable;
36
37/**
38 * This class acts as a middle-man handler for potential auto-scrolling before passing the dragEvent
Riddle Hsuded3dc52018-05-17 21:27:07 +080039 * onto {@link ItemDragListener}.
Ben Linc5e3e8e2016-07-13 18:16:36 -070040 */
Riddle Hsuded3dc52018-05-17 21:27:07 +080041public class DragHoverListener implements OnDragListener {
Ben Linc5e3e8e2016-07-13 18:16:36 -070042
43 private final ItemDragListener<? extends DragHost> mDragHandler;
44 private final IntSupplier mHeight;
45 private final BooleanSupplier mCanScrollUp;
46 private final BooleanSupplier mCanScrollDown;
Ben Linc5e3e8e2016-07-13 18:16:36 -070047 private final Runnable mDragScroller;
48
Garfield, Tan61f564b2016-08-16 13:36:15 -070049 /**
Riddle Hsuded3dc52018-05-17 21:27:07 +080050 * Predicate to tests whether it's the scroll view itself.
Garfield, Tan61f564b2016-08-16 13:36:15 -070051 *
Riddle Hsuded3dc52018-05-17 21:27:07 +080052 * {@link DragHoverListener} is used for both the scroll view and its children.
53 * When we decide whether it's in the scroll zone we need to obtain the coordinate
54 * relative to container view so we need to transform the coordinate if the view
55 * that gets drag and drop events is a child of scroll view.
Garfield, Tan61f564b2016-08-16 13:36:15 -070056 */
57 private final Predicate<View> mIsScrollView;
58
Ben Linc5e3e8e2016-07-13 18:16:36 -070059 private boolean mDragHappening;
60 private @Nullable Point mCurrentPosition;
61
Ben Lin2abb4c72016-08-01 18:05:23 -070062 @VisibleForTesting
Ben Lin35f99e02016-08-31 12:46:04 -070063 DragHoverListener(
Ben Linc5e3e8e2016-07-13 18:16:36 -070064 ItemDragListener<? extends DragHost> dragHandler,
65 IntSupplier heightSupplier,
Garfield, Tan61f564b2016-08-16 13:36:15 -070066 Predicate<View> isScrollView,
Ben Linc5e3e8e2016-07-13 18:16:36 -070067 BooleanSupplier scrollUpSupplier,
68 BooleanSupplier scrollDownSupplier,
Steve McKay5a620372017-09-11 12:18:56 -070069 ViewAutoScroller.ScrollerCallbacks scrollCallbacks) {
Steve McKay12394522017-08-24 14:14:10 -070070
Ben Linc5e3e8e2016-07-13 18:16:36 -070071 mDragHandler = dragHandler;
Ben Linc5e3e8e2016-07-13 18:16:36 -070072 mHeight = heightSupplier;
Garfield, Tan61f564b2016-08-16 13:36:15 -070073 mIsScrollView = isScrollView;
Ben Linc5e3e8e2016-07-13 18:16:36 -070074 mCanScrollUp = scrollUpSupplier;
75 mCanScrollDown = scrollDownSupplier;
76
Steve McKay12394522017-08-24 14:14:10 -070077 ScrollHost scrollHost = new ScrollHost() {
Ben Linc5e3e8e2016-07-13 18:16:36 -070078 @Override
79 public Point getCurrentPosition() {
80 return mCurrentPosition;
81 }
82
83 @Override
84 public int getViewHeight() {
85 return mHeight.getAsInt();
86 }
87
88 @Override
89 public boolean isActive() {
90 return mDragHappening;
91 }
92 };
93
Steve McKay12394522017-08-24 14:14:10 -070094 mDragScroller = new ViewAutoScroller(scrollHost, scrollCallbacks);
Ben Linc5e3e8e2016-07-13 18:16:36 -070095 }
96
Riddle Hsuded3dc52018-05-17 21:27:07 +080097 public static DragHoverListener create(
98 ItemDragListener<? extends DragHost> dragHandler,
99 AbsListView scrollView) {
100 return create(dragHandler, scrollView, scrollView::scrollListBy);
101 }
102
103 public static DragHoverListener create(
Ben Lin2abb4c72016-08-01 18:05:23 -0700104 ItemDragListener<? extends DragHost> dragHandler,
105 View scrollView) {
Riddle Hsuded3dc52018-05-17 21:27:07 +0800106 return create(
107 dragHandler,
108 scrollView,
109 (int dy) -> {
110 scrollView.scrollBy(0, dy);
111 });
112 }
113
114 static DragHoverListener create(
115 ItemDragListener<? extends DragHost> dragHandler,
116 View scrollView,
117 IntConsumer scroller) {
Steve McKay12394522017-08-24 14:14:10 -0700118
Steve McKay5a620372017-09-11 12:18:56 -0700119 ScrollerCallbacks scrollCallbacks = new ScrollerCallbacks() {
Ben Linc5e3e8e2016-07-13 18:16:36 -0700120 @Override
121 public void scrollBy(int dy) {
Riddle Hsuded3dc52018-05-17 21:27:07 +0800122 scroller.accept(dy);
Ben Linc5e3e8e2016-07-13 18:16:36 -0700123 }
124
125 @Override
126 public void runAtNextFrame(Runnable r) {
127 scrollView.postOnAnimation(r);
Ben Linc5e3e8e2016-07-13 18:16:36 -0700128 }
129
130 @Override
131 public void removeCallback(Runnable r) {
132 scrollView.removeCallbacks(r);
133 }
134 };
Steve McKay12394522017-08-24 14:14:10 -0700135
Riddle Hsuded3dc52018-05-17 21:27:07 +0800136 return new DragHoverListener(
Ben Linc5e3e8e2016-07-13 18:16:36 -0700137 dragHandler,
138 scrollView::getHeight,
Garfield, Tan61f564b2016-08-16 13:36:15 -0700139 (view) -> (scrollView == view),
140 () -> scrollView.canScrollVertically(-1),
141 () -> scrollView.canScrollVertically(1),
Steve McKay12394522017-08-24 14:14:10 -0700142 scrollCallbacks);
Ben Linc5e3e8e2016-07-13 18:16:36 -0700143 }
144
145 @Override
146 public boolean onDrag(View v, DragEvent event) {
Ben Linc5e3e8e2016-07-13 18:16:36 -0700147 switch (event.getAction()) {
148 case DragEvent.ACTION_DRAG_STARTED:
149 mDragHappening = true;
150 break;
151 case DragEvent.ACTION_DRAG_ENDED:
152 mDragHappening = false;
153 break;
Ben Linc5e3e8e2016-07-13 18:16:36 -0700154 case DragEvent.ACTION_DRAG_LOCATION:
Ben Lin7c5deaa2016-10-04 14:31:54 -0700155 handleLocationEvent(v, event.getX(), event.getY());
Ben Linc5e3e8e2016-07-13 18:16:36 -0700156 break;
157 default:
158 break;
159 }
160
Ben Lin7c5deaa2016-10-04 14:31:54 -0700161 // Always forward events to the drag handler for item highlight, spring load, etc.
162 return mDragHandler.onDrag(v, event);
Ben Linc5e3e8e2016-07-13 18:16:36 -0700163 }
164
165 private boolean handleLocationEvent(View v, float x, float y) {
Garfield, Tan61f564b2016-08-16 13:36:15 -0700166 mCurrentPosition = transformToScrollViewCoordinate(v, x, y);
Ben Linc5e3e8e2016-07-13 18:16:36 -0700167 if (insideDragZone()) {
168 mDragScroller.run();
169 return true;
170 }
171 return false;
172 }
173
Garfield, Tan61f564b2016-08-16 13:36:15 -0700174 private Point transformToScrollViewCoordinate(View v, float x, float y) {
Riddle Hsuded3dc52018-05-17 21:27:07 +0800175 // Check if v is the scroll view itself. If not we need to transform the coordinate to
176 // relative to the scroll view because we need to test the scroll zone in the coordinate
177 // relative to the scroll view; if yes we don't need to transform coordinates.
Garfield, Tan61f564b2016-08-16 13:36:15 -0700178 final boolean isScrollView = mIsScrollView.test(v);
179 final float offsetX = isScrollView ? 0 : v.getX();
180 final float offsetY = isScrollView ? 0 : v.getY();
181 return new Point(Math.round(offsetX + x), Math.round(offsetY + y));
182 }
183
Ben Linc5e3e8e2016-07-13 18:16:36 -0700184 private boolean insideDragZone() {
185 if (mCurrentPosition == null) {
186 return false;
187 }
188
Ben Lin3dbd3b12016-09-27 14:03:04 -0700189 float topBottomRegionHeight = mHeight.getAsInt()
190 * ViewAutoScroller.TOP_BOTTOM_THRESHOLD_RATIO;
191 boolean shouldScrollUp = mCurrentPosition.y < topBottomRegionHeight
Ben Linc5e3e8e2016-07-13 18:16:36 -0700192 && mCanScrollUp.getAsBoolean();
Ben Lin3dbd3b12016-09-27 14:03:04 -0700193 boolean shouldScrollDown = mCurrentPosition.y > mHeight.getAsInt() - topBottomRegionHeight
Ben Linc5e3e8e2016-07-13 18:16:36 -0700194 && mCanScrollDown.getAsBoolean();
195 return shouldScrollUp || shouldScrollDown;
196 }
Riddle Hsuded3dc52018-05-17 21:27:07 +0800197}