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);
|
avatarDrawable.setInfo(user);
|
||||||
avatarView.setForUserOrChat(user, avatarDrawable);
|
avatarView.setForUserOrChat(user, avatarDrawable);
|
||||||
titleView.setText(UserObject.getUserName(user));
|
titleView.setText(UserObject.getUserName(user));
|
||||||
final StringBuilder sb = new StringBuilder();
|
final android.text.SpannableStringBuilder sb = new android.text.SpannableStringBuilder();
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
sb.append(tw.nekomimi.nekogram.NekoConfig.formatOwnPhone(PhoneFormat.getInstance().format("+" + user.phone)));
|
sb.append(tw.nekomimi.nekogram.NekoConfig.formatOwnPhone(PhoneFormat.getInstance().format("+" + user.phone)));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -701,16 +701,12 @@ public class NekoConfig {
|
||||||
if (hidePhoneNumber == PHONE_HIDE) {
|
if (hidePhoneNumber == PHONE_HIDE) {
|
||||||
return org.telegram.messenger.LocaleController.getString(org.telegram.messenger.R.string.MobileHidden);
|
return org.telegram.messenger.LocaleController.getString(org.telegram.messenger.R.string.MobileHidden);
|
||||||
}
|
}
|
||||||
StringBuilder sb = new StringBuilder(formatted.length());
|
// PHONE_BLUR: render the real number, but genuinely blurred.
|
||||||
for (int i = 0; i < formatted.length(); i++) {
|
String text = formatted.toString();
|
||||||
char c = formatted.charAt(i);
|
android.text.SpannableString span = new android.text.SpannableString(text);
|
||||||
if (Character.isDigit(c)) {
|
span.setSpan(new tw.nekomimi.nekogram.helpers.BlurredTextSpan(text), 0, text.length(),
|
||||||
sb.append('•');
|
android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
} else {
|
return span;
|
||||||
sb.append(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setNameOrder(int order) {
|
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