NekoX/TMessagesProj/src/main/java/org/telegram/ui/EmojiAnimationsOverlay.java

453 lines
20 KiB
Java
Raw Normal View History

2021-09-20 00:10:42 +02:00
package org.telegram.ui;
import android.graphics.Canvas;
import android.util.Log;
import android.view.HapticFeedbackConstants;
import android.view.View;
import android.widget.FrameLayout;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.Emoji;
import org.telegram.messenger.EmojiData;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.ImageLocation;
import org.telegram.messenger.ImageReceiver;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.MediaDataController;
import org.telegram.messenger.MessagesController;
import org.telegram.messenger.NotificationCenter;
import org.telegram.messenger.R;
import org.telegram.messenger.SharedConfig;
import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.Cells.ChatMessageCell;
import org.telegram.ui.Components.Bulletin;
import org.telegram.ui.Components.BulletinFactory;
import org.telegram.ui.Components.RecyclerListView;
import org.telegram.ui.Components.StickerSetBulletinLayout;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Random;
import tw.nekomimi.nekogram.NekoConfig;
2021-09-20 00:10:42 +02:00
public class EmojiAnimationsOverlay implements NotificationCenter.NotificationCenterDelegate {
private final int ANIMATION_JSON_VERSION = 1;
private final String INTERACTIONS_STICKER_PACK = "EmojiAnimations";
ChatActivity chatActivity;
int currentAccount;
TLRPC.TL_messages_stickerSet set;
boolean inited = false;
HashMap<String, ArrayList<TLRPC.Document>> emojiInteractionsStickersMap = new HashMap<>();
HashMap<Long, Integer> lastAnimationIndex = new HashMap<>();
Random random = new Random();
private boolean attached;
int lastTappedMsgId = -1;
long lastTappedTime = 0;
String lastTappedEmoji;
ArrayList<Long> timeIntervals = new ArrayList<>();
ArrayList<Integer> animationIndexes = new ArrayList<>();
Runnable sentInteractionsRunnable;
private final static HashSet<String> supportedEmoji = new HashSet<>();
private final static HashSet<String> excludeEmojiFromPack = new HashSet<>();
static {
// 1⃣, 2⃣, 3⃣... etc
excludeEmojiFromPack.add("\u0030\u20E3");
excludeEmojiFromPack.add("\u0031\u20E3");
excludeEmojiFromPack.add("\u0032\u20E3");
excludeEmojiFromPack.add("\u0033\u20E3");
excludeEmojiFromPack.add("\u0034\u20E3");
excludeEmojiFromPack.add("\u0035\u20E3");
excludeEmojiFromPack.add("\u0036\u20E3");
excludeEmojiFromPack.add("\u0037\u20E3");
excludeEmojiFromPack.add("\u0038\u20E3");
excludeEmojiFromPack.add("\u0039\u20E3");
}
ArrayList<DrawingObject> drawingObjects = new ArrayList<>();
FrameLayout contentLayout;
RecyclerListView listView;
long dialogId;
int threadMsgId;
public EmojiAnimationsOverlay(ChatActivity chatActivity, FrameLayout frameLayout, RecyclerListView chatListView, int currentAccount, long dialogId, int threadMsgId) {
this.chatActivity = chatActivity;
this.contentLayout = frameLayout;
this.listView = chatListView;
this.currentAccount = currentAccount;
this.dialogId = dialogId;
this.threadMsgId = threadMsgId;
}
protected void onAttachedToWindow() {
attached = true;
checkStickerPack();
NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.diceStickersDidLoad);
NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.onEmojiInteractionsReceived);
}
protected void onDetachedFromWindow() {
attached = false;
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.diceStickersDidLoad);
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.onEmojiInteractionsReceived);
}
public void checkStickerPack() {
if (inited) {
return;
}
set = MediaDataController.getInstance(currentAccount).getStickerSetByName(INTERACTIONS_STICKER_PACK);
if (set == null) {
set = MediaDataController.getInstance(currentAccount).getStickerSetByEmojiOrName(INTERACTIONS_STICKER_PACK);
}
if (set == null) {
MediaDataController.getInstance(currentAccount).loadStickersByEmojiOrName(INTERACTIONS_STICKER_PACK, false, true);
}
if (set != null) {
HashMap<Long, TLRPC.Document> stickersMap = new HashMap<>();
for (int i = 0; i < set.documents.size(); i++) {
stickersMap.put(set.documents.get(i).id, set.documents.get(i));
}
for (int i = 0; i < set.packs.size(); i++) {
TLRPC.TL_stickerPack pack = set.packs.get(i);
if (!excludeEmojiFromPack.contains(pack.emoticon) && pack.documents.size() > 0) {
supportedEmoji.add(pack.emoticon);
ArrayList<TLRPC.Document> stickers = new ArrayList<>();
emojiInteractionsStickersMap.put(pack.emoticon, stickers);
for (int j = 0; j < pack.documents.size(); j++) {
stickers.add(stickersMap.get(pack.documents.get(j)));
}
if (pack.emoticon.equals("")) {
String[] heartEmojies = new String[]{"🧡", "💛", "💚", "💙", "💜", "🖤", "🤍", "🤎"};
for (String heart : heartEmojies) {
supportedEmoji.add(heart);
emojiInteractionsStickersMap.put(heart, stickers);
}
}
}
}
inited = true;
}
}
@Override
public void didReceivedNotification(int id, int account, Object... args) {
if (id == NotificationCenter.diceStickersDidLoad) {
String name = (String) args[0];
if (INTERACTIONS_STICKER_PACK.equals(name)) {
checkStickerPack();
}
} else if (id == NotificationCenter.onEmojiInteractionsReceived) {
if (NekoConfig.disableRemoteEmojiInteractions)
return;
2021-09-20 00:10:42 +02:00
long dialogId = (long) args[0];
TLRPC.TL_sendMessageEmojiInteraction action = (TLRPC.TL_sendMessageEmojiInteraction) args[1];
if (dialogId == this.dialogId && supportedEmoji.contains(action.emoticon)) {
int messageId = action.msg_id;
if (action.interaction.data != null) {
try {
JSONObject jsonObject = new JSONObject(action.interaction.data);
JSONArray array = jsonObject.getJSONArray("a");
for (int i = 0; i < array.length(); i++) {
JSONObject actionObject = array.getJSONObject(i);
int animation = actionObject.optInt("i", 1) - 1;
double time = actionObject.optDouble("t", 0.0);
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
findViewAndShowAnimation(messageId, animation);
}
}, (long) (time * 1000));
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
}
private void findViewAndShowAnimation(int messageId, int animation) {
if (!attached) {
return;
}
ChatMessageCell bestView = null;
for (int i = 0; i < listView.getChildCount(); i++) {
View child = listView.getChildAt(i);
if (child instanceof ChatMessageCell) {
ChatMessageCell cell = (ChatMessageCell) child;
if (cell.getPhotoImage().hasNotThumb() && cell.getMessageObject().getStickerEmoji() != null) {
if (cell.getMessageObject().getId() == messageId) {
bestView = cell;
break;
}
}
}
}
if (bestView != null) {
chatActivity.restartSticker(bestView);
if (!EmojiData.hasEmojiSupportVibration(bestView.getMessageObject().getStickerEmoji())) {
bestView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
}
showAnimationForCell(bestView, animation, false, true);
}
}
public void draw(Canvas canvas) {
if (!drawingObjects.isEmpty()) {
for (int i = 0; i < drawingObjects.size(); i++) {
DrawingObject drawingObject = drawingObjects.get(i);
drawingObject.viewFound = false;
for (int k = 0; k < listView.getChildCount(); k++) {
View child = listView.getChildAt(k);
if (child instanceof ChatMessageCell) {
ChatMessageCell cell = (ChatMessageCell) child;
if (cell.getMessageObject().getId() == drawingObject.messageId) {
drawingObject.viewFound = true;
float viewX = listView.getX() + child.getX() + cell.getPhotoImage().getImageX();
float viewY = listView.getY() + child.getY() + cell.getPhotoImage().getImageY();
if (drawingObject.isOut) {
viewX += -cell.getPhotoImage().getImageWidth() * 2 + AndroidUtilities.dp(24);
} else {
viewX += -AndroidUtilities.dp(24);
}
viewY -= cell.getPhotoImage().getImageWidth();
drawingObject.lastX = viewX;
drawingObject.lastY = viewY;
drawingObject.lastW = cell.getPhotoImage().getImageWidth();
break;
}
}
}
drawingObject.imageReceiver.setImageCoords(drawingObject.lastX + drawingObject.randomOffsetX, drawingObject.lastY + drawingObject.randomOffsetY, drawingObject.lastW * 3, drawingObject.lastW * 3);
if (!drawingObject.isOut) {
canvas.save();
canvas.scale(-1f, 1, drawingObject.imageReceiver.getCenterX(), drawingObject.imageReceiver.getCenterY());
drawingObject.imageReceiver.draw(canvas);
canvas.restore();
} else {
drawingObject.imageReceiver.draw(canvas);
}
if (drawingObject.wasPlayed && drawingObject.imageReceiver.getLottieAnimation() != null && drawingObject.imageReceiver.getLottieAnimation().getCurrentFrame() == drawingObject.imageReceiver.getLottieAnimation().getFramesCount() - 2) {
drawingObjects.remove(i);
i--;
} else if (drawingObject.imageReceiver.getLottieAnimation() != null && drawingObject.imageReceiver.getLottieAnimation().isRunning()) {
drawingObject.wasPlayed = true;
} else if (drawingObject.imageReceiver.getLottieAnimation() != null && !drawingObject.imageReceiver.getLottieAnimation().isRunning()) {
drawingObject.imageReceiver.getLottieAnimation().setCurrentFrame(0, true);
drawingObject.imageReceiver.getLottieAnimation().start();
}
}
contentLayout.invalidate();
}
}
public void onTapItem(ChatMessageCell view, ChatActivity chatActivity) {
if (chatActivity.currentUser == null || chatActivity.isSecretChat()) {
return;
}
boolean show = showAnimationForCell(view, -1, true, false);
if (show && !EmojiData.hasEmojiSupportVibration(view.getMessageObject().getStickerEmoji())) {
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
}
if ((Bulletin.getVisibleBulletin() == null || !Bulletin.getVisibleBulletin().isShowing()) && SharedConfig.emojiInteractionsHintCount > 0) {
SharedConfig.updateEmojiInteractionsHintCount(SharedConfig.emojiInteractionsHintCount - 1);
TLRPC.Document document = MediaDataController.getInstance(currentAccount).getEmojiAnimatedSticker(view.getMessageObject().getStickerEmoji());
StickerSetBulletinLayout layout = new StickerSetBulletinLayout(chatActivity.getParentActivity(), null, StickerSetBulletinLayout.TYPE_EMPTY, document, chatActivity.getResourceProvider());
layout.subtitleTextView.setVisibility(View.GONE);
layout.titleTextView.setText(AndroidUtilities.replaceTags(LocaleController.formatString("EmojiInteractionTapHint", R.string.EmojiInteractionTapHint, chatActivity.currentUser.first_name)));
layout.titleTextView.setTypeface(null);
layout.titleTextView.setMaxLines(3);
layout.titleTextView.setSingleLine(false);
Bulletin.make(chatActivity, layout, Bulletin.DURATION_LONG).show();
}
}
private boolean showAnimationForCell(ChatMessageCell view, int animation, boolean sendTap, boolean sendSeen) {
if (drawingObjects.size() > 12) {
return false;
}
if (!view.getPhotoImage().hasNotThumb()) {
return false;
}
String emoji = view.getMessageObject().getStickerEmoji();
if (emoji == null) {
return false;
}
float imageH = view.getPhotoImage().getImageHeight();
float imageW = view.getPhotoImage().getImageWidth();
if (imageH <= 0 || imageW <= 0) {
return false;
}
if (supportedEmoji.contains(emoji)) {
ArrayList<TLRPC.Document> arrayList = emojiInteractionsStickersMap.get(view.getMessageObject().getStickerEmoji());
if (arrayList != null && !arrayList.isEmpty()) {
int sameAnimationsCount = 0;
for (int i = 0; i < drawingObjects.size(); i++) {
if (drawingObjects.get(i).messageId == view.getMessageObject().getId()) {
sameAnimationsCount++;
if (drawingObjects.get(i).imageReceiver.getLottieAnimation() == null || drawingObjects.get(i).imageReceiver.getLottieAnimation().isGeneratingCache()) {
return false;
}
}
}
if (sameAnimationsCount >= 4) {
return false;
}
if (animation < 0 || animation > arrayList.size() - 1) {
animation = Math.abs(random.nextInt()) % arrayList.size();
}
TLRPC.Document document = arrayList.get(animation);
DrawingObject drawingObject = new DrawingObject();
drawingObject.randomOffsetX = imageW / 4 * ((random.nextInt() % 101) / 100f);
drawingObject.randomOffsetY = imageH / 4 * ((random.nextInt() % 101) / 100f);
drawingObject.messageId = view.getMessageObject().getId();
drawingObject.document = document;
drawingObject.isOut = view.getMessageObject().isOutOwner();
Integer lastIndex = lastAnimationIndex.get(document.id);
int currentIndex = lastIndex == null ? 0 : lastIndex;
lastAnimationIndex.put(document.id, (currentIndex + 1) % 4);
ImageLocation imageLocation = ImageLocation.getForDocument(document);
drawingObject.imageReceiver.setUniqKeyPrefix(currentIndex + "_" + drawingObject.messageId + "_");
int w = (int) (2f * imageW / AndroidUtilities.density);
drawingObject.imageReceiver.setImage(imageLocation, w + "_" + w + "_pcache", null, "tgs", set, 1);
drawingObject.imageReceiver.setLayerNum(Integer.MAX_VALUE);
drawingObject.imageReceiver.setAllowStartAnimation(true);
drawingObject.imageReceiver.setAutoRepeat(0);
if (drawingObject.imageReceiver.getLottieAnimation() != null) {
drawingObject.imageReceiver.getLottieAnimation().start();
}
drawingObjects.add(drawingObject);
drawingObject.imageReceiver.onAttachedToWindow();
drawingObject.imageReceiver.setParentView(contentLayout);
contentLayout.invalidate();
if (sendTap) {
if (lastTappedMsgId != 0 && lastTappedMsgId != view.getMessageObject().getId()) {
if (sentInteractionsRunnable != null) {
AndroidUtilities.cancelRunOnUIThread(sentInteractionsRunnable);
sentInteractionsRunnable.run();
}
}
lastTappedMsgId = view.getMessageObject().getId();
lastTappedEmoji = emoji;
if (lastTappedTime == 0) {
lastTappedTime = System.currentTimeMillis();
timeIntervals.clear();
animationIndexes.clear();;
timeIntervals.add(0L);
animationIndexes.add(animation);
} else {
timeIntervals.add(System.currentTimeMillis() - lastTappedTime);
animationIndexes.add(animation);
}
if (sentInteractionsRunnable != null) {
AndroidUtilities.cancelRunOnUIThread(sentInteractionsRunnable);
sentInteractionsRunnable = null;
}
AndroidUtilities.runOnUIThread(sentInteractionsRunnable = () -> {
sendCurrentTaps();
sentInteractionsRunnable = null;
}, 500);
}
if (sendSeen) {
MessagesController.getInstance(currentAccount).sendTyping(dialogId, threadMsgId, 11, emoji, 0);
}
return true;
}
}
return false;
}
private void sendCurrentTaps() {
if (lastTappedMsgId == 0) {
return;
}
TLRPC.TL_sendMessageEmojiInteraction interaction = new TLRPC.TL_sendMessageEmojiInteraction();
interaction.msg_id = lastTappedMsgId;
interaction.emoticon = lastTappedEmoji;
interaction.interaction = new TLRPC.TL_dataJSON();
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("v", ANIMATION_JSON_VERSION);
JSONArray array = new JSONArray();
for (int i = 0; i < timeIntervals.size(); i++) {
JSONObject action = new JSONObject();
action.put("i", animationIndexes.get(i) + 1);
action.put("t", timeIntervals.get(i) / 1000f);
array.put(i, action);
}
jsonObject.put("a", array);
} catch (JSONException e) {
clearSendingInfo();
FileLog.e(e);
return;
}
interaction.interaction.data = jsonObject.toString();
TLRPC.TL_messages_setTyping req = new TLRPC.TL_messages_setTyping();
if (threadMsgId != 0) {
req.top_msg_id = threadMsgId;
req.flags |= 1;
}
req.action = interaction;
req.peer = MessagesController.getInstance(currentAccount).getInputPeer(dialogId);
ConnectionsManager.getInstance(currentAccount).sendRequest(req, null);
clearSendingInfo();
}
private void clearSendingInfo() {
lastTappedMsgId = 0;
lastTappedEmoji = null;
lastTappedTime = 0;
timeIntervals.clear();
animationIndexes.clear();
}
public void onScrolled(int dy) {
for (int i = 0; i < drawingObjects.size(); i++) {
if (!drawingObjects.get(i).viewFound) {
drawingObjects.get(i).lastY -= dy;
}
}
}
private class DrawingObject {
public float lastX;
public float lastY;
public boolean viewFound;
public float lastW;
public float randomOffsetX;
public float randomOffsetY;
boolean wasPlayed;
boolean isOut;
int messageId;
TLRPC.Document document;
ImageReceiver imageReceiver = new ImageReceiver();
}
}