NekoX/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiTabsStrip.java

1099 lines
46 KiB
Java

package org.telegram.ui.Components;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.RectF;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.OvershootInterpolator;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ViewAnimator;
import androidx.core.graphics.ColorUtils;
import androidx.core.math.MathUtils;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.ImageReceiver;
import org.telegram.messenger.MediaDataController;
import org.telegram.messenger.MessageObject;
import org.telegram.messenger.R;
import org.telegram.messenger.UserConfig;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Components.Premium.PremiumLockIconView;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class EmojiTabsStrip extends ScrollableHorizontalScrollView {
private int recentDrawableId = R.drawable.msg_emoji_recent;
private static int[] emojiTabsDrawableIds = {
R.drawable.msg_emoji_smiles,
R.drawable.msg_emoji_cat,
R.drawable.msg_emoji_food,
R.drawable.msg_emoji_activities,
R.drawable.msg_emoji_travel,
R.drawable.msg_emoji_objects,
R.drawable.msg_emoji_other,
R.drawable.msg_emoji_flags
};
private static int[] emojiTabsAnimatedDrawableIds = {
R.raw.msg_emoji_smiles,
R.raw.msg_emoji_cat,
R.raw.msg_emoji_food,
R.raw.msg_emoji_activities,
R.raw.msg_emoji_travel,
R.raw.msg_emoji_objects,
R.raw.msg_emoji_other,
R.raw.msg_emoji_flags
};
private int settingsDrawableId = R.drawable.smiles_tab_settings;
private Theme.ResourcesProvider resourcesProvider;
private EmojiView parent;
private boolean includeAnimated;
private LinearLayout contentView;
private EmojiTabButton recentTab;
private EmojiTabButton settingsTab;
private EmojiTabsView emojiTabs;
private ArrayList<EmojiTabButton> emojipackTabs;
private HashMap<View, Rect> removingViews = new HashMap<>();
private ValueAnimator selectAnimator;
private float selectT = 0f;
private float selectAnimationT = 0f;
private int selected = 0;
private int wasIndex = 0;
private Runnable onSettingsOpenRunnable;
private boolean wasDrawn;
public EmojiTabsStrip(Context context, Theme.ResourcesProvider resourcesProvider, boolean includeAnimated, EmojiView parent, Runnable onSettingsOpen) {
super(context);
this.parent = parent;
this.includeAnimated = includeAnimated;
this.resourcesProvider = resourcesProvider;
this.onSettingsOpenRunnable = onSettingsOpen;
contentView = new LinearLayout(context) {
HashMap<Integer, Integer> lastX = new HashMap<>();
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int cy = (b - t) / 2;
if (includeAnimated) {
int x = getPaddingLeft() - (!recentIsShown ? AndroidUtilities.dp(30 + 3) : 0);
for (int i = 0; i < getChildCount(); ++i) {
View child = getChildAt(i);
if (child == settingsTab || removingViews.containsKey(child)) {
continue;
}
if (child != null) {
child.layout(x, cy - child.getMeasuredHeight() / 2, x + child.getMeasuredWidth(), cy + child.getMeasuredHeight() / 2);
Integer id = child instanceof EmojiTabButton ? ((EmojiTabButton) child).id : (child instanceof EmojiTabsView ? ((EmojiTabsView) child).id : null);
if (child instanceof EmojiTabButton && ((EmojiTabButton) child).newly) {
((EmojiTabButton) child).newly = false;
child.setScaleX(0);
child.setScaleY(0);
child.setAlpha(0);
child.animate().scaleX(1f).scaleY(1f).alpha(1f).setDuration(200).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).start();
}
if (id != null) {
if (lastX.get(id) != null && lastX.get(id) != x) {
child.setTranslationX(lastX.get(id) - x);
child.animate().translationX(0).setDuration(250).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).start();
}
lastX.put(id, x);
}
x += child.getMeasuredWidth() + AndroidUtilities.dp(3);
}
}
if (settingsTab != null) {
x += (!recentIsShown ? AndroidUtilities.dp(30 + 3) : 0);
Integer id = settingsTab.id;
if (x + settingsTab.getMeasuredWidth() + getPaddingRight() <= EmojiTabsStrip.this.getMeasuredWidth()) {
settingsTab.layout(x = (r - l - getPaddingRight() - settingsTab.getMeasuredWidth()), cy - settingsTab.getMeasuredHeight() / 2, r - l - getPaddingRight(), cy + settingsTab.getMeasuredHeight() / 2);
} else {
settingsTab.layout(x, cy - settingsTab.getMeasuredHeight() / 2, x + settingsTab.getMeasuredWidth(), cy + settingsTab.getMeasuredHeight() / 2);
}
if (id != null) {
if (lastX.get(id) != null && lastX.get(id) != x) {
settingsTab.setTranslationX(lastX.get(id) - x);
settingsTab.animate().translationX(0).setDuration(350).start();
}
lastX.put(id, x);
}
}
} else {
final int childCount = getChildCount() - (!recentIsShown ? 1 : 0);
int margin = (int) ((r - l - getPaddingLeft() - getPaddingRight() - childCount * AndroidUtilities.dp(30)) / (float) Math.max(1, childCount - 1));
int x = getPaddingLeft();
for (int i = 0; i < childCount; ++i) {
View child = getChildAt((!recentIsShown ? 1 : 0) + i);
if (child != null) {
child.layout(x, cy - child.getMeasuredHeight() / 2, x + child.getMeasuredWidth(), cy + child.getMeasuredHeight() / 2);
x += child.getMeasuredWidth() + margin;
}
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int AT_MOST = MeasureSpec.makeMeasureSpec(99999999, MeasureSpec.AT_MOST);
int width = getPaddingLeft() + getPaddingRight() - (int) (recentIsShown ? 0 : recentTab.getAlpha() * AndroidUtilities.dp(30 + 3));
for (int i = 0; i < getChildCount(); ++i) {
View child = getChildAt(i);
if (child != null) {
child.measure(AT_MOST, heightMeasureSpec);
width += child.getMeasuredWidth() + (i + 1 < getChildCount() ? AndroidUtilities.dp(3) : 0);
}
}
if (!includeAnimated) {
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec));
return;
}
setMeasuredDimension(Math.max(width, MeasureSpec.getSize(widthMeasureSpec)), MeasureSpec.getSize(heightMeasureSpec));
}
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private RectF from = new RectF();
private RectF to = new RectF();
private RectF rect = new RectF();
private Path path = new Path();
@Override
protected void dispatchDraw(Canvas canvas) {
for (Map.Entry<View, Rect> entry : removingViews.entrySet()) {
View view = entry.getKey();
Rect bounds = entry.getValue();
canvas.save();
canvas.translate(bounds.left, bounds.top);
canvas.scale(view.getScaleX(), view.getScaleY(), bounds.width() / 2f, bounds.height() / 2f);
view.draw(canvas);
canvas.restore();
}
int selectFrom = (int) Math.floor(selectT), selectTo = (int) Math.ceil(selectT);
getChildBounds(selectFrom, from);
getChildBounds(selectTo, to);
AndroidUtilities.lerp(from, to, selectT - selectFrom, rect);
float isEmojiTabs = emojiTabs == null ? 0 : MathUtils.clamp(1f - Math.abs(selectT - 1), 0, 1);
float isMiddle = 4f * selectAnimationT * (1f - selectAnimationT);
float hw = rect.width() / 2 * (1f + isMiddle * .3f);
float hh = rect.height() / 2 * (1f - isMiddle * .05f);
rect.set(rect.centerX() - hw, rect.centerY() - hh, rect.centerX() + hw, rect.centerY() + hh);
float r = AndroidUtilities.dp(AndroidUtilities.lerp(8f, 16f, isEmojiTabs));
paint.setColor(selectorColor());
path.rewind();
path.addRoundRect(rect, r, r, Path.Direction.CW);
canvas.drawPath(path, paint);
if (emojiTabs != null) {
path.addCircle(emojiTabs.getLeft() + AndroidUtilities.dp(15), (emojiTabs.getTop() + emojiTabs.getBottom()) / 2f, AndroidUtilities.dp(15), Path.Direction.CW);
}
super.dispatchDraw(canvas);
wasDrawn = true;
}
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
if (child == emojiTabs) {
canvas.save();
canvas.clipPath(path);
boolean res = super.drawChild(canvas, child, drawingTime);
canvas.restore();
return res;
}
return super.drawChild(canvas, child, drawingTime);
}
private void getChildBounds(int i, RectF out) {
View child = getChildAt(MathUtils.clamp(i, 0, getChildCount() - 1));
out.set(child.getLeft(), child.getTop(), child.getRight(), child.getBottom());
// out.offset(recentIsShown ? recentTab.getTranslationX() : AndroidUtilities.dp(30 + 3) - recentTab.getTranslationX(), 0);
}
};
contentView.setClipToPadding(false);
contentView.setPadding(AndroidUtilities.dp(5 + 6), 0, AndroidUtilities.dp(3), 0);
contentView.setOrientation(LinearLayout.HORIZONTAL);
setVerticalScrollBarEnabled(false);
setHorizontalScrollBarEnabled(false);
addView(contentView);
contentView.addView(recentTab = new EmojiTabButton(context, recentDrawableId, false, false));
recentTab.id = "recent".hashCode();
if (!includeAnimated) {
for (int i = 0; i < emojiTabsDrawableIds.length; ++i) {
contentView.addView(new EmojiTabButton(context, emojiTabsDrawableIds[i], false, i == 0));
}
updateClickListeners();
} else {
contentView.addView(emojiTabs = new EmojiTabsView(context));
emojiTabs.id = "tabs".hashCode();
if (onSettingsOpen != null) {
contentView.addView(settingsTab = new EmojiTabButton(context, settingsDrawableId, false, true));
settingsTab.id = "settings".hashCode();
settingsTab.setAlpha(0);
}
updateClickListeners();
updateEmojiPacks();
}
}
private boolean recentFirstChange = true;
private boolean recentIsShown = true;
public void showRecent(boolean show) {
if (recentIsShown == show) {
return;
}
recentIsShown = show;
if (recentFirstChange) {
recentTab.setAlpha(show ? 1f : 0f);
} else {
recentTab.animate().alpha(show ? 1f : 0f).setDuration(200).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).start();
}
if (!show && selected == 0 || show && selected == 1) {
select(0, !recentFirstChange);
}
contentView.requestLayout();
recentFirstChange = false;
}
private boolean isFreeEmojiPack(TLRPC.StickerSet set, ArrayList<TLRPC.Document> documents) {
if (set == null || documents == null) {
return false;
}
for (int i = 0; i < documents.size(); ++i) {
if (!MessageObject.isFreeEmoji(documents.get(i))) {
return false;
}
}
return true;
}
private TLRPC.Document getThumbDocument(TLRPC.StickerSet set, ArrayList<TLRPC.Document> documents) {
if (set == null) {
return null;
}
if (documents != null) {
for (int i = 0; i < documents.size(); ++i) {
TLRPC.Document d = documents.get(i);
if (d.id == set.thumb_document_id) {
return d;
}
}
}
if (documents != null && documents.size() >= 1) {
return documents.get(0);
}
return null;
}
boolean first = true;
private ValueAnimator appearAnimation;
private int appearCount;
public void updateEmojiPacks() {
if (!includeAnimated) {
return;
}
if (first && !MediaDataController.getInstance(UserConfig.selectedAccount).areStickersLoaded(MediaDataController.TYPE_EMOJIPACKS)) {
return;
}
first = false;
ArrayList<EmojiView.EmojiPack> emojiPacks = parent.getEmojipacks();
if (emojiPacks == null) {
return;
}
if (emojipackTabs == null) {
emojipackTabs = new ArrayList<>();
}
boolean first = emojipackTabs.size() == 0 && emojiPacks.size() > 0 && appearCount != emojiPacks.size() && wasDrawn;
boolean doAppearAnimation = false;//emojipackTabs.size() == 0 && emojiPacks.size() > 0 && appearCount != emojiPacks.size() && wasDrawn;
if (appearAnimation != null && appearCount != emojiPacks.size()) {
appearAnimation.cancel();
appearAnimation = null;
}
appearCount = emojiPacks.size();
final boolean isPremium = UserConfig.getInstance(UserConfig.selectedAccount).isPremium();
for (int i = 0; i < emojipackTabs.size(); ++i) {
EmojiTabButton emojipackTab = emojipackTabs.get(i);
EmojiView.EmojiPack pack = null;
if (emojipackTab != null && emojipackTab.id != null) {
for (int j = 0; j < emojiPacks.size(); ++j) {
EmojiView.EmojiPack p = emojiPacks.get(j);
final int id = Objects.hash(p.set.id, p.featured);
if (id == emojipackTab.id) {
pack = p;
break;
}
}
}
if (pack == null && emojipackTab != null) {
Rect bounds = new Rect();
bounds.set(emojipackTab.getLeft(), emojipackTab.getTop(), emojipackTab.getRight(), emojipackTab.getBottom());
removingViews.put(emojipackTab, bounds);
ValueAnimator anm = ValueAnimator.ofFloat(emojipackTab.getAlpha(), 0f);
anm.addUpdateListener(a -> {
float alpha = (float) a.getAnimatedValue();
emojipackTab.setAlpha(alpha);
emojipackTab.setScaleX(alpha);
emojipackTab.setScaleY(alpha);
contentView.invalidate();
});
anm.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
removingViews.remove(emojipackTab);
contentView.invalidate();
}
});
anm.setDuration(200);
anm.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT);
anm.start();
emojipackTabs.remove(i--);
}
contentView.removeView(emojipackTab);
}
for (int i = 0; i < emojiPacks.size(); ++i) {
EmojiView.EmojiPack pack = emojiPacks.get(i);
final int id = Objects.hash(pack.set.id, pack.featured);
EmojiTabButton emojipackTab = null;
for (int j = 0; j < emojipackTabs.size(); ++j) {
EmojiTabButton tab = emojipackTabs.get(j);
if (tab != null && tab.id != null && tab.id == id) {
emojipackTab = tab;
break;
}
}
final boolean free = isFreeEmojiPack(pack.set, pack.documents);
AnimatedEmojiDrawable drawable = emojipackTab == null ? null : (AnimatedEmojiDrawable) emojipackTab.getDrawable();
TLRPC.Document thumbDocument = getThumbDocument(pack.set, pack.documents);
if (thumbDocument != null && (drawable == null || drawable.getDocumentId() != thumbDocument.id)) {
drawable = AnimatedEmojiDrawable.make(UserConfig.selectedAccount, AnimatedEmojiDrawable.CACHE_TYPE_TAB_STRIP, thumbDocument);
}
if (emojipackTab == null) {
emojipackTab = new EmojiTabButton(getContext(), drawable, free, false,false);
emojipackTab.id = id;
if (drawable != null) {
drawable.addView(emojipackTab.imageView);
}
emojipackTabs.add(emojipackTab);
} else {
if (emojipackTab.getDrawable() instanceof AnimatedEmojiDrawable) {
((AnimatedEmojiDrawable) emojipackTab.getDrawable()).removeView(emojipackTab.imageView);
}
emojipackTab.setDrawable(drawable);
if (drawable != null) {
drawable.addView(emojipackTab.imageView);
}
}
if (!isPremium && !free) {
emojipackTab.setLock(true);
} else if (!pack.installed && !parent.installedEmojiSets.contains(pack.set.id)) {
emojipackTab.setLock(false);
} else {
emojipackTab.setLock(null);
}
if (doAppearAnimation && !first) {
emojipackTab.newly = false;
}
if (emojipackTab.getParent() instanceof ViewGroup) {
((ViewGroup) emojipackTab.getParent()).removeView(emojipackTab);
}
contentView.addView(emojipackTab);
}
if (settingsTab != null) {
settingsTab.bringToFront();
if (settingsTab.getAlpha() < 1) {
settingsTab.animate().alpha(1f).setDuration(200).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
}
}
if (doAppearAnimation) {
if (emojipackTabs != null) {
for (int i = 0; i < emojipackTabs.size(); ++i) {
emojipackTabs.get(i).setScaleX(0);
emojipackTabs.get(i).setScaleY(0);
}
}
appearAnimation = ValueAnimator.ofFloat(0, 1);
final OvershootInterpolator innerInterpolator = new OvershootInterpolator(3f);
appearAnimation.addUpdateListener(anm -> {
if (emojipackTabs == null) {
return;
}
final float t = (float) anm.getAnimatedValue();
final int count = emojipackTabs.size();
final float dur = 1f / count * 4.5f;
for (int i = 0; i < count; ++i) {
final float off = i / (float) count * (1f - dur);
final float T = MathUtils.clamp((t - off) / dur, 0, 1);
final float scale = innerInterpolator.getInterpolation(T);
emojipackTabs.get(i).setScaleX(scale);
emojipackTabs.get(i).setScaleY(scale);
}
});
appearAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationCancel(Animator animation) {
if (emojipackTabs == null) {
return;
}
for (int i = 0; i < emojipackTabs.size(); ++i) {
emojipackTabs.get(i).setScaleX(1);
emojipackTabs.get(i).setScaleY(1);
}
}
});
appearAnimation.setStartDelay(150);
appearAnimation.setDuration((emojipackTabs == null ? 0 : emojipackTabs.size()) * 75L);
appearAnimation.setInterpolator(CubicBezierInterpolator.EASE_OUT);
appearAnimation.start();
}
updateClickListeners();
}
public void updateClickListeners() {
for (int i = 0, j = 0; i < contentView.getChildCount(); ++i, ++j) {
View child = contentView.getChildAt(i);
if (child instanceof EmojiTabsView) {
EmojiTabsView tabsView = (EmojiTabsView) child;
for (int a = 0; a < tabsView.contentView.getChildCount(); ++a, ++j) {
final int index = j;
tabsView.contentView.getChildAt(a).setOnClickListener(e -> {
onTabClick(index);
});
}
--j;
} else if (child != null) {
final int index = j;
child.setOnClickListener(e -> {
onTabClick(index);
});
}
}
if (settingsTab != null) {
settingsTab.setOnClickListener(e -> {
if (onSettingsOpenRunnable != null) {
onSettingsOpenRunnable.run();
}
});
}
}
protected boolean onTabClick(int index) {
return true;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
contentView.setPadding(AndroidUtilities.dp(5 + 6), 0, AndroidUtilities.dp(5 + 6), 0);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
public void updateColors() {
if (recentTab != null) {
recentTab.updateColor();
}
}
public void select(int index) {
select(index, true);
}
public void select(int index, boolean animated) {
animated = animated && !first;
if (!recentIsShown) {
index = Math.max(1, index);
}
final int wasSelected = selected;
for (int i = 0, j = 0; i < contentView.getChildCount(); ++i, ++j) {
View child = contentView.getChildAt(i);
int from = j;
if (child instanceof EmojiTabsView) {
EmojiTabsView tabs = (EmojiTabsView) child;
for (int a = 0; a < tabs.contentView.getChildCount(); ++a, ++j) {
View child2 = tabs.contentView.getChildAt(a);
if (child2 instanceof EmojiTabButton) {
((EmojiTabButton) child2).updateSelect(index == j, animated);
}
}
--j;
} else if (child instanceof EmojiTabButton) {
((EmojiTabButton) child).updateSelect(index == j, animated);
}
if (index >= from && index <= j) {
selected = i;
}
}
if (wasSelected != selected) {
if (selectAnimator != null) {
selectAnimator.cancel();
}
final float from = selectT, to = selected;
if (animated) {
selectAnimator = ValueAnimator.ofFloat(0, 1);
selectAnimator.addUpdateListener(a -> {
selectAnimationT = (float) a.getAnimatedValue();
selectT = AndroidUtilities.lerp(from, to, selectAnimationT);
contentView.invalidate();
});
selectAnimator.setDuration(350);
selectAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT);
selectAnimator.start();
} else {
selectAnimationT = 1f;
selectT = AndroidUtilities.lerp(from, to, selectAnimationT);
contentView.invalidate();
}
if (emojiTabs != null) {
emojiTabs.show(selected == 1, animated);
}
View child = contentView.getChildAt(selected);
if (selected >= 2) {
scrollToVisible(child.getLeft(), child.getRight());
} else {
scrollTo(0);
}
}
if (wasIndex != index) {
if (emojiTabs != null && selected == 1 && index >= 1 && index <= 1 + emojiTabs.contentView.getChildCount()) {
emojiTabs.scrollToVisible(AndroidUtilities.dp(36 * (index - 1) - 6), AndroidUtilities.dp(36 * (index - 1) - 6 + 30));
}
wasIndex = index;
}
}
private int selectorColor() {
return 0x2effffff & Theme.getColor(Theme.key_chat_emojiPanelIcon, resourcesProvider);
}
private class EmojiTabButton extends ViewGroup {
public boolean shown = true;
public Integer id;
public boolean newly;
private ImageView imageView;
private RLottieDrawable lottieDrawable;
private PremiumLockIconView lockView;
private boolean round, forceSelector;
public EmojiTabButton(Context context, int drawableId, int lottieId, boolean roundSelector, boolean forceSelector) {
super(context);
this.round = roundSelector;
this.forceSelector = forceSelector;
if (round) {
setBackground(Theme.createCircleSelectorDrawable(selectorColor(), 0, 0));
} else if (forceSelector) {
setBackground(Theme.createRadSelectorDrawable(selectorColor(), 8, 8));
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
lottieDrawable = new RLottieDrawable(lottieId, "" + lottieId, AndroidUtilities.dp(24), AndroidUtilities.dp(24), false, null);
lottieDrawable.setBounds(AndroidUtilities.dp(3), AndroidUtilities.dp(3), AndroidUtilities.dp(27), AndroidUtilities.dp(27));
lottieDrawable.setMasterParent(this);
lottieDrawable.setAllowDecodeSingleFrame(true);
lottieDrawable.start();
} else {
imageView = new ImageView(context);
imageView.setImageDrawable(context.getResources().getDrawable(drawableId).mutate());
addView(imageView);
}
setColor(Theme.getColor(Theme.key_chat_emojiPanelIcon, resourcesProvider));
}
public EmojiTabButton(Context context, int drawableId, boolean roundSelector, boolean forceSelector) {
super(context);
this.round = roundSelector;
this.forceSelector = forceSelector;
if (round) {
setBackground(Theme.createCircleSelectorDrawable(selectorColor(), 0, 0));
} else if (forceSelector) {
setBackground(Theme.createRadSelectorDrawable(selectorColor(), 8, 8));
}
imageView = new ImageView(context);
imageView.setImageDrawable(context.getResources().getDrawable(drawableId).mutate());
setColor(Theme.getColor(Theme.key_chat_emojiPanelIcon, resourcesProvider));
addView(imageView);
}
public EmojiTabButton(Context context, Drawable drawable, boolean free, boolean roundSelector, boolean forceSelector) {
super(context);
this.newly = true;
this.round = roundSelector;
this.forceSelector = forceSelector;
if (round) {
setBackground(Theme.createCircleSelectorDrawable(selectorColor(), 0, 0));
} else if (forceSelector) {
setBackground(Theme.createRadSelectorDrawable(selectorColor(), 8, 8));
}
imageView = new ImageView(context) {
@Override
public void invalidate() {
super.invalidate();
updateLockImageReceiver();
}
@Override
protected void dispatchDraw(Canvas canvas) {
if (getDrawable() != null) {
getDrawable().setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
getDrawable().setAlpha(255);
getDrawable().draw(canvas);
}
}
};
imageView.setImageDrawable(drawable);
addView(imageView);
lockView = new PremiumLockIconView(context, PremiumLockIconView.TYPE_STICKERS_PREMIUM_LOCKED);
lockView.setAlpha(0f);
lockView.setScaleX(0);
lockView.setScaleY(0);
updateLockImageReceiver();
addView(lockView);
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (lottieDrawable != null) {
lottieDrawable.draw(canvas);
}
}
@Override
public boolean performClick() {
// if (lottieDrawable != null) {
// lottieDrawable.setProgress(0);
// AndroidUtilities.runOnUIThread(() -> lottieDrawable.start(), 75);
// }
return super.performClick();
}
private boolean wasVisible;
public void updateVisibilityInbounds(boolean visible, boolean ignore) {
if (!wasVisible && visible) {
if (lottieDrawable != null && !lottieDrawable.isRunning() && !ignore) {
lottieDrawable.setProgress(0);
lottieDrawable.start();
}
}
wasVisible = visible;
}
public void setLock(Boolean lock) {
if (lockView == null) {
return;
}
if (lock == null) {
updateLock(false);
} else {
updateLock(true);
if (lock) {
lockView.setImageResource(R.drawable.msg_mini_lockedemoji);
} else {
Drawable addIcon = getResources().getDrawable(R.drawable.msg_mini_addemoji).mutate();
addIcon.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.MULTIPLY));
lockView.setImageDrawable(addIcon);
}
}
}
private float lockT;
private ValueAnimator lockAnimator;
private void updateLock(boolean enable) {
if (lockAnimator != null) {
lockAnimator.cancel();
}
if (Math.abs(lockT - (enable ? 1f : 0f)) < 0.01f) {
return;
}
lockView.setVisibility(View.VISIBLE);
lockAnimator = ValueAnimator.ofFloat(lockT, enable ? 1f : 0f);
lockAnimator.addUpdateListener(anm -> {
lockT = (float) anm.getAnimatedValue();
lockView.setScaleX(lockT);
lockView.setScaleY(lockT);
lockView.setAlpha(lockT);
});
lockAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (!enable) {
lockView.setVisibility(View.GONE);
}
}
});
lockAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT);
lockAnimator.setDuration(200);
lockAnimator.start();
}
public void updateLockImageReceiver() {
if (lockView != null && !lockView.ready() && getDrawable() instanceof AnimatedEmojiDrawable) {
ImageReceiver imageReceiver = ((AnimatedEmojiDrawable) getDrawable()).getImageReceiver();
if (imageReceiver != null) {
lockView.setImageReceiver(imageReceiver);
lockView.invalidate();
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(AndroidUtilities.dp(30), AndroidUtilities.dp(30));
if (imageView != null) {
imageView.measure(
MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(24), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(24), MeasureSpec.EXACTLY)
);
}
if (lockView != null) {
lockView.measure(
MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(12), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(12), MeasureSpec.EXACTLY)
);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (imageView != null) {
int cx = (r - l) / 2, cy = (b - t) / 2;
imageView.layout(cx - imageView.getMeasuredWidth() / 2, cy - imageView.getMeasuredHeight() / 2, cx + imageView.getMeasuredWidth() / 2, cy + imageView.getMeasuredHeight() / 2);
}
if (lockView != null) {
lockView.layout(r - l - lockView.getMeasuredWidth(), b - t - lockView.getMeasuredHeight(), r - l, b - t);
}
}
public Drawable getDrawable() {
return imageView != null ? imageView.getDrawable() : null;
}
public void setDrawable(Drawable drawable) {
if (lockView != null && drawable instanceof AnimatedEmojiDrawable) {
ImageReceiver imageReceiver = ((AnimatedEmojiDrawable) drawable).getImageReceiver();
if (imageReceiver != null) {
lockView.setImageReceiver(imageReceiver);
}
}
if (imageView != null) {
imageView.setImageDrawable(drawable);
}
}
private float selectT;
private boolean selected;
private ValueAnimator selectAnimator;
public void updateSelect(boolean selected, boolean animated) {
if (imageView != null && imageView.getDrawable() == null) {
return;
}
if (this.selected == selected) {
return;
}
this.selected = selected;
if (selectAnimator != null) {
selectAnimator.cancel();
selectAnimator = null;
}
if (animated) {
selectAnimator = ValueAnimator.ofFloat(selectT, selected ? 1f : 0f);
selectAnimator.addUpdateListener(a -> {
selectT = (float) a.getAnimatedValue();
setColor(ColorUtils.blendARGB(Theme.getColor(Theme.key_chat_emojiPanelIcon, resourcesProvider), Theme.getColor(Theme.key_chat_emojiPanelIconSelected, resourcesProvider), selectT));
});
selectAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (!round) {
if (selected || forceSelector) {
if (getBackground() == null) {
setBackground(Theme.createRadSelectorDrawable(selectorColor(), 8, 8));
}
} else {
setBackground(null);
}
}
}
});
selectAnimator.setDuration(350);
selectAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT);
selectAnimator.start();
} else {
selectT = selected ? 1f : 0f;
updateColor();
}
}
public void updateColor() {
Theme.setSelectorDrawableColor(getBackground(), selectorColor(), false);
setColor(
ColorUtils.blendARGB(
Theme.getColor(Theme.key_chat_emojiPanelIcon, resourcesProvider),
Theme.getColor(Theme.key_chat_emojiPanelIconSelected, resourcesProvider),
selectT
)
);
}
private void setColor(int color) {
PorterDuffColorFilter colorFilter = new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY);
if (imageView != null) {
imageView.setColorFilter(colorFilter);
imageView.invalidate();
}
if (lottieDrawable != null) {
lottieDrawable.setColorFilter(colorFilter);
invalidate();
}
}
}
private class EmojiTabsView extends ScrollableHorizontalScrollView {
public int id;
private LinearLayout contentView;
public EmojiTabsView(Context context) {
super(context);
setSmoothScrollingEnabled(true);
setHorizontalScrollBarEnabled(false);
setVerticalScrollBarEnabled(false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setNestedScrollingEnabled(true);
}
contentView = new LinearLayout(context) {
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int x = getPaddingLeft(), cy = (b - t) / 2;
for (int i = 0; i < getChildCount(); ++i) {
View child = getChildAt(i);
if (child == settingsTab) {
continue;
}
if (child != null) {
child.layout(x, cy - child.getMeasuredHeight() / 2, x + child.getMeasuredWidth(), cy + child.getMeasuredHeight() / 2);
x += child.getMeasuredWidth() + AndroidUtilities.dp(2);
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(
Math.max(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp((30 + 2) * contentView.getChildCount()), MeasureSpec.EXACTLY)),
heightMeasureSpec
);
}
};
contentView.setOrientation(LinearLayout.HORIZONTAL);
addView(contentView, new LayoutParams(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT));
for (int i = 0; i < emojiTabsDrawableIds.length; ++i) {
contentView.addView(new EmojiTabButton(context, emojiTabsDrawableIds[i], emojiTabsAnimatedDrawableIds[i], true, false) {
@Override
public boolean onTouchEvent(MotionEvent ev) {
intercept(ev);
return super.onTouchEvent(ev);
}
});
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(
MeasureSpec.makeMeasureSpec(AndroidUtilities.lerp(AndroidUtilities.dp(30), maxWidth(), showT), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(30), MeasureSpec.EXACTLY)
);
}
public int maxWidth() {
return AndroidUtilities.dp((30 + 2) * Math.min(5.7f, contentView.getChildCount()));
}
private boolean touching = false;
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (Math.abs(t - oldt) < 2 || t >= getMeasuredHeight() || t == 0) {
if (!touching) {
EmojiTabsStrip.this.requestDisallowInterceptTouchEvent(false);
}
}
updateButtonsVisibility();
}
private void updateButtonsVisibility() {
final int count = contentView.getChildCount();
for (int i = 0; i < count; ++i) {
View child = contentView.getChildAt(i);
if (child instanceof EmojiTabButton) {
((EmojiTabButton) child).updateVisibilityInbounds(child.getRight() - getScrollX() > 0 && child.getLeft() - getScrollX() < getMeasuredWidth(), scrollingAnimation && !(showAnimator != null && showAnimator.isRunning()));
}
}
}
private void intercept(MotionEvent ev) {
if (shown && !scrollingAnimation) {
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
touching = false;
break;
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
touching = true;
if (!scrollingAnimation) {
resetScrollTo();
}
EmojiTabsStrip.this.requestDisallowInterceptTouchEvent(true);
break;
}
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
intercept(ev);
return super.onTouchEvent(ev);
}
private boolean shown = false;
private float showT = 0f;
private ValueAnimator showAnimator;
public void show(boolean show, boolean animated) {
if (show == shown) {
return;
}
shown = show;
if (!show) {
scrollTo(0);
}
if (showAnimator != null) {
showAnimator.cancel();
}
if (animated) {
showAnimator = ValueAnimator.ofFloat(showT, show ? 1f : 0f);
showAnimator.addUpdateListener(a -> {
showT = (float) a.getAnimatedValue();
invalidate();
requestLayout();
updateButtonsVisibility();
EmojiTabsStrip.this.contentView.invalidate();
});
showAnimator.setDuration(475);
showAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT);
showAnimator.start();
} else {
showT = show ? 1f : 0f;
invalidate();
requestLayout();
updateButtonsVisibility();
EmojiTabsStrip.this.contentView.invalidate();
}
}
}
}
class ScrollableHorizontalScrollView extends HorizontalScrollView {
public ScrollableHorizontalScrollView(Context context) {
super(context);
}
protected boolean scrollingAnimation;
public boolean isScrolling() {
return scrollingAnimation;
}
public boolean scrollToVisible(int left, int right) {
if (getChildCount() <= 0) {
return false;
}
final int padding = AndroidUtilities.dp(50);
int to;
if (left < getScrollX() + padding) {
to = left - padding;
} else if (right > getScrollX() + (getMeasuredWidth() - padding)) {
to = right - getMeasuredWidth() + padding;
} else {
return false;
}
scrollTo(MathUtils.clamp(to, 0, getChildAt(0).getMeasuredWidth() - getMeasuredWidth()));
return true;
}
private int scrollingTo = -1;
private ValueAnimator scrollAnimator;
public void scrollTo(int x) {
if (scrollingTo == x) {
return;
}
scrollingTo = x;
if (scrollAnimator != null) {
scrollAnimator.cancel();
}
if (this.getScrollX() == x) {
return;
}
scrollAnimator = ValueAnimator.ofFloat(this.getScrollX(), x);
scrollAnimator.addUpdateListener(a -> this.setScrollX((int) (float) a.getAnimatedValue()));
scrollAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT);
scrollAnimator.setDuration(250);
scrollAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
scrollingAnimation = false;
}
@Override
public void onAnimationStart(Animator animation) {
scrollingAnimation = true;
if (getParent() instanceof HorizontalScrollView) {
((HorizontalScrollView) getParent()).requestDisallowInterceptTouchEvent(false);
}
}
});
scrollAnimator.start();
}
public void resetScrollTo() {
scrollingTo = -1;
}
}