blob: 63d93ecf8a62b0d61b927ad60c7de7bbd42603e8 [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
17package com.android.documentsui.dirlist;
18
Ben Linc5e3e8e2016-07-13 18:16:36 -070019import android.graphics.Point;
Ben Lin2abb4c72016-08-01 18:05:23 -070020import android.support.annotation.VisibleForTesting;
Ben Linc5e3e8e2016-07-13 18:16:36 -070021import android.view.DragEvent;
22import android.view.View;
23import android.view.View.OnDragListener;
24
25import com.android.documentsui.ItemDragListener;
26import com.android.documentsui.ItemDragListener.DragHost;
Steve McKay4f78ba62016-10-04 16:48:49 -070027import com.android.documentsui.ui.ViewAutoScroller;
28import com.android.documentsui.ui.ViewAutoScroller.ScrollActionDelegate;
29import com.android.documentsui.ui.ViewAutoScroller.ScrollDistanceDelegate;
Ben Linc5e3e8e2016-07-13 18:16:36 -070030
31import java.util.function.BooleanSupplier;
32import 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
39 * onto {@link DirectoryDragListener}.
40 */
Ben Lin35f99e02016-08-31 12:46:04 -070041class 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 /**
50 * Predicate to tests whether it's the scroll view ({@link DirectoryFragment#mRecView}) itself.
51 *
Ben Lin35f99e02016-08-31 12:46:04 -070052 * {@link DragHoverListener} is used for both {@link DirectoryFragment#mRecView} and its
Garfield, Tan61f564b2016-08-16 13:36:15 -070053 * children. When we decide whether it's in the scroll zone we need to obtain the coordinate
54 * relative to {@link DirectoryFragment#mRecView} so we need to transform the coordinate if the
55 * view that gets drag and drop events is a child of {@link DirectoryFragment#mRecView}.
56 */
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,
69 ViewAutoScroller.ScrollActionDelegate actionDelegate) {
70 mDragHandler = dragHandler;
Ben Linc5e3e8e2016-07-13 18:16:36 -070071 mHeight = heightSupplier;
Garfield, Tan61f564b2016-08-16 13:36:15 -070072 mIsScrollView = isScrollView;
Ben Linc5e3e8e2016-07-13 18:16:36 -070073 mCanScrollUp = scrollUpSupplier;
74 mCanScrollDown = scrollDownSupplier;
75
76 ScrollDistanceDelegate distanceDelegate = new ScrollDistanceDelegate() {
77 @Override
78 public Point getCurrentPosition() {
79 return mCurrentPosition;
80 }
81
82 @Override
83 public int getViewHeight() {
84 return mHeight.getAsInt();
85 }
86
87 @Override
88 public boolean isActive() {
89 return mDragHappening;
90 }
91 };
92
Ben Lin3dbd3b12016-09-27 14:03:04 -070093 mDragScroller = new ViewAutoScroller(distanceDelegate, actionDelegate);
Ben Linc5e3e8e2016-07-13 18:16:36 -070094 }
95
Ben Lin35f99e02016-08-31 12:46:04 -070096 static DragHoverListener create(
Ben Lin2abb4c72016-08-01 18:05:23 -070097 ItemDragListener<? extends DragHost> dragHandler,
98 View scrollView) {
Ben Linc5e3e8e2016-07-13 18:16:36 -070099 ScrollActionDelegate actionDelegate = new ScrollActionDelegate() {
100 @Override
101 public void scrollBy(int dy) {
102 scrollView.scrollBy(0, dy);
103 }
104
105 @Override
106 public void runAtNextFrame(Runnable r) {
107 scrollView.postOnAnimation(r);
108
109 }
110
111 @Override
112 public void removeCallback(Runnable r) {
113 scrollView.removeCallbacks(r);
114 }
115 };
Ben Lin35f99e02016-08-31 12:46:04 -0700116 DragHoverListener listener = new DragHoverListener(
Ben Linc5e3e8e2016-07-13 18:16:36 -0700117 dragHandler,
118 scrollView::getHeight,
Garfield, Tan61f564b2016-08-16 13:36:15 -0700119 (view) -> (scrollView == view),
120 () -> scrollView.canScrollVertically(-1),
121 () -> scrollView.canScrollVertically(1),
Ben Linc5e3e8e2016-07-13 18:16:36 -0700122 actionDelegate);
123 return listener;
124 }
125
126 @Override
127 public boolean onDrag(View v, DragEvent event) {
Ben Linc5e3e8e2016-07-13 18:16:36 -0700128 switch (event.getAction()) {
129 case DragEvent.ACTION_DRAG_STARTED:
130 mDragHappening = true;
131 break;
132 case DragEvent.ACTION_DRAG_ENDED:
133 mDragHappening = false;
134 break;
Ben Linc5e3e8e2016-07-13 18:16:36 -0700135 case DragEvent.ACTION_DRAG_LOCATION:
Ben Lin7c5deaa2016-10-04 14:31:54 -0700136 handleLocationEvent(v, event.getX(), event.getY());
Ben Linc5e3e8e2016-07-13 18:16:36 -0700137 break;
138 default:
139 break;
140 }
141
Ben Lin7c5deaa2016-10-04 14:31:54 -0700142 // Always forward events to the drag handler for item highlight, spring load, etc.
143 return mDragHandler.onDrag(v, event);
Ben Linc5e3e8e2016-07-13 18:16:36 -0700144 }
145
146 private boolean handleLocationEvent(View v, float x, float y) {
Garfield, Tan61f564b2016-08-16 13:36:15 -0700147 mCurrentPosition = transformToScrollViewCoordinate(v, x, y);
Ben Linc5e3e8e2016-07-13 18:16:36 -0700148 if (insideDragZone()) {
149 mDragScroller.run();
150 return true;
151 }
152 return false;
153 }
154
Garfield, Tan61f564b2016-08-16 13:36:15 -0700155 private Point transformToScrollViewCoordinate(View v, float x, float y) {
156 // Check if v is the RecyclerView itself. If not we need to transform the coordinate to
157 // relative to the RecyclerView because we need to test the scroll zone in the coordinate
158 // relative to the RecyclerView; if yes we don't need to transform coordinates.
159 final boolean isScrollView = mIsScrollView.test(v);
160 final float offsetX = isScrollView ? 0 : v.getX();
161 final float offsetY = isScrollView ? 0 : v.getY();
162 return new Point(Math.round(offsetX + x), Math.round(offsetY + y));
163 }
164
Ben Linc5e3e8e2016-07-13 18:16:36 -0700165 private boolean insideDragZone() {
166 if (mCurrentPosition == null) {
167 return false;
168 }
169
Ben Lin3dbd3b12016-09-27 14:03:04 -0700170 float topBottomRegionHeight = mHeight.getAsInt()
171 * ViewAutoScroller.TOP_BOTTOM_THRESHOLD_RATIO;
172 boolean shouldScrollUp = mCurrentPosition.y < topBottomRegionHeight
Ben Linc5e3e8e2016-07-13 18:16:36 -0700173 && mCanScrollUp.getAsBoolean();
Ben Lin3dbd3b12016-09-27 14:03:04 -0700174 boolean shouldScrollDown = mCurrentPosition.y > mHeight.getAsInt() - topBottomRegionHeight
Ben Linc5e3e8e2016-07-13 18:16:36 -0700175 && mCanScrollDown.getAsBoolean();
176 return shouldScrollUp || shouldScrollDown;
177 }
178}