Show sponsor badge next to names everywhere + update app icon
- Sponsor heart now appears in the chats list (DialogCell), chat header (ChatAvatarContainer), message author names (ChatMessageCell) and user lists (UserCell), not just on the profile screen - Added ShimmerHeartDrawable.drawStatic() for cheap static rendering in frequently-repainted list cells; header keeps the animated shimmer - Cells request sponsor status via SponsorHelper batch lookup and redraw on sponsorStatusUpdated - Regenerate launcher icon from updated foxigram4.png
|
|
@ -1663,6 +1663,8 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
|||
private boolean drawName;
|
||||
private boolean drawNameLayout;
|
||||
private boolean drawNameAvatar;
|
||||
private boolean drawNameSponsor;
|
||||
private final android.graphics.RectF nameSponsorRect = new android.graphics.RectF();
|
||||
|
||||
private boolean drawTopic;
|
||||
private TopicButton topicButton;
|
||||
|
|
@ -18762,6 +18764,13 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
|||
} else {
|
||||
currentNameString = "";
|
||||
}
|
||||
drawNameSponsor = false;
|
||||
if (!messageObject.isOutOwner() && currentNameStatus == null && currentNameBotVerificationId == 0
|
||||
&& currentUser != null && currentUser.id != 0 && messageObject.isFromUser()
|
||||
&& !UserObject.isUserSelf(currentUser) && !currentUser.verified && !currentUser.bot) {
|
||||
tw.nekomimi.nekogram.helpers.SponsorHelper.requestSponsorStatus(currentUser.id);
|
||||
drawNameSponsor = tw.nekomimi.nekogram.helpers.SponsorHelper.isKnownSponsor(currentUser.id);
|
||||
}
|
||||
int additionalWidth = dp(currentMessageObject.isSponsored() ? -24 : 0);
|
||||
CharSequence nameStringFinal = AndroidUtilities.removeDiacritics(currentNameString.replace('\n', ' ').replace('\u200F', ' '));
|
||||
try {
|
||||
|
|
@ -21910,6 +21919,14 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
|||
Theme.chat_namePaint.setAlpha(oldAlpha);
|
||||
canvas.restore();
|
||||
|
||||
if (drawNameSponsor && viaNameWidth <= 0) {
|
||||
int sz = dp(15);
|
||||
float hx = nx + nameOffsetX + nameLayoutWidth + dp(4);
|
||||
float hy = ny + (nameLayout.getHeight() - sz) / 2f;
|
||||
nameSponsorRect.set(hx, hy, hx + sz, hy + sz);
|
||||
tw.nekomimi.nekogram.helpers.ShimmerHeartDrawable.drawStatic(canvas, nameSponsorRect, (int) (0xFF * nameAlpha));
|
||||
}
|
||||
|
||||
float end;
|
||||
if (currentMessagesGroup != null && !currentMessagesGroup.isDocuments) {
|
||||
int dWidth = getGroupPhotosWidth();
|
||||
|
|
|
|||
|
|
@ -596,6 +596,8 @@ public class DialogCell extends BaseCell implements StoriesListPlaceProvider.Ava
|
|||
private boolean drawVerified;
|
||||
private boolean drawBotVerified;
|
||||
private boolean drawPremium;
|
||||
private boolean drawSponsor;
|
||||
private final android.graphics.RectF sponsorHeartRect = new android.graphics.RectF();
|
||||
private final View emojiStatusView;
|
||||
private final AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable emojiStatus;
|
||||
private final AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable botVerification;
|
||||
|
|
@ -1190,6 +1192,7 @@ public class DialogCell extends BaseCell implements StoriesListPlaceProvider.Ava
|
|||
drawVerified = false;
|
||||
drawBotVerified = false;
|
||||
drawPremium = false;
|
||||
drawSponsor = false;
|
||||
drawForwardIcon = false;
|
||||
drawGiftIcon = false;
|
||||
drawScam = 0;
|
||||
|
|
@ -1418,6 +1421,10 @@ public class DialogCell extends BaseCell implements StoriesListPlaceProvider.Ava
|
|||
emojiStatus.setParticles(false, false);
|
||||
}
|
||||
}
|
||||
if (!drawVerified && !drawPremium && drawScam == 0 && user.id != 0 && !UserObject.isUserSelf(user)) {
|
||||
tw.nekomimi.nekogram.helpers.SponsorHelper.requestSponsorStatus(user.id);
|
||||
drawSponsor = tw.nekomimi.nekogram.helpers.SponsorHelper.isKnownSponsor(user.id);
|
||||
}
|
||||
}
|
||||
if (dialogBotVerificationIcon != 0 && drawBotVerified) {
|
||||
botVerification.set(dialogBotVerificationIcon, false);
|
||||
|
|
@ -2260,6 +2267,13 @@ public class DialogCell extends BaseCell implements StoriesListPlaceProvider.Ava
|
|||
if (LocaleController.isRTL) {
|
||||
nameLeft += w;
|
||||
}
|
||||
} else if (drawSponsor) {
|
||||
int w = dp(6) + dp(18);
|
||||
nameWidth -= w;
|
||||
nameAdditionalsForChannelSubscriber += w;
|
||||
if (LocaleController.isRTL) {
|
||||
nameLeft += w;
|
||||
}
|
||||
}
|
||||
if (drawBotVerified) {
|
||||
nameWidth -= dp(21);
|
||||
|
|
@ -2691,6 +2705,8 @@ public class DialogCell extends BaseCell implements StoriesListPlaceProvider.Ava
|
|||
nameMutedIconLeft = nameMuteLeft - dp(6) - Theme.dialogs_muteDrawable.getIntrinsicWidth();
|
||||
} else if (drawScam != 0) {
|
||||
nameMuteLeft = (int) (nameLeft + (nameWidth - widthpx) - dp(6) - (drawScam == 1 ? Theme.dialogs_scamDrawable : Theme.dialogs_fakeDrawable).getIntrinsicWidth());
|
||||
} else if (drawSponsor) {
|
||||
nameMuteLeft = (int) (nameLeft + (nameWidth - widthpx) - dp(6) - dp(18));
|
||||
} else {
|
||||
nameMuteLeft = (int) (nameLeft + (nameWidth - widthpx) - dp(6) - Theme.dialogs_muteDrawable.getIntrinsicWidth());
|
||||
}
|
||||
|
|
@ -4343,6 +4359,15 @@ public class DialogCell extends BaseCell implements StoriesListPlaceProvider.Ava
|
|||
}
|
||||
setDrawableBounds((drawScam == 1 ? Theme.dialogs_scamDrawable : Theme.dialogs_fakeDrawable), nameMuteLeft, y);
|
||||
(drawScam == 1 ? Theme.dialogs_scamDrawable : Theme.dialogs_fakeDrawable).draw(canvas);
|
||||
} else if (drawSponsor) {
|
||||
int y = dp(useForceThreeLines || SharedConfig.useThreeLinesLayout ? 13.5f : 16.5f);
|
||||
if ((!(useForceThreeLines || SharedConfig.useThreeLinesLayout) || isForumCell()) && hasTags()) {
|
||||
y -= dp(9);
|
||||
}
|
||||
int sz = dp(18);
|
||||
int sx = nameMuteLeft - dp(1);
|
||||
sponsorHeartRect.set(sx, y, sx + sz, y + sz);
|
||||
tw.nekomimi.nekogram.helpers.ShimmerHeartDrawable.drawStatic(canvas, sponsorHeartRect, 255);
|
||||
}
|
||||
|
||||
if (drawReorder || reorderIconProgress != 0) {
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ public class UserCell extends FrameLayout implements NotificationCenter.Notifica
|
|||
private TextView addButton;
|
||||
private ImageView mutualView;
|
||||
private Drawable premiumDrawable;
|
||||
private final AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable botVerification;
|
||||
private Drawable sponsorDrawable; private final AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable botVerification;
|
||||
private final AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable emojiStatus;
|
||||
private ImageView closeView;
|
||||
protected Theme.ResourcesProvider resourcesProvider;
|
||||
|
|
@ -712,10 +712,21 @@ public class UserCell extends FrameLayout implements NotificationCenter.Notifica
|
|||
nameTextView.setRightDrawable(premiumDrawable);
|
||||
}
|
||||
nameTextView.setRightDrawableTopPadding(-dp(0.5f));
|
||||
} else if (currentUser != null && currentUser.id != 0 && !UserObject.isUserSelf(currentUser)
|
||||
&& !currentUser.verified && !currentUser.bot
|
||||
&& tw.nekomimi.nekogram.helpers.SponsorHelper.isKnownSponsor(currentUser.id)) {
|
||||
if (sponsorDrawable == null) {
|
||||
sponsorDrawable = new tw.nekomimi.nekogram.helpers.ShimmerHeartDrawable(dp(16));
|
||||
}
|
||||
nameTextView.setRightDrawable(sponsorDrawable);
|
||||
nameTextView.setRightDrawableTopPadding(-dp(0.5f));
|
||||
} else {
|
||||
nameTextView.setRightDrawable(null);
|
||||
nameTextView.setRightDrawableTopPadding(0);
|
||||
}
|
||||
if (currentUser != null && currentUser.id != 0 && !UserObject.isUserSelf(currentUser) && !currentUser.bot) {
|
||||
tw.nekomimi.nekogram.helpers.SponsorHelper.requestSponsorStatus(currentUser.id);
|
||||
}
|
||||
if (currentStatus != null) {
|
||||
statusTextView.setTextColor(statusColor);
|
||||
CharSequence status = currentStatus;
|
||||
|
|
@ -829,6 +840,8 @@ public class UserCell extends FrameLayout implements NotificationCenter.Notifica
|
|||
public void didReceivedNotification(int id, int account, Object... args) {
|
||||
if (id == NotificationCenter.emojiLoaded) {
|
||||
nameTextView.invalidate();
|
||||
} else if (id == NotificationCenter.sponsorStatusUpdated) {
|
||||
update(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -836,6 +849,7 @@ public class UserCell extends FrameLayout implements NotificationCenter.Notifica
|
|||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.emojiLoaded);
|
||||
NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.sponsorStatusUpdated);
|
||||
emojiStatus.attach();
|
||||
botVerification.attach();
|
||||
}
|
||||
|
|
@ -844,6 +858,7 @@ public class UserCell extends FrameLayout implements NotificationCenter.Notifica
|
|||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.emojiLoaded);
|
||||
NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.sponsorStatusUpdated);
|
||||
emojiStatus.detach();
|
||||
botVerification.detach();
|
||||
storyParams.onDetachFromWindow();
|
||||
|
|
|
|||
|
|
@ -3204,6 +3204,7 @@ public class ChatActivity extends BaseFragment implements
|
|||
if (themeDelegate.isThemeChangeAvailable(false)) {
|
||||
globalObserversGroup.add(NotificationCenter.needSetDayNightTheme);
|
||||
}
|
||||
globalObserversGroup.add(NotificationCenter.sponsorStatusUpdated);
|
||||
|
||||
if (chatInvite != null) {
|
||||
int timeout = chatInvite.expires - getConnectionsManager().getCurrentTime();
|
||||
|
|
@ -19775,6 +19776,16 @@ public class ChatActivity extends BaseFragment implements
|
|||
avatarContainer.setTitle(AndroidUtilities.removeRTL(AndroidUtilities.removeDiacritics(UserObject.getUserName(currentUser))), currentUser.scam, currentUser.fake, currentUser.verified, getMessagesController().isPremiumUser(currentUser), !MessagesController.isSupportUser(currentUser) ? currentUser.emoji_status : null, animated);
|
||||
}
|
||||
}
|
||||
if (avatarContainer != null) {
|
||||
boolean sponsor = false;
|
||||
if (currentChat == null && currentUser != null && !currentUser.self
|
||||
&& !currentUser.verified && !currentUser.scam && !currentUser.fake
|
||||
&& !getMessagesController().isPremiumUser(currentUser)) {
|
||||
tw.nekomimi.nekogram.helpers.SponsorHelper.requestSponsorStatus(currentUser.id);
|
||||
sponsor = tw.nekomimi.nekogram.helpers.SponsorHelper.isKnownSponsor(currentUser.id);
|
||||
}
|
||||
avatarContainer.setSponsorBadge(sponsor);
|
||||
}
|
||||
setParentActivityTitle(avatarContainer.getTitleTextView().getText());
|
||||
updateTitleIcons();
|
||||
}
|
||||
|
|
@ -20496,6 +20507,15 @@ public class ChatActivity extends BaseFragment implements
|
|||
|
||||
@Override
|
||||
public void didReceivedNotification(int id, int account, final Object... args) {
|
||||
if (id == NotificationCenter.sponsorStatusUpdated) {
|
||||
if (avatarContainer != null) {
|
||||
updateTitle(false);
|
||||
}
|
||||
if (chatListView != null) {
|
||||
chatListView.invalidateViews();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (id == NotificationCenter.messagesDidLoad) {
|
||||
int guid = (Integer) args[10];
|
||||
if (guid != classGuid) {
|
||||
|
|
|
|||
|
|
@ -950,6 +950,36 @@ public class ChatAvatarContainer extends FrameLayout implements NotificationCent
|
|||
private Drawable verifiedBackground;
|
||||
private Drawable verifiedCheck;
|
||||
|
||||
private boolean sponsorBadgeShown;
|
||||
|
||||
/**
|
||||
* Show/hide the FoxiGram sponsor heart next to the title. Only applied when
|
||||
* the title has no scam/verified badge in {@code rightDrawable2}. Uses the
|
||||
* animated {@link tw.nekomimi.nekogram.helpers.ShimmerHeartDrawable} since a
|
||||
* single header is cheap to animate.
|
||||
*/
|
||||
public void setSponsorBadge(boolean show) {
|
||||
if (show == sponsorBadgeShown) {
|
||||
return;
|
||||
}
|
||||
if (show) {
|
||||
if (rightDrawableIsScamOrVerified) {
|
||||
return; // scam/verified takes precedence
|
||||
}
|
||||
tw.nekomimi.nekogram.helpers.ShimmerHeartDrawable heart =
|
||||
new tw.nekomimi.nekogram.helpers.ShimmerHeartDrawable(dp(18));
|
||||
titleTextView.setRightDrawable2(heart);
|
||||
rightDrawable2ContentDescription = getString(R.string.FoxSponsorBadge);
|
||||
sponsorBadgeShown = true;
|
||||
} else {
|
||||
if (sponsorBadgeShown) {
|
||||
titleTextView.setRightDrawable2(null);
|
||||
rightDrawable2ContentDescription = null;
|
||||
}
|
||||
sponsorBadgeShown = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setSubtitle(CharSequence value) {
|
||||
if (lastSubtitle == null) {
|
||||
|
|
|
|||
|
|
@ -72,6 +72,30 @@ public class ShimmerHeartDrawable extends Drawable {
|
|||
return sharedBitmap;
|
||||
}
|
||||
|
||||
/** Shared heart artwork, lazily decoded. May be null if decoding failed. */
|
||||
public static Bitmap getSharedBitmap() {
|
||||
return loadBitmap();
|
||||
}
|
||||
|
||||
private static final Paint STATIC_PAINT = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
|
||||
|
||||
/**
|
||||
* Draw the sponsor heart statically (no shimmer) into {@code dst}. Cheap
|
||||
* enough for list cells and headers that repaint frequently.
|
||||
*/
|
||||
public static void drawStatic(@NonNull Canvas canvas, @NonNull RectF dst, int alpha) {
|
||||
Bitmap bmp = loadBitmap();
|
||||
if (bmp == null) {
|
||||
return;
|
||||
}
|
||||
if (alpha < 255) {
|
||||
STATIC_PAINT.setAlpha(alpha);
|
||||
} else {
|
||||
STATIC_PAINT.setAlpha(255);
|
||||
}
|
||||
canvas.drawBitmap(bmp, null, dst, STATIC_PAINT);
|
||||
}
|
||||
|
||||
private void buildShine(int w) {
|
||||
// A bright diagonal band, narrow relative to the badge width.
|
||||
shineWidth = Math.max(1, (int) (w * 0.55f));
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 211 KiB After Width: | Height: | Size: 177 KiB |