Use a real gaussian blur for the masked phone number
- Add BlurredTextSpan: draws the real glyphs through a BlurMaskFilter into a cached offscreen bitmap, so the blur works on both software and hardware-accelerated views - formatOwnPhone() now returns this span in Blur mode instead of replacing digits with bullets - SettingsActivity header builds the subtitle with a SpannableStringBuilder so the span is preserved
This commit is contained in:
parent
edd7c4a8b4
commit
87b8ea4949
3 changed files with 116 additions and 11 deletions
|
|
@ -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)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue