2021-06-25 02:43:10 +02:00
|
|
|
package org.telegram.ui.Components;
|
|
|
|
|
|
|
|
import android.animation.Animator;
|
|
|
|
import android.animation.AnimatorListenerAdapter;
|
|
|
|
import android.animation.ObjectAnimator;
|
|
|
|
import android.content.Context;
|
|
|
|
import android.graphics.Canvas;
|
|
|
|
import android.graphics.Paint;
|
|
|
|
import android.graphics.PorterDuff;
|
|
|
|
import android.graphics.PorterDuffColorFilter;
|
|
|
|
import android.graphics.drawable.Drawable;
|
2022-02-01 14:00:45 +01:00
|
|
|
import android.view.HapticFeedbackConstants;
|
2021-06-25 02:43:10 +02:00
|
|
|
import android.view.MotionEvent;
|
|
|
|
import android.view.View;
|
|
|
|
import android.view.animation.OvershootInterpolator;
|
|
|
|
import android.widget.FrameLayout;
|
|
|
|
|
|
|
|
import androidx.core.view.NestedScrollingParent;
|
|
|
|
import androidx.core.view.NestedScrollingParentHelper;
|
|
|
|
import androidx.core.view.ViewCompat;
|
|
|
|
|
|
|
|
import org.telegram.messenger.AndroidUtilities;
|
|
|
|
import org.telegram.messenger.R;
|
|
|
|
import org.telegram.ui.ActionBar.Theme;
|
|
|
|
|
|
|
|
public class BotCommandsMenuContainer extends FrameLayout implements NestedScrollingParent {
|
|
|
|
|
|
|
|
private ObjectAnimator currentAnimation = null;
|
|
|
|
private NestedScrollingParentHelper nestedScrollingParentHelper;
|
|
|
|
|
|
|
|
public RecyclerListView listView;
|
|
|
|
Paint backgroundPaint = new Paint();
|
|
|
|
Paint topBackground = new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
|
|
|
|
|
|
boolean dismissed = true;
|
|
|
|
|
|
|
|
float scrollYOffset;
|
|
|
|
|
|
|
|
Drawable shadowDrawable;
|
|
|
|
public BotCommandsMenuContainer(Context context) {
|
|
|
|
super(context);
|
|
|
|
|
|
|
|
nestedScrollingParentHelper = new NestedScrollingParentHelper(this);
|
|
|
|
shadowDrawable = context.getResources().getDrawable(R.drawable.sheet_shadow_round).mutate();
|
|
|
|
listView = new RecyclerListView(context) {
|
|
|
|
@Override
|
|
|
|
protected void dispatchDraw(Canvas canvas) {
|
|
|
|
if (listView.getLayoutManager() == null || listView.getAdapter() == null || listView.getAdapter().getItemCount() == 0) {
|
|
|
|
super.dispatchDraw(canvas);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
View firstView = listView.getLayoutManager().findViewByPosition(0);
|
|
|
|
float y = 0;
|
|
|
|
if (firstView != null) {
|
|
|
|
y = firstView.getY();
|
|
|
|
}
|
|
|
|
if (y < 0) {
|
|
|
|
y = 0;
|
|
|
|
}
|
|
|
|
scrollYOffset = y;
|
|
|
|
y -= AndroidUtilities.dp(8);
|
|
|
|
if (y > 0) {
|
|
|
|
shadowDrawable.setBounds(-AndroidUtilities.dp(8), (int) y - AndroidUtilities.dp(24), getMeasuredWidth() + AndroidUtilities.dp(8), (int) y);
|
|
|
|
shadowDrawable.draw(canvas);
|
|
|
|
}
|
|
|
|
canvas.drawRect(0, y, getMeasuredWidth(), getMeasuredHeight() + AndroidUtilities.dp(16), backgroundPaint);
|
|
|
|
AndroidUtilities.rectTmp.set(getMeasuredWidth() / 2f - AndroidUtilities.dp(12), y - AndroidUtilities.dp(4),getMeasuredWidth() / 2f + AndroidUtilities.dp(12), y);
|
|
|
|
canvas.drawRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(4), AndroidUtilities.dp(4), topBackground);
|
|
|
|
super.dispatchDraw(canvas);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
listView.setClipToPadding(false);
|
|
|
|
addView(listView);
|
|
|
|
updateColors();
|
|
|
|
setClipChildren(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
|
|
|
|
return !dismissed && nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
|
|
|
|
nestedScrollingParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes);
|
|
|
|
if (dismissed) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
cancelCurrentAnimation();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onStopNestedScroll(View target) {
|
|
|
|
nestedScrollingParentHelper.onStopNestedScroll(target);
|
|
|
|
if (dismissed) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
checkDismiss();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void checkDismiss() {
|
|
|
|
if (dismissed) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (listView.getTranslationY() > AndroidUtilities.dp(16)) {
|
|
|
|
dismiss();
|
|
|
|
} else {
|
|
|
|
playEnterAnim(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
|
|
|
|
if (dismissed) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
cancelCurrentAnimation();
|
|
|
|
if (dyUnconsumed != 0) {
|
|
|
|
float currentTranslation = listView.getTranslationY();
|
|
|
|
currentTranslation -= dyUnconsumed;
|
|
|
|
if (currentTranslation < 0) {
|
|
|
|
currentTranslation = 0;
|
|
|
|
}
|
|
|
|
listView.setTranslationY(currentTranslation);
|
|
|
|
invalidate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
|
|
|
|
if (dismissed) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
cancelCurrentAnimation();
|
|
|
|
float currentTranslation = listView.getTranslationY();
|
|
|
|
if (currentTranslation > 0 && dy > 0) {
|
|
|
|
currentTranslation -= dy;
|
|
|
|
consumed[1] = dy;
|
|
|
|
if (currentTranslation < 0) {
|
|
|
|
currentTranslation = 0;
|
|
|
|
}
|
|
|
|
listView.setTranslationY(currentTranslation);
|
|
|
|
invalidate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getNestedScrollAxes() {
|
|
|
|
return nestedScrollingParentHelper.getNestedScrollAxes();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void cancelCurrentAnimation() {
|
|
|
|
if (currentAnimation != null) {
|
|
|
|
currentAnimation.removeAllListeners();
|
|
|
|
currentAnimation.cancel();
|
|
|
|
currentAnimation = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean entering;
|
|
|
|
|
|
|
|
public void show() {
|
|
|
|
if (getVisibility() != View.VISIBLE) {
|
|
|
|
setVisibility(View.VISIBLE);
|
|
|
|
listView.scrollToPosition(0);
|
|
|
|
entering = true;
|
|
|
|
dismissed = false;
|
|
|
|
} else if (dismissed) {
|
|
|
|
dismissed = false;
|
|
|
|
cancelCurrentAnimation();
|
|
|
|
playEnterAnim(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
|
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
|
|
if (entering && !dismissed) {
|
|
|
|
listView.setTranslationY(listView.getMeasuredHeight() - listView.getPaddingTop() + AndroidUtilities.dp(16));
|
|
|
|
playEnterAnim(true);
|
|
|
|
entering = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void playEnterAnim(boolean firstTime) {
|
|
|
|
if (dismissed) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
currentAnimation = ObjectAnimator.ofFloat(listView, TRANSLATION_Y, listView.getTranslationY(), 0);
|
|
|
|
if (firstTime) {
|
|
|
|
currentAnimation.setDuration(320);
|
|
|
|
currentAnimation.setInterpolator(new OvershootInterpolator(0.8f));
|
|
|
|
} else {
|
|
|
|
currentAnimation.setDuration(150);
|
|
|
|
currentAnimation.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
|
|
|
}
|
|
|
|
currentAnimation.start();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void dismiss() {
|
|
|
|
if (!dismissed) {
|
|
|
|
dismissed = true;
|
|
|
|
cancelCurrentAnimation();
|
|
|
|
currentAnimation = ObjectAnimator.ofFloat(listView, TRANSLATION_Y, listView.getTranslationY(), getMeasuredHeight() - scrollYOffset + AndroidUtilities.dp(40));
|
|
|
|
currentAnimation.addListener(new AnimatorListenerAdapter() {
|
|
|
|
@Override
|
|
|
|
public void onAnimationEnd(Animator animation) {
|
|
|
|
setVisibility(View.GONE);
|
|
|
|
currentAnimation = null;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
currentAnimation.setDuration(150);
|
|
|
|
currentAnimation.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
|
|
|
currentAnimation.start();
|
|
|
|
onDismiss();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void onDismiss() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean dispatchTouchEvent(MotionEvent ev) {
|
|
|
|
if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getY() < scrollYOffset - AndroidUtilities.dp(24)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return super.dispatchTouchEvent(ev);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void updateColors() {
|
|
|
|
topBackground.setColor(Theme.getColor(Theme.key_dialogGrayLine));
|
|
|
|
backgroundPaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhite));
|
|
|
|
shadowDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhite), PorterDuff.Mode.MULTIPLY));
|
|
|
|
invalidate();
|
|
|
|
}
|
|
|
|
}
|