mirror of https://github.com/KokaKiwi/BarInsta
986 lines
40 KiB
Java
Executable File
986 lines
40 KiB
Java
Executable File
package awais.instagrabber.customviews;
|
|
|
|
import android.annotation.SuppressLint;
|
|
import android.content.Context;
|
|
import android.content.res.TypedArray;
|
|
import android.graphics.Canvas;
|
|
import android.graphics.Matrix;
|
|
import android.graphics.PixelFormat;
|
|
import android.graphics.Rect;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.os.Build;
|
|
import android.os.Parcel;
|
|
import android.os.Parcelable;
|
|
import android.util.AttributeSet;
|
|
import android.util.Log;
|
|
import android.view.Gravity;
|
|
import android.view.InputDevice;
|
|
import android.view.KeyEvent;
|
|
import android.view.MotionEvent;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
|
|
import androidx.annotation.IntDef;
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.core.view.GravityCompat;
|
|
import androidx.core.view.ViewCompat;
|
|
import androidx.customview.view.AbsSavedState;
|
|
import androidx.customview.widget.ViewDragHelper;
|
|
|
|
import java.lang.annotation.Retention;
|
|
import java.lang.annotation.RetentionPolicy;
|
|
import java.lang.reflect.Field;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
import awais.instagrabber.BuildConfig;
|
|
|
|
// exactly same as the LayoutDrawer with some edits
|
|
@SuppressLint("RtlHardcoded")
|
|
public class MouseDrawer extends ViewGroup {
|
|
@IntDef({ViewDragHelper.STATE_IDLE, ViewDragHelper.STATE_DRAGGING, ViewDragHelper.STATE_SETTLING})
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
private @interface State {}
|
|
|
|
@IntDef(value = {Gravity.NO_GRAVITY, Gravity.LEFT, Gravity.RIGHT, GravityCompat.START, GravityCompat.END}, flag = true)
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
public @interface EdgeGravity {}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
private static final boolean CHILDREN_DISALLOW_INTERCEPT = true;
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
private final ArrayList<View> mNonDrawerViews = new ArrayList<>();
|
|
private final ViewDragHelper mLeftDragger, mRightDragger;
|
|
private boolean mInLayout, mFirstLayout = true;
|
|
private float mDrawerElevation, mInitialMotionX, mInitialMotionY;
|
|
private int mDrawerState;
|
|
private List<DrawerListener> mListeners;
|
|
private Matrix mChildInvertedMatrix;
|
|
private Rect mChildHitRect;
|
|
|
|
public interface DrawerListener {
|
|
void onDrawerSlide(final View drawerView, @EdgeGravity final int gravity, final float slideOffset);
|
|
default void onDrawerOpened(final View drawerView, @EdgeGravity final int gravity) {}
|
|
default void onDrawerClosed(final View drawerView, @EdgeGravity final int gravity) {}
|
|
default void onDrawerStateChanged() {}
|
|
}
|
|
|
|
public MouseDrawer(@NonNull final Context context) {
|
|
this(context, null);
|
|
}
|
|
|
|
public MouseDrawer(@NonNull final Context context, @Nullable final AttributeSet attrs) {
|
|
this(context, attrs, 0);
|
|
}
|
|
|
|
public MouseDrawer(@NonNull final Context context, @Nullable final AttributeSet attrs, final int defStyle) {
|
|
super(context, attrs, defStyle);
|
|
setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
|
|
|
|
final float density = getResources().getDisplayMetrics().density;
|
|
this.mDrawerElevation = 10 * density;
|
|
|
|
final float touchSlopSensitivity = 0.5f; // was 1.0f
|
|
final float minFlingVelocity = 400 /* dips per second */ * density;
|
|
|
|
final ViewDragCallback mLeftCallback = new ViewDragCallback(Gravity.LEFT);
|
|
this.mLeftDragger = ViewDragHelper.create(this, touchSlopSensitivity, mLeftCallback);
|
|
this.mLeftDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
|
|
this.mLeftDragger.setMinVelocity(minFlingVelocity);
|
|
|
|
final ViewDragCallback mRightCallback = new ViewDragCallback(Gravity.RIGHT);
|
|
this.mRightDragger = ViewDragHelper.create(this, touchSlopSensitivity, mRightCallback);
|
|
this.mRightDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_RIGHT);
|
|
this.mRightDragger.setMinVelocity(minFlingVelocity);
|
|
|
|
try {
|
|
final Field edgeSizeField = ViewDragHelper.class.getDeclaredField("mEdgeSize");
|
|
if (!edgeSizeField.isAccessible()) edgeSizeField.setAccessible(true);
|
|
final int widthPixels = getResources().getDisplayMetrics().widthPixels; // whole screen
|
|
edgeSizeField.set(this.mLeftDragger, widthPixels / 2);
|
|
edgeSizeField.set(this.mRightDragger, widthPixels / 2);
|
|
} catch (final Exception e) {
|
|
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
|
|
}
|
|
|
|
mLeftCallback.setDragger(mLeftDragger);
|
|
mRightCallback.setDragger(mRightDragger);
|
|
|
|
setFocusableInTouchMode(true);
|
|
//setMotionEventSplittingEnabled(false);
|
|
}
|
|
|
|
public void setDrawerElevation(final float elevation) {
|
|
mDrawerElevation = elevation;
|
|
for (int i = 0; i < getChildCount(); i++) {
|
|
final View child = getChildAt(i);
|
|
if (isDrawerView(child)) ViewCompat.setElevation(child, mDrawerElevation);
|
|
}
|
|
}
|
|
|
|
public float getDrawerElevation() {
|
|
return Build.VERSION.SDK_INT >= 21 ? mDrawerElevation : 0f;
|
|
}
|
|
|
|
public void addDrawerListener(@NonNull final DrawerListener listener) {
|
|
if (mListeners == null) mListeners = new ArrayList<>();
|
|
mListeners.add(listener);
|
|
}
|
|
|
|
private boolean isInBoundsOfChild(final float x, final float y, final View child) {
|
|
if (mChildHitRect == null) mChildHitRect = new Rect();
|
|
child.getHitRect(mChildHitRect);
|
|
return mChildHitRect.contains((int) x, (int) y);
|
|
}
|
|
|
|
private boolean dispatchTransformedGenericPointerEvent(final MotionEvent event, @NonNull final View child) {
|
|
final boolean handled;
|
|
final Matrix childMatrix = child.getMatrix();
|
|
if (!childMatrix.isIdentity()) {
|
|
final MotionEvent transformedEvent = getTransformedMotionEvent(event, child);
|
|
handled = child.dispatchGenericMotionEvent(transformedEvent);
|
|
transformedEvent.recycle();
|
|
} else {
|
|
final float offsetX = getScrollX() - child.getLeft();
|
|
final float offsetY = getScrollY() - child.getTop();
|
|
event.offsetLocation(offsetX, offsetY);
|
|
handled = child.dispatchGenericMotionEvent(event);
|
|
event.offsetLocation(-offsetX, -offsetY);
|
|
}
|
|
return handled;
|
|
}
|
|
|
|
@NonNull
|
|
private MotionEvent getTransformedMotionEvent(final MotionEvent event, @NonNull final View child) {
|
|
final float offsetX = getScrollX() - child.getLeft();
|
|
final float offsetY = getScrollY() - child.getTop();
|
|
final MotionEvent transformedEvent = MotionEvent.obtain(event);
|
|
transformedEvent.offsetLocation(offsetX, offsetY);
|
|
final Matrix childMatrix = child.getMatrix();
|
|
if (!childMatrix.isIdentity()) {
|
|
if (mChildInvertedMatrix == null) mChildInvertedMatrix = new Matrix();
|
|
childMatrix.invert(mChildInvertedMatrix);
|
|
transformedEvent.transform(mChildInvertedMatrix);
|
|
}
|
|
return transformedEvent;
|
|
}
|
|
|
|
void updateDrawerState(@State final int activeState, final View activeDrawer) {
|
|
final int leftState = mLeftDragger.getViewDragState();
|
|
final int rightState = mRightDragger.getViewDragState();
|
|
|
|
final int state;
|
|
if (leftState == ViewDragHelper.STATE_DRAGGING || rightState == ViewDragHelper.STATE_DRAGGING)
|
|
state = ViewDragHelper.STATE_DRAGGING;
|
|
else if (leftState == ViewDragHelper.STATE_SETTLING || rightState == ViewDragHelper.STATE_SETTLING)
|
|
state = ViewDragHelper.STATE_SETTLING;
|
|
else state = ViewDragHelper.STATE_IDLE;
|
|
|
|
if (activeDrawer != null && activeState == ViewDragHelper.STATE_IDLE) {
|
|
final LayoutParams lp = (LayoutParams) activeDrawer.getLayoutParams();
|
|
if (lp.onScreen == 0) dispatchOnDrawerClosed(activeDrawer);
|
|
else if (lp.onScreen == 1) dispatchOnDrawerOpened(activeDrawer);
|
|
}
|
|
|
|
if (state != mDrawerState) {
|
|
mDrawerState = state;
|
|
|
|
if (mListeners != null) {
|
|
final int listenerCount = mListeners.size();
|
|
for (int i = listenerCount - 1; i >= 0; i--) mListeners.get(i).onDrawerStateChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
void dispatchOnDrawerClosed(@NonNull final View drawerView) {
|
|
final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
|
|
if ((lp.openState & LayoutParams.FLAG_IS_OPENED) == 1) {
|
|
lp.openState = 0;
|
|
|
|
if (mListeners != null) {
|
|
final int listenerCount = mListeners.size();
|
|
for (int i = listenerCount - 1; i >= 0; i--) mListeners.get(i).onDrawerClosed(drawerView, lp.gravity);
|
|
}
|
|
}
|
|
}
|
|
|
|
void dispatchOnDrawerOpened(@NonNull final View drawerView) {
|
|
final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
|
|
if ((lp.openState & LayoutParams.FLAG_IS_OPENED) == 0) {
|
|
lp.openState = LayoutParams.FLAG_IS_OPENED;
|
|
if (mListeners != null) {
|
|
final int listenerCount = mListeners.size();
|
|
for (int i = listenerCount - 1; i >= 0; i--) mListeners.get(i).onDrawerOpened(drawerView, lp.gravity);
|
|
}
|
|
}
|
|
}
|
|
|
|
void setDrawerViewOffset(@NonNull final View drawerView, final float slideOffset) {
|
|
final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
|
|
if (slideOffset != lp.onScreen) {
|
|
lp.onScreen = slideOffset;
|
|
|
|
if (mListeners != null) {
|
|
final int listenerCount = mListeners.size();
|
|
for (int i = listenerCount - 1; i >= 0; i--)
|
|
mListeners.get(i).onDrawerSlide(drawerView, lp.gravity, slideOffset);
|
|
}
|
|
}
|
|
}
|
|
|
|
float getDrawerViewOffset(@NonNull final View drawerView) {
|
|
return ((LayoutParams) drawerView.getLayoutParams()).onScreen;
|
|
}
|
|
|
|
int getDrawerViewAbsoluteGravity(@NonNull final View drawerView) {
|
|
final int gravity = ((LayoutParams) drawerView.getLayoutParams()).gravity;
|
|
return GravityCompat.getAbsoluteGravity(gravity, ViewCompat.getLayoutDirection(this));
|
|
}
|
|
|
|
boolean checkDrawerViewAbsoluteGravity(final View drawerView, final int checkFor) {
|
|
final int absGravity = getDrawerViewAbsoluteGravity(drawerView);
|
|
return (absGravity & checkFor) == checkFor;
|
|
}
|
|
|
|
void moveDrawerToOffset(final View drawerView, final float slideOffset) {
|
|
final float oldOffset = getDrawerViewOffset(drawerView);
|
|
final int width = drawerView.getWidth();
|
|
final int oldPos = (int) (width * oldOffset);
|
|
final int newPos = (int) (width * slideOffset);
|
|
final int dx = newPos - oldPos;
|
|
|
|
drawerView.offsetLeftAndRight(checkDrawerViewAbsoluteGravity(drawerView, Gravity.LEFT) ? dx : -dx);
|
|
setDrawerViewOffset(drawerView, slideOffset);
|
|
}
|
|
|
|
public View findOpenDrawer() {
|
|
final int childCount = getChildCount();
|
|
for (int i = 0; i < childCount; i++) {
|
|
final View child = getChildAt(i);
|
|
final LayoutParams childLp = (LayoutParams) child.getLayoutParams();
|
|
if ((childLp.openState & LayoutParams.FLAG_IS_OPENED) == 1) return child;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public View findDrawerWithGravity(final int gravity) {
|
|
final int absHorizGravity = GravityCompat.getAbsoluteGravity(gravity, ViewCompat.getLayoutDirection(this)) & Gravity.HORIZONTAL_GRAVITY_MASK;
|
|
final int childCount = getChildCount();
|
|
for (int i = 0; i < childCount; i++) {
|
|
final View child = getChildAt(i);
|
|
final int childAbsGravity = getDrawerViewAbsoluteGravity(child);
|
|
if ((childAbsGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == absHorizGravity) return child;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@NonNull
|
|
static String gravityToString(@EdgeGravity final int gravity) {
|
|
if ((gravity & Gravity.LEFT) == Gravity.LEFT) return "LEFT";
|
|
if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) return "RIGHT";
|
|
return Integer.toHexString(gravity);
|
|
}
|
|
|
|
@Override
|
|
protected void onDetachedFromWindow() {
|
|
super.onDetachedFromWindow();
|
|
mFirstLayout = true;
|
|
}
|
|
|
|
@Override
|
|
protected void onAttachedToWindow() {
|
|
super.onAttachedToWindow();
|
|
mFirstLayout = true;
|
|
}
|
|
|
|
@SuppressLint("WrongConstant")
|
|
@Override
|
|
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
|
|
final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
|
|
final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
|
|
|
|
setMeasuredDimension(widthSize, heightSize);
|
|
|
|
boolean hasDrawerOnLeftEdge = false;
|
|
boolean hasDrawerOnRightEdge = false;
|
|
final int childCount = getChildCount();
|
|
for (int i = 0; i < childCount; i++) {
|
|
final View child = getChildAt(i);
|
|
|
|
if (child.getVisibility() != GONE) {
|
|
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
|
|
|
if (isContentView(child)) {
|
|
// Content views get measured at exactly the layout's size.
|
|
final int contentWidthSpec = MeasureSpec.makeMeasureSpec(widthSize - lp.leftMargin - lp.rightMargin, MeasureSpec.EXACTLY);
|
|
final int contentHeightSpec = MeasureSpec.makeMeasureSpec(heightSize - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY);
|
|
child.measure(contentWidthSpec, contentHeightSpec);
|
|
|
|
} else if (isDrawerView(child)) {
|
|
if (Build.VERSION.SDK_INT >= 21 && ViewCompat.getElevation(child) != mDrawerElevation)
|
|
ViewCompat.setElevation(child, mDrawerElevation);
|
|
final int childGravity = getDrawerViewAbsoluteGravity(child) & Gravity.HORIZONTAL_GRAVITY_MASK;
|
|
|
|
final boolean isLeftEdgeDrawer = (childGravity == Gravity.LEFT);
|
|
if (isLeftEdgeDrawer && hasDrawerOnLeftEdge || !isLeftEdgeDrawer && hasDrawerOnRightEdge)
|
|
throw new IllegalStateException("Child drawer has absolute gravity " + gravityToString(childGravity)
|
|
+ " but this MouseDrawer already has a drawer view along that edge");
|
|
|
|
if (isLeftEdgeDrawer) hasDrawerOnLeftEdge = true;
|
|
else hasDrawerOnRightEdge = true;
|
|
|
|
final int drawerWidthSpec = getChildMeasureSpec(widthMeasureSpec, lp.leftMargin + lp.rightMargin, lp.width);
|
|
final int drawerHeightSpec = getChildMeasureSpec(heightMeasureSpec, lp.topMargin + lp.bottomMargin, lp.height);
|
|
child.measure(drawerWidthSpec, drawerHeightSpec);
|
|
} else
|
|
throw new IllegalStateException("Child " + child + " at index " + i
|
|
+ " does not have a valid layout_gravity - must be Gravity.LEFT, Gravity.RIGHT or Gravity.NO_GRAVITY");
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onLayout(final boolean changed, final int left, final int top, final int right, final int bottom) {
|
|
mInLayout = true;
|
|
final int width = right - left;
|
|
final int childCount = getChildCount();
|
|
for (int i = 0; i < childCount; i++) {
|
|
final View child = getChildAt(i);
|
|
|
|
if (child.getVisibility() != GONE) {
|
|
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
|
|
|
if (isContentView(child)) {
|
|
child.layout(lp.leftMargin, lp.topMargin, lp.leftMargin + child.getMeasuredWidth(),
|
|
lp.topMargin + child.getMeasuredHeight());
|
|
|
|
} else { // Drawer, if it wasn't onMeasure would have thrown an exception.
|
|
final int childWidth = child.getMeasuredWidth();
|
|
final int childHeight = child.getMeasuredHeight();
|
|
final int childLeft;
|
|
final float newOffset;
|
|
|
|
if (checkDrawerViewAbsoluteGravity(child, Gravity.LEFT)) {
|
|
childLeft = -childWidth + (int) (childWidth * lp.onScreen);
|
|
newOffset = (float) (childWidth + childLeft) / childWidth;
|
|
} else { // Right; onMeasure checked for us.
|
|
childLeft = width - (int) (childWidth * lp.onScreen);
|
|
newOffset = (float) (width - childLeft) / childWidth;
|
|
}
|
|
|
|
final boolean changeOffset = newOffset != lp.onScreen;
|
|
|
|
final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
|
|
switch (vgrav) {
|
|
default:
|
|
case Gravity.TOP:
|
|
child.layout(childLeft, lp.topMargin, childLeft + childWidth, lp.topMargin + childHeight);
|
|
break;
|
|
|
|
case Gravity.BOTTOM: {
|
|
final int height = bottom - top;
|
|
child.layout(childLeft, height - lp.bottomMargin - child.getMeasuredHeight(),
|
|
childLeft + childWidth, height - lp.bottomMargin);
|
|
break;
|
|
}
|
|
|
|
case Gravity.CENTER_VERTICAL: {
|
|
final int height = bottom - top;
|
|
int childTop = (height - childHeight) / 2;
|
|
|
|
if (childTop < lp.topMargin) childTop = lp.topMargin;
|
|
else if (childTop + childHeight > height - lp.bottomMargin)
|
|
childTop = height - lp.bottomMargin - childHeight;
|
|
|
|
child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (changeOffset) setDrawerViewOffset(child, newOffset);
|
|
|
|
final int newVisibility = lp.onScreen > 0 ? VISIBLE : INVISIBLE;
|
|
if (child.getVisibility() != newVisibility) child.setVisibility(newVisibility);
|
|
}
|
|
}
|
|
}
|
|
mInLayout = false;
|
|
mFirstLayout = false;
|
|
}
|
|
|
|
@Override
|
|
public void requestLayout() {
|
|
if (!mInLayout) super.requestLayout();
|
|
}
|
|
|
|
@Override
|
|
public void computeScroll() {
|
|
final boolean leftDraggerSettling = mLeftDragger.continueSettling(true);
|
|
final boolean rightDraggerSettling = mRightDragger.continueSettling(true);
|
|
if (leftDraggerSettling || rightDraggerSettling) postInvalidateOnAnimation();
|
|
}
|
|
|
|
private static boolean hasOpaqueBackground(@NonNull final View v) {
|
|
final Drawable bg = v.getBackground();
|
|
if (bg != null) return bg.getOpacity() == PixelFormat.OPAQUE;
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
protected boolean drawChild(@NonNull final Canvas canvas, final View child, final long drawingTime) {
|
|
final int height = getHeight();
|
|
final boolean drawingContent = isContentView(child);
|
|
int clipLeft = 0, clipRight = getWidth();
|
|
|
|
final int restoreCount = canvas.save();
|
|
if (drawingContent) {
|
|
final int childCount = getChildCount();
|
|
for (int i = 0; i < childCount; i++) {
|
|
final View v = getChildAt(i);
|
|
if (v != child && v.getVisibility() == VISIBLE && hasOpaqueBackground(v) && isDrawerView(v) && v.getHeight() >= height) {
|
|
if (checkDrawerViewAbsoluteGravity(v, Gravity.LEFT)) {
|
|
final int vright = v.getRight();
|
|
if (vright > clipLeft) clipLeft = vright;
|
|
} else {
|
|
final int vleft = v.getLeft();
|
|
if (vleft < clipRight) clipRight = vleft;
|
|
}
|
|
}
|
|
}
|
|
canvas.clipRect(clipLeft, 0, clipRight, getHeight());
|
|
}
|
|
|
|
final boolean result = super.drawChild(canvas, child, drawingTime);
|
|
canvas.restoreToCount(restoreCount);
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean isContentView(@NonNull final View child) {
|
|
return ((LayoutParams) child.getLayoutParams()).gravity == Gravity.NO_GRAVITY;
|
|
}
|
|
|
|
boolean isDrawerView(@NonNull final View child) {
|
|
final int gravity = ((LayoutParams) child.getLayoutParams()).gravity;
|
|
final int absGravity = GravityCompat.getAbsoluteGravity(gravity, ViewCompat.getLayoutDirection(child));
|
|
return (absGravity & Gravity.LEFT) != 0 || (absGravity & Gravity.RIGHT) != 0;
|
|
}
|
|
|
|
@Override
|
|
public boolean onInterceptTouchEvent(@NonNull final MotionEvent ev) {
|
|
final int action = ev.getActionMasked();
|
|
|
|
// "|" used deliberately here; both methods should be invoked.
|
|
final boolean interceptForDrag = mLeftDragger.shouldInterceptTouchEvent(ev) | mRightDragger.shouldInterceptTouchEvent(ev);
|
|
|
|
switch (action) {
|
|
case MotionEvent.ACTION_DOWN:
|
|
mInitialMotionX = ev.getX();
|
|
mInitialMotionY = ev.getY();
|
|
break;
|
|
|
|
case MotionEvent.ACTION_MOVE:
|
|
mLeftDragger.checkTouchSlop(ViewDragHelper.DIRECTION_ALL);
|
|
break;
|
|
|
|
case MotionEvent.ACTION_CANCEL:
|
|
case MotionEvent.ACTION_UP:
|
|
closeDrawers(true);
|
|
}
|
|
|
|
return interceptForDrag || hasPeekingDrawer();
|
|
}
|
|
|
|
@Override
|
|
public boolean dispatchGenericMotionEvent(@NonNull final MotionEvent event) {
|
|
if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0 || event.getAction() == MotionEvent.ACTION_HOVER_EXIT)
|
|
return super.dispatchGenericMotionEvent(event);
|
|
|
|
final int childrenCount = getChildCount();
|
|
if (childrenCount != 0) {
|
|
final float x = event.getX();
|
|
final float y = event.getY();
|
|
|
|
// Walk through children from top to bottom.
|
|
for (int i = childrenCount - 1; i >= 0; i--) {
|
|
final View child = getChildAt(i);
|
|
if (isInBoundsOfChild(x, y, child) && !isContentView(child) && dispatchTransformedGenericPointerEvent(event, child))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
@SuppressLint("ClickableViewAccessibility")
|
|
@Override
|
|
public boolean onTouchEvent(final MotionEvent ev) {
|
|
mLeftDragger.processTouchEvent(ev);
|
|
mRightDragger.processTouchEvent(ev);
|
|
|
|
final int action = ev.getActionMasked();
|
|
switch (action) {
|
|
case MotionEvent.ACTION_DOWN:
|
|
mInitialMotionX = ev.getX();
|
|
mInitialMotionY = ev.getY();
|
|
break;
|
|
|
|
case MotionEvent.ACTION_UP:
|
|
final float x = ev.getX();
|
|
final float y = ev.getY();
|
|
|
|
boolean peekingOnly = true;
|
|
final View touchedView = mLeftDragger.findTopChildUnder((int) x, (int) y);
|
|
if (touchedView != null && isContentView(touchedView)) {
|
|
final float dx = x - mInitialMotionX;
|
|
final float dy = y - mInitialMotionY;
|
|
final int slop = mLeftDragger.getTouchSlop();
|
|
if (dx * dx + dy * dy < slop * slop) {
|
|
// Taps close a dimmed open drawer but only if it isn't locked open.
|
|
final View openDrawer = findOpenDrawer();
|
|
if (openDrawer != null) peekingOnly = false;
|
|
}
|
|
}
|
|
closeDrawers(peekingOnly);
|
|
break;
|
|
|
|
case MotionEvent.ACTION_CANCEL:
|
|
closeDrawers(true);
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void requestDisallowInterceptTouchEvent(final boolean disallowIntercept) {
|
|
if (CHILDREN_DISALLOW_INTERCEPT || (!mLeftDragger.isEdgeTouched(ViewDragHelper.EDGE_LEFT) && !mRightDragger.isEdgeTouched(ViewDragHelper.EDGE_RIGHT)))
|
|
super.requestDisallowInterceptTouchEvent(disallowIntercept);
|
|
if (disallowIntercept) closeDrawers(true);
|
|
}
|
|
|
|
public void closeDrawers() {
|
|
closeDrawers(false);
|
|
}
|
|
|
|
void closeDrawers(final boolean peekingOnly) {
|
|
boolean needsInvalidate = false;
|
|
final int childCount = getChildCount();
|
|
for (int i = 0; i < childCount; i++) {
|
|
final View child = getChildAt(i);
|
|
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
|
|
|
if (isDrawerView(child) && (!peekingOnly || lp.isPeeking)) {
|
|
final int childWidth = child.getWidth();
|
|
|
|
if (checkDrawerViewAbsoluteGravity(child, Gravity.LEFT))
|
|
needsInvalidate |= mLeftDragger.smoothSlideViewTo(child, -childWidth, child.getTop());
|
|
else
|
|
needsInvalidate |= mRightDragger.smoothSlideViewTo(child, getWidth(), child.getTop());
|
|
|
|
lp.isPeeking = false;
|
|
}
|
|
}
|
|
|
|
if (needsInvalidate) invalidate();
|
|
}
|
|
|
|
public void openDrawer(@NonNull final View drawerView, final boolean animate) {
|
|
if (isDrawerView(drawerView)) {
|
|
final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
|
|
|
|
if (mFirstLayout) {
|
|
lp.onScreen = 1.f;
|
|
lp.openState = LayoutParams.FLAG_IS_OPENED;
|
|
} else if (animate) {
|
|
lp.openState |= LayoutParams.FLAG_IS_OPENING;
|
|
|
|
if (checkDrawerViewAbsoluteGravity(drawerView, Gravity.LEFT))
|
|
mLeftDragger.smoothSlideViewTo(drawerView, 0, drawerView.getTop());
|
|
else
|
|
mRightDragger.smoothSlideViewTo(drawerView, getWidth() - drawerView.getWidth(), drawerView.getTop());
|
|
} else {
|
|
moveDrawerToOffset(drawerView, 1.f);
|
|
updateDrawerState(ViewDragHelper.STATE_IDLE, drawerView);
|
|
drawerView.setVisibility(VISIBLE);
|
|
}
|
|
|
|
invalidate();
|
|
return;
|
|
}
|
|
throw new IllegalArgumentException("View " + drawerView + " is not a sliding drawer");
|
|
}
|
|
|
|
public void openDrawer(@NonNull final View drawerView) {
|
|
openDrawer(drawerView, true);
|
|
}
|
|
|
|
// public void openDrawer(@EdgeGravity final int gravity, final boolean animate) {
|
|
// final View drawerView = findDrawerWithGravity(gravity);
|
|
// if (drawerView != null) openDrawer(drawerView, animate);
|
|
// else throw new IllegalArgumentException("No drawer view found with gravity " + gravityToString(gravity));
|
|
// }
|
|
|
|
// public void openDrawer(@EdgeGravity final int gravity) {
|
|
// openDrawer(gravity, true);
|
|
// }
|
|
|
|
public void closeDrawer(@NonNull final View drawerView) {
|
|
closeDrawer(drawerView, true);
|
|
}
|
|
|
|
public void closeDrawer(@NonNull final View drawerView, final boolean animate) {
|
|
if (isDrawerView(drawerView)) {
|
|
final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
|
|
if (mFirstLayout) {
|
|
lp.onScreen = 0.f;
|
|
lp.openState = 0;
|
|
} else if (animate) {
|
|
lp.openState |= LayoutParams.FLAG_IS_CLOSING;
|
|
|
|
if (checkDrawerViewAbsoluteGravity(drawerView, Gravity.LEFT))
|
|
mLeftDragger.smoothSlideViewTo(drawerView, -drawerView.getWidth(), drawerView.getTop());
|
|
else
|
|
mRightDragger.smoothSlideViewTo(drawerView, getWidth(), drawerView.getTop());
|
|
} else {
|
|
moveDrawerToOffset(drawerView, 0.f);
|
|
updateDrawerState(ViewDragHelper.STATE_IDLE, drawerView);
|
|
drawerView.setVisibility(INVISIBLE);
|
|
}
|
|
invalidate();
|
|
} else throw new IllegalArgumentException("View " + drawerView + " is not a sliding drawer");
|
|
}
|
|
|
|
// public void closeDrawer(@EdgeGravity final int gravity) {
|
|
// closeDrawer(gravity, true);
|
|
// }
|
|
|
|
// public void closeDrawer(@EdgeGravity final int gravity, final boolean animate) {
|
|
// final View drawerView = findDrawerWithGravity(gravity);
|
|
// if (drawerView != null) closeDrawer(drawerView, animate);
|
|
// else throw new IllegalArgumentException("No drawer view found with gravity " + gravityToString(gravity));
|
|
// }
|
|
|
|
public boolean isDrawerOpen(@NonNull final View drawer) {
|
|
if (isDrawerView(drawer)) return (((LayoutParams) drawer.getLayoutParams()).openState & LayoutParams.FLAG_IS_OPENED) == 1;
|
|
else throw new IllegalArgumentException("View " + drawer + " is not a drawer");
|
|
}
|
|
|
|
// public boolean isDrawerOpen(@EdgeGravity final int drawerGravity) {
|
|
// final View drawerView = findDrawerWithGravity(drawerGravity);
|
|
// return drawerView != null && isDrawerOpen(drawerView);
|
|
// }
|
|
|
|
public boolean isDrawerVisible(@NonNull final View drawer) {
|
|
if (isDrawerView(drawer)) return ((LayoutParams) drawer.getLayoutParams()).onScreen > 0;
|
|
throw new IllegalArgumentException("View " + drawer + " is not a drawer");
|
|
}
|
|
|
|
// public boolean isDrawerVisible(@EdgeGravity final int drawerGravity) {
|
|
// final View drawerView = findDrawerWithGravity(drawerGravity);
|
|
// return drawerView != null && isDrawerVisible(drawerView);
|
|
// }
|
|
|
|
private boolean hasPeekingDrawer() {
|
|
final int childCount = getChildCount();
|
|
for (int i = 0; i < childCount; i++) {
|
|
final LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
|
|
if (lp.isPeeking) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
|
|
return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
|
}
|
|
|
|
@Override
|
|
protected ViewGroup.LayoutParams generateLayoutParams(final ViewGroup.LayoutParams params) {
|
|
return params instanceof LayoutParams ? new LayoutParams((LayoutParams) params) :
|
|
params instanceof ViewGroup.MarginLayoutParams ? new LayoutParams((MarginLayoutParams) params) : new LayoutParams(params);
|
|
}
|
|
|
|
@Override
|
|
protected boolean checkLayoutParams(final ViewGroup.LayoutParams params) {
|
|
return params instanceof LayoutParams && super.checkLayoutParams(params);
|
|
}
|
|
|
|
@Override
|
|
public ViewGroup.LayoutParams generateLayoutParams(final AttributeSet attrs) {
|
|
return new LayoutParams(getContext(), attrs);
|
|
}
|
|
|
|
@Override
|
|
public void addFocusables(final ArrayList<View> views, final int direction, final int focusableMode) {
|
|
if (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS) {
|
|
final int childCount = getChildCount();
|
|
boolean isDrawerOpen = false;
|
|
for (int i = 0; i < childCount; i++) {
|
|
final View child = getChildAt(i);
|
|
if (!isDrawerView(child)) mNonDrawerViews.add(child);
|
|
else if (isDrawerOpen(child)) {
|
|
isDrawerOpen = true;
|
|
child.addFocusables(views, direction, focusableMode);
|
|
}
|
|
}
|
|
|
|
if (!isDrawerOpen) {
|
|
final int nonDrawerViewsCount = mNonDrawerViews.size();
|
|
for (int i = 0; i < nonDrawerViewsCount; ++i) {
|
|
final View child = mNonDrawerViews.get(i);
|
|
if (child.getVisibility() == View.VISIBLE) child.addFocusables(views, direction, focusableMode);
|
|
}
|
|
}
|
|
|
|
mNonDrawerViews.clear();
|
|
}
|
|
}
|
|
|
|
private boolean hasVisibleDrawer() {
|
|
return findVisibleDrawer() != null;
|
|
}
|
|
|
|
@Nullable
|
|
final View findVisibleDrawer() {
|
|
final int childCount = getChildCount();
|
|
for (int i = 0; i < childCount; i++) {
|
|
final View child = getChildAt(i);
|
|
if (isDrawerView(child) && isDrawerVisible(child)) return child;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public boolean onKeyDown(final int keyCode, final KeyEvent event) {
|
|
if (keyCode == KeyEvent.KEYCODE_BACK && hasVisibleDrawer()) {
|
|
event.startTracking();
|
|
return true;
|
|
}
|
|
return super.onKeyDown(keyCode, event);
|
|
}
|
|
|
|
@Override
|
|
public boolean onKeyUp(final int keyCode, final KeyEvent event) {
|
|
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
|
final View visibleDrawer = findVisibleDrawer();
|
|
if (visibleDrawer != null && isDrawerView(visibleDrawer)) closeDrawers();
|
|
return visibleDrawer != null;
|
|
}
|
|
return super.onKeyUp(keyCode, event);
|
|
}
|
|
|
|
@Override
|
|
protected void onRestoreInstanceState(final Parcelable state) {
|
|
if (state instanceof SavedState) {
|
|
final SavedState ss = (SavedState) state;
|
|
super.onRestoreInstanceState(ss.getSuperState());
|
|
|
|
if (ss.openDrawerGravity != Gravity.NO_GRAVITY) {
|
|
final View toOpen = findDrawerWithGravity(ss.openDrawerGravity);
|
|
if (toOpen != null) openDrawer(toOpen);
|
|
}
|
|
} else super.onRestoreInstanceState(state);
|
|
}
|
|
|
|
@Override
|
|
protected Parcelable onSaveInstanceState() {
|
|
final Parcelable superState = super.onSaveInstanceState();
|
|
assert superState != null;
|
|
final SavedState ss = new SavedState(superState);
|
|
|
|
final int childCount = getChildCount();
|
|
for (int i = 0; i < childCount; i++) {
|
|
final View child = getChildAt(i);
|
|
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
|
// Is the current child fully opened (that is, not closing)?
|
|
final boolean isOpenedAndNotClosing = (lp.openState == LayoutParams.FLAG_IS_OPENED);
|
|
// Is the current child opening?
|
|
final boolean isClosedAndOpening = (lp.openState == LayoutParams.FLAG_IS_OPENING);
|
|
if (isOpenedAndNotClosing || isClosedAndOpening) {
|
|
// If one of the conditions above holds, save the child's gravity so that we open that child during state restore.
|
|
ss.openDrawerGravity = lp.gravity;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ss;
|
|
}
|
|
|
|
@Override
|
|
public void addView(final View child, final int index, final ViewGroup.LayoutParams params) {
|
|
super.addView(child, index, params);
|
|
final View openDrawer = findOpenDrawer();
|
|
if (openDrawer == null) isDrawerView(child);
|
|
}
|
|
|
|
protected static class SavedState extends AbsSavedState {
|
|
public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
|
|
@NonNull
|
|
@Override
|
|
public SavedState createFromParcel(final Parcel in, final ClassLoader loader) {
|
|
return new SavedState(in, loader);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
public SavedState createFromParcel(final Parcel in) {
|
|
return new SavedState(in, null);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
public SavedState[] newArray(final int size) {
|
|
return new SavedState[size];
|
|
}
|
|
};
|
|
int openDrawerGravity = Gravity.NO_GRAVITY;
|
|
|
|
public SavedState(@NonNull final Parcelable superState) {
|
|
super(superState);
|
|
}
|
|
|
|
public SavedState(@NonNull final Parcel in, @Nullable final ClassLoader loader) {
|
|
super(in, loader);
|
|
openDrawerGravity = in.readInt();
|
|
}
|
|
|
|
@Override
|
|
public void writeToParcel(final Parcel dest, final int flags) {
|
|
super.writeToParcel(dest, flags);
|
|
dest.writeInt(openDrawerGravity);
|
|
}
|
|
}
|
|
|
|
private class ViewDragCallback extends ViewDragHelper.Callback {
|
|
private final int mAbsGravity;
|
|
private ViewDragHelper mDragger;
|
|
|
|
ViewDragCallback(final int gravity) {
|
|
mAbsGravity = gravity;
|
|
}
|
|
|
|
public void setDragger(final ViewDragHelper dragger) {
|
|
mDragger = dragger;
|
|
}
|
|
|
|
@Override
|
|
public boolean tryCaptureView(@NonNull final View child, final int pointerId) {
|
|
return isDrawerView(child) && checkDrawerViewAbsoluteGravity(child, mAbsGravity);
|
|
}
|
|
|
|
@Override
|
|
public void onViewDragStateChanged(final int state) {
|
|
updateDrawerState(state, mDragger.getCapturedView());
|
|
}
|
|
|
|
@Override
|
|
public void onViewPositionChanged(@NonNull final View changedView, final int left, final int top, final int dx, final int dy) {
|
|
final float offset;
|
|
final int childWidth = changedView.getWidth();
|
|
|
|
if (checkDrawerViewAbsoluteGravity(changedView, Gravity.LEFT)) offset = (float) (childWidth + left) / childWidth;
|
|
else offset = (float) (getWidth() - left) / childWidth;
|
|
|
|
setDrawerViewOffset(changedView, offset);
|
|
changedView.setVisibility(offset == 0 ? INVISIBLE : VISIBLE);
|
|
invalidate();
|
|
}
|
|
|
|
@Override
|
|
public void onViewCaptured(@NonNull final View capturedChild, final int activePointerId) {
|
|
final LayoutParams lp = (LayoutParams) capturedChild.getLayoutParams();
|
|
lp.isPeeking = false;
|
|
closeOtherDrawer();
|
|
}
|
|
|
|
private void closeOtherDrawer() {
|
|
final int otherGrav = mAbsGravity == Gravity.LEFT ? Gravity.RIGHT : Gravity.LEFT;
|
|
final View toClose = findDrawerWithGravity(otherGrav);
|
|
if (toClose != null) closeDrawer(toClose);
|
|
}
|
|
|
|
@Override
|
|
public void onViewReleased(@NonNull final View releasedChild, final float xvel, final float yvel) {
|
|
final float offset = getDrawerViewOffset(releasedChild);
|
|
final int childWidth = releasedChild.getWidth();
|
|
|
|
final int left;
|
|
if (checkDrawerViewAbsoluteGravity(releasedChild, Gravity.LEFT))
|
|
left = xvel > 0 || (xvel == 0 && offset > 0.5f) ? 0 : -childWidth;
|
|
else {
|
|
final int width = getWidth();
|
|
left = xvel < 0 || (xvel == 0 && offset > 0.5f) ? width - childWidth : width;
|
|
}
|
|
|
|
mDragger.settleCapturedViewAt(left, releasedChild.getTop());
|
|
invalidate();
|
|
}
|
|
|
|
@Override
|
|
public void onEdgeDragStarted(final int edgeFlags, final int pointerId) {
|
|
final View toCapture;
|
|
if ((edgeFlags & ViewDragHelper.EDGE_LEFT) == ViewDragHelper.EDGE_LEFT)
|
|
toCapture = findDrawerWithGravity(Gravity.LEFT);
|
|
else toCapture = findDrawerWithGravity(Gravity.RIGHT);
|
|
|
|
if (toCapture != null && isDrawerView(toCapture)) mDragger.captureChildView(toCapture, pointerId);
|
|
}
|
|
|
|
@Override
|
|
public int getViewHorizontalDragRange(@NonNull final View child) {
|
|
return isDrawerView(child) ? child.getWidth() : 0;
|
|
}
|
|
|
|
@Override
|
|
public int clampViewPositionHorizontal(@NonNull final View child, final int left, final int dx) {
|
|
if (checkDrawerViewAbsoluteGravity(child, Gravity.LEFT)) return Math.max(-child.getWidth(), Math.min(left, 0));
|
|
final int width = getWidth();
|
|
return Math.max(width - child.getWidth(), Math.min(left, width));
|
|
}
|
|
|
|
@Override
|
|
public int clampViewPositionVertical(@NonNull final View child, final int top, final int dy) {
|
|
return child.getTop();
|
|
}
|
|
}
|
|
|
|
public static class LayoutParams extends ViewGroup.MarginLayoutParams {
|
|
private static final int FLAG_IS_CLOSING = 0x4;
|
|
public static final int FLAG_IS_OPENED = 0x1;
|
|
public static final int FLAG_IS_OPENING = 0x2;
|
|
public int openState;
|
|
@EdgeGravity
|
|
public int gravity = Gravity.NO_GRAVITY;
|
|
public boolean isPeeking;
|
|
public float onScreen;
|
|
|
|
public LayoutParams(@NonNull final Context c, @Nullable final AttributeSet attrs) {
|
|
super(c, attrs);
|
|
final TypedArray a = c.obtainStyledAttributes(attrs, new int[]{android.R.attr.layout_gravity});
|
|
try {
|
|
this.gravity = a.getInt(0, Gravity.NO_GRAVITY);
|
|
} finally {
|
|
a.recycle();
|
|
}
|
|
}
|
|
|
|
public LayoutParams(final int width, final int height) {
|
|
super(width, height);
|
|
}
|
|
|
|
public LayoutParams(@NonNull final LayoutParams source) {
|
|
super(source);
|
|
this.gravity = source.gravity;
|
|
}
|
|
|
|
public LayoutParams(@NonNull final ViewGroup.LayoutParams source) {
|
|
super(source);
|
|
}
|
|
|
|
public LayoutParams(@NonNull final ViewGroup.MarginLayoutParams source) {
|
|
super(source);
|
|
}
|
|
}
|
|
} |