Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 1 | /* |
| 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 Hsu | ded3dc5 | 2018-05-17 21:27:07 +0800 | [diff] [blame] | 17 | package com.android.documentsui; |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 18 | |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 19 | import android.graphics.Point; |
KOUSHIK PANUGANTI | 6ca7acc | 2018-04-17 16:00:10 -0700 | [diff] [blame] | 20 | import androidx.annotation.VisibleForTesting; |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 21 | import android.view.DragEvent; |
| 22 | import android.view.View; |
| 23 | import android.view.View.OnDragListener; |
Riddle Hsu | ded3dc5 | 2018-05-17 21:27:07 +0800 | [diff] [blame] | 24 | import android.widget.AbsListView; |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 25 | |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 26 | import com.android.documentsui.ItemDragListener.DragHost; |
Riddle Hsu | 0c37598 | 2018-06-21 22:06:43 +0800 | [diff] [blame] | 27 | import com.android.documentsui.ViewAutoScroller.ScrollHost; |
| 28 | import com.android.documentsui.ViewAutoScroller.ScrollerCallbacks; |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 29 | |
| 30 | import java.util.function.BooleanSupplier; |
Riddle Hsu | ded3dc5 | 2018-05-17 21:27:07 +0800 | [diff] [blame] | 31 | import java.util.function.IntConsumer; |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 32 | import java.util.function.IntSupplier; |
Garfield, Tan | 61f564b | 2016-08-16 13:36:15 -0700 | [diff] [blame] | 33 | import java.util.function.Predicate; |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 34 | |
| 35 | import javax.annotation.Nullable; |
| 36 | |
| 37 | /** |
| 38 | * This class acts as a middle-man handler for potential auto-scrolling before passing the dragEvent |
Riddle Hsu | ded3dc5 | 2018-05-17 21:27:07 +0800 | [diff] [blame] | 39 | * onto {@link ItemDragListener}. |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 40 | */ |
Riddle Hsu | ded3dc5 | 2018-05-17 21:27:07 +0800 | [diff] [blame] | 41 | public class DragHoverListener implements OnDragListener { |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 42 | |
| 43 | private final ItemDragListener<? extends DragHost> mDragHandler; |
| 44 | private final IntSupplier mHeight; |
| 45 | private final BooleanSupplier mCanScrollUp; |
| 46 | private final BooleanSupplier mCanScrollDown; |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 47 | private final Runnable mDragScroller; |
| 48 | |
Garfield, Tan | 61f564b | 2016-08-16 13:36:15 -0700 | [diff] [blame] | 49 | /** |
Riddle Hsu | ded3dc5 | 2018-05-17 21:27:07 +0800 | [diff] [blame] | 50 | * Predicate to tests whether it's the scroll view itself. |
Garfield, Tan | 61f564b | 2016-08-16 13:36:15 -0700 | [diff] [blame] | 51 | * |
Riddle Hsu | ded3dc5 | 2018-05-17 21:27:07 +0800 | [diff] [blame] | 52 | * {@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, Tan | 61f564b | 2016-08-16 13:36:15 -0700 | [diff] [blame] | 56 | */ |
| 57 | private final Predicate<View> mIsScrollView; |
| 58 | |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 59 | private boolean mDragHappening; |
| 60 | private @Nullable Point mCurrentPosition; |
| 61 | |
Ben Lin | 2abb4c7 | 2016-08-01 18:05:23 -0700 | [diff] [blame] | 62 | @VisibleForTesting |
Ben Lin | 35f99e0 | 2016-08-31 12:46:04 -0700 | [diff] [blame] | 63 | DragHoverListener( |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 64 | ItemDragListener<? extends DragHost> dragHandler, |
| 65 | IntSupplier heightSupplier, |
Garfield, Tan | 61f564b | 2016-08-16 13:36:15 -0700 | [diff] [blame] | 66 | Predicate<View> isScrollView, |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 67 | BooleanSupplier scrollUpSupplier, |
| 68 | BooleanSupplier scrollDownSupplier, |
Steve McKay | 5a62037 | 2017-09-11 12:18:56 -0700 | [diff] [blame] | 69 | ViewAutoScroller.ScrollerCallbacks scrollCallbacks) { |
Steve McKay | 1239452 | 2017-08-24 14:14:10 -0700 | [diff] [blame] | 70 | |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 71 | mDragHandler = dragHandler; |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 72 | mHeight = heightSupplier; |
Garfield, Tan | 61f564b | 2016-08-16 13:36:15 -0700 | [diff] [blame] | 73 | mIsScrollView = isScrollView; |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 74 | mCanScrollUp = scrollUpSupplier; |
| 75 | mCanScrollDown = scrollDownSupplier; |
| 76 | |
Steve McKay | 1239452 | 2017-08-24 14:14:10 -0700 | [diff] [blame] | 77 | ScrollHost scrollHost = new ScrollHost() { |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 78 | @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 McKay | 1239452 | 2017-08-24 14:14:10 -0700 | [diff] [blame] | 94 | mDragScroller = new ViewAutoScroller(scrollHost, scrollCallbacks); |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 95 | } |
| 96 | |
Riddle Hsu | ded3dc5 | 2018-05-17 21:27:07 +0800 | [diff] [blame] | 97 | 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 Lin | 2abb4c7 | 2016-08-01 18:05:23 -0700 | [diff] [blame] | 104 | ItemDragListener<? extends DragHost> dragHandler, |
| 105 | View scrollView) { |
Riddle Hsu | ded3dc5 | 2018-05-17 21:27:07 +0800 | [diff] [blame] | 106 | 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 McKay | 1239452 | 2017-08-24 14:14:10 -0700 | [diff] [blame] | 118 | |
Steve McKay | 5a62037 | 2017-09-11 12:18:56 -0700 | [diff] [blame] | 119 | ScrollerCallbacks scrollCallbacks = new ScrollerCallbacks() { |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 120 | @Override |
| 121 | public void scrollBy(int dy) { |
Riddle Hsu | ded3dc5 | 2018-05-17 21:27:07 +0800 | [diff] [blame] | 122 | scroller.accept(dy); |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 123 | } |
| 124 | |
| 125 | @Override |
| 126 | public void runAtNextFrame(Runnable r) { |
| 127 | scrollView.postOnAnimation(r); |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 128 | } |
| 129 | |
| 130 | @Override |
| 131 | public void removeCallback(Runnable r) { |
| 132 | scrollView.removeCallbacks(r); |
| 133 | } |
| 134 | }; |
Steve McKay | 1239452 | 2017-08-24 14:14:10 -0700 | [diff] [blame] | 135 | |
Riddle Hsu | ded3dc5 | 2018-05-17 21:27:07 +0800 | [diff] [blame] | 136 | return new DragHoverListener( |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 137 | dragHandler, |
| 138 | scrollView::getHeight, |
Garfield, Tan | 61f564b | 2016-08-16 13:36:15 -0700 | [diff] [blame] | 139 | (view) -> (scrollView == view), |
| 140 | () -> scrollView.canScrollVertically(-1), |
| 141 | () -> scrollView.canScrollVertically(1), |
Steve McKay | 1239452 | 2017-08-24 14:14:10 -0700 | [diff] [blame] | 142 | scrollCallbacks); |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 143 | } |
| 144 | |
| 145 | @Override |
| 146 | public boolean onDrag(View v, DragEvent event) { |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 147 | 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 Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 154 | case DragEvent.ACTION_DRAG_LOCATION: |
Ben Lin | 7c5deaa | 2016-10-04 14:31:54 -0700 | [diff] [blame] | 155 | handleLocationEvent(v, event.getX(), event.getY()); |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 156 | break; |
| 157 | default: |
| 158 | break; |
| 159 | } |
| 160 | |
Ben Lin | 7c5deaa | 2016-10-04 14:31:54 -0700 | [diff] [blame] | 161 | // Always forward events to the drag handler for item highlight, spring load, etc. |
| 162 | return mDragHandler.onDrag(v, event); |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 163 | } |
| 164 | |
| 165 | private boolean handleLocationEvent(View v, float x, float y) { |
Garfield, Tan | 61f564b | 2016-08-16 13:36:15 -0700 | [diff] [blame] | 166 | mCurrentPosition = transformToScrollViewCoordinate(v, x, y); |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 167 | if (insideDragZone()) { |
| 168 | mDragScroller.run(); |
| 169 | return true; |
| 170 | } |
| 171 | return false; |
| 172 | } |
| 173 | |
Garfield, Tan | 61f564b | 2016-08-16 13:36:15 -0700 | [diff] [blame] | 174 | private Point transformToScrollViewCoordinate(View v, float x, float y) { |
Riddle Hsu | ded3dc5 | 2018-05-17 21:27:07 +0800 | [diff] [blame] | 175 | // 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, Tan | 61f564b | 2016-08-16 13:36:15 -0700 | [diff] [blame] | 178 | 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 Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 184 | private boolean insideDragZone() { |
| 185 | if (mCurrentPosition == null) { |
| 186 | return false; |
| 187 | } |
| 188 | |
Ben Lin | 3dbd3b1 | 2016-09-27 14:03:04 -0700 | [diff] [blame] | 189 | float topBottomRegionHeight = mHeight.getAsInt() |
| 190 | * ViewAutoScroller.TOP_BOTTOM_THRESHOLD_RATIO; |
| 191 | boolean shouldScrollUp = mCurrentPosition.y < topBottomRegionHeight |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 192 | && mCanScrollUp.getAsBoolean(); |
Ben Lin | 3dbd3b1 | 2016-09-27 14:03:04 -0700 | [diff] [blame] | 193 | boolean shouldScrollDown = mCurrentPosition.y > mHeight.getAsInt() - topBottomRegionHeight |
Ben Lin | c5e3e8e | 2016-07-13 18:16:36 -0700 | [diff] [blame] | 194 | && mCanScrollDown.getAsBoolean(); |
| 195 | return shouldScrollUp || shouldScrollDown; |
| 196 | } |
Riddle Hsu | ded3dc5 | 2018-05-17 21:27:07 +0800 | [diff] [blame] | 197 | } |