Sponsor badge: shimmering heart + tap-to-explain bulletin
- Replace static star with self-animating iridescent ShimmerHeartDrawable - Move badge to clickable rightDrawable slot; tap shows a Nekogram-style bulletin explaining why the badge was granted - Add en/ru strings; drop unused vector resource
This commit is contained in:
parent
e926e7afc6
commit
323c1ff10e
5 changed files with 159 additions and 27 deletions
|
|
@ -11180,17 +11180,23 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter.
|
||||||
|
|
||||||
private Drawable getFoxSponsorDrawable(int a) {
|
private Drawable getFoxSponsorDrawable(int a) {
|
||||||
if (foxSponsorDrawable[a] == null) {
|
if (foxSponsorDrawable[a] == null) {
|
||||||
Drawable d = ContextCompat.getDrawable(getParentActivity(), R.drawable.foxsponsor_badge).mutate();
|
foxSponsorDrawable[a] = new tw.nekomimi.nekogram.helpers.ShimmerHeartDrawable(AndroidUtilities.dp(a == 1 ? 22 : 18));
|
||||||
int color = getThemedColor(Theme.key_profile_verifiedBackground);
|
|
||||||
if (a == 1) {
|
|
||||||
color = dontApplyPeerColor(color);
|
|
||||||
}
|
|
||||||
d.setColorFilter(color, PorterDuff.Mode.SRC_IN);
|
|
||||||
foxSponsorDrawable[a] = d;
|
|
||||||
}
|
}
|
||||||
return foxSponsorDrawable[a];
|
return foxSponsorDrawable[a];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showSponsorInfo() {
|
||||||
|
if (getParentActivity() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BulletinFactory.of(ProfileActivity.this)
|
||||||
|
.createSimpleBulletin(
|
||||||
|
new tw.nekomimi.nekogram.helpers.ShimmerHeartDrawable(AndroidUtilities.dp(28)),
|
||||||
|
LocaleController.getString(R.string.FoxSponsorBadge),
|
||||||
|
LocaleController.getString(R.string.FoxSponsorBadgeInfo))
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
private Drawable getPremiumCrossfadeDrawable(int a) {
|
private Drawable getPremiumCrossfadeDrawable(int a) {
|
||||||
if (premiumCrossfadeDrawable[a] == null) {
|
if (premiumCrossfadeDrawable[a] == null) {
|
||||||
premiumStarDrawable[a] = ContextCompat.getDrawable(getParentActivity(), R.drawable.msg_premium_liststar).mutate();
|
premiumStarDrawable[a] = ContextCompat.getDrawable(getParentActivity(), R.drawable.msg_premium_liststar).mutate();
|
||||||
|
|
@ -11550,7 +11556,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter.
|
||||||
}, resourcesProvider);
|
}, resourcesProvider);
|
||||||
} : null);
|
} : null);
|
||||||
Drawable leftIcon = currentEncryptedChat != null ? getLockIconDrawable() : null;
|
Drawable leftIcon = currentEncryptedChat != null ? getLockIconDrawable() : null;
|
||||||
boolean rightIconIsPremium = false, rightIconIsStatus = false;
|
boolean rightIconIsPremium = false, rightIconIsStatus = false, rightIconIsSponsor = false;
|
||||||
nameTextView[a].setRightDrawableOutside(a == 0);
|
nameTextView[a].setRightDrawableOutside(a == 0);
|
||||||
if (a == 0 && !copyFromChatActivity) {
|
if (a == 0 && !copyFromChatActivity) {
|
||||||
if (user.scam || user.fake) {
|
if (user.scam || user.fake) {
|
||||||
|
|
@ -11562,14 +11568,15 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter.
|
||||||
} else if (getMessagesController().isDialogMuted(dialogId != 0 ? dialogId : userId, topicId)) {
|
} else if (getMessagesController().isDialogMuted(dialogId != 0 ? dialogId : userId, topicId)) {
|
||||||
nameTextView[a].setRightDrawable2(getThemedDrawable(Theme.key_drawable_muteIconDrawable));
|
nameTextView[a].setRightDrawable2(getThemedDrawable(Theme.key_drawable_muteIconDrawable));
|
||||||
nameTextViewRightDrawable2ContentDescription = LocaleController.getString(R.string.NotificationsMuted);
|
nameTextViewRightDrawable2ContentDescription = LocaleController.getString(R.string.NotificationsMuted);
|
||||||
} else if (user.self && SponsorHelper.isSponsor(user.id)) {
|
|
||||||
nameTextView[a].setRightDrawable2(getFoxSponsorDrawable(a));
|
|
||||||
nameTextViewRightDrawable2ContentDescription = LocaleController.getString(R.string.FoxSponsorBadge);
|
|
||||||
} else {
|
} else {
|
||||||
nameTextView[a].setRightDrawable2(null);
|
nameTextView[a].setRightDrawable2(null);
|
||||||
nameTextViewRightDrawable2ContentDescription = null;
|
nameTextViewRightDrawable2ContentDescription = null;
|
||||||
}
|
}
|
||||||
if (user != null/* && !getMessagesController().premiumFeaturesBlocked()*/ && !MessagesController.isSupportUser(user) && DialogObject.getEmojiStatusDocumentId(user.emoji_status) != 0) {
|
if (user.self && SponsorHelper.isSponsor(user.id)) {
|
||||||
|
rightIconIsSponsor = true;
|
||||||
|
nameTextView[a].setRightDrawable(getFoxSponsorDrawable(a));
|
||||||
|
nameTextViewRightDrawableContentDescription = LocaleController.getString(R.string.FoxSponsorBadge);
|
||||||
|
} else if (user != null/* && !getMessagesController().premiumFeaturesBlocked()*/ && !MessagesController.isSupportUser(user) && DialogObject.getEmojiStatusDocumentId(user.emoji_status) != 0) {
|
||||||
rightIconIsStatus = true;
|
rightIconIsStatus = true;
|
||||||
rightIconIsPremium = false;
|
rightIconIsPremium = false;
|
||||||
nameTextView[a].setRightDrawable(getEmojiStatusDrawable(user.emoji_status, false, false, a));
|
nameTextView[a].setRightDrawable(getEmojiStatusDrawable(user.emoji_status, false, false, a));
|
||||||
|
|
@ -11588,12 +11595,13 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter.
|
||||||
nameTextView[a].setRightDrawable2(getScamDrawable(user.scam ? 0 : 1));
|
nameTextView[a].setRightDrawable2(getScamDrawable(user.scam ? 0 : 1));
|
||||||
} else if (user.verified) {
|
} else if (user.verified) {
|
||||||
nameTextView[a].setRightDrawable2(getVerifiedCrossfadeDrawable(a));
|
nameTextView[a].setRightDrawable2(getVerifiedCrossfadeDrawable(a));
|
||||||
} else if (user.self && SponsorHelper.isSponsor(user.id)) {
|
|
||||||
nameTextView[a].setRightDrawable2(getFoxSponsorDrawable(a));
|
|
||||||
} else {
|
} else {
|
||||||
nameTextView[a].setRightDrawable2(null);
|
nameTextView[a].setRightDrawable2(null);
|
||||||
}
|
}
|
||||||
if (/*!getMessagesController().premiumFeaturesBlocked() && */user != null && !MessagesController.isSupportUser(user) && DialogObject.getEmojiStatusDocumentId(user.emoji_status) != 0) {
|
if (user.self && SponsorHelper.isSponsor(user.id)) {
|
||||||
|
rightIconIsSponsor = true;
|
||||||
|
nameTextView[a].setRightDrawable(getFoxSponsorDrawable(a));
|
||||||
|
} else if (/*!getMessagesController().premiumFeaturesBlocked() && */user != null && !MessagesController.isSupportUser(user) && DialogObject.getEmojiStatusDocumentId(user.emoji_status) != 0) {
|
||||||
rightIconIsStatus = true;
|
rightIconIsStatus = true;
|
||||||
rightIconIsPremium = false;
|
rightIconIsPremium = false;
|
||||||
nameTextView[a].setRightDrawable(getEmojiStatusDrawable(user.emoji_status, true, true, a));
|
nameTextView[a].setRightDrawable(getEmojiStatusDrawable(user.emoji_status, true, true, a));
|
||||||
|
|
@ -11612,10 +11620,12 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter.
|
||||||
nameTextView[a].setLeftDrawableOutside(false);
|
nameTextView[a].setLeftDrawableOutside(false);
|
||||||
}
|
}
|
||||||
nameTextView[a].setLeftDrawable(leftIcon);
|
nameTextView[a].setLeftDrawable(leftIcon);
|
||||||
if (a == 1 && (rightIconIsStatus || rightIconIsPremium)) {
|
if (a == 1 && (rightIconIsStatus || rightIconIsPremium || rightIconIsSponsor)) {
|
||||||
nameTextView[a].setRightDrawableOutside(true);
|
nameTextView[a].setRightDrawableOutside(true);
|
||||||
}
|
}
|
||||||
if (user.self && getMessagesController().isPremiumUser(user)) {
|
if (rightIconIsSponsor) {
|
||||||
|
nameTextView[a].setRightDrawableOnClick(v -> showSponsorInfo());
|
||||||
|
} else if (user.self && getMessagesController().isPremiumUser(user)) {
|
||||||
nameTextView[a].setRightDrawableOnClick(v -> {
|
nameTextView[a].setRightDrawableOnClick(v -> {
|
||||||
showStatusSelect();
|
showStatusSelect();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,128 @@
|
||||||
|
package tw.nekomimi.nekogram.helpers;
|
||||||
|
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.ColorFilter;
|
||||||
|
import android.graphics.LinearGradient;
|
||||||
|
import android.graphics.Matrix;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Path;
|
||||||
|
import android.graphics.PixelFormat;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.Shader;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.telegram.messenger.AndroidUtilities;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A heart-shaped badge filled with a continuously moving iridescent gradient
|
||||||
|
* ("shimmer"). It self-invalidates each frame, so when attached to a view via
|
||||||
|
* {@code setRightDrawable(...)} the host keeps repainting automatically (the
|
||||||
|
* SimpleTextView drawable callback chain handles invalidation).
|
||||||
|
*/
|
||||||
|
public class ShimmerHeartDrawable extends Drawable {
|
||||||
|
|
||||||
|
private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
private final Path heart = new Path();
|
||||||
|
private final Matrix gradientMatrix = new Matrix();
|
||||||
|
|
||||||
|
private LinearGradient gradient;
|
||||||
|
private int lastWidth = -1;
|
||||||
|
private int gradientWidth;
|
||||||
|
|
||||||
|
// Iridescent palette swept across the heart.
|
||||||
|
private static final int[] COLORS = new int[] {
|
||||||
|
0xFFFF4D6D, // pink-red
|
||||||
|
0xFFFF8FA3, // light pink
|
||||||
|
0xFFFFC2D1, // pale
|
||||||
|
0xFFFF5C8A, // magenta
|
||||||
|
0xFFFF1E56, // deep red
|
||||||
|
0xFFFF4D6D, // back to start (seamless loop)
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final long CYCLE_MS = 2200L;
|
||||||
|
private final int size;
|
||||||
|
|
||||||
|
public ShimmerHeartDrawable() {
|
||||||
|
this(AndroidUtilities.dp(18));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShimmerHeartDrawable(int sizePx) {
|
||||||
|
this.size = sizePx;
|
||||||
|
paint.setStyle(Paint.Style.FILL);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildGradient(int width) {
|
||||||
|
gradientWidth = width * 2;
|
||||||
|
gradient = new LinearGradient(0, 0, gradientWidth, 0, COLORS, null, Shader.TileMode.MIRROR);
|
||||||
|
paint.setShader(gradient);
|
||||||
|
lastWidth = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildHeart(Rect b) {
|
||||||
|
heart.reset();
|
||||||
|
float w = b.width();
|
||||||
|
float h = b.height();
|
||||||
|
float l = b.left;
|
||||||
|
float t = b.top;
|
||||||
|
// Heart path normalized to the bounds.
|
||||||
|
float cx = l + w / 2f;
|
||||||
|
heart.moveTo(cx, t + h * 0.30f);
|
||||||
|
heart.cubicTo(l + w * 0.40f, t + h * 0.05f, l + w * 0.02f, t + h * 0.18f, l + w * 0.10f, t + h * 0.45f);
|
||||||
|
heart.cubicTo(l + w * 0.17f, t + h * 0.66f, l + w * 0.40f, t + h * 0.80f, cx, t + h * 0.97f);
|
||||||
|
heart.cubicTo(l + w * 0.60f, t + h * 0.80f, l + w * 0.83f, t + h * 0.66f, l + w * 0.90f, t + h * 0.45f);
|
||||||
|
heart.cubicTo(l + w * 0.98f, t + h * 0.18f, l + w * 0.60f, t + h * 0.05f, cx, t + h * 0.30f);
|
||||||
|
heart.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onBoundsChange(@NonNull Rect bounds) {
|
||||||
|
super.onBoundsChange(bounds);
|
||||||
|
buildHeart(bounds);
|
||||||
|
buildGradient(bounds.width());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(@NonNull Canvas canvas) {
|
||||||
|
Rect b = getBounds();
|
||||||
|
if (b.width() == 0 || b.height() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (gradient == null || lastWidth != b.width()) {
|
||||||
|
buildGradient(b.width());
|
||||||
|
buildHeart(b);
|
||||||
|
}
|
||||||
|
float phase = (System.currentTimeMillis() % CYCLE_MS) / (float) CYCLE_MS;
|
||||||
|
gradientMatrix.reset();
|
||||||
|
gradientMatrix.setTranslate(b.left - phase * gradientWidth, 0);
|
||||||
|
gradient.setLocalMatrix(gradientMatrix);
|
||||||
|
canvas.drawPath(heart, paint);
|
||||||
|
invalidateSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getIntrinsicWidth() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getIntrinsicHeight() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAlpha(int alpha) {
|
||||||
|
paint.setAlpha(alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setColorFilter(ColorFilter colorFilter) {
|
||||||
|
paint.setColorFilter(colorFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOpacity() {
|
||||||
|
return PixelFormat.TRANSLUCENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:fillColor="#FFFFFF"
|
|
||||||
android:pathData="M12,2L14.6,7.27L20.42,8.12L16.21,12.22L17.2,18.02L12,15.28L6.8,18.02L7.79,12.22L3.58,8.12L9.4,7.27L12,2Z" />
|
|
||||||
</vector>
|
|
||||||
|
|
@ -322,5 +322,7 @@
|
||||||
<string name="TabsPositionBottom">Снизу</string>
|
<string name="TabsPositionBottom">Снизу</string>
|
||||||
<string name="StrokeOnViews">Эффекты бликов</string>
|
<string name="StrokeOnViews">Эффекты бликов</string>
|
||||||
<!-- Accessibility -->
|
<!-- Accessibility -->
|
||||||
<string name="AccessibilitySettings">Настройки доступности</string>
|
<string name="AccessibilitySettings">Настройки специальных возможностей</string>
|
||||||
|
<string name="FoxSponsorBadge">Спонсор</string>
|
||||||
|
<string name="FoxSponsorBadgeInfo">Эта отметка выдана за поддержку проекта. Спасибо!</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,7 @@
|
||||||
<string name="NekogramVersion">FoxiGram %1$s\nBased on Telegram %2$s\nDesigned by %3$s</string>
|
<string name="NekogramVersion">FoxiGram %1$s\nBased on Telegram %2$s\nDesigned by %3$s</string>
|
||||||
<string name="UpdateInstalling">Installing update...</string>
|
<string name="UpdateInstalling">Installing update...</string>
|
||||||
<string name="FoxSponsorBadge">Sponsor</string>
|
<string name="FoxSponsorBadge">Sponsor</string>
|
||||||
|
<string name="FoxSponsorBadgeInfo">You received this badge for supporting the project. Thank you!</string>
|
||||||
<string name="UpdateDownloading">Downloading update...</string>
|
<string name="UpdateDownloading">Downloading update...</string>
|
||||||
<string name="UpdateInstallingNotification">A notification will be shown when the update completes.</string>
|
<string name="UpdateInstallingNotification">A notification will be shown when the update completes.</string>
|
||||||
<string name="UpdateInstallingRelaunch">The app will relaunch when the update completes.</string>
|
<string name="UpdateInstallingRelaunch">The app will relaunch when the update completes.</string>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue