Animate sponsor badge shimmer everywhere

- ShimmerHeartDrawable.drawStatic() now renders the moving shine band
  instead of a flat heart, using shared static paints
- DialogCell and ChatMessageCell invalidate each frame while a sponsor
  heart is visible so the shimmer animates in lists and message names
- Header and user-list badges already used the animated drawable
This commit is contained in:
instant992 2026-06-10 01:20:12 +04:00
parent 00479f6f98
commit 220b7334d5
3 changed files with 51 additions and 6 deletions

View file

@ -21925,6 +21925,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
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));
invalidate();
}
float end;

View file

@ -4368,6 +4368,7 @@ public class DialogCell extends BaseCell implements StoriesListPlaceProvider.Ava
int sx = nameMuteLeft - dp(1);
sponsorHeartRect.set(sx, y, sx + sz, y + sz);
tw.nekomimi.nekogram.helpers.ShimmerHeartDrawable.drawStatic(canvas, sponsorHeartRect, 255);
invalidate();
}
if (drawReorder || reorderIconProgress != 0) {

View file

@ -78,22 +78,65 @@ public class ShimmerHeartDrawable extends Drawable {
}
private static final Paint STATIC_PAINT = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
private static final Paint STATIC_SHINE_PAINT = new Paint(Paint.ANTI_ALIAS_FLAG);
private static final Paint STATIC_MASK_PAINT = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
private static final Matrix STATIC_SHINE_MATRIX = new Matrix();
private static LinearGradient staticShineGradient;
private static int staticShineWidth;
static {
STATIC_MASK_PAINT.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
}
/**
* Draw the sponsor heart statically (no shimmer) into {@code dst}. Cheap
* enough for list cells and headers that repaint frequently.
* Draw the sponsor heart with the moving "shimmer" highlight into
* {@code dst}. Used by canvas-drawn cells (chats list, message author
* names, etc.). The host must keep repainting (e.g. invalidate each frame)
* for the highlight to actually move.
*/
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);
int w = (int) dst.width();
if (w <= 0) {
return;
}
int wantShine = Math.max(1, (int) (w * 0.55f));
if (staticShineGradient == null || staticShineWidth != wantShine) {
staticShineWidth = wantShine;
staticShineGradient = new LinearGradient(
0, 0, staticShineWidth, 0,
new int[]{0x00FFFFFF, 0x00FFFFFF, 0x99FFFFFF, 0x00FFFFFF, 0x00FFFFFF},
new float[]{0f, 0.35f, 0.5f, 0.65f, 1f},
Shader.TileMode.CLAMP);
STATIC_SHINE_PAINT.setShader(staticShineGradient);
}
STATIC_PAINT.setAlpha(alpha < 255 ? alpha : 255);
int sc = canvas.saveLayer(dst, null);
// 1) heart artwork
canvas.drawBitmap(bmp, null, dst, STATIC_PAINT);
// 2) moving highlight band, swept diagonally and clipped to artwork alpha
float phase = (System.currentTimeMillis() % CYCLE_MS) / (float) CYCLE_MS;
float travel = dst.width() + staticShineWidth;
float x = dst.left - staticShineWidth + phase * travel;
STATIC_SHINE_MATRIX.reset();
STATIC_SHINE_MATRIX.postRotate(20f, 0, 0);
STATIC_SHINE_MATRIX.postTranslate(x, dst.top);
staticShineGradient.setLocalMatrix(STATIC_SHINE_MATRIX);
int ssc = canvas.saveLayer(dst, null);
STATIC_SHINE_PAINT.setAlpha(alpha < 255 ? alpha : 255);
canvas.drawRect(dst, STATIC_SHINE_PAINT);
canvas.drawBitmap(bmp, null, dst, STATIC_MASK_PAINT);
canvas.restoreToCount(ssc);
canvas.restoreToCount(sc);
}
private void buildShine(int w) {