diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java index 903e0a94..281d6dc0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java @@ -529,7 +529,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter avatarDrawable.setInfo(user); avatarView.setForUserOrChat(user, avatarDrawable); titleView.setText(UserObject.getUserName(user)); - final StringBuilder sb = new StringBuilder(); + final android.text.SpannableStringBuilder sb = new android.text.SpannableStringBuilder(); if (user != null) { sb.append(tw.nekomimi.nekogram.NekoConfig.formatOwnPhone(PhoneFormat.getInstance().format("+" + user.phone))); } diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/NekoConfig.java b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/NekoConfig.java index a23f8290..0c01a7c6 100644 --- a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/NekoConfig.java +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/NekoConfig.java @@ -701,16 +701,12 @@ public class NekoConfig { if (hidePhoneNumber == PHONE_HIDE) { return org.telegram.messenger.LocaleController.getString(org.telegram.messenger.R.string.MobileHidden); } - StringBuilder sb = new StringBuilder(formatted.length()); - for (int i = 0; i < formatted.length(); i++) { - char c = formatted.charAt(i); - if (Character.isDigit(c)) { - sb.append('•'); - } else { - sb.append(c); - } - } - return sb.toString(); + // PHONE_BLUR: render the real number, but genuinely blurred. + String text = formatted.toString(); + android.text.SpannableString span = new android.text.SpannableString(text); + span.setSpan(new tw.nekomimi.nekogram.helpers.BlurredTextSpan(text), 0, text.length(), + android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + return span; } public static void setNameOrder(int order) { diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/BlurredTextSpan.java b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/BlurredTextSpan.java new file mode 100644 index 00000000..f2c51dba --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/BlurredTextSpan.java @@ -0,0 +1,109 @@ +package tw.nekomimi.nekogram.helpers; + +import android.graphics.Bitmap; +import android.graphics.BlurMaskFilter; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.text.style.ReplacementSpan; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.telegram.messenger.AndroidUtilities; + +/** + * Renders a piece of text genuinely blurred (Gaussian-style), instead of just + * masking the characters. The real glyphs are drawn into an offscreen software + * bitmap with a {@link BlurMaskFilter} applied and then blitted onto the target + * canvas, so it works the same on both software and hardware-accelerated views. + */ +public class BlurredTextSpan extends ReplacementSpan { + + private final String text; + private final float blurRadius; + + private Bitmap cache; + private int cacheWidth; + private int cacheHeight; + private float cacheTextSize; + private int cacheColor; + private float cacheBaseline; + private int cachePad; + + public BlurredTextSpan(String text) { + this(text, AndroidUtilities.dp(3.5f)); + } + + public BlurredTextSpan(String text, float blurRadius) { + this.text = text == null ? "" : text; + this.blurRadius = Math.max(0.5f, blurRadius); + } + + @Override + public int getSize(@NonNull Paint paint, CharSequence charSequence, int start, int end, @Nullable Paint.FontMetricsInt fm) { + if (fm != null) { + Paint.FontMetricsInt pfm = paint.getFontMetricsInt(); + fm.ascent = pfm.ascent; + fm.descent = pfm.descent; + fm.top = pfm.top; + fm.bottom = pfm.bottom; + fm.leading = pfm.leading; + } + return (int) Math.ceil(paint.measureText(text)); + } + + @Override + public void draw(@NonNull Canvas canvas, CharSequence charSequence, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) { + ensureCache(paint); + if (cache == null) { + return; + } + canvas.drawBitmap(cache, x - cachePad, y - cacheBaseline, null); + } + + private void ensureCache(Paint paint) { + final float textSize = paint.getTextSize(); + final int color = paint.getColor(); + final int width = (int) Math.ceil(paint.measureText(text)); + if (width <= 0) { + cache = null; + return; + } + // padding so the blur isn't clipped at the edges + final int pad = (int) Math.ceil(blurRadius * 2.5f); + Paint.FontMetricsInt fm = paint.getFontMetricsInt(); + final int textHeight = fm.descent - fm.ascent; + final int w = width + pad * 2; + final int h = textHeight + pad * 2; + + if (cache != null && cacheWidth == w && cacheHeight == h + && cacheTextSize == textSize && cacheColor == color) { + return; + } + + if (cache != null) { + cache.recycle(); + } + try { + cache = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); + } catch (Throwable e) { + cache = null; + return; + } + cacheWidth = w; + cacheHeight = h; + cacheTextSize = textSize; + cacheColor = color; + cachePad = pad; + // baseline offset inside the cache bitmap, relative to the text top + cacheBaseline = pad - fm.ascent; + + Canvas c = new Canvas(cache); + Paint p = new Paint(Paint.ANTI_ALIAS_FLAG); + p.setTextSize(textSize); + p.setColor(color); + p.setTypeface(paint.getTypeface()); + p.setMaskFilter(new BlurMaskFilter(blurRadius, BlurMaskFilter.Blur.NORMAL)); + c.drawText(text, pad, cacheBaseline, p); + } +}