Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom Emoji: fixes #486

Merged
merged 21 commits into from Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -21,6 +21,7 @@
import android.graphics.drawable.Drawable;
import android.view.View;

import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

Expand All @@ -31,6 +32,7 @@
import org.thunderdog.challegram.loader.ImageReceiver;
import org.thunderdog.challegram.loader.gif.GifFile;
import org.thunderdog.challegram.loader.gif.GifReceiver;
import org.thunderdog.challegram.theme.PorterDuffColorId;
import org.thunderdog.challegram.tool.Drawables;

import me.vkryl.core.lambda.Destroyable;
Expand Down Expand Up @@ -114,6 +116,28 @@ public void setAlpha (int i) {
}
}

public void setThemedPorterDuffColorId (@PorterDuffColorId int colorId) {
gifReceiver.setThemedPorterDuffColorId(colorId);
imageReceiver.setThemedPorterDuffColorId(colorId);
if (drawable != null) {
drawable.setColorFilter(imageReceiver.getBitmapPaint().getColorFilter());
}
}
public void setPorterDuffColorFilter (@ColorInt int color) {
gifReceiver.setPorterDuffColorFilter(color);
imageReceiver.setPorterDuffColorFilter(color);
if (drawable != null) {
drawable.setColorFilter(imageReceiver.getBitmapPaint().getColorFilter());
}
}
public void disablePorterDuffColorFilter () {
gifReceiver.disablePorterDuffColorFilter();
imageReceiver.disablePorterDuffColorFilter();
if (drawable != null) {
drawable.setColorFilter(null);
}
}

