add EmojiSpan that scaled w/ TextView

Fixes #3317
Closes #3605
// FREEBIE
pull/1/head
Jake McGinty 9 years ago committed by Moxie Marlinspike
parent 35159ac456
commit 9b2aabfdc8

@ -68,7 +68,7 @@
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?conversation_item_received_text_primary_color"
android:textColorLink="?conversation_item_received_text_primary_color"
android:textSize="16sp"
android:textSize="@dimen/conversation_item_body_text_size"
android:autoLink="all"
android:linksClickable="true" />

@ -81,7 +81,7 @@
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?conversation_item_sent_text_primary_color"
android:textColorLink="?conversation_item_sent_text_primary_color"
android:textSize="16sp"
android:textSize="@dimen/conversation_item_body_text_size"
tools:text="Mango pickle lorem ipsum" />
<LinearLayout android:id="@+id/mms_download_controls"

@ -8,7 +8,8 @@
android:layout_gravity="center_vertical"
android:gravity="center_vertical">
<TextView android:id="@+id/title"
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"

@ -7,6 +7,7 @@
<dimen name="emoji_drawer_item_padding">5dp</dimen>
<dimen name="emoji_drawer_indicator_height">1.5dp</dimen>
<dimen name="emoji_drawer_left_right_padding">5dp</dimen>
<dimen name="conversation_item_body_text_size">16sp</dimen>
<dimen name="conversation_item_date_text_size">12sp</dimen>
<dimen name="transport_selection_popup_width">200sp</dimen>
<dimen name="transport_selection_popup_xoff">2dp</dimen>

