summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/com/android/internal/view/ScrollCaptureViewHelper.java37
-rw-r--r--core/java/com/android/internal/view/ScrollCaptureViewSupport.java94
-rw-r--r--core/java/com/android/internal/view/ScrollViewCaptureHelper.java40
-rw-r--r--core/tests/coretests/src/com/android/internal/view/ScrollViewCaptureHelperTest.java200
4 files changed, 217 insertions, 154 deletions
diff --git a/core/java/com/android/internal/view/ScrollCaptureViewHelper.java b/core/java/com/android/internal/view/ScrollCaptureViewHelper.java
index 9f100bd6440f..a92e978b2fc1 100644
--- a/core/java/com/android/internal/view/ScrollCaptureViewHelper.java
+++ b/core/java/com/android/internal/view/ScrollCaptureViewHelper.java
@@ -26,6 +26,36 @@ interface ScrollCaptureViewHelper<V extends View> {
int DOWN = 1;
/**
+ * Contains the result of a scroll request.
+ */
+ class ScrollResult {
+ /**
+ * The area requested in pixels, within {@link #onComputeScrollBounds scroll bounds}, with
+ * top/bottom relative to the scroll position at the start of capture.
+ */
+ public Rect requestedArea;
+ /**
+ * The area, in pixels of the request which is visible and available for capture. In the
+ * same coordinate space as {@link #requestedArea}.
+ */
+ public Rect availableArea;
+ /**
+ * The updated scroll delta (the relative distance, in pixels that the scroll position has
+ * moved from the starting position since capture started).
+ */
+ public int scrollDelta; // visible top offset from start
+
+ @Override
+ public String toString() {
+ return "ScrollResult{"
+ + "requestedArea=" + requestedArea
+ + ", availableArea=" + availableArea
+ + ", scrollDelta=" + scrollDelta
+ + '}';
+ }
+ }
+
+ /**
* Verifies that the view is still visible and scrollable. If true is returned here, expect a
* call to {@link #onComputeScrollBounds(View)} to follow.
*
@@ -48,6 +78,7 @@ interface ScrollCaptureViewHelper<V extends View> {
view.getWidth() - view.getPaddingRight(),
view.getHeight() - view.getPaddingBottom());
}
+
/**
* Adjust the target for capture.
* <p>
@@ -67,14 +98,14 @@ interface ScrollCaptureViewHelper<V extends View> {
* needed and return the resulting rectangle describing the position and bounds of the area
* which is visible.
*
+ * @param view the view being captured
* @param scrollBounds the area in which scrolling content moves, local to the {@code containing
* view}
* @param requestRect the area relative to {@code scrollBounds} which describes the location of
* content to capture for the request
- * @return the visible area within scrollBounds of the requested rectangle, return {@code null}
- * in the case of an unrecoverable error condition, to abort the capture process
+ * @return the result of the request as a {@link ScrollResult}
*/
- Rect onScrollRequested(@NonNull V view, Rect scrollBounds, Rect requestRect);
+ ScrollResult onScrollRequested(@NonNull V view, Rect scrollBounds, Rect requestRect);
/**
* Restore the target after capture.
diff --git a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
index 4087eda944e0..7b4f73ffde2b 100644
--- a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
+++ b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
@@ -30,21 +30,24 @@ import android.view.ScrollCaptureSession;
import android.view.Surface;
import android.view.View;
+import com.android.internal.view.ScrollCaptureViewHelper.ScrollResult;
+
import java.lang.ref.WeakReference;
import java.util.function.Consumer;
/**
- * Provides a ScrollCaptureCallback implementation for to handle arbitrary View-based scrolling
- * containers.
- * <p>
- * To use this class, supply the target view and an implementation of {@ScrollCaptureViewHelper}
- * to the callback.
+ * Provides a base ScrollCaptureCallback implementation to handle arbitrary View-based scrolling
+ * containers. This class handles the bookkeeping aspects of {@link ScrollCaptureCallback}
+ * including rendering output using HWUI. Adaptable to any {@link View} using
+ * {@link ScrollCaptureViewHelper}.
*
* @param <V> the specific View subclass handled
- * @hide
+ * @see ScrollCaptureViewHelper
*/
public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCallback {
+ private static final String TAG = "ScrollCaptureViewSupport";
+
private final WeakReference<V> mWeakView;
private final ScrollCaptureViewHelper<V> mViewHelper;
private ViewRenderer mRenderer;
@@ -52,11 +55,6 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa
private boolean mStarted;
private boolean mEnded;
- static <V extends View> ScrollCaptureCallback createCallback(V view,
- ScrollCaptureViewHelper<V> impl) {
- return new ScrollCaptureViewSupport<>(view, impl);
- }
-
ScrollCaptureViewSupport(V containingView, ScrollCaptureViewHelper<V> viewHelper) {
mWeakView = new WeakReference<>(containingView);
mRenderer = new ViewRenderer();
@@ -82,6 +80,7 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa
@Override
public final void onScrollCaptureStart(ScrollCaptureSession session, Runnable onReady) {
V view = mWeakView.get();
+
mEnded = false;
mStarted = true;
@@ -103,21 +102,30 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa
session.notifyBufferSent(0, null);
return;
}
- Rect captureArea = mViewHelper.onScrollRequested(view, session.getScrollBounds(),
+ // Ask the view to scroll as needed to bring this area into view.
+ ScrollResult scrollResult = mViewHelper.onScrollRequested(view, session.getScrollBounds(),
requestRect);
- mRenderer.renderFrame(view, captureArea, mUiHandler,
- () -> session.notifyBufferSent(0, captureArea));
+ view.invalidate(); // don't wait for vsync
+
+ // For image capture, shift back by scrollDelta to arrive at the location within the view
+ // where the requested content will be drawn
+ Rect viewCaptureArea = new Rect(scrollResult.availableArea);
+ viewCaptureArea.offset(0, -scrollResult.scrollDelta);
+
+ mRenderer.renderView(view, viewCaptureArea, mUiHandler,
+ (frameNumber) -> session.notifyBufferSent(frameNumber, scrollResult.availableArea));
}
@Override
public final void onScrollCaptureEnd(Runnable onReady) {
V view = mWeakView.get();
if (mStarted && !mEnded) {
- mViewHelper.onPrepareForEnd(view);
- /* empty */
+ if (view != null) {
+ mViewHelper.onPrepareForEnd(view);
+ view.invalidate();
+ }
mEnded = true;
- mRenderer.trimMemory();
- mRenderer.setSurface(null);
+ mRenderer.destroy();
}
onReady.run();
}
@@ -142,7 +150,7 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa
private static final String TAG = "ViewRenderer";
private HardwareRenderer mRenderer;
- private RenderNode mRootRenderNode;
+ private RenderNode mCaptureRenderNode;
private final RectF mTempRectF = new RectF();
private final Rect mSourceRect = new Rect();
private final Rect mTempRect = new Rect();
@@ -151,10 +159,14 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa
private long mLastRenderedSourceDrawingId = -1;
+ public interface FrameCompleteListener {
+ void onFrameComplete(long frameNumber);
+ }
+
ViewRenderer() {
mRenderer = new HardwareRenderer();
- mRootRenderNode = new RenderNode("ScrollCaptureRoot");
- mRenderer.setContentRoot(mRootRenderNode);
+ mCaptureRenderNode = new RenderNode("ScrollCaptureRoot");
+ mRenderer.setContentRoot(mCaptureRenderNode);
// TODO: Figure out a way to flip this on when we are sure the source window is opaque
mRenderer.setOpaque(false);
@@ -193,18 +205,36 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa
// Enable shadows for elevation/Z
mRenderer.setLightSourceGeometry(lightX, lightY, lightZ, lightRadius);
mRenderer.setLightSourceAlpha(AMBIENT_SHADOW_ALPHA, SPOT_SHADOW_ALPHA);
+ }
+
+ private void updateRootNode(View source, Rect localSourceRect) {
+ final View rootView = source.getRootView();
+ transformToRoot(source, localSourceRect, mTempRect);
+
+ mCaptureRenderNode.setPosition(0, 0, mTempRect.width(), mTempRect.height());
+ RecordingCanvas canvas = mCaptureRenderNode.beginRecording();
+ canvas.enableZ();
+ canvas.translate(-mTempRect.left, -mTempRect.top);
+ RenderNode rootViewRenderNode = rootView.updateDisplayListIfDirty();
+ if (rootViewRenderNode.hasDisplayList()) {
+ canvas.drawRenderNode(rootViewRenderNode);
+ }
+ mCaptureRenderNode.endRecording();
}
- public void renderFrame(View localReference, Rect sourceRect, Handler handler,
- Runnable onFrameCommitted) {
- if (updateForView(localReference)) {
- setupLighting(localReference);
+ public void renderView(View view, Rect sourceRect, Handler handler,
+ FrameCompleteListener frameListener) {
+ if (updateForView(view)) {
+ setupLighting(view);
}
- buildRootDisplayList(localReference, sourceRect);
+ view.invalidate();
+ updateRootNode(view, sourceRect);
HardwareRenderer.FrameRenderRequest request = mRenderer.createRenderRequest();
request.setVsyncTime(SystemClock.elapsedRealtimeNanos());
- request.setFrameCommitCallback(handler::post, onFrameCommitted);
+ // private API b/c request.setFrameCommitCallback does not provide access to frameNumber
+ mRenderer.setFrameCompleteCallback(
+ frameNr -> handler.post(() -> frameListener.onFrameComplete(frameNr)));
request.setWaitForPresent(true);
request.syncAndDraw();
}
@@ -225,15 +255,5 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa
mTempRectF.round(outRect);
}
- private void buildRootDisplayList(View source, Rect localSourceRect) {
- final View captureSource = source.getRootView();
- transformToRoot(source, localSourceRect, mTempRect);
- mRootRenderNode.setPosition(0, 0, mTempRect.width(), mTempRect.height());
- RecordingCanvas canvas = mRootRenderNode.beginRecording(mTempRect.width(),
- mTempRect.height());
- canvas.translate(-mTempRect.left, -mTempRect.top);
- canvas.drawRenderNode(captureSource.updateDisplayListIfDirty());
- mRootRenderNode.endRecording();
- }
}
}
diff --git a/core/java/com/android/internal/view/ScrollViewCaptureHelper.java b/core/java/com/android/internal/view/ScrollViewCaptureHelper.java
index 12bd461f810b..1514b9a285dd 100644
--- a/core/java/com/android/internal/view/ScrollViewCaptureHelper.java
+++ b/core/java/com/android/internal/view/ScrollViewCaptureHelper.java
@@ -35,13 +35,14 @@ import android.view.ViewParent;
* <li>correctly implements {@link ViewParent#requestChildRectangleOnScreen(View,
* Rect, boolean)}
* </ul>
+ *
+ * @see ScrollCaptureViewSupport
*/
public class ScrollViewCaptureHelper implements ScrollCaptureViewHelper<ViewGroup> {
private int mStartScrollY;
private boolean mScrollBarEnabled;
private int mOverScrollMode;
- /** @see ScrollCaptureViewHelper#onPrepareForStart(View, Rect) */
public void onPrepareForStart(@NonNull ViewGroup view, Rect scrollBounds) {
mStartScrollY = view.getScrollY();
mOverScrollMode = view.getOverScrollMode();
@@ -54,8 +55,8 @@ public class ScrollViewCaptureHelper implements ScrollCaptureViewHelper<ViewGrou
}
}
- /** @see ScrollCaptureViewHelper#onScrollRequested(View, Rect, Rect) */
- public Rect onScrollRequested(@NonNull ViewGroup view, Rect scrollBounds, Rect requestRect) {
+ public ScrollResult onScrollRequested(@NonNull ViewGroup view, Rect scrollBounds,
+ Rect requestRect) {
final View contentView = view.getChildAt(0); // returns null, does not throw IOOBE
if (contentView == null) {
return null;
@@ -87,6 +88,9 @@ public class ScrollViewCaptureHelper implements ScrollCaptureViewHelper<ViewGrou
\__ Requested Bounds[0,300 - 200,400] (200x100)
*/
+ ScrollResult result = new ScrollResult();
+ result.requestedArea = new Rect(requestRect);
+
// 0) adjust the requestRect to account for scroll change since start
//
// Scroll Bounds[50,50 - 250,250] (w=200,h=200)
@@ -117,8 +121,6 @@ public class ScrollViewCaptureHelper implements ScrollCaptureViewHelper<ViewGrou
view.getScrollX() - contentView.getLeft(),
view.getScrollY() - contentView.getTop());
-
-
// requestRect is now local to contentView as requestedContentBounds
// contentView (and each parent in turn if possible) will be scrolled
// (if necessary) to make all of requestedContent visible, (if possible!)
@@ -126,35 +128,37 @@ public class ScrollViewCaptureHelper implements ScrollCaptureViewHelper<ViewGrou
// update new offset between starting and current scroll position
scrollDelta = view.getScrollY() - mStartScrollY;
+ result.scrollDelta = scrollDelta;
-
- // TODO: adjust to avoid occlusions/minimize scroll changes
+ // TODO: crop capture area to avoid occlusions/minimize scroll changes
Point offset = new Point();
- final Rect capturedRect = new Rect(requestedContentBounds); // empty
- if (!view.getChildVisibleRect(contentView, capturedRect, offset)) {
- capturedRect.setEmpty();
- return capturedRect;
+ final Rect available = new Rect(requestedContentBounds); // empty
+ if (!view.getChildVisibleRect(contentView, available, offset)) {
+ available.setEmpty();
+ result.availableArea = available;
+ return result;
}
// Transform back from global to content-view local
- capturedRect.offset(-offset.x, -offset.y);
+ available.offset(-offset.x, -offset.y);
// Then back to container view
- capturedRect.offset(
+ available.offset(
contentView.getLeft() - view.getScrollX(),
contentView.getTop() - view.getScrollY());
// And back to relative to scrollBounds
- capturedRect.offset(-scrollBounds.left, -scrollBounds.top);
+ available.offset(-scrollBounds.left, -scrollBounds.top);
- // Apply scrollDelta again to return to make capturedRect relative to scrollBounds at
+ // Apply scrollDelta again to return to make `available` relative to `scrollBounds` at
// the scroll position at start of capture.
- capturedRect.offset(0, scrollDelta);
- return capturedRect;
+ available.offset(0, scrollDelta);
+
+ result.availableArea = new Rect(available);
+ return result;
}
- /** @see ScrollCaptureViewHelper#onPrepareForEnd(View) */
public void onPrepareForEnd(@NonNull ViewGroup view) {
view.scrollTo(0, mStartScrollY);
if (mOverScrollMode != View.OVER_SCROLL_NEVER) {
diff --git a/core/tests/coretests/src/com/android/internal/view/ScrollViewCaptureHelperTest.java b/core/tests/coretests/src/com/android/internal/view/ScrollViewCaptureHelperTest.java
index 63a68e99b788..ab13fd7d81e0 100644
--- a/core/tests/coretests/src/com/android/internal/view/ScrollViewCaptureHelperTest.java
+++ b/core/tests/coretests/src/com/android/internal/view/ScrollViewCaptureHelperTest.java
@@ -21,12 +21,11 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-import static androidx.test.InstrumentationRegistry.getContext;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.content.Context;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Rect;
@@ -40,10 +39,12 @@ import android.widget.ScrollView;
import android.widget.TextView;
import androidx.test.annotation.UiThreadTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.view.ScrollCaptureViewHelper.ScrollResult;
import org.junit.After;
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Test;
import java.util.Random;
@@ -67,28 +68,27 @@ public class ScrollViewCaptureHelperTest {
private Random mRandom;
- private static float sDensity;
-
- @BeforeClass
- public static void setUpClass() {
- sDensity = getContext().getResources().getDisplayMetrics().density;
- }
+ private Context mContext;
+ private float mDensity;
@Before
@UiThreadTest
public void setUp() {
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ mDensity = mContext.getResources().getDisplayMetrics().density;
+
mRandom = new Random();
- mParent = new FrameLayout(getContext());
+ mParent = new FrameLayout(mContext);
- mTarget = new ScrollView(getContext());
+ mTarget = new ScrollView(mContext);
mParent.addView(mTarget, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
- mContent = new LinearLayout(getContext());
+ mContent = new LinearLayout(mContext);
mContent.setOrientation(LinearLayout.VERTICAL);
mTarget.addView(mContent, new ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
for (int i = 0; i < CHILD_VIEWS; i++) {
- TextView view = new TextView(getContext());
+ TextView view = new TextView(mContext);
view.setText("Child #" + i);
view.setTextColor(Color.WHITE);
view.setTextSize(30f);
@@ -99,7 +99,7 @@ public class ScrollViewCaptureHelperTest {
// Window -> Parent -> Target -> Content
- mWm = getContext().getSystemService(WindowManager.class);
+ mWm = mContext.getSystemService(WindowManager.class);
// Setup the window that we are going to use
mWindowLayoutParams = new WindowManager.LayoutParams(WINDOW_WIDTH, WINDOW_HEIGHT,
@@ -123,58 +123,6 @@ public class ScrollViewCaptureHelperTest {
svc.onPrepareForStart(mTarget, scrollBounds);
}
- static void assertEmpty(Rect r) {
- if (r != null && !r.isEmpty()) {
- fail("Not true that " + r + " is empty");
- }
- }
-
- static void assertContains(Rect parent, Rect child) {
- if (!parent.contains(child)) {
- fail("Not true that " + parent + " contains " + child);
- }
- }
-
- static void assertRectEquals(Rect parent, Rect child) {
- if (!parent.equals(child)) {
- fail("Not true that " + parent + " is equal to " + child);
- }
- }
-
- static Rect getVisibleRect(View v) {
- Rect r = new Rect(0, 0, v.getWidth(), v.getHeight());
- v.getLocalVisibleRect(r);
- return r;
- }
-
-
- static int assertScrollToY(View v, int scrollY) {
- v.scrollTo(0, scrollY);
- int dest = v.getScrollY();
- assertEquals(scrollY, dest);
- return scrollY;
- }
-
-
- static void assertCapturedAreaCompletelyVisible(int startScrollY, Rect requestRect,
- Rect localVisibleNow) {
- Rect captured = new Rect(localVisibleNow);
- captured.offset(0, -startScrollY); // make relative
-
- if (!captured.contains(requestRect)) {
- fail("Not true that all of " + requestRect + " is contained by " + captured);
- }
- }
- static void assertCapturedAreaPartiallyVisible(int startScrollY, Rect requestRect,
- Rect localVisibleNow) {
- Rect captured = new Rect(localVisibleNow);
- captured.offset(0, -startScrollY); // make relative
-
- if (!Rect.intersects(captured, requestRect)) {
- fail("Not true that any of " + requestRect + " intersects " + captured);
- }
- }
-
@Test
@UiThreadTest
public void onScrollRequested_up_fromTop() {
@@ -188,12 +136,13 @@ public class ScrollViewCaptureHelperTest {
Rect request = new Rect(0, -CAPTURE_HEIGHT, scrollBounds.width(), 0);
- Rect result = svc.onScrollRequested(mTarget, scrollBounds, request);
+ ScrollResult scrollResult = svc.onScrollRequested(mTarget,
+ scrollBounds, request);
// The result is an empty rectangle and no scrolling, since it
// is not possible to physically scroll further up to make the
// requested area visible at all (it doesn't exist).
- assertEmpty(result);
+ assertEmpty(scrollResult.availableArea);
}
@Test
@@ -201,7 +150,6 @@ public class ScrollViewCaptureHelperTest {
public void onScrollRequested_down_fromTop() {
final int startScrollY = assertScrollToY(mTarget, 0);
-
ScrollViewCaptureHelper svc = new ScrollViewCaptureHelper();
Rect scrollBounds = svc.onComputeScrollBounds(mTarget);
svc.onPrepareForStart(mTarget, scrollBounds);
@@ -212,13 +160,13 @@ public class ScrollViewCaptureHelperTest {
Rect request = new Rect(0, WINDOW_HEIGHT, scrollBounds.width(),
WINDOW_HEIGHT + CAPTURE_HEIGHT);
- Rect result = svc.onScrollRequested(mTarget, scrollBounds, request);
- assertRectEquals(request, result);
-
- assertCapturedAreaCompletelyVisible(startScrollY, request, getVisibleRect(mContent));
+ ScrollResult scrollResult = svc.onScrollRequested(mTarget, scrollBounds, request);
+ assertRectEquals(request, scrollResult.requestedArea);
+ assertRectEquals(request, scrollResult.availableArea);
+ assertRequestedRectCompletelyVisible(startScrollY, request, getVisibleRect(mContent));
+ assertEquals(CAPTURE_HEIGHT, scrollResult.scrollDelta);
}
-
@Test
@UiThreadTest
public void onScrollRequested_up_fromMiddle() {
@@ -230,12 +178,11 @@ public class ScrollViewCaptureHelperTest {
Rect request = new Rect(0, -CAPTURE_HEIGHT, scrollBounds.width(), 0);
-
- Rect result = svc.onScrollRequested(mTarget, scrollBounds, request);
-
- assertRectEquals(request, result);
-
- assertCapturedAreaCompletelyVisible(startScrollY, request, getVisibleRect(mContent));
+ ScrollResult scrollResult = svc.onScrollRequested(mTarget, scrollBounds, request);
+ assertRectEquals(request, scrollResult.requestedArea);
+ assertRectEquals(request, scrollResult.availableArea);
+ assertRequestedRectCompletelyVisible(startScrollY, request, getVisibleRect(mContent));
+ assertEquals(-CAPTURE_HEIGHT, scrollResult.scrollDelta);
}
@Test
@@ -250,10 +197,12 @@ public class ScrollViewCaptureHelperTest {
Rect request = new Rect(0, WINDOW_HEIGHT, scrollBounds.width(),
WINDOW_HEIGHT + CAPTURE_HEIGHT);
- Rect result = svc.onScrollRequested(mTarget, scrollBounds, request);
- assertRectEquals(request, result);
+ ScrollResult scrollResult = svc.onScrollRequested(mTarget, scrollBounds, request);
+ assertRectEquals(request, scrollResult.requestedArea);
+ assertRectEquals(request, scrollResult.availableArea);
+ assertRequestedRectCompletelyVisible(startScrollY, request, getVisibleRect(mContent));
+ assertEquals(CAPTURE_HEIGHT, scrollResult.scrollDelta);
- assertCapturedAreaCompletelyVisible(startScrollY, request, getVisibleRect(mContent));
}
@Test
@@ -267,10 +216,11 @@ public class ScrollViewCaptureHelperTest {
Rect request = new Rect(0, -CAPTURE_HEIGHT, scrollBounds.width(), 0);
- Rect result = svc.onScrollRequested(mTarget, scrollBounds, request);
- assertRectEquals(request, result);
-
- assertCapturedAreaCompletelyVisible(startScrollY, request, getVisibleRect(mContent));
+ ScrollResult scrollResult = svc.onScrollRequested(mTarget, scrollBounds, request);
+ assertRectEquals(request, scrollResult.requestedArea);
+ assertRectEquals(request, scrollResult.availableArea);
+ assertRequestedRectCompletelyVisible(startScrollY, request, getVisibleRect(mContent));
+ assertEquals(-CAPTURE_HEIGHT, scrollResult.scrollDelta);
}
@Test
@@ -285,12 +235,14 @@ public class ScrollViewCaptureHelperTest {
Rect request = new Rect(0, WINDOW_HEIGHT, scrollBounds.width(),
WINDOW_HEIGHT + CAPTURE_HEIGHT);
- Rect result = svc.onScrollRequested(mTarget, scrollBounds, request);
+ ScrollResult scrollResult = svc.onScrollRequested(mTarget, scrollBounds, request);
+ assertRectEquals(request, scrollResult.requestedArea);
// The result is an empty rectangle and no scrolling, since it
// is not possible to physically scroll further down to make the
// requested area visible at all (it doesn't exist).
- assertEmpty(result);
+ assertEmpty(scrollResult.availableArea);
+ assertEquals(0, scrollResult.scrollDelta);
}
@Test
@@ -309,12 +261,16 @@ public class ScrollViewCaptureHelperTest {
0, top - (CAPTURE_HEIGHT / 2),
scrollBounds.width(), top + (CAPTURE_HEIGHT / 2));
- Rect result = svc.onScrollRequested(mTarget, scrollBounds, request);
+ ScrollResult scrollResult = svc.onScrollRequested(mTarget, scrollBounds, request);
+ assertRectEquals(request, scrollResult.requestedArea);
+
+ ScrollResult result = svc.onScrollRequested(mTarget, scrollBounds, request);
// The result is a partial result
Rect expectedResult = new Rect(request);
expectedResult.top += 300; // top half clipped
- assertRectEquals(expectedResult, result);
- assertCapturedAreaPartiallyVisible(startScrollY, request, getVisibleRect(mContent));
+ assertRectEquals(expectedResult, result.availableArea);
+ assertRequestedRectPartiallyVisible(startScrollY, request, getVisibleRect(mContent));
+ assertEquals(0, scrollResult.scrollDelta);
}
@Test
@@ -334,13 +290,13 @@ public class ScrollViewCaptureHelperTest {
0, bottom - (CAPTURE_HEIGHT / 2),
scrollBounds.width(), bottom + (CAPTURE_HEIGHT / 2));
- Rect result = svc.onScrollRequested(mTarget, scrollBounds, request);
+ ScrollResult result = svc.onScrollRequested(mTarget, scrollBounds, request);
Rect expectedResult = new Rect(request);
expectedResult.bottom -= 300; // bottom half clipped
- assertRectEquals(expectedResult, result);
- assertCapturedAreaPartiallyVisible(startScrollY, request, getVisibleRect(mContent));
-
+ assertRectEquals(expectedResult, result.availableArea);
+ assertRequestedRectPartiallyVisible(startScrollY, request, getVisibleRect(mContent));
+ assertEquals(0, result.scrollDelta);
}
@Test
@@ -349,4 +305,56 @@ public class ScrollViewCaptureHelperTest {
ScrollViewCaptureHelper svc = new ScrollViewCaptureHelper();
svc.onPrepareForEnd(mTarget);
}
+
+
+ static void assertEmpty(Rect r) {
+ if (r != null && !r.isEmpty()) {
+ fail("Not true that " + r + " is empty");
+ }
+ }
+
+ static void assertContains(Rect parent, Rect child) {
+ if (!parent.contains(child)) {
+ fail("Not true that " + parent + " contains " + child);
+ }
+ }
+
+ static void assertRectEquals(Rect parent, Rect child) {
+ if (!parent.equals(child)) {
+ fail("Not true that " + parent + " is equal to " + child);
+ }
+ }
+
+ static Rect getVisibleRect(View v) {
+ Rect r = new Rect(0, 0, v.getWidth(), v.getHeight());
+ v.getLocalVisibleRect(r);
+ return r;
+ }
+
+
+ static int assertScrollToY(View v, int scrollY) {
+ v.scrollTo(0, scrollY);
+ int dest = v.getScrollY();
+ assertEquals(scrollY, dest);
+ return scrollY;
+ }
+
+ static void assertRequestedRectCompletelyVisible(int startScrollY, Rect requestRect,
+ Rect localVisibleNow) {
+ Rect captured = new Rect(localVisibleNow);
+ captured.offset(0, -startScrollY); // make relative
+
+ if (!captured.contains(requestRect)) {
+ fail("Not true that all of " + requestRect + " is contained by " + captured);
+ }
+ }
+ static void assertRequestedRectPartiallyVisible(int startScrollY, Rect requestRect,
+ Rect localVisibleNow) {
+ Rect captured = new Rect(localVisibleNow);
+ captured.offset(0, -startScrollY); // make relative
+
+ if (!Rect.intersects(captured, requestRect)) {
+ fail("Not true that any of " + requestRect + " intersects " + captured);
+ }
+ }
}