@Override
public void setColorFilter (@Nullable ColorFilter colorFilter) {

Expand Down
Expand Up @@ -18,17 +18,20 @@
import android.graphics.Rect;
import android.view.View;

import androidx.annotation.ColorInt;

import org.thunderdog.challegram.charts.CubicBezierInterpolator;
import org.thunderdog.challegram.tool.Paints;
import org.thunderdog.challegram.theme.PorterDuffColorId;
import org.thunderdog.challegram.tool.Screen;
import org.thunderdog.challegram.tool.Views;

import java.util.ArrayList;

import kotlin.random.Random;
import me.vkryl.core.MathUtils;
import me.vkryl.core.lambda.Destroyable;

public class AnimatedEmojiEffect {
public class AnimatedEmojiEffect implements Destroyable {

public AnimatedEmojiDrawable animatedEmojiDrawable;
Rect bounds = new Rect();
Expand All @@ -40,23 +43,23 @@ public class AnimatedEmojiEffect {
boolean longAnimation;
boolean firsDraw = true;

private AnimatedEmojiEffect(AnimatedEmojiDrawable animatedEmojiDrawable, boolean longAnimation) {
private AnimatedEmojiEffect (AnimatedEmojiDrawable animatedEmojiDrawable, boolean longAnimation) {
this.animatedEmojiDrawable = animatedEmojiDrawable;
this.longAnimation = longAnimation;
startTime = System.currentTimeMillis();
}

public static AnimatedEmojiEffect createFrom(AnimatedEmojiDrawable animatedEmojiDrawable, boolean longAnimation) {
public static AnimatedEmojiEffect createFrom (AnimatedEmojiDrawable animatedEmojiDrawable, boolean longAnimation) {
return new AnimatedEmojiEffect(animatedEmojiDrawable, longAnimation);
}

public void setBounds(int l, int t, int r, int b) {
public void setBounds (int l, int t, int r, int b) {
bounds.set(l, t, r, b);
}

long lastGenerateTime;

public void draw(Canvas canvas) {
public void draw (Canvas canvas) {
if (!longAnimation) {
if (firsDraw) {
for (int i = 0; i < 7; i++) {
Expand Down Expand Up @@ -90,19 +93,34 @@ public void draw(Canvas canvas) {
firsDraw = false;
}

public boolean done() {
public boolean done () {
return System.currentTimeMillis() - startTime > 2500;
}

public void setView(View view) {
public void setView (View view) {
animatedEmojiDrawable.attach();
parentView = view;
}

public void removeView() {
public void removeView () {
animatedEmojiDrawable.detach();
}

@Override
public void performDestroy () {
animatedEmojiDrawable.performDestroy();
}

public void setThemedPorterDuffColorId (@PorterDuffColorId int colorId) {
animatedEmojiDrawable.setThemedPorterDuffColorId(colorId);
}
public void setPorterDuffColorFilter (@ColorInt int color) {
animatedEmojiDrawable.setPorterDuffColorFilter(color);
}
public void disablePorterDuffColorFilter () {
animatedEmojiDrawable.disablePorterDuffColorFilter();
}

private class Particle {
float fromX, fromY;
float toX;
Expand All @@ -116,7 +134,7 @@ private class Particle {
boolean mirror;
float randomRotation;

public void generate() {
public void generate () {
progress = 0;
float bestDistance = 0;
float bestX = randX();
Expand Down Expand Up @@ -155,8 +173,6 @@ public void generate() {

fromY = bounds.height() * 0.45f + bounds.height() * 0.1f * (Math.abs(Random.Default.nextInt() % 100) / 100f);



if (longAnimation) {
fromSize = bounds.width() * 0.05f + bounds.width() * 0.1f * (Math.abs(Random.Default.nextInt() % 100) / 100f);
toSize = fromSize * (1.5f + 1.5f * (Math.abs(Random.Default.nextInt() % 100) / 100f));
Expand All @@ -175,23 +191,23 @@ public void generate() {
randomRotation = 20 * ((Random.Default.nextInt() % 100) / 100f);
}

private float randY() {
private float randY () {
return (bounds.height() * 0.5f * (Math.abs(Random.Default.nextInt() % 100) / 100f));
}

private long randDuration() {
private long randDuration () {
return 1000 + Math.abs(Random.Default.nextInt() % 900);
}

private float randX() {
private float randX () {
if (longAnimation) {
return bounds.width() * -0.25f + bounds.width() * 1.5f * (Math.abs(Random.Default.nextInt() % 100) / 100f);
} else {
return bounds.width() * (Math.abs(Random.Default.nextInt() % 100) / 100f);
}
}

public void draw(Canvas canvas) {
public void draw (Canvas canvas) {
progress += (float) Math.min(40, 1000f / Screen.refreshRate()) / duration;
progress = MathUtils.clamp(progress);
float progressInternal = CubicBezierInterpolator.EASE_OUT.getInterpolation(progress);
Expand Down
@@ -0,0 +1,28 @@
/*
* This file is a part of Telegram X
* Copyright © 2014 (tgx-android@pm.me)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* File created on 24/10/2023 at 02:14
*/
package org.thunderdog.challegram.data;

import androidx.annotation.IntDef;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.SOURCE)
@IntDef({
EmojiMessageContentType.NOT_EMOJI, EmojiMessageContentType.ANIMATED_EMOJI, EmojiMessageContentType.NON_BUBBLE_EMOJI
})
public @interface EmojiMessageContentType {
int NOT_EMOJI = 0, ANIMATED_EMOJI = 1, NON_BUBBLE_EMOJI = 2;
}
11 changes: 11 additions & 0 deletions app/src/main/java/org/thunderdog/challegram/data/TD.java
Expand Up @@ -5442,4 +5442,15 @@ public static boolean hasCustomEmoji (TdApi.FormattedText text) {

return false;
}

public static boolean isStickerFromAnimatedEmojiPack (@Nullable TdApi.MessageContent content) {
if (content == null || content.getConstructor() != TdApi.MessageAnimatedEmoji.CONSTRUCTOR) {
return false;
}
return isStickerFromAnimatedEmojiPack(((TdApi.MessageAnimatedEmoji) content).animatedEmoji.sticker);
}

public static boolean isStickerFromAnimatedEmojiPack (@Nullable TdApi.Sticker sticker) {
return sticker != null && sticker.setId == TdConstants.TELEGRAM_ANIMATED_EMOJI_STICKER_SET_ID;
}
}
75 changes: 68 additions & 7 deletions app/src/main/java/org/thunderdog/challegram/data/TGMessage.java
Expand Up @@ -157,6 +157,7 @@
import me.vkryl.td.ChatId;
import me.vkryl.td.MessageId;
import me.vkryl.td.Td;
import me.vkryl.td.TdConstants;

public abstract class TGMessage implements InvalidateContentProvider, TdlibDelegate, FactorAnimator.Target, Comparable<TGMessage>, Counter.Callback, TGAvatars.Callback, TranslationsManager.Translatable {
private static final int MAXIMUM_CHANNEL_MERGE_TIME_DIFF = 150;
Expand Down Expand Up @@ -2009,7 +2010,8 @@ public final void draw (MessageView view, Canvas c, @NonNull AvatarReceiver avat
hAuthorEmojiStatus.draw(c, left + hAuthorNameT.getWidth() + Screen.dp(3), newTop, 1f, view.getEmojiStatusReceiver());
}
if (sender.hasChatMark() && hAuthorChatMark != null) {
int cmLeft = left + hAuthorNameT.getWidth() + Screen.dp(6f);
int cmLeft = left + hAuthorNameT.getWidth() + Screen.dp(3f)
+ (hAuthorEmojiStatus != null ? hAuthorEmojiStatus.getWidth(Screen.dp(3)) : 0);
RectF rct = Paints.getRectF();
rct.set(cmLeft, newTop, cmLeft + hAuthorChatMark.getWidth() + Screen.dp(8f), newTop + hAuthorNameT.getLineHeight(false));
c.drawRoundRect(rct, Screen.dp(2f), Screen.dp(2f), Paints.getProgressPaint(Theme.getColor(ColorId.textNegative), Screen.dp(1.5f)));
Expand Down Expand Up @@ -7661,11 +7663,38 @@ public static TGMessage valueOf (MessagesManager context, TdApi.Message msg) {
}

@Nullable
private static TGMessage checkPendingContent (MessagesManager context, TdApi.Message msg, TdApi.MessageContent oldContent, @Nullable TdApi.MessageContent pendingContent, boolean allowCustomEmoji) {
if (pendingContent == null) {
private static TGMessage checkPendingContent (MessagesManager context, TdApi.Message msg, TdApi.MessageContent oldContent, @Nullable TdApi.MessageContent pendingContent, boolean allowAnimatedEmoji, boolean allowNonBubbleEmoji) {
if (pendingContent == null || oldContent.getConstructor() != TdApi.MessageAnimatedEmoji.CONSTRUCTOR && oldContent.getConstructor() != TdApi.MessageText.CONSTRUCTOR) {
return null;
}

final @EmojiMessageContentType int emojiPendingContentType = getEmojiMessageContentType(pendingContent, allowAnimatedEmoji, allowNonBubbleEmoji);
if (emojiPendingContentType == EmojiMessageContentType.NOT_EMOJI) {
final TdApi.MessageText oldMessageText;
if (oldContent.getConstructor() == TdApi.MessageAnimatedEmoji.CONSTRUCTOR) {
TdApi.MessageAnimatedEmoji oldEmoji = nonNull((TdApi.MessageAnimatedEmoji) oldContent);
oldMessageText = new TdApi.MessageText(Td.textOrCaption(oldEmoji), null);
} else if (oldContent.getConstructor() == TdApi.MessageText.CONSTRUCTOR) {
oldMessageText = nonNull((TdApi.MessageText) oldContent);
} else {
throw new IllegalArgumentException("Wrong content type");
}

final TdApi.MessageText newMessageText;
if (pendingContent.getConstructor() == TdApi.MessageAnimatedEmoji.CONSTRUCTOR) {
TdApi.MessageAnimatedEmoji newEmoji = nonNull((TdApi.MessageAnimatedEmoji) pendingContent);
newMessageText = new TdApi.MessageText(Td.textOrCaption(newEmoji), null);
} else if (pendingContent.getConstructor() == TdApi.MessageText.CONSTRUCTOR) {
newMessageText = (TdApi.MessageText) pendingContent;
} else {
throw new IllegalArgumentException("Wrong content type");
}

return new TGMessageText(context, msg, oldMessageText, newMessageText);
} else {
return new TGMessageSticker(context, msg, oldContent, pendingContent);
}
/*
final TdApi.MessageText pendingMessageText = pendingContent.getConstructor() == TdApi.MessageText.CONSTRUCTOR ?
((TdApi.MessageText) pendingContent) : null;
final TdApi.MessageAnimatedEmoji pendingMessageEmoji = pendingContent.getConstructor() == TdApi.MessageAnimatedEmoji.CONSTRUCTOR ?
Expand All @@ -7690,6 +7719,7 @@ private static TGMessage checkPendingContent (MessagesManager context, TdApi.Mes
}

return null;
*/
}

public static TGMessage valueOf (MessagesManager context, TdApi.Message msg, TdApi.MessageContent content) {
Expand All @@ -7711,26 +7741,27 @@ public static TGMessage valueOf (MessagesManager context, TdApi.Message msg, TdA

int unsupportedStringRes = R.string.UnsupportedMessage;

final boolean allowEmoji = !Settings.instance().getNewSetting(Settings.SETTING_FLAG_NO_ANIMATED_EMOJI);
final boolean allowAnimatedEmoji = !Settings.instance().getNewSetting(Settings.SETTING_FLAG_NO_ANIMATED_EMOJI);
final boolean allowNonBubbleEmoji = Settings.instance().useBigEmoji();
final TdApi.MessageContent pendingContent = tdlib.getPendingMessageText(msg.chatId, msg.id);

TGMessage message = checkPendingContent(context, msg, content, pendingContent, allowEmoji);
TGMessage message = checkPendingContent(context, msg, content, pendingContent, allowAnimatedEmoji, allowNonBubbleEmoji);
if (message != null) {
return message;
}

switch (content.getConstructor()) {
case TdApi.MessageAnimatedEmoji.CONSTRUCTOR: {
TdApi.MessageAnimatedEmoji emoji = nonNull((TdApi.MessageAnimatedEmoji) content);
if (!allowEmoji) {
if (getEmojiMessageContentType(content, allowAnimatedEmoji, allowNonBubbleEmoji) == EmojiMessageContentType.NOT_EMOJI) {
return new TGMessageText(context, msg, new TdApi.MessageText(Td.textOrCaption(emoji), null), null);
} else {
return new TGMessageSticker(context, msg, emoji, null);
}
}
case TdApi.MessageText.CONSTRUCTOR: {
TdApi.MessageText messageText = nonNull((TdApi.MessageText) content);
if (allowEmoji && NonBubbleEmojiLayout.isValidEmojiText(messageText.text)) {
if (getEmojiMessageContentType(content, allowAnimatedEmoji, allowNonBubbleEmoji) != EmojiMessageContentType.NOT_EMOJI) {
return new TGMessageSticker(context, msg, messageText, null);
}
return new TGMessageText(context, msg, nonNull((TdApi.MessageText) content), null);
Expand Down Expand Up @@ -8498,6 +8529,7 @@ private void startSetReactionAnimationIfReady () {
new ReactionsOverlayView.ReactionInfo(context().reactionsOverlayManager())
.setSticker(nextSetReactionAnimation.reaction.staticCenterAnimationSicker(), false)
.setAnimationEndListener(this::onQuickReactionAnimationFinish)
.setRepaintingColorIds(ColorId.text, ColorId.text)
.setAnimatedPosition(
new Point(startX, startY),
new Point(finishX, finishY),
Expand All @@ -8515,6 +8547,7 @@ private void startSetReactionAnimationIfReady () {
new ReactionsOverlayView.ReactionInfo(context().reactionsOverlayManager())
.setSticker(nextSetReactionAnimation.reaction.staticCenterAnimationSicker(), false)
.setAnimationEndListener(this::onQuickReactionAnimationFinish)
.setRepaintingColorIds(ColorId.text, ColorId.text)
.setAnimatedPosition(
new Point(startX, startY),
new Point(finishX, finishY),
Expand Down Expand Up @@ -8636,6 +8669,7 @@ public void startReactionBubbleAnimation (TdApi.ReactionType reactionType) {
context().reactionsOverlayManager().addOverlay(
new ReactionsOverlayView.ReactionInfo(context().reactionsOverlayManager())
.setSticker(overlaySticker, true)
.setRepaintingColorIds(ColorId.text, ColorId.text)
.setUseDefaultSprayAnimation(tgReaction.isCustom())
.setEmojiStatusEffect(tgReaction.isCustom() ? tgReaction.newCenterAnimationSicker() : null)
.setPosition(new Point(bubbleX, bubbleY), Screen.dp(90))
Expand Down Expand Up @@ -9190,4 +9224,31 @@ public String getSponsoredMessageUrl () {

return null;
}

/* * */

public static @EmojiMessageContentType int getEmojiMessageContentType (TdApi.MessageContent content) {
final boolean allowAnimatedEmoji = !Settings.instance().getNewSetting(Settings.SETTING_FLAG_NO_ANIMATED_EMOJI);
final boolean allowNonBubbleEmoji = Settings.instance().useBigEmoji();
return getEmojiMessageContentType(content, allowAnimatedEmoji, allowNonBubbleEmoji);
}

public static @EmojiMessageContentType int getEmojiMessageContentType (TdApi.MessageContent content, boolean allowAnimatedEmoji, boolean allowNonBubbleEmoji) {
if (content == null) {
return EmojiMessageContentType.NOT_EMOJI;
}

if (content.getConstructor() == TdApi.MessageAnimatedEmoji.CONSTRUCTOR) {
if (allowAnimatedEmoji && TD.isStickerFromAnimatedEmojiPack(content)) {
return EmojiMessageContentType.ANIMATED_EMOJI;
} else if (allowNonBubbleEmoji) {
return EmojiMessageContentType.NON_BUBBLE_EMOJI;
}
} else if (content.getConstructor() == TdApi.MessageText.CONSTRUCTOR) {
if (allowNonBubbleEmoji && NonBubbleEmojiLayout.isValidEmojiText(((TdApi.MessageText) content).text)) {
return EmojiMessageContentType.NON_BUBBLE_EMOJI;
}
}
return EmojiMessageContentType.NOT_EMOJI;
}
}