@ -4,8 +4,8 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.Drawable.Callback;
import android.text.style.ImageSpan;
public class InvalidatingDrawableSpan extends ImageSpan {
public InvalidatingDrawableSpan(Drawable drawable, Callback callback) {
public class AnimatingImageSpan extends ImageSpan {
public AnimatingImageSpan(Drawable drawable, Callback callback) {
super(drawable, ALIGN_BOTTOM);
drawable.setCallback(callback);
}

@ -9,7 +9,6 @@ import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Drawable.Callback;
import android.os.AsyncTask;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
@ -17,6 +16,7 @@ import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.util.Log;
import android.util.SparseArray;
import android.widget.TextView;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.BitmapDecodingException;
@ -44,17 +44,14 @@ public class EmojiProvider {
// |==== misc ====||======== emoticons ========||========= flags ==========|
private static final Pattern EMOJI_RANGE = Pattern.compile("[\\u20a0-\\u32ff\\ud83c\\udc00-\\ud83d\\udeff\\udbb9\\udce5-\\udbb9\\udcee]");
public static final double EMOJI_FULL = 1.00;
public static final double EMOJI_SMALL = 0.90;
public static final int EMOJI_RAW_HEIGHT = 64;
public static final int EMOJI_RAW_WIDTH = 64;
public static final int EMOJI_VERT_PAD = 0;
public static final int EMOJI_PER_ROW = 32;
private final Context context;
private final double drawWidth;
private final double drawHeight;
private final double verticalPad;
private final float decodeScale;
private final float verticalPad;
public static EmojiProvider getInstance(Context context) {
if (instance == null) {
@ -69,11 +66,8 @@ public class EmojiProvider {
private EmojiProvider(Context context) {
this.context = context.getApplicationContext();
this.drawHeight = Math.min(context.getResources().getDimension(R.dimen.emoji_drawer_size), EMOJI_RAW_HEIGHT);
double drawScale = drawHeight / EMOJI_RAW_HEIGHT;
this.drawWidth = EMOJI_RAW_WIDTH * drawScale;
this.verticalPad = EMOJI_VERT_PAD * drawScale;
Log.w(TAG, "draw size: " + drawWidth + "x" + drawHeight);
this.decodeScale = Math.min(1f, context.getResources().getDimension(R.dimen.emoji_drawer_size) / EMOJI_RAW_HEIGHT);
this.verticalPad = EMOJI_VERT_PAD * this.decodeScale;
for (EmojiPageModel page : EmojiPages.PAGES) {
if (page.hasSpriteMap()) {
final EmojiPageBitmap pageBitmap = new EmojiPageBitmap(page);
@ -84,15 +78,15 @@ public class EmojiProvider {
}
}
public Spannable emojify(CharSequence text, Callback callback) {
public Spannable emojify(CharSequence text, TextView tv) {
Matcher matches = EMOJI_RANGE.matcher(text);
SpannableStringBuilder builder = new SpannableStringBuilder(text);
while (matches.find()) {
int codePoint = matches.group().codePointAt(0);
Drawable drawable = getEmojiDrawable(codePoint, EMOJI_SMALL);
Drawable drawable = getEmojiDrawable(codePoint);
if (drawable != null) {
builder.setSpan(new InvalidatingDrawableSpan(drawable, callback), matches.start(), matches.end(),
builder.setSpan(new EmojiSpan(drawable, tv), matches.start(), matches.end(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
@ -100,17 +94,16 @@ public class EmojiProvider {
return builder;
}
public Drawable getEmojiDrawable(int emojiCode, double size) {
return getEmojiDrawable(offsets.get(emojiCode), size);
public Drawable getEmojiDrawable(int emojiCode) {
return getEmojiDrawable(offsets.get(emojiCode));
}
private Drawable getEmojiDrawable(DrawInfo drawInfo, double size) {
private Drawable getEmojiDrawable(DrawInfo drawInfo) {
if (drawInfo == null) {
return null;
}
final EmojiDrawable drawable = new EmojiDrawable(drawInfo, drawWidth, drawHeight);
drawable.setBounds(0, 0, (int)(drawWidth * size), (int)(drawHeight * size));
final EmojiDrawable drawable = new EmojiDrawable(drawInfo, decodeScale);
drawInfo.page.get().addListener(new FutureTaskListener<Bitmap>() {
@Override public void onSuccess(final Bitmap result) {
Util.runOnMain(new Runnable() {
@ -129,22 +122,22 @@ public class EmojiProvider {
public class EmojiDrawable extends Drawable {
private final DrawInfo info;
private final double width;
private final double height;
private Bitmap bmp;
private float intrinsicWidth;
private float intrinsicHeight;
@Override public int getIntrinsicWidth() {
return (int)width;
return (int)intrinsicWidth;
}
@Override public int getIntrinsicHeight() {
return (int)height;
return (int)intrinsicHeight;
}
public EmojiDrawable(DrawInfo info, double width, double height) {
this.info = info;
this.width = width;
this.height = height;
public EmojiDrawable(DrawInfo info, float decodeScale) {
this.info = info;
this.intrinsicWidth = EMOJI_RAW_WIDTH * decodeScale;
this.intrinsicHeight = EMOJI_RAW_HEIGHT * decodeScale;
}
@Override
@ -157,10 +150,10 @@ public class EmojiProvider {
final int row_index = info.index % EMOJI_PER_ROW;
canvas.drawBitmap(bmp,
new Rect((int)(row_index * width),
(int)(row * height + row * verticalPad),
(int)((row_index + 1) * width),
(int)((row + 1) * height + row * verticalPad)),
new Rect((int)(row_index * intrinsicWidth),
(int)(row * intrinsicHeight + row * verticalPad),
(int)((row_index + 1) * intrinsicWidth),
(int)((row + 1) * intrinsicHeight + row * verticalPad)),
getBounds(),
paint);
}
@ -253,7 +246,7 @@ public class EmojiProvider {
try {
final InputStream measureStream = context.getAssets().open(model.getSprite());
final InputStream bitmapStream = context.getAssets().open(model.getSprite());
final Bitmap bitmap = BitmapUtil.createScaledBitmap(measureStream, bitmapStream, (float) drawHeight / (float) EMOJI_RAW_HEIGHT);
final Bitmap bitmap = BitmapUtil.createScaledBitmap(measureStream, bitmapStream, decodeScale);
bitmapReference = new SoftReference<>(bitmap);
Log.w(TAG, "onPageLoaded(" + model.getSprite() + ")");
return bitmap;

@ -0,0 +1,18 @@
package org.thoughtcrime.securesms.components.emoji;
import android.graphics.Paint.FontMetricsInt;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.widget.TextView;
import org.thoughtcrime.securesms.R;
public class EmojiSpan extends AnimatingImageSpan {
public EmojiSpan(@NonNull Drawable drawable, @NonNull TextView tv) {
super(drawable, tv);
FontMetricsInt fm = tv.getPaint().getFontMetricsInt();
final int size = fm != null ? Math.abs(fm.descent) + Math.abs(fm.ascent)
: tv.getResources().getDimensionPixelSize(R.dimen.conversation_item_body_text_size);
getDrawable().setBounds(0, 0, size, size);
}
}

@ -1,13 +1,10 @@
package org.thoughtcrime.securesms.components.emoji;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build.VERSION_CODES;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.View;
@ -37,8 +34,7 @@ public class EmojiView extends View implements Drawable.Callback {
public void setEmoji(String emoji) {
this.emoji = emoji;
this.drawable = EmojiProvider.getInstance(getContext())
.getEmojiDrawable(Character.codePointAt(emoji, 0),
EmojiProvider.EMOJI_FULL);
.getEmojiDrawable(Character.codePointAt(emoji, 0));
postInvalidate();
}

Loading…
Cancel
Save