NewPipe/app/src/main/java/com/google/android/material/appbar/FlingBehavior.java

166 lines
5.9 KiB
Java
Raw Normal View History

2019-10-04 14:59:08 +02:00
package com.google.android.material.appbar;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
2019-10-02 02:59:20 +02:00
import android.view.MotionEvent;
import android.view.View;
2019-10-02 02:59:20 +02:00
import android.widget.OverScroller;
import androidx.annotation.NonNull;
2019-10-04 14:59:08 +02:00
import androidx.annotation.Nullable;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import org.schabi.newpipe.R;
2019-10-04 14:59:08 +02:00
2019-10-02 02:59:20 +02:00
import java.lang.reflect.Field;
import java.util.List;
// See https://stackoverflow.com/questions/56849221#57997489
2019-10-02 02:59:20 +02:00
public final class FlingBehavior extends AppBarLayout.Behavior {
private final Rect focusScrollRect = new Rect();
public FlingBehavior(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
private boolean allowScroll = true;
private final Rect globalRect = new Rect();
2022-07-15 04:10:29 +02:00
private final List<Integer> skipInterceptionOfElements = List.of(
2021-01-14 21:58:19 +01:00
R.id.itemsListPanel, R.id.playbackSeekBar,
R.id.playPauseButton, R.id.playPreviousButton, R.id.playNextButton);
@Override
public boolean onRequestChildRectangleOnScreen(
@NonNull final CoordinatorLayout coordinatorLayout, @NonNull final AppBarLayout child,
@NonNull final Rect rectangle, final boolean immediate) {
focusScrollRect.set(rectangle);
coordinatorLayout.offsetDescendantRectToMyCoords(child, focusScrollRect);
2020-08-16 10:24:58 +02:00
final int height = coordinatorLayout.getHeight();
if (focusScrollRect.top <= 0 && focusScrollRect.bottom >= height) {
// the child is too big to fit inside ourselves completely, ignore request
return false;
}
2020-08-16 10:24:58 +02:00
final int dy;
if (focusScrollRect.bottom > height) {
dy = focusScrollRect.top;
} else if (focusScrollRect.top < 0) {
// scrolling up
dy = -(height - focusScrollRect.bottom);
} else {
// nothing to do
return false;
}
2020-08-16 10:24:58 +02:00
final int consumed = scroll(coordinatorLayout, child, dy, getMaxDragOffset(child), 0);
return consumed == dy;
}
@Override
public boolean onInterceptTouchEvent(@NonNull final CoordinatorLayout parent,
@NonNull final AppBarLayout child,
@NonNull final MotionEvent ev) {
2022-07-15 04:10:29 +02:00
for (final int element : skipInterceptionOfElements) {
final View view = child.findViewById(element);
if (view != null) {
final boolean visible = view.getGlobalVisibleRect(globalRect);
if (visible && globalRect.contains((int) ev.getRawX(), (int) ev.getRawY())) {
allowScroll = false;
return false;
}
2020-08-16 21:20:37 +02:00
}
}
allowScroll = true;
2019-10-02 02:59:20 +02:00
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
// remove reference to old nested scrolling child
resetNestedScrollingChild();
// Stop fling when your finger touches the screen
stopAppBarLayoutFling();
break;
default:
break;
}
2019-10-02 02:59:20 +02:00
return super.onInterceptTouchEvent(parent, child, ev);
}
@Override
2020-07-14 21:08:12 +02:00
public boolean onStartNestedScroll(@NonNull final CoordinatorLayout parent,
@NonNull final AppBarLayout child,
@NonNull final View directTargetChild,
2020-07-14 19:21:32 +02:00
final View target,
final int nestedScrollAxes,
final int type) {
return allowScroll && super.onStartNestedScroll(
parent, child, directTargetChild, target, nestedScrollAxes, type);
}
@Override
2020-07-14 21:08:12 +02:00
public boolean onNestedFling(@NonNull final CoordinatorLayout coordinatorLayout,
@NonNull final AppBarLayout child,
@NonNull final View target, final float velocityX,
2020-07-14 19:21:32 +02:00
final float velocityY, final boolean consumed) {
return allowScroll && super.onNestedFling(
coordinatorLayout, child, target, velocityX, velocityY, consumed);
}
2019-10-02 02:59:20 +02:00
@Nullable
private OverScroller getScrollerField() {
try {
2020-08-16 10:24:58 +02:00
final Class<?> headerBehaviorType = this.getClass()
.getSuperclass().getSuperclass().getSuperclass();
2019-10-02 02:59:20 +02:00
if (headerBehaviorType != null) {
2020-08-16 10:24:58 +02:00
final Field field = headerBehaviorType.getDeclaredField("scroller");
2019-10-02 02:59:20 +02:00
field.setAccessible(true);
return ((OverScroller) field.get(this));
}
2020-08-16 10:24:58 +02:00
} catch (final NoSuchFieldException | IllegalAccessException e) {
2019-10-02 02:59:20 +02:00
// ?
}
2019-10-02 02:59:20 +02:00
return null;
}
2019-10-02 02:59:20 +02:00
@Nullable
private Field getLastNestedScrollingChildRefField() {
try {
2020-08-16 10:24:58 +02:00
final Class<?> headerBehaviorType = this.getClass().getSuperclass().getSuperclass();
2019-10-02 02:59:20 +02:00
if (headerBehaviorType != null) {
final Field field =
headerBehaviorType.getDeclaredField("lastNestedScrollingChildRef");
2019-10-02 02:59:20 +02:00
field.setAccessible(true);
return field;
}
2020-08-16 10:24:58 +02:00
} catch (final NoSuchFieldException e) {
2019-10-02 02:59:20 +02:00
// ?
}
2019-10-02 02:59:20 +02:00
return null;
}
private void resetNestedScrollingChild() {
2020-08-16 10:24:58 +02:00
final Field field = getLastNestedScrollingChildRefField();
if (field != null) {
2019-10-02 02:59:20 +02:00
try {
2020-08-16 10:24:58 +02:00
final Object value = field.get(this);
if (value != null) {
field.set(this, null);
}
2020-08-16 10:24:58 +02:00
} catch (final IllegalAccessException e) {
2019-10-02 02:59:20 +02:00
// ?
}
}
2019-10-02 02:59:20 +02:00
}
2019-10-02 02:59:20 +02:00
private void stopAppBarLayoutFling() {
2020-08-16 10:24:58 +02:00
final OverScroller scroller = getScrollerField();
if (scroller != null) {
scroller.forceFinished(true);
}
}
}