NekoX/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AdjustPanLayoutHelper.java

298 lines
10 KiB
Java

package org.telegram.ui.ActionBar;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.Context;
import android.view.ContextThemeWrapper;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.NotificationCenter;
import org.telegram.messenger.SharedConfig;
import org.telegram.messenger.UserConfig;
import org.telegram.ui.Components.CubicBezierInterpolator;
import java.util.ArrayList;
public class AdjustPanLayoutHelper {
private final static float[] interpolatorValues = new float[]{0f, 0.11162791f, 0.40232557f, 0.6534884f, 0.7930232f, 0.8802326f, 0.9302326f, 0.96046513f, 0.9767442f, 0.9860465f, 0.99186045f, 0.9953488f, 0.9976744f, 0.99883723f, 1};
public final static Interpolator keyboardInterpolator = CubicBezierInterpolator.DEFAULT;//new LookupTableInterpolator(interpolatorValues);
public final static long keyboardDuration = 250;
private final View parent;
private View resizableViewToSet;
private ViewGroup decorView;
private ViewGroup contentView;
private View resizableView;
private boolean animationInProgress;
int previousHeight = -1;
int previousContentHeight = -1;
int previousStartOffset = -1;
View parentForListener;
ValueAnimator animator;
int notificationsIndex;
ArrayList<View> viewsToHeightSet = new ArrayList<>();
protected float keyboardSize;
ViewTreeObserver.OnPreDrawListener onPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
if (!SharedConfig.smoothKeyboard) {
onDetach();
return true;
}
int contentHeight = parent.getHeight();
if (contentHeight - startOffset() == previousHeight - previousStartOffset || contentHeight == previousHeight || animator != null) {
if (animator == null) {
previousHeight = contentHeight;
previousContentHeight = contentView.getHeight();
previousStartOffset = startOffset();
}
return true;
}
if (!heightAnimationEnabled() || Math.abs(previousHeight - contentHeight) < AndroidUtilities.dp(20)) {
previousHeight = contentHeight;
previousContentHeight = contentView.getHeight();
previousStartOffset = startOffset();
return true;
}
if (previousHeight != -1 && previousContentHeight == contentView.getHeight()) {
boolean isKeyboardVisible = contentHeight < contentView.getBottom();
animateHeight(previousHeight, contentHeight, isKeyboardVisible);
previousHeight = contentHeight;
previousContentHeight = contentView.getHeight();
previousStartOffset = startOffset();
return false;
}
previousHeight = contentHeight;
previousContentHeight = contentView.getHeight();
previousStartOffset = startOffset();
return false;
}
};
private void animateHeight(int previousHeight, int contentHeight, boolean isKeyboardVisible) {
if (animator != null) {
animator.cancel();
}
int startOffset = startOffset();
getViewsToSetHeight(parent);
setViewHeight(Math.max(previousHeight, contentHeight));
resizableView.requestLayout();
onTransitionStart(isKeyboardVisible);
float dy = contentHeight - previousHeight;
float from;
float to;
keyboardSize = Math.abs(dy);
if (contentHeight > previousHeight) {
dy -= startOffset;
parent.setTranslationY(-dy);
onPanTranslationUpdate(dy, 1f, isKeyboardVisible);
from = -dy;
to = 0;
animator = ValueAnimator.ofFloat(1f, 0);
} else {
parent.setTranslationY(previousStartOffset);
onPanTranslationUpdate(-previousStartOffset, 0f, isKeyboardVisible);
to = -previousStartOffset;
from = dy;
animator = ValueAnimator.ofFloat(0, 1f);
}
animator.addUpdateListener(animation -> {
float v = (float) animation.getAnimatedValue();
float y = from * v + to * (1f - v);
parent.setTranslationY(y);
onPanTranslationUpdate(-y, v, isKeyboardVisible);
});
animationInProgress = true;
int selectedAccount = UserConfig.selectedAccount;
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
animationInProgress = false;
NotificationCenter.getInstance(selectedAccount).onAnimationFinish(notificationsIndex);
animator = null;
setViewHeight(ViewGroup.LayoutParams.MATCH_PARENT);
viewsToHeightSet.clear();
resizableView.requestLayout();
onPanTranslationUpdate(0, isKeyboardVisible ? 1f : 0f, isKeyboardVisible);
parent.setTranslationY(0);
onTransitionEnd();
}
});
animator.setDuration(220);
animator.setInterpolator(CubicBezierInterpolator.DEFAULT);
notificationsIndex = NotificationCenter.getInstance(selectedAccount).setAnimationInProgress(notificationsIndex, null);
animator.start();
}
private void setViewHeight(int height) {
for (int i = 0; i < viewsToHeightSet.size(); i++) {
viewsToHeightSet.get(i).getLayoutParams().height = height;
viewsToHeightSet.get(i).requestLayout();
}
}
protected int startOffset() {
return 0;
}
private void getViewsToSetHeight(View parent) {
viewsToHeightSet.clear();
View v = parent;
while (v != null) {
viewsToHeightSet.add(v);
if (v == resizableView) {
return;
}
if (v.getParent() instanceof View) {
v = (View) v.getParent();
} else {
v = null;
}
}
}
public AdjustPanLayoutHelper(View parent) {
this.parent = parent;
onAttach();
}
public void onAttach() {
if (!SharedConfig.smoothKeyboard) {
return;
}
onDetach();
Context context = parent.getContext();
Activity activity = getActivity(context);
if (activity != null) {
decorView = (android.view.ViewGroup) activity.getWindow().getDecorView();
contentView = decorView.findViewById(Window.ID_ANDROID_CONTENT);
}
resizableView = findResizableView(parent);
if (resizableView != null) {
parentForListener = resizableView;
resizableView.getViewTreeObserver().addOnPreDrawListener(onPreDrawListener);
}
}
private Activity getActivity(Context context) {
if (context instanceof Activity) {
return (Activity) context;
} else if (context instanceof ContextThemeWrapper) {
return getActivity(((ContextThemeWrapper) context).getBaseContext());
}
return null;
}
private View findResizableView(View parent) {
if (resizableViewToSet != null) {
return resizableViewToSet;
}
View view = parent;
while (view != null) {
if (view.getParent() instanceof DrawerLayoutContainer) {
return view;
}
if (view.getParent() instanceof View) {
view = (View) view.getParent();
} else {
return null;
}
}
return null;
}
public void onDetach() {
if (animator != null) {
animator.cancel();
}
if (parentForListener != null) {
parentForListener.getViewTreeObserver().removeOnPreDrawListener(onPreDrawListener);
parentForListener = null;
}
}
protected boolean heightAnimationEnabled() {
return true;
}
protected void onPanTranslationUpdate(float y, float progress, boolean keyboardVisible) {
}
protected void onTransitionStart(boolean keyboardVisible) {
}
protected void onTransitionEnd() {
}
public void setResizableView(FrameLayout windowView) {
resizableViewToSet = windowView;
}
public boolean animationInProgress() {
return animationInProgress;
}
/**
* copy from androidx.interpolator.view.animation.LookupTableInterpolator
*/
static class LookupTableInterpolator implements Interpolator {
private final float[] mValues;
private final float mStepSize;
protected LookupTableInterpolator(float[] values) {
mValues = values;
mStepSize = 1f / (mValues.length - 1);
}
@Override
public float getInterpolation(float input) {
if (input >= 1.0f) {
return 1.0f;
}
if (input <= 0f) {
return 0f;
}
// Calculate index - We use min with length - 2 to avoid IndexOutOfBoundsException when
// we lerp (linearly interpolate) in the return statement
int position = Math.min((int) (input * (mValues.length - 1)), mValues.length - 2);
// Calculate values to account for small offsets as the lookup table has discrete values
float quantized = position * mStepSize;
float diff = input - quantized;
float weight = diff / mStepSize;
// Linearly interpolate between the table values
return mValues[position] + weight * (mValues[position + 1] - mValues[position]);
}
}
}