blob: c589afdeaa1a21ec52a4a32807cc3e91ee035fe6 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.view;
import android.annotation.Nullable;
import android.graphics.Point;
import android.graphics.Rect;
import android.view.ScrollCaptureCallback;
import android.view.View;
import android.view.ViewGroup;
/**
* Provides built-in framework level Scroll Capture support for standard scrolling Views.
*/
public class ScrollCaptureInternal {
private static final String TAG = "ScrollCaptureInternal";
private static final int UP = -1;
private static final int DOWN = 1;
/**
* Not a ViewGroup, or cannot scroll according to View APIs.
*/
public static final int TYPE_FIXED = 0;
/**
* Slides a single child view using mScrollX/mScrollY.
*/
public static final int TYPE_SCROLLING = 1;
/**
* Slides child views through the viewport by translating their layout positions with {@link
* View#offsetTopAndBottom(int)}. Manages Child view lifecycle, creating as needed and
* binding views to data from an adapter. Views are reused whenever possible.
*/
public static final int TYPE_RECYCLING = 2;
/**
* Performs tests on the given View and determines:
* 1. If scrolling is possible
* 2. What mechanisms are used for scrolling.
* <p>
* This needs to be fast and not alloc memory. It's called on everything in the tree not marked
* as excluded during scroll capture search.
*/
public static int detectScrollingType(View view) {
// Must be a ViewGroup
if (!(view instanceof ViewGroup)) {
return TYPE_FIXED;
}
// Confirm that it can scroll.
if (!(view.canScrollVertically(DOWN) || view.canScrollVertically(UP))) {
// Nothing to scroll here, move along.
return TYPE_FIXED;
}
// ScrollViews accept only a single child.
if (((ViewGroup) view).getChildCount() > 1) {
return TYPE_RECYCLING;
}
//Because recycling containers don't use scrollY, a non-zero value means Scroll view.
if (view.getScrollY() != 0) {
return TYPE_SCROLLING;
}
// Since scrollY cannot be negative, this means a Recycling view.
if (view.canScrollVertically(UP)) {
return TYPE_RECYCLING;
}
// canScrollVertically(UP) == false, getScrollY() == 0, getChildCount() == 1.
// For Recycling containers, this should be a no-op (RecyclerView logs a warning)
view.scrollTo(view.getScrollX(), 1);
// A scrolling container would have moved by 1px.
if (view.getScrollY() == 1) {
view.scrollTo(view.getScrollX(), 0);
return TYPE_SCROLLING;
}
return TYPE_RECYCLING;
}
/**
* Creates a scroll capture callback for the given view if possible.
*
* @param view the view to capture
* @param localVisibleRect the visible area of the given view in local coordinates, as supplied
* by the view parent
* @param positionInWindow the offset of localVisibleRect within the window
*
* @return a new callback or null if the View isn't supported
*/
@Nullable
public ScrollCaptureCallback requestCallback(View view, Rect localVisibleRect,
Point positionInWindow) {
// Nothing to see here yet.
int i = detectScrollingType(view);
switch (i) {
case TYPE_SCROLLING:
return new ScrollCaptureViewSupport<>((ViewGroup) view,
new ScrollViewCaptureHelper());
}
return null;
}
}