Loading core/java/com/android/internal/view/ScrollCaptureViewHelper.java +34 −3 Original line number Diff line number Diff line Loading @@ -25,6 +25,36 @@ interface ScrollCaptureViewHelper<V extends View> { int UP = -1; 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. Loading @@ -48,6 +78,7 @@ interface ScrollCaptureViewHelper<V extends View> { view.getWidth() - view.getPaddingRight(), view.getHeight() - view.getPaddingBottom()); } /** * Adjust the target for capture. * <p> Loading @@ -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. Loading core/java/com/android/internal/view/ScrollCaptureViewSupport.java +57 −37 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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(); Loading @@ -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; Loading @@ -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) { if (view != null) { mViewHelper.onPrepareForEnd(view); /* empty */ view.invalidate(); } mEnded = true; mRenderer.trimMemory(); mRenderer.setSurface(null); mRenderer.destroy(); } onReady.run(); } Loading @@ -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(); Loading @@ -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); Loading Loading @@ -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(); } Loading @@ -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(); } } } core/java/com/android/internal/view/ScrollViewCaptureHelper.java +22 −18 Original line number Diff line number Diff line Loading @@ -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(); Loading @@ -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; Loading Loading @@ -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) Loading Loading @@ -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!) Loading @@ -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) { Loading core/tests/coretests/src/com/android/internal/view/ScrollViewCaptureHelperTest.java +104 −96 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/com/android/internal/view/ScrollCaptureViewHelper.java +34 −3 Original line number Diff line number Diff line Loading @@ -25,6 +25,36 @@ interface ScrollCaptureViewHelper<V extends View> { int UP = -1; 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. Loading @@ -48,6 +78,7 @@ interface ScrollCaptureViewHelper<V extends View> { view.getWidth() - view.getPaddingRight(), view.getHeight() - view.getPaddingBottom()); } /** * Adjust the target for capture. * <p> Loading @@ -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. Loading
core/java/com/android/internal/view/ScrollCaptureViewSupport.java +57 −37 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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(); Loading @@ -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; Loading @@ -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) { if (view != null) { mViewHelper.onPrepareForEnd(view); /* empty */ view.invalidate(); } mEnded = true; mRenderer.trimMemory(); mRenderer.setSurface(null); mRenderer.destroy(); } onReady.run(); } Loading @@ -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(); Loading @@ -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); Loading Loading @@ -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(); } Loading @@ -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(); } } }
core/java/com/android/internal/view/ScrollViewCaptureHelper.java +22 −18 Original line number Diff line number Diff line Loading @@ -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(); Loading @@ -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; Loading Loading @@ -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) Loading Loading @@ -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!) Loading @@ -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) { Loading
core/tests/coretests/src/com/android/internal/view/ScrollViewCaptureHelperTest.java +104 −96 File changed.Preview size limit exceeded, changes collapsed. Show changes