Merge "Add test for loading init location from rootUri." into nyc-andromeda-dev
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index f8e7932..ce6f247 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -47,8 +47,6 @@
<dimen name="drag_shadow_width">160dp</dimen>
<dimen name="drag_shadow_height">48dp</dimen>
- <dimen name="autoscroll_edge_height">32dp</dimen>
-
<dimen name="doc_header_sort_icon_size">16dp</dimen>
<dimen name="doc_header_height">60dp</dimen>
diff --git a/src/com/android/documentsui/dirlist/BandController.java b/src/com/android/documentsui/dirlist/BandController.java
index 91afa23..c938d93 100644
--- a/src/com/android/documentsui/dirlist/BandController.java
+++ b/src/com/android/documentsui/dirlist/BandController.java
@@ -56,7 +56,6 @@
public class BandController extends OnScrollListener {
private static final String TAG = "BandController";
- private static final int AUTOSCROLL_EDGE_HEIGHT = 1;
private final Runnable mModelBuilder;
private final SelectionEnvironment mEnvironment;
@@ -92,7 +91,6 @@
mEnvironment.addOnScrollListener(this);
mViewScroller = new ViewAutoScroller(
- AUTOSCROLL_EDGE_HEIGHT,
new ScrollDistanceDelegate() {
@Override
public Point getCurrentPosition() {
diff --git a/src/com/android/documentsui/dirlist/DirectoryFragment.java b/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 497cfa3..32ea333 100644
--- a/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -215,9 +215,7 @@
mRecView.setItemAnimator(new DirectoryItemAnimator(getActivity()));
mFileList = view.findViewById(R.id.file_list);
- final int edgeHeight = (int) getResources().getDimension(R.dimen.autoscroll_edge_height);
- mDragHoverListener = DragHoverListener.create(
- edgeHeight, new DirectoryDragListener(this), mRecView);
+ mDragHoverListener = DragHoverListener.create(new DirectoryDragListener(this), mRecView);
// Make the recycler and the empty views responsive to drop events.
mRecView.setOnDragListener(mDragHoverListener);
@@ -300,9 +298,7 @@
mModel.addUpdateListener(mAdapter.getModelUpdateListener());
mModel.addUpdateListener(mModelUpdateListener);
- final int edgeHeight = (int) getResources().getDimension(R.dimen.autoscroll_edge_height);
- GestureSelector gestureSel = GestureSelector.create(
- edgeHeight, mSelectionMgr, mRecView);
+ GestureSelector gestureSel = GestureSelector.create(mSelectionMgr, mRecView);
final BaseActivity activity = getBaseActivity();
mTuner = activity.getFragmentTuner(mModel, mConfig.mSearchMode);
diff --git a/src/com/android/documentsui/dirlist/DragHoverListener.java b/src/com/android/documentsui/dirlist/DragHoverListener.java
index 0382c21..ae13d97 100644
--- a/src/com/android/documentsui/dirlist/DragHoverListener.java
+++ b/src/com/android/documentsui/dirlist/DragHoverListener.java
@@ -43,7 +43,6 @@
private final IntSupplier mHeight;
private final BooleanSupplier mCanScrollUp;
private final BooleanSupplier mCanScrollDown;
- private final int mAutoScrollEdgeHeight;
private final Runnable mDragScroller;
/**
@@ -61,7 +60,6 @@
@VisibleForTesting
DragHoverListener(
- int autoScrollEdgeHeight,
ItemDragListener<? extends DragHost> dragHandler,
IntSupplier heightSupplier,
Predicate<View> isScrollView,
@@ -69,7 +67,6 @@
BooleanSupplier scrollDownSupplier,
ViewAutoScroller.ScrollActionDelegate actionDelegate) {
mDragHandler = dragHandler;
- mAutoScrollEdgeHeight = autoScrollEdgeHeight;
mHeight = heightSupplier;
mIsScrollView = isScrollView;
mCanScrollUp = scrollUpSupplier;
@@ -92,12 +89,10 @@
}
};
- mDragScroller = new ViewAutoScroller(
- mAutoScrollEdgeHeight, distanceDelegate, actionDelegate);
+ mDragScroller = new ViewAutoScroller(distanceDelegate, actionDelegate);
}
static DragHoverListener create(
- int autoScrollEdgeHeight,
ItemDragListener<? extends DragHost> dragHandler,
View scrollView) {
ScrollActionDelegate actionDelegate = new ScrollActionDelegate() {
@@ -118,7 +113,6 @@
}
};
DragHoverListener listener = new DragHoverListener(
- autoScrollEdgeHeight,
dragHandler,
scrollView::getHeight,
(view) -> (scrollView == view),
@@ -179,9 +173,11 @@
return false;
}
- boolean shouldScrollUp = mCurrentPosition.y < mAutoScrollEdgeHeight
+ float topBottomRegionHeight = mHeight.getAsInt()
+ * ViewAutoScroller.TOP_BOTTOM_THRESHOLD_RATIO;
+ boolean shouldScrollUp = mCurrentPosition.y < topBottomRegionHeight
&& mCanScrollUp.getAsBoolean();
- boolean shouldScrollDown = mCurrentPosition.y > mHeight.getAsInt() - mAutoScrollEdgeHeight
+ boolean shouldScrollDown = mCurrentPosition.y > mHeight.getAsInt() - topBottomRegionHeight
&& mCanScrollDown.getAsBoolean();
return shouldScrollUp || shouldScrollDown;
}
diff --git a/src/com/android/documentsui/dirlist/GestureSelector.java b/src/com/android/documentsui/dirlist/GestureSelector.java
index c8799fb..d0b68ae 100644
--- a/src/com/android/documentsui/dirlist/GestureSelector.java
+++ b/src/com/android/documentsui/dirlist/GestureSelector.java
@@ -37,7 +37,6 @@
private final MultiSelectManager mSelectionMgr;
private final Runnable mDragScroller;
- private final int mAutoScrollEdgeHeight;
private final IntSupplier mHeight;
private final ViewFinder mViewFinder;
private int mLastStartedItemPos = -1;
@@ -45,12 +44,10 @@
private Point mLastInterceptedPoint;
GestureSelector(
- int autoScrollEdgeHeight,
MultiSelectManager selectionMgr,
IntSupplier heightSupplier,
ViewFinder viewFinder,
ScrollActionDelegate actionDelegate) {
- mAutoScrollEdgeHeight = autoScrollEdgeHeight;
mSelectionMgr = selectionMgr;
mHeight = heightSupplier;
mViewFinder = viewFinder;
@@ -72,12 +69,10 @@
}
};
- mDragScroller = new ViewAutoScroller(
- mAutoScrollEdgeHeight, distanceDelegate, actionDelegate);
+ mDragScroller = new ViewAutoScroller(distanceDelegate, actionDelegate);
}
static GestureSelector create(
- int autoScrollEdgeHeight,
MultiSelectManager selectionMgr,
RecyclerView scrollView) {
ScrollActionDelegate actionDelegate = new ScrollActionDelegate() {
@@ -98,8 +93,10 @@
};
GestureSelector helper =
new GestureSelector(
- autoScrollEdgeHeight, selectionMgr, scrollView::getHeight,
- scrollView::findChildViewUnder, actionDelegate);
+ selectionMgr,
+ scrollView::getHeight,
+ scrollView::findChildViewUnder,
+ actionDelegate);
return helper;
}
@@ -205,9 +202,7 @@
if (lastGlidedItemPos != RecyclerView.NO_POSITION) {
doGestureMultiSelect(lastGlidedItemPos);
}
- if (insideDragZone(rv)) {
- mDragScroller.run();
- }
+ scrollIfNecessary();
}
// It's possible for events to go over the top/bottom of the RecyclerView.
@@ -243,16 +238,8 @@
mSelectionMgr.snapProvisionalRangeSelection(endPos);
}
- private boolean insideDragZone(View scrollView) {
- if (mLastInterceptedPoint == null) {
- return false;
- }
-
- boolean shouldScrollUp = mLastInterceptedPoint.y < mAutoScrollEdgeHeight
- && scrollView.canScrollVertically(-1);
- boolean shouldScrollDown = mLastInterceptedPoint.y > scrollView.getHeight() -
- mAutoScrollEdgeHeight && scrollView.canScrollVertically(1);
- return shouldScrollUp || shouldScrollDown;
+ private void scrollIfNecessary() {
+ mDragScroller.run();
}
@FunctionalInterface
diff --git a/src/com/android/documentsui/dirlist/ViewAutoScroller.java b/src/com/android/documentsui/dirlist/ViewAutoScroller.java
index 761529c..b6e5f00 100644
--- a/src/com/android/documentsui/dirlist/ViewAutoScroller.java
+++ b/src/com/android/documentsui/dirlist/ViewAutoScroller.java
@@ -18,51 +18,28 @@
package com.android.documentsui.dirlist;
import android.graphics.Point;
-import android.support.annotation.VisibleForTesting;
-
-import java.util.function.LongSupplier;
/**
* Provides auto-scrolling upon request when user's interaction with the application
- * introduces a natural intent to scroll. Used by {@link BandController} and
- * {@link DragHoverListener} to allow auto scrolling when user either does band selection, or
- * attempting to drag and drop files to somewhere off the current screen.
+ * introduces a natural intent to scroll. Used by {@link BandController}, {@link GestureSelector}
+ * and {@link DragHoverListener} to allow auto scrolling when user either does band selection,
+ * attempting to drag and drop files to somewhere off the current screen, or trying to motion select
+ * past top/bottom of the screen.
*/
public final class ViewAutoScroller implements Runnable {
public static final int NOT_SET = -1;
- /**
- * The number of milliseconds of scrolling at which scroll speed continues to increase.
- * At first, the scroll starts slowly; then, the rate of scrolling increases until it
- * reaches its maximum value at after this many milliseconds.
- */
- private static final long SCROLL_ACCELERATION_LIMIT_TIME_MS = 2000;
+ // ratio used to calculate the top/bottom hotspot region; used with view height
+ public static final float TOP_BOTTOM_THRESHOLD_RATIO = 0.125f;
+ public static final int MAX_SCROLL_STEP = 70;
// Top and bottom inner buffer such that user's cursor does not have to be exactly off screen
// for auto scrolling to begin
- private final int mTopBottomThreshold;
private final ScrollDistanceDelegate mCalcDelegate;
private final ScrollActionDelegate mUiDelegate;
- private final LongSupplier mCurrentTime;
- private long mScrollStartTime = NOT_SET;
-
- public ViewAutoScroller(
- int topBottomThreshold,
- ScrollDistanceDelegate calcDelegate,
- ScrollActionDelegate uiDelegate) {
- this(topBottomThreshold, calcDelegate, uiDelegate, System::currentTimeMillis);
- }
-
- @VisibleForTesting
- ViewAutoScroller(
- int topBottomThreshold,
- ScrollDistanceDelegate calcDelegate,
- ScrollActionDelegate uiDelegate,
- LongSupplier clock) {
- mTopBottomThreshold = topBottomThreshold;
+ public ViewAutoScroller(ScrollDistanceDelegate calcDelegate, ScrollActionDelegate uiDelegate) {
mCalcDelegate = calcDelegate;
mUiDelegate = uiDelegate;
- mCurrentTime = clock;
}
/**
@@ -79,30 +56,29 @@
// pointer are in these buffer pixels.
int pixelsPastView = 0;
- if (mCalcDelegate.getCurrentPosition().y <= mTopBottomThreshold) {
- pixelsPastView = mCalcDelegate.getCurrentPosition().y - mTopBottomThreshold;
+ final int topBottomThreshold = (int) (mCalcDelegate.getViewHeight()
+ * TOP_BOTTOM_THRESHOLD_RATIO);
+
+ if (mCalcDelegate.getCurrentPosition().y <= topBottomThreshold) {
+ pixelsPastView = mCalcDelegate.getCurrentPosition().y - topBottomThreshold;
} else if (mCalcDelegate.getCurrentPosition().y >= mCalcDelegate.getViewHeight()
- - mTopBottomThreshold) {
+ - topBottomThreshold) {
pixelsPastView = mCalcDelegate.getCurrentPosition().y - mCalcDelegate.getViewHeight()
- + mTopBottomThreshold;
+ + topBottomThreshold;
}
if (!mCalcDelegate.isActive() || pixelsPastView == 0) {
// If the operation that started the scrolling is no longer inactive, or if it is active
// but not at the edge of the view, no scrolling is necessary.
- mScrollStartTime = NOT_SET;
return;
}
- if (mScrollStartTime == NOT_SET) {
- // If the pointer was previously not at the edge of the view but now is, set the
- // start time for the scroll.
- mScrollStartTime = mCurrentTime.getAsLong();
+ if (pixelsPastView > topBottomThreshold) {
+ pixelsPastView = topBottomThreshold;
}
// Compute the number of pixels to scroll, and scroll that many pixels.
- final int numPixels = computeScrollDistance(
- pixelsPastView, mCurrentTime.getAsLong() - mScrollStartTime);
+ final int numPixels = computeScrollDistance(pixelsPastView);
mUiDelegate.scrollBy(numPixels);
// Remove callback to this, and then properly run at next frame again
@@ -112,63 +88,43 @@
/**
* Computes the number of pixels to scroll based on how far the pointer is past the end
- * of the view and how long it has been there. Roughly based on ItemTouchHelper's
- * algorithm for computing the number of pixels to scroll when an item is dragged to the
- * end of a view.
- * @param pixelsPastView
- * @param scrollDuration
+ * of the region. Roughly based on ItemTouchHelper's algorithm for computing the number of
+ * pixels to scroll when an item is dragged to the end of a view.
* @return
*/
- public int computeScrollDistance(int pixelsPastView, long scrollDuration) {
- final int maxScrollStep = mCalcDelegate.getViewHeight();
+ public int computeScrollDistance(int pixelsPastView) {
+ final int topBottomThreshold = (int) (mCalcDelegate.getViewHeight()
+ * TOP_BOTTOM_THRESHOLD_RATIO);
+
final int direction = (int) Math.signum(pixelsPastView);
final int absPastView = Math.abs(pixelsPastView);
// Calculate the ratio of how far out of the view the pointer currently resides to
- // the entire height of the view.
+ // the top/bottom scrolling hotspot of the view.
final float outOfBoundsRatio = Math.min(
- 1.0f, (float) absPastView / mCalcDelegate.getViewHeight());
+ 1.0f, (float) absPastView / topBottomThreshold);
// Interpolate this ratio and use it to compute the maximum scroll that should be
// possible for this step.
- final float cappedScrollStep =
- direction * maxScrollStep * smoothOutOfBoundsRatio(outOfBoundsRatio);
-
- // Likewise, calculate the ratio of the time spent in the scroll to the limit.
- final float timeRatio = Math.min(
- 1.0f, (float) scrollDuration / SCROLL_ACCELERATION_LIMIT_TIME_MS);
- // Interpolate this ratio and use it to compute the final number of pixels to
- // scroll.
- final int numPixels = (int) (cappedScrollStep * smoothTimeRatio(timeRatio));
+ final int cappedScrollStep =
+ (int) (direction * MAX_SCROLL_STEP * smoothOutOfBoundsRatio(outOfBoundsRatio));
// If the final number of pixels to scroll ends up being 0, the view should still
// scroll at least one pixel.
- return numPixels != 0 ? numPixels : direction;
+ return cappedScrollStep != 0 ? cappedScrollStep : direction;
}
/**
* Interpolates the given out of bounds ratio on a curve which starts at (0,0) and ends
* at (1,1) and quickly approaches 1 near the start of that interval. This ensures that
- * drags that are at the edge or barely past the edge of the view still cause sufficient
- * scrolling. The equation y=(x-1)^5+1 is used, but this could also be tweaked if
+ * drags that are at the edge or barely past the edge of the threshold does little to no
+ * scrolling, while drags that are near the edge of the view does a lot of
+ * scrolling. The equation y=x^10 is used, but this could also be tweaked if
* needed.
* @param ratio A ratio which is in the range [0, 1].
* @return A "smoothed" value, also in the range [0, 1].
*/
private float smoothOutOfBoundsRatio(float ratio) {
- return (float) Math.pow(ratio - 1.0f, 5) + 1.0f;
- }
-
- /**
- * Interpolates the given time ratio on a curve which starts at (0,0) and ends at (1,1)
- * and stays close to 0 for most input values except those very close to 1. This ensures
- * that scrolls start out very slowly but speed up drastically after the scroll has been
- * in progress close to SCROLL_ACCELERATION_LIMIT_TIME_MS. The equation y=x^5 is used,
- * but this could also be tweaked if needed.
- * @param ratio A ratio which is in the range [0, 1].
- * @return A "smoothed" value, also in the range [0, 1].
- */
- private float smoothTimeRatio(float ratio) {
- return (float) Math.pow(ratio, 5);
+ return (float) Math.pow(ratio, 10);
}
/**
diff --git a/tests/unit/com/android/documentsui/dirlist/DragScrollListenerTest.java b/tests/unit/com/android/documentsui/dirlist/DragScrollListenerTest.java
index 824d246..46d9c67 100644
--- a/tests/unit/com/android/documentsui/dirlist/DragScrollListenerTest.java
+++ b/tests/unit/com/android/documentsui/dirlist/DragScrollListenerTest.java
@@ -40,7 +40,8 @@
public class DragScrollListenerTest {
private static final int VIEW_HEIGHT = 100;
- private static final int EDGE_HEIGHT = 10;
+ private static final int TOP_Y_POINT = 0;
+ private static final int BOTTOM_Y_POINT = VIEW_HEIGHT;
private View mTestView;
private TestDragHost mTestDragHost;
@@ -58,7 +59,6 @@
mTestDragHost = new TestDragHost();
mDragHandler = new TestDragHandler(mTestDragHost, mTestTimer);
mListener = new DragHoverListener(
- EDGE_HEIGHT,
mDragHandler,
() -> VIEW_HEIGHT,
view -> (view == mTestView),
@@ -89,19 +89,19 @@
triggerDragEvent(DragEvent.ACTION_DRAG_STARTED);
// Not in hotspot
- triggerDragLocationEvent(0, 50);
+ triggerDragLocationEvent(0, VIEW_HEIGHT / 2);
assertTrue(mDragHandler.mLastDropEvent.getAction() == DragEvent.ACTION_DRAG_LOCATION);
// Can't scroll up
mCanScrollUp = false;
mCanScrollDown = true;
- triggerDragLocationEvent(0, 5);
+ triggerDragLocationEvent(0, TOP_Y_POINT);
assertTrue(mDragHandler.mLastDropEvent.getAction() == DragEvent.ACTION_DRAG_LOCATION);
// Can't scroll Down
mCanScrollDown = false;
mCanScrollUp = true;
- triggerDragLocationEvent(0, 95);
+ triggerDragLocationEvent(0, BOTTOM_Y_POINT);
assertTrue(mDragHandler.mLastDropEvent.getAction() == DragEvent.ACTION_DRAG_LOCATION);
triggerDragEvent(DragEvent.ACTION_DRAG_ENDED);
@@ -120,14 +120,14 @@
// Can't scroll up
mCanScrollUp = false;
mCanScrollDown = true;
- triggerDragLocationEvent(0, 5);
+ triggerDragLocationEvent(0, TOP_Y_POINT);
triggerDragEvent(DragEvent.ACTION_DRAG_ENTERED);
assertTrue(mDragHandler.mLastDropEvent.getAction() == DragEvent.ACTION_DRAG_ENTERED);
// Can't scroll Down
mCanScrollDown = false;
mCanScrollUp = true;
- triggerDragLocationEvent(0, 95);
+ triggerDragLocationEvent(0, BOTTOM_Y_POINT);
triggerDragEvent(DragEvent.ACTION_DRAG_ENTERED);
assertTrue(mDragHandler.mLastDropEvent.getAction() == DragEvent.ACTION_DRAG_ENTERED);
@@ -137,11 +137,11 @@
// Make sure given correct location/enter events, DragScrollListener handle them itself
@Test
public void testDragScrollEvent_Handled() {
- triggerDragLocationEvent(0, 5);
+ triggerDragLocationEvent(0, TOP_Y_POINT);
triggerDragEvent(DragEvent.ACTION_DRAG_ENTERED);
assertTrue(mDragHandler.mLastDropEvent == null);
- triggerDragLocationEvent(0, 95);
+ triggerDragLocationEvent(0, BOTTOM_Y_POINT);
triggerDragEvent(DragEvent.ACTION_DRAG_ENTERED);
assertTrue(mDragHandler.mLastDropEvent == null);
}
@@ -153,12 +153,12 @@
public void testActualDragScrolldEvents() {
triggerDragEvent(DragEvent.ACTION_DRAG_STARTED);
- triggerDragLocationEvent(0, 5);
+ triggerDragLocationEvent(0, TOP_Y_POINT);
triggerDragEvent(DragEvent.ACTION_DRAG_ENTERED);
assertTrue(mDragHandler.mLastDropEvent.getAction() == DragEvent.ACTION_DRAG_STARTED);
mActionDelegate.assertScrollNegative();
- triggerDragLocationEvent(0, 95);
+ triggerDragLocationEvent(0, BOTTOM_Y_POINT);
triggerDragEvent(DragEvent.ACTION_DRAG_ENTERED);
triggerDragEvent(DragEvent.ACTION_DRAG_ENDED);
@@ -230,11 +230,11 @@
}
public void assertScrollPositive() {
- assertTrue(mDy > 0);
+ assertTrue("actual: " + mDy, mDy > 0);
}
public void assertScrollNegative() {
- assertTrue(mDy < 0);
+ assertTrue("actual: " + mDy, mDy < 0);
}
};
}
diff --git a/tests/unit/com/android/documentsui/dirlist/ViewAutoScrollerTest.java b/tests/unit/com/android/documentsui/dirlist/ViewAutoScrollerTest.java
index e2aaa84..f7a148a 100644
--- a/tests/unit/com/android/documentsui/dirlist/ViewAutoScrollerTest.java
+++ b/tests/unit/com/android/documentsui/dirlist/ViewAutoScrollerTest.java
@@ -34,7 +34,12 @@
public final class ViewAutoScrollerTest {
private static final int VIEW_HEIGHT = 100;
- private static final int EDGE_HEIGHT = 10;
+ private static final int TOP_Y_POINT = (int) (VIEW_HEIGHT
+ * ViewAutoScroller.TOP_BOTTOM_THRESHOLD_RATIO) - 1;
+ private static final int BOTTOM_Y_POINT = VIEW_HEIGHT
+ - (int) (VIEW_HEIGHT * ViewAutoScroller.TOP_BOTTOM_THRESHOLD_RATIO)
+ + 1;
+ private static final int EDGE_HEIGHT = 5;
private ViewAutoScroller mAutoScroller;
private Point mPoint;
@@ -77,8 +82,7 @@
public void removeCallback(Runnable r) {
}
};
- mAutoScroller = new ViewAutoScroller(
- EDGE_HEIGHT, mDistanceDelegate, mActionDelegate, new TestClock()::getCurrentTime);
+ mAutoScroller = new ViewAutoScroller(mDistanceDelegate, mActionDelegate);
}
@Test
@@ -95,7 +99,7 @@
@Test
public void testCursorInScrollZone_notActive() {
mActive = false;
- mPoint = new Point(0, EDGE_HEIGHT - 1);
+ mPoint = new Point(0, TOP_Y_POINT);
mScrollAssert = (int dy) -> {
// Should not have called this method
fail("Received unexpected scroll event");
@@ -107,8 +111,8 @@
@Test
public void testCursorInScrollZone_top() {
mActive = true;
- mPoint = new Point(0, EDGE_HEIGHT - 1);
- int expectedScrollDistance = mAutoScroller.computeScrollDistance(-1, 1);
+ mPoint = new Point(0, TOP_Y_POINT);
+ int expectedScrollDistance = mAutoScroller.computeScrollDistance(-1);
mScrollAssert = (int dy) -> {
assertTrue(dy == expectedScrollDistance);
};
@@ -118,19 +122,11 @@
@Test
public void testCursorInScrollZone_bottom() {
mActive = true;
- mPoint = new Point(0, VIEW_HEIGHT - EDGE_HEIGHT + 1);
- int expectedScrollDistance = mAutoScroller.computeScrollDistance(1, 1);
+ mPoint = new Point(0, BOTTOM_Y_POINT);
+ int expectedScrollDistance = mAutoScroller.computeScrollDistance(1);
mScrollAssert = (int dy) -> {
assertTrue(dy == expectedScrollDistance);
};
mAutoScroller.run();
}
-
- class TestClock {
- private int timesCalled = 0;
-
- public long getCurrentTime() {
- return ++timesCalled;
- }
- }
}