2014-06-04 18:00:42 +02:00
|
|
|
/*
|
|
|
|
* This is the source code of Telegram for Android v. 1.4.x.
|
|
|
|
* It is licensed under GNU GPL v. 2 or later.
|
|
|
|
* You should have received a copy of the license in this archive (see LICENSE).
|
|
|
|
*
|
|
|
|
* Copyright Nikolai Kudashov, 2013-2014.
|
|
|
|
*/
|
|
|
|
|
|
|
|
//Thanks to https://github.com/JakeWharton/ActionBarSherlock/
|
|
|
|
|
2014-11-13 21:10:14 +01:00
|
|
|
package org.telegram.ui.ActionBar;
|
2014-06-04 18:00:42 +02:00
|
|
|
|
2015-06-29 19:12:11 +02:00
|
|
|
import android.animation.Animator;
|
|
|
|
import android.animation.AnimatorSet;
|
|
|
|
import android.animation.ObjectAnimator;
|
2014-06-04 18:00:42 +02:00
|
|
|
import android.content.Context;
|
2015-06-29 19:12:11 +02:00
|
|
|
import android.graphics.Canvas;
|
|
|
|
import android.graphics.drawable.Drawable;
|
|
|
|
import android.os.Build;
|
2014-06-17 16:45:21 +02:00
|
|
|
import android.view.KeyEvent;
|
2014-06-04 18:00:42 +02:00
|
|
|
import android.view.View;
|
2015-07-22 20:56:37 +02:00
|
|
|
import android.view.ViewGroup;
|
2014-06-04 18:00:42 +02:00
|
|
|
import android.view.ViewTreeObserver;
|
2015-06-29 19:12:11 +02:00
|
|
|
import android.view.animation.DecelerateInterpolator;
|
2015-07-22 20:56:37 +02:00
|
|
|
import android.widget.FrameLayout;
|
2014-06-17 16:45:21 +02:00
|
|
|
import android.widget.LinearLayout;
|
2014-06-04 18:00:42 +02:00
|
|
|
import android.widget.PopupWindow;
|
2015-07-22 20:56:37 +02:00
|
|
|
import android.widget.ScrollView;
|
2014-06-04 18:00:42 +02:00
|
|
|
|
2015-06-29 19:12:11 +02:00
|
|
|
import org.telegram.android.AndroidUtilities;
|
2014-11-21 11:59:05 +01:00
|
|
|
import org.telegram.messenger.FileLog;
|
2015-06-29 19:12:11 +02:00
|
|
|
import org.telegram.messenger.R;
|
2015-07-22 20:56:37 +02:00
|
|
|
import org.telegram.ui.Components.LayoutHelper;
|
2014-11-21 11:59:05 +01:00
|
|
|
|
2014-06-04 18:00:42 +02:00
|
|
|
import java.lang.reflect.Field;
|
2015-06-29 19:12:11 +02:00
|
|
|
import java.util.HashMap;
|
2014-06-04 18:00:42 +02:00
|
|
|
|
|
|
|
public class ActionBarPopupWindow extends PopupWindow {
|
2014-12-01 18:56:31 +01:00
|
|
|
|
2014-06-04 18:00:42 +02:00
|
|
|
private static final Field superListenerField;
|
2015-06-29 19:12:11 +02:00
|
|
|
private static final boolean animationEnabled = Build.VERSION.SDK_INT >= 18;
|
|
|
|
private static DecelerateInterpolator decelerateInterpolator = new DecelerateInterpolator();
|
|
|
|
private AnimatorSet windowAnimatorSet;
|
2014-06-04 18:00:42 +02:00
|
|
|
static {
|
|
|
|
Field f = null;
|
|
|
|
try {
|
|
|
|
f = PopupWindow.class.getDeclaredField("mOnScrollChangedListener");
|
|
|
|
f.setAccessible(true);
|
|
|
|
} catch (NoSuchFieldException e) {
|
|
|
|
/* ignored */
|
|
|
|
}
|
|
|
|
superListenerField = f;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final ViewTreeObserver.OnScrollChangedListener NOP = new ViewTreeObserver.OnScrollChangedListener() {
|
|
|
|
@Override
|
|
|
|
public void onScrollChanged() {
|
|
|
|
/* do nothing */
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
private ViewTreeObserver.OnScrollChangedListener mSuperScrollListener;
|
|
|
|
private ViewTreeObserver mViewTreeObserver;
|
|
|
|
|
2015-03-19 00:09:45 +01:00
|
|
|
public interface OnDispatchKeyEventListener {
|
|
|
|
void onDispatchKeyEvent(KeyEvent keyEvent);
|
2014-06-17 16:45:21 +02:00
|
|
|
}
|
|
|
|
|
2015-07-22 20:56:37 +02:00
|
|
|
public static class ActionBarPopupWindowLayout extends FrameLayout {
|
2014-06-17 16:45:21 +02:00
|
|
|
|
|
|
|
private OnDispatchKeyEventListener mOnDispatchKeyEventListener;
|
2015-06-29 19:12:11 +02:00
|
|
|
protected static Drawable backgroundDrawable;
|
|
|
|
private float backScaleX = 1;
|
|
|
|
private float backScaleY = 1;
|
|
|
|
private int backAlpha = 255;
|
|
|
|
private int lastStartedChild = 0;
|
|
|
|
private boolean showedFromBotton;
|
|
|
|
private HashMap<View, Integer> positions = new HashMap<>();
|
2014-06-17 16:45:21 +02:00
|
|
|
|
2015-07-22 20:56:37 +02:00
|
|
|
private ScrollView scrollView;
|
|
|
|
private LinearLayout linearLayout;
|
|
|
|
|
2014-06-17 16:45:21 +02:00
|
|
|
public ActionBarPopupWindowLayout(Context context) {
|
|
|
|
super(context);
|
2015-06-29 19:12:11 +02:00
|
|
|
|
|
|
|
if (backgroundDrawable == null) {
|
|
|
|
backgroundDrawable = getResources().getDrawable(R.drawable.popup_fixed);
|
|
|
|
}
|
2015-07-22 20:56:37 +02:00
|
|
|
|
|
|
|
setPadding(AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8));
|
|
|
|
setWillNotDraw(false);
|
|
|
|
|
|
|
|
scrollView = new ScrollView(context);
|
|
|
|
scrollView.setVerticalScrollBarEnabled(false);
|
|
|
|
addView(scrollView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT));
|
|
|
|
|
|
|
|
linearLayout = new LinearLayout(context);
|
|
|
|
linearLayout.setOrientation(LinearLayout.VERTICAL);
|
|
|
|
scrollView.addView(linearLayout, new ScrollView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
2015-06-29 19:12:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void setShowedFromBotton(boolean value) {
|
|
|
|
showedFromBotton = value;
|
2014-06-17 16:45:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void setDispatchKeyEventListener(OnDispatchKeyEventListener listener) {
|
|
|
|
mOnDispatchKeyEventListener = listener;
|
|
|
|
}
|
|
|
|
|
2015-06-29 19:12:11 +02:00
|
|
|
public void setBackAlpha(int value) {
|
|
|
|
backAlpha = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getBackAlpha() {
|
|
|
|
return backAlpha;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setBackScaleX(float value) {
|
|
|
|
backScaleX = value;
|
|
|
|
invalidate();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setBackScaleY(float value) {
|
|
|
|
backScaleY = value;
|
|
|
|
if (animationEnabled) {
|
2015-07-22 20:56:37 +02:00
|
|
|
int count = getItemsCount();
|
2015-06-29 19:12:11 +02:00
|
|
|
int visibleCount = 0;
|
|
|
|
for (int a = 0; a < count; a++) {
|
2015-07-22 20:56:37 +02:00
|
|
|
visibleCount += getItemAt(a).getVisibility() == VISIBLE ? 1 : 0;
|
2015-06-29 19:12:11 +02:00
|
|
|
}
|
|
|
|
int height = getMeasuredHeight() - AndroidUtilities.dp(16);
|
|
|
|
if (showedFromBotton) {
|
|
|
|
for (int a = lastStartedChild; a >= 0; a--) {
|
2015-07-22 20:56:37 +02:00
|
|
|
View child = getItemAt(a);
|
2015-06-29 19:12:11 +02:00
|
|
|
if (child.getVisibility() != VISIBLE) {
|
|
|
|
continue;
|
|
|
|
}
|
2015-08-13 11:23:31 +02:00
|
|
|
Integer position = positions.get(child);
|
|
|
|
if (position != null && height - (position * AndroidUtilities.dp(48) + AndroidUtilities.dp(32)) > value * height) {
|
2015-06-29 19:12:11 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
lastStartedChild = a - 1;
|
|
|
|
startChildAnimation(child);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (int a = lastStartedChild; a < count; a++) {
|
2015-07-22 20:56:37 +02:00
|
|
|
View child = getItemAt(a);
|
2015-06-29 19:12:11 +02:00
|
|
|
if (child.getVisibility() != VISIBLE) {
|
|
|
|
continue;
|
|
|
|
}
|
2015-08-13 11:23:31 +02:00
|
|
|
Integer position = positions.get(child);
|
|
|
|
if (position != null && (position + 1) * AndroidUtilities.dp(48) - AndroidUtilities.dp(24) > value * height) {
|
2015-06-29 19:12:11 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
lastStartedChild = a + 1;
|
|
|
|
startChildAnimation(child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
invalidate();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void startChildAnimation(View child) {
|
|
|
|
if (animationEnabled) {
|
|
|
|
AnimatorSet animatorSet = new AnimatorSet();
|
|
|
|
animatorSet.playTogether(
|
|
|
|
ObjectAnimator.ofFloat(child, "alpha", 0.0f, 1.0f),
|
|
|
|
ObjectAnimator.ofFloat(child, "translationY", AndroidUtilities.dp(showedFromBotton ? 6 : -6), 0));
|
|
|
|
animatorSet.setDuration(180);
|
|
|
|
animatorSet.setInterpolator(decelerateInterpolator);
|
|
|
|
animatorSet.start();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-22 20:56:37 +02:00
|
|
|
@Override
|
|
|
|
public void addView(View child) {
|
|
|
|
linearLayout.addView(child);
|
|
|
|
}
|
|
|
|
|
2015-06-29 19:12:11 +02:00
|
|
|
public float getBackScaleX() {
|
|
|
|
return backScaleX;
|
|
|
|
}
|
|
|
|
|
|
|
|
public float getBackScaleY() {
|
|
|
|
return backScaleY;
|
|
|
|
}
|
|
|
|
|
2014-06-17 16:45:21 +02:00
|
|
|
@Override
|
|
|
|
public boolean dispatchKeyEvent(KeyEvent event) {
|
|
|
|
if (mOnDispatchKeyEventListener != null) {
|
|
|
|
mOnDispatchKeyEventListener.onDispatchKeyEvent(event);
|
|
|
|
}
|
|
|
|
return super.dispatchKeyEvent(event);
|
|
|
|
}
|
2015-06-29 19:12:11 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onDraw(Canvas canvas) {
|
|
|
|
if (backgroundDrawable != null) {
|
|
|
|
backgroundDrawable.setAlpha(backAlpha);
|
|
|
|
if (showedFromBotton) {
|
|
|
|
backgroundDrawable.setBounds(0, (int) (getMeasuredHeight() * (1.0f - backScaleY)), (int) (getMeasuredWidth() * backScaleX), getMeasuredHeight());
|
|
|
|
} else {
|
|
|
|
backgroundDrawable.setBounds(0, 0, (int) (getMeasuredWidth() * backScaleX), (int) (getMeasuredHeight() * backScaleY));
|
|
|
|
}
|
|
|
|
backgroundDrawable.draw(canvas);
|
|
|
|
}
|
|
|
|
}
|
2015-07-22 20:56:37 +02:00
|
|
|
|
|
|
|
public int getItemsCount() {
|
|
|
|
return linearLayout.getChildCount();
|
|
|
|
}
|
|
|
|
|
|
|
|
public View getItemAt(int index) {
|
|
|
|
return linearLayout.getChildAt(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void scrollToTop() {
|
|
|
|
scrollView.scrollTo(0, 0);
|
|
|
|
}
|
2014-06-17 16:45:21 +02:00
|
|
|
}
|
|
|
|
|
2014-06-04 18:00:42 +02:00
|
|
|
public ActionBarPopupWindow() {
|
|
|
|
super();
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
public ActionBarPopupWindow(Context context) {
|
|
|
|
super(context);
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
public ActionBarPopupWindow(int width, int height) {
|
|
|
|
super(width, height);
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
public ActionBarPopupWindow(View contentView) {
|
|
|
|
super(contentView);
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
public ActionBarPopupWindow(View contentView, int width, int height, boolean focusable) {
|
|
|
|
super(contentView, width, height, focusable);
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
public ActionBarPopupWindow(View contentView, int width, int height) {
|
|
|
|
super(contentView, width, height);
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void init() {
|
|
|
|
if (superListenerField != null) {
|
|
|
|
try {
|
|
|
|
mSuperScrollListener = (ViewTreeObserver.OnScrollChangedListener) superListenerField.get(this);
|
|
|
|
superListenerField.set(this, NOP);
|
|
|
|
} catch (Exception e) {
|
|
|
|
mSuperScrollListener = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void unregisterListener() {
|
|
|
|
if (mSuperScrollListener != null && mViewTreeObserver != null) {
|
|
|
|
if (mViewTreeObserver.isAlive()) {
|
|
|
|
mViewTreeObserver.removeOnScrollChangedListener(mSuperScrollListener);
|
|
|
|
}
|
|
|
|
mViewTreeObserver = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void registerListener(View anchor) {
|
|
|
|
if (mSuperScrollListener != null) {
|
2014-06-17 16:45:21 +02:00
|
|
|
ViewTreeObserver vto = (anchor.getWindowToken() != null) ? anchor.getViewTreeObserver() : null;
|
2014-06-04 18:00:42 +02:00
|
|
|
if (vto != mViewTreeObserver) {
|
|
|
|
if (mViewTreeObserver != null && mViewTreeObserver.isAlive()) {
|
|
|
|
mViewTreeObserver.removeOnScrollChangedListener(mSuperScrollListener);
|
|
|
|
}
|
|
|
|
if ((mViewTreeObserver = vto) != null) {
|
|
|
|
vto.addOnScrollChangedListener(mSuperScrollListener);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void showAsDropDown(View anchor, int xoff, int yoff) {
|
2014-11-21 11:59:05 +01:00
|
|
|
try {
|
|
|
|
super.showAsDropDown(anchor, xoff, yoff);
|
|
|
|
registerListener(anchor);
|
|
|
|
} catch (Exception e) {
|
|
|
|
FileLog.e("tmessages", e);
|
|
|
|
}
|
2014-06-04 18:00:42 +02:00
|
|
|
}
|
|
|
|
|
2015-06-29 19:12:11 +02:00
|
|
|
public void startAnimation() {
|
|
|
|
if (animationEnabled) {
|
|
|
|
if (windowAnimatorSet != null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ActionBarPopupWindowLayout content = (ActionBarPopupWindowLayout) getContentView();
|
|
|
|
content.setTranslationY(0);
|
|
|
|
content.setAlpha(1.0f);
|
|
|
|
content.setPivotX(content.getMeasuredWidth());
|
|
|
|
content.setPivotY(0);
|
2015-07-22 20:56:37 +02:00
|
|
|
int count = content.getItemsCount();
|
2015-06-29 19:12:11 +02:00
|
|
|
content.positions.clear();
|
|
|
|
int visibleCount = 0;
|
|
|
|
for (int a = 0; a < count; a++) {
|
2015-07-22 20:56:37 +02:00
|
|
|
View child = content.getItemAt(a);
|
2015-06-29 19:12:11 +02:00
|
|
|
if (child.getVisibility() != View.VISIBLE) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
content.positions.put(child, visibleCount);
|
|
|
|
child.setAlpha(0.0f);
|
|
|
|
visibleCount++;
|
|
|
|
}
|
|
|
|
if (content.showedFromBotton) {
|
|
|
|
content.lastStartedChild = count - 1;
|
|
|
|
} else {
|
|
|
|
content.lastStartedChild = 0;
|
|
|
|
}
|
|
|
|
windowAnimatorSet = new AnimatorSet();
|
|
|
|
windowAnimatorSet.playTogether(
|
|
|
|
ObjectAnimator.ofFloat(content, "backScaleY", 0.0f, 1.0f),
|
|
|
|
ObjectAnimator.ofInt(content, "backAlpha", 0, 255));
|
|
|
|
windowAnimatorSet.setDuration(150 + 16 * visibleCount);
|
|
|
|
windowAnimatorSet.addListener(new Animator.AnimatorListener() {
|
|
|
|
@Override
|
|
|
|
public void onAnimationStart(Animator animation) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAnimationEnd(Animator animation) {
|
|
|
|
windowAnimatorSet = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAnimationCancel(Animator animation) {
|
|
|
|
onAnimationEnd(animation);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAnimationRepeat(Animator animation) {
|
|
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
windowAnimatorSet.start();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-04 18:00:42 +02:00
|
|
|
@Override
|
|
|
|
public void update(View anchor, int xoff, int yoff, int width, int height) {
|
|
|
|
super.update(anchor, xoff, yoff, width, height);
|
|
|
|
registerListener(anchor);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void update(View anchor, int width, int height) {
|
|
|
|
super.update(anchor, width, height);
|
|
|
|
registerListener(anchor);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void showAtLocation(View parent, int gravity, int x, int y) {
|
|
|
|
super.showAtLocation(parent, gravity, x, y);
|
|
|
|
unregisterListener();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void dismiss() {
|
2015-06-29 19:12:11 +02:00
|
|
|
dismiss(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void dismiss(boolean animated) {
|
|
|
|
if (animationEnabled && animated) {
|
|
|
|
if (windowAnimatorSet != null) {
|
|
|
|
windowAnimatorSet.cancel();
|
|
|
|
}
|
|
|
|
ActionBarPopupWindowLayout content = (ActionBarPopupWindowLayout) getContentView();
|
|
|
|
windowAnimatorSet = new AnimatorSet();
|
|
|
|
windowAnimatorSet.playTogether(
|
|
|
|
ObjectAnimator.ofFloat(content, "translationY", AndroidUtilities.dp(content.showedFromBotton ? 5 : -5)),
|
|
|
|
ObjectAnimator.ofFloat(content, "alpha", 0.0f));
|
|
|
|
windowAnimatorSet.setDuration(150);
|
|
|
|
windowAnimatorSet.addListener(new Animator.AnimatorListener() {
|
|
|
|
@Override
|
|
|
|
public void onAnimationStart(Animator animation) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAnimationEnd(Animator animation) {
|
|
|
|
windowAnimatorSet = null;
|
|
|
|
setFocusable(false);
|
|
|
|
try {
|
|
|
|
ActionBarPopupWindow.super.dismiss();
|
|
|
|
} catch (Exception e) {
|
|
|
|
//don't promt
|
|
|
|
}
|
|
|
|
unregisterListener();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAnimationCancel(Animator animation) {
|
|
|
|
onAnimationEnd(animation);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAnimationRepeat(Animator animation) {
|
|
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
windowAnimatorSet.start();
|
|
|
|
} else {
|
|
|
|
setFocusable(false);
|
|
|
|
try {
|
|
|
|
super.dismiss();
|
|
|
|
} catch (Exception e) {
|
|
|
|
//don't promt
|
|
|
|
}
|
|
|
|
unregisterListener();
|
2015-02-26 15:36:15 +01:00
|
|
|
}
|
2014-06-04 18:00:42 +02:00
|
|
|
}
|
|
|
|
}
|