2013-10-25 17:19:00 +02:00
|
|
|
/*
|
2015-10-29 18:10:07 +01:00
|
|
|
* This is the source code of Telegram for Android v. 3.x.x.
|
2013-10-25 17:19:00 +02:00
|
|
|
* It is licensed under GNU GPL v. 2 or later.
|
|
|
|
* You should have received a copy of the license in this archive (see LICENSE).
|
|
|
|
*
|
2016-01-11 18:19:48 +01:00
|
|
|
* Copyright Nikolai Kudashov, 2013-2016.
|
2013-10-25 17:19:00 +02:00
|
|
|
*/
|
|
|
|
|
2015-09-24 22:52:02 +02:00
|
|
|
package org.telegram.messenger;
|
2013-10-25 17:19:00 +02:00
|
|
|
|
2014-05-17 01:05:49 +02:00
|
|
|
import java.io.File;
|
2013-10-25 17:19:00 +02:00
|
|
|
import java.io.InputStream;
|
|
|
|
import java.util.HashMap;
|
2014-05-17 01:05:49 +02:00
|
|
|
import java.util.Locale;
|
2013-10-25 17:19:00 +02:00
|
|
|
|
2014-10-30 22:27:41 +01:00
|
|
|
import android.graphics.Bitmap;
|
2013-10-25 17:19:00 +02:00
|
|
|
import android.graphics.BitmapFactory;
|
|
|
|
import android.graphics.Canvas;
|
|
|
|
import android.graphics.ColorFilter;
|
|
|
|
import android.graphics.Paint;
|
2015-10-29 18:10:07 +01:00
|
|
|
import android.graphics.PixelFormat;
|
2013-10-25 17:19:00 +02:00
|
|
|
import android.graphics.Rect;
|
|
|
|
import android.graphics.drawable.Drawable;
|
|
|
|
import android.text.Spannable;
|
2015-06-29 19:12:11 +02:00
|
|
|
import android.text.Spanned;
|
2013-10-25 17:19:00 +02:00
|
|
|
import android.text.style.DynamicDrawableSpan;
|
|
|
|
import android.text.style.ImageSpan;
|
|
|
|
import android.view.View;
|
|
|
|
import android.view.ViewGroup;
|
|
|
|
import android.widget.TextView;
|
|
|
|
|
|
|
|
public class Emoji {
|
2015-10-29 18:10:07 +01:00
|
|
|
private static HashMap<CharSequence, DrawableInfo> rects = new HashMap<>();
|
2015-07-22 20:56:37 +02:00
|
|
|
private static int drawImgSize;
|
2015-05-03 13:48:36 +02:00
|
|
|
private static int bigImgSize;
|
2015-07-22 20:56:37 +02:00
|
|
|
private static boolean inited = false;
|
|
|
|
private static Paint placeholderPaint;
|
2015-10-29 18:10:07 +01:00
|
|
|
private static final int splitCount = 4;
|
|
|
|
private static Bitmap emojiBmp[][] = new Bitmap[5][splitCount];
|
|
|
|
private static boolean loadingEmoji[][] = new boolean[5][splitCount];
|
|
|
|
|
|
|
|
private static final int[][] cols = {
|
|
|
|
{11, 11, 11, 11},
|
|
|
|
{6, 6, 6, 6},
|
|
|
|
{9, 9, 9, 9},
|
|
|
|
{9, 9, 9, 9},
|
|
|
|
{8, 8, 8, 7}
|
2013-10-25 17:19:00 +02:00
|
|
|
};
|
|
|
|
|
2015-07-22 20:56:37 +02:00
|
|
|
static {
|
2014-10-30 22:27:41 +01:00
|
|
|
int emojiFullSize;
|
2014-07-03 00:39:05 +02:00
|
|
|
if (AndroidUtilities.density <= 1.0f) {
|
2015-05-03 13:48:36 +02:00
|
|
|
emojiFullSize = 32;
|
2014-07-03 00:39:05 +02:00
|
|
|
} else if (AndroidUtilities.density <= 1.5f) {
|
2015-05-03 13:48:36 +02:00
|
|
|
emojiFullSize = 48;
|
2014-07-03 00:39:05 +02:00
|
|
|
} else if (AndroidUtilities.density <= 2.0f) {
|
2015-05-03 13:48:36 +02:00
|
|
|
emojiFullSize = 64;
|
2014-05-17 01:05:49 +02:00
|
|
|
} else {
|
2015-10-29 18:10:07 +01:00
|
|
|
emojiFullSize = 64;
|
2013-10-25 17:19:00 +02:00
|
|
|
}
|
2015-07-22 20:56:37 +02:00
|
|
|
drawImgSize = AndroidUtilities.dp(20);
|
2015-10-29 18:10:07 +01:00
|
|
|
bigImgSize = AndroidUtilities.dp(AndroidUtilities.isTablet() ? 40 : 32);
|
|
|
|
|
|
|
|
for (int j = 0; j < EmojiData.data.length; j++) {
|
|
|
|
int count2 = (int) Math.ceil(EmojiData.data[j].length / (float) splitCount);
|
|
|
|
int position;
|
|
|
|
for (int i = 0; i < EmojiData.data[j].length; i++) {
|
|
|
|
int page = i / count2;
|
|
|
|
position = i - page * count2;
|
|
|
|
Rect rect = new Rect((position % cols[j][page]) * emojiFullSize, (position / cols[j][page]) * emojiFullSize, (position % cols[j][page] + 1) * emojiFullSize, (position / cols[j][page] + 1) * emojiFullSize);
|
|
|
|
rects.put(EmojiData.data[j][i], new DrawableInfo(rect, (byte) j, (byte) page));
|
2015-07-22 20:56:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
placeholderPaint = new Paint();
|
|
|
|
placeholderPaint.setColor(0x00000000);
|
|
|
|
}
|
|
|
|
|
2015-10-29 18:10:07 +01:00
|
|
|
private static void loadEmoji(final int page, final int page2) {
|
2015-07-22 20:56:37 +02:00
|
|
|
try {
|
2015-05-21 23:27:27 +02:00
|
|
|
float scale;
|
2014-05-17 01:05:49 +02:00
|
|
|
int imageResize = 1;
|
2014-07-03 00:39:05 +02:00
|
|
|
if (AndroidUtilities.density <= 1.0f) {
|
2014-05-17 01:05:49 +02:00
|
|
|
scale = 2.0f;
|
|
|
|
imageResize = 2;
|
2014-07-03 00:39:05 +02:00
|
|
|
} else if (AndroidUtilities.density <= 1.5f) {
|
2014-05-17 01:05:49 +02:00
|
|
|
scale = 3.0f;
|
|
|
|
imageResize = 2;
|
2014-07-03 00:39:05 +02:00
|
|
|
} else if (AndroidUtilities.density <= 2.0f) {
|
2014-05-17 01:05:49 +02:00
|
|
|
scale = 2.0f;
|
|
|
|
} else {
|
2015-10-29 18:10:07 +01:00
|
|
|
scale = 2.0f;
|
2013-10-25 17:19:00 +02:00
|
|
|
}
|
|
|
|
|
2015-05-03 13:48:36 +02:00
|
|
|
String imageName;
|
|
|
|
File imageFile;
|
|
|
|
|
|
|
|
try {
|
2015-10-29 18:10:07 +01:00
|
|
|
for (int a = 4; a < 6; a++) {
|
|
|
|
imageName = String.format(Locale.US, "v%d_emoji%.01fx_%d.jpg", a, scale, page);
|
|
|
|
imageFile = ApplicationLoader.applicationContext.getFileStreamPath(imageName);
|
|
|
|
if (imageFile.exists()) {
|
|
|
|
imageFile.delete();
|
|
|
|
}
|
|
|
|
imageName = String.format(Locale.US, "v%d_emoji%.01fx_a_%d.jpg", a, scale, page);
|
|
|
|
imageFile = ApplicationLoader.applicationContext.getFileStreamPath(imageName);
|
|
|
|
if (imageFile.exists()) {
|
|
|
|
imageFile.delete();
|
|
|
|
}
|
2015-05-03 13:48:36 +02:00
|
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
|
|
FileLog.e("tmessages", e);
|
|
|
|
}
|
|
|
|
|
2015-10-29 18:10:07 +01:00
|
|
|
|
|
|
|
imageName = String.format(Locale.US, "v7_emoji%.01fx_%d_%d.jpg", scale, page, page2);
|
2015-05-03 13:48:36 +02:00
|
|
|
imageFile = ApplicationLoader.applicationContext.getFileStreamPath(imageName);
|
2014-05-17 01:05:49 +02:00
|
|
|
if (!imageFile.exists()) {
|
|
|
|
InputStream is = ApplicationLoader.applicationContext.getAssets().open("emoji/" + imageName);
|
2015-05-21 23:27:27 +02:00
|
|
|
AndroidUtilities.copyFile(is, imageFile);
|
2014-05-17 01:05:49 +02:00
|
|
|
is.close();
|
|
|
|
}
|
2013-10-25 17:19:00 +02:00
|
|
|
|
2014-05-17 01:05:49 +02:00
|
|
|
BitmapFactory.Options opts = new BitmapFactory.Options();
|
|
|
|
opts.inJustDecodeBounds = true;
|
|
|
|
BitmapFactory.decodeFile(imageFile.getAbsolutePath(), opts);
|
2013-10-25 17:19:00 +02:00
|
|
|
|
2014-11-19 02:23:46 +01:00
|
|
|
int width = opts.outWidth / imageResize;
|
|
|
|
int height = opts.outHeight / imageResize;
|
|
|
|
int stride = width * 4;
|
|
|
|
|
|
|
|
final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
|
|
|
Utilities.loadBitmap(imageFile.getAbsolutePath(), bitmap, imageResize, width, height, stride);
|
2013-10-25 17:19:00 +02:00
|
|
|
|
2015-10-29 18:10:07 +01:00
|
|
|
imageName = String.format(Locale.US, "v7_emoji%.01fx_a_%d_%d.jpg", scale, page, page2);
|
2014-05-17 01:05:49 +02:00
|
|
|
imageFile = ApplicationLoader.applicationContext.getFileStreamPath(imageName);
|
|
|
|
if (!imageFile.exists()) {
|
|
|
|
InputStream is = ApplicationLoader.applicationContext.getAssets().open("emoji/" + imageName);
|
2015-05-21 23:27:27 +02:00
|
|
|
AndroidUtilities.copyFile(is, imageFile);
|
2014-05-17 01:05:49 +02:00
|
|
|
is.close();
|
2013-10-25 17:19:00 +02:00
|
|
|
}
|
|
|
|
|
2014-11-19 02:23:46 +01:00
|
|
|
Utilities.loadBitmap(imageFile.getAbsolutePath(), bitmap, imageResize, width, height, stride);
|
2013-10-25 17:19:00 +02:00
|
|
|
|
2014-11-06 22:34:47 +01:00
|
|
|
AndroidUtilities.runOnUIThread(new Runnable() {
|
2013-10-25 17:19:00 +02:00
|
|
|
@Override
|
|
|
|
public void run() {
|
2015-10-29 18:10:07 +01:00
|
|
|
emojiBmp[page][page2] = bitmap;
|
2014-08-22 16:24:33 +02:00
|
|
|
NotificationCenter.getInstance().postNotificationName(NotificationCenter.emojiDidLoaded);
|
2013-10-25 17:19:00 +02:00
|
|
|
}
|
|
|
|
});
|
2015-07-22 20:56:37 +02:00
|
|
|
} catch (Throwable x) {
|
2013-12-20 20:25:49 +01:00
|
|
|
FileLog.e("tmessages", "Error loading emoji", x);
|
2013-10-25 17:19:00 +02:00
|
|
|
}
|
2015-07-22 20:56:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public static void invalidateAll(View view) {
|
|
|
|
if (view instanceof ViewGroup) {
|
|
|
|
ViewGroup g = (ViewGroup) view;
|
|
|
|
for (int i = 0; i < g.getChildCount(); i++) {
|
|
|
|
invalidateAll(g.getChildAt(i));
|
|
|
|
}
|
|
|
|
} else if (view instanceof TextView) {
|
|
|
|
view.invalidate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-29 18:10:07 +01:00
|
|
|
public static String fixEmoji(String emoji) {
|
|
|
|
char ch;
|
|
|
|
int lenght = emoji.length();
|
|
|
|
for (int a = 0; a < lenght; a++) {
|
|
|
|
ch = emoji.charAt(a);
|
|
|
|
if (ch >= 0xD83C && ch <= 0xD83E) {
|
|
|
|
if (ch == 0xD83C && a < lenght - 1) {
|
|
|
|
ch = emoji.charAt(a + 1);
|
|
|
|
if (ch == 0xDE2F || ch == 0xDC04 || ch == 0xDE1A || ch == 0xDD7F) {
|
|
|
|
emoji = emoji.substring(0, a + 2) + "\uFE0F" + emoji.substring(a + 2);
|
|
|
|
lenght++;
|
|
|
|
a += 2;
|
|
|
|
} else {
|
|
|
|
a++;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
a++;
|
|
|
|
}
|
|
|
|
} else if (ch == 0x20E3) {
|
|
|
|
return emoji;
|
|
|
|
} else if (ch >= 0x203C && ch <= 0x3299) {
|
|
|
|
if (EmojiData.emojiToFE0FMap.containsKey(ch)) {
|
|
|
|
emoji = emoji.substring(0, a + 1) + "\uFE0F" + emoji.substring(a + 1);
|
|
|
|
lenght++;
|
|
|
|
a++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return emoji;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static EmojiDrawable getEmojiDrawable(CharSequence code) {
|
2015-07-22 20:56:37 +02:00
|
|
|
DrawableInfo info = rects.get(code);
|
|
|
|
if (info == null) {
|
2015-10-29 18:10:07 +01:00
|
|
|
FileLog.e("tmessages", "No drawable for emoji " + code);
|
2013-10-25 17:19:00 +02:00
|
|
|
return null;
|
|
|
|
}
|
2015-07-22 20:56:37 +02:00
|
|
|
EmojiDrawable ed = new EmojiDrawable(info);
|
|
|
|
ed.setBounds(0, 0, drawImgSize, drawImgSize);
|
|
|
|
return ed;
|
|
|
|
}
|
|
|
|
|
2015-10-29 18:10:07 +01:00
|
|
|
public static Drawable getEmojiBigDrawable(String code) {
|
2015-07-22 20:56:37 +02:00
|
|
|
EmojiDrawable ed = getEmojiDrawable(code);
|
|
|
|
if (ed == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
ed.setBounds(0, 0, bigImgSize, bigImgSize);
|
|
|
|
ed.fullSize = true;
|
|
|
|
return ed;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static class EmojiDrawable extends Drawable {
|
2014-06-11 01:05:54 +02:00
|
|
|
private DrawableInfo info;
|
2015-07-22 20:56:37 +02:00
|
|
|
private boolean fullSize = false;
|
2015-10-29 18:10:07 +01:00
|
|
|
private static Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG);
|
|
|
|
private static Rect rect = new Rect();
|
2015-07-22 20:56:37 +02:00
|
|
|
|
|
|
|
public EmojiDrawable(DrawableInfo i) {
|
|
|
|
info = i;
|
|
|
|
}
|
2014-10-30 22:27:41 +01:00
|
|
|
|
|
|
|
public DrawableInfo getDrawableInfo() {
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Rect getDrawRect() {
|
2015-10-29 18:10:07 +01:00
|
|
|
Rect original = getBounds();
|
|
|
|
int cX = original.centerX(), cY = original.centerY();
|
|
|
|
rect.left = cX - (fullSize ? bigImgSize : drawImgSize) / 2;
|
|
|
|
rect.right = cX + (fullSize ? bigImgSize : drawImgSize) / 2;
|
|
|
|
rect.top = cY - (fullSize ? bigImgSize : drawImgSize) / 2;
|
|
|
|
rect.bottom = cY + (fullSize ? bigImgSize : drawImgSize) / 2;
|
|
|
|
return rect;
|
2014-10-30 22:27:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2015-07-22 20:56:37 +02:00
|
|
|
public void draw(Canvas canvas) {
|
2015-10-29 18:10:07 +01:00
|
|
|
if (emojiBmp[info.page][info.page2] == null) {
|
|
|
|
if (loadingEmoji[info.page][info.page2]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
loadingEmoji[info.page][info.page2] = true;
|
|
|
|
Utilities.globalQueue.postRunnable(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
loadEmoji(info.page, info.page2);
|
|
|
|
loadingEmoji[info.page][info.page2] = false;
|
|
|
|
}
|
|
|
|
});
|
2015-07-22 20:56:37 +02:00
|
|
|
canvas.drawRect(getBounds(), placeholderPaint);
|
|
|
|
return;
|
|
|
|
}
|
2015-10-29 18:10:07 +01:00
|
|
|
|
2014-10-31 20:02:29 +01:00
|
|
|
Rect b;
|
|
|
|
if (fullSize) {
|
|
|
|
b = getDrawRect();
|
|
|
|
} else {
|
|
|
|
b = getBounds();
|
|
|
|
}
|
|
|
|
|
2016-03-16 13:26:32 +01:00
|
|
|
//if (!canvas.quickReject(b.left, b.top, b.right, b.bottom, Canvas.EdgeType.AA)) {
|
2015-11-26 22:04:02 +01:00
|
|
|
canvas.drawBitmap(emojiBmp[info.page][info.page2], info.rect, b, paint);
|
2016-03-16 13:26:32 +01:00
|
|
|
//}
|
2015-07-22 20:56:37 +02:00
|
|
|
}
|
2013-10-25 17:19:00 +02:00
|
|
|
|
2015-07-22 20:56:37 +02:00
|
|
|
@Override
|
|
|
|
public int getOpacity() {
|
2015-10-29 18:10:07 +01:00
|
|
|
return PixelFormat.TRANSPARENT;
|
2015-07-22 20:56:37 +02:00
|
|
|
}
|
2013-10-25 17:19:00 +02:00
|
|
|
|
2015-07-22 20:56:37 +02:00
|
|
|
@Override
|
|
|
|
public void setAlpha(int alpha) {
|
2013-10-25 17:19:00 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-07-22 20:56:37 +02:00
|
|
|
@Override
|
|
|
|
public void setColorFilter(ColorFilter cf) {
|
2013-10-25 17:19:00 +02:00
|
|
|
|
|
|
|
}
|
2015-07-22 20:56:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private static class DrawableInfo {
|
2014-06-11 01:05:54 +02:00
|
|
|
public Rect rect;
|
|
|
|
public byte page;
|
2015-10-29 18:10:07 +01:00
|
|
|
public byte page2;
|
2014-06-11 01:05:54 +02:00
|
|
|
|
2015-10-29 18:10:07 +01:00
|
|
|
public DrawableInfo(Rect r, byte p, byte p2) {
|
2015-07-22 20:56:37 +02:00
|
|
|
rect = r;
|
2014-06-11 01:05:54 +02:00
|
|
|
page = p;
|
2015-10-29 18:10:07 +01:00
|
|
|
page2 = p2;
|
2015-07-22 20:56:37 +02:00
|
|
|
}
|
|
|
|
}
|
2013-10-25 17:19:00 +02:00
|
|
|
|
|
|
|
private static boolean inArray(char c, char[] a) {
|
2014-05-17 01:05:49 +02:00
|
|
|
for (char cc : a) {
|
|
|
|
if (cc == c) {
|
2013-10-25 17:19:00 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-07-22 20:56:37 +02:00
|
|
|
public static CharSequence replaceEmoji(CharSequence cs, Paint.FontMetricsInt fontMetrics, int size, boolean createNew) {
|
2013-10-25 17:19:00 +02:00
|
|
|
if (cs == null || cs.length() == 0) {
|
|
|
|
return cs;
|
|
|
|
}
|
2015-07-22 20:56:37 +02:00
|
|
|
//SpannableStringLight.isFieldsAvailable();
|
|
|
|
//SpannableStringLight s = new SpannableStringLight(cs.toString());
|
2013-10-25 17:19:00 +02:00
|
|
|
Spannable s;
|
2015-07-22 20:56:37 +02:00
|
|
|
if (!createNew && cs instanceof Spannable) {
|
|
|
|
s = (Spannable) cs;
|
2013-10-25 17:19:00 +02:00
|
|
|
} else {
|
2015-07-22 20:56:37 +02:00
|
|
|
s = Spannable.Factory.getInstance().newSpannable(cs.toString());
|
2013-10-25 17:19:00 +02:00
|
|
|
}
|
|
|
|
long buf = 0;
|
2013-10-27 00:34:39 +02:00
|
|
|
int emojiCount = 0;
|
2015-10-29 18:10:07 +01:00
|
|
|
char c;
|
|
|
|
int startIndex = -1;
|
|
|
|
int startLength = 0;
|
|
|
|
int previousGoodIndex = 0;
|
|
|
|
StringBuilder emojiCode = new StringBuilder(16);
|
|
|
|
boolean nextIsSkinTone;
|
|
|
|
EmojiDrawable drawable;
|
|
|
|
EmojiSpan span;
|
|
|
|
int length = cs.length();
|
|
|
|
boolean doneEmoji = false;
|
2015-07-22 20:56:37 +02:00
|
|
|
//s.setSpansCount(emojiCount);
|
|
|
|
|
2014-06-13 17:03:06 +02:00
|
|
|
try {
|
2015-10-29 18:10:07 +01:00
|
|
|
for (int i = 0; i < length; i++) {
|
|
|
|
c = cs.charAt(i);
|
|
|
|
if (c >= 0xD83C && c <= 0xD83E || (buf != 0 && (buf & 0xFFFFFFFF00000000L) == 0 && (buf & 0xFFFF) == 0xD83C && (c >= 0xDDE6 && c <= 0xDDFF))) {
|
|
|
|
if (startIndex == -1) {
|
|
|
|
startIndex = i;
|
|
|
|
}
|
|
|
|
emojiCode.append(c);
|
|
|
|
startLength++;
|
2014-06-13 17:03:06 +02:00
|
|
|
buf <<= 16;
|
|
|
|
buf |= c;
|
|
|
|
} else if (buf > 0 && (c & 0xF000) == 0xD000) {
|
2015-10-29 18:10:07 +01:00
|
|
|
emojiCode.append(c);
|
|
|
|
startLength++;
|
2014-06-13 17:03:06 +02:00
|
|
|
buf = 0;
|
2015-10-29 18:10:07 +01:00
|
|
|
doneEmoji = true;
|
2014-06-13 17:03:06 +02:00
|
|
|
} else if (c == 0x20E3) {
|
|
|
|
if (i > 0) {
|
2015-10-29 18:10:07 +01:00
|
|
|
char c2 = cs.charAt(previousGoodIndex);
|
|
|
|
if ((c2 >= '0' && c2 <= '9') || c2 == '#' || c2 == '*') {
|
|
|
|
startIndex = previousGoodIndex;
|
|
|
|
startLength = i - previousGoodIndex + 1;
|
|
|
|
emojiCode.append(c2);
|
|
|
|
emojiCode.append(c);
|
|
|
|
doneEmoji = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if ((c == 0x00A9 || c == 0x00AE || c >= 0x203C && c <= 0x3299) && EmojiData.dataCharsMap.containsKey(c)) {
|
|
|
|
if (startIndex == -1) {
|
|
|
|
startIndex = i;
|
|
|
|
}
|
|
|
|
startLength++;
|
|
|
|
emojiCode.append(c);
|
|
|
|
doneEmoji = true;
|
|
|
|
} else if (startIndex != -1) {
|
|
|
|
emojiCode.setLength(0);
|
|
|
|
startIndex = -1;
|
|
|
|
startLength = 0;
|
|
|
|
doneEmoji = false;
|
|
|
|
}
|
|
|
|
previousGoodIndex = i;
|
|
|
|
for (int a = 0; a < 3; a++) {
|
|
|
|
if (i + 1 < length) {
|
|
|
|
c = cs.charAt(i + 1);
|
|
|
|
if (a == 1) {
|
|
|
|
if (c == 0x200D) {
|
|
|
|
emojiCode.append(c);
|
|
|
|
i++;
|
|
|
|
startLength++;
|
|
|
|
doneEmoji = false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (c >= 0xFE00 && c <= 0xFE0F) {
|
|
|
|
i++;
|
|
|
|
startLength++;
|
2014-06-13 17:03:06 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-10-29 18:10:07 +01:00
|
|
|
}
|
|
|
|
if (doneEmoji) {
|
|
|
|
if (i + 2 < length) {
|
|
|
|
if (cs.charAt(i + 1) == 0xD83C && cs.charAt(i + 2) >= 0xDFFB && cs.charAt(i + 2) <= 0xDFFF) {
|
|
|
|
emojiCode.append(cs.subSequence(i + 1, i + 3));
|
|
|
|
startLength += 2;
|
2015-06-29 19:12:11 +02:00
|
|
|
i += 2;
|
|
|
|
}
|
2013-10-25 17:19:00 +02:00
|
|
|
}
|
2015-10-29 18:10:07 +01:00
|
|
|
drawable = Emoji.getEmojiDrawable(emojiCode.subSequence(0, emojiCode.length()));
|
|
|
|
if (drawable != null) {
|
|
|
|
span = new EmojiSpan(drawable, DynamicDrawableSpan.ALIGN_BOTTOM, size, fontMetrics);
|
|
|
|
s.setSpan(span, startIndex, startIndex + startLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
|
|
|
emojiCount++;
|
|
|
|
}
|
|
|
|
startLength = 0;
|
|
|
|
startIndex = -1;
|
|
|
|
emojiCode.setLength(0);
|
|
|
|
doneEmoji = false;
|
2013-10-25 17:19:00 +02:00
|
|
|
}
|
2015-10-29 18:10:07 +01:00
|
|
|
if (emojiCount >= 50) { //654 new
|
2014-06-13 17:03:06 +02:00
|
|
|
break;
|
2013-10-25 17:19:00 +02:00
|
|
|
}
|
|
|
|
}
|
2014-06-13 17:03:06 +02:00
|
|
|
} catch (Exception e) {
|
|
|
|
FileLog.e("tmessages", e);
|
|
|
|
return cs;
|
2013-10-25 17:19:00 +02:00
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static class EmojiSpan extends ImageSpan {
|
2014-05-17 01:05:49 +02:00
|
|
|
private Paint.FontMetricsInt fontMetrics = null;
|
2014-10-30 22:27:41 +01:00
|
|
|
private int size = AndroidUtilities.dp(20);
|
2013-10-25 17:19:00 +02:00
|
|
|
|
2014-10-30 22:27:41 +01:00
|
|
|
public EmojiSpan(EmojiDrawable d, int verticalAlignment, int s, Paint.FontMetricsInt original) {
|
2013-10-25 17:19:00 +02:00
|
|
|
super(d, verticalAlignment);
|
2014-05-17 01:05:49 +02:00
|
|
|
fontMetrics = original;
|
2014-06-15 10:03:43 +02:00
|
|
|
if (original != null) {
|
|
|
|
size = Math.abs(fontMetrics.descent) + Math.abs(fontMetrics.ascent);
|
|
|
|
if (size == 0) {
|
2014-07-03 00:39:05 +02:00
|
|
|
size = AndroidUtilities.dp(20);
|
2014-06-15 10:03:43 +02:00
|
|
|
}
|
|
|
|
}
|
2013-10-25 17:19:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
|
|
|
|
if (fm == null) {
|
|
|
|
fm = new Paint.FontMetricsInt();
|
|
|
|
}
|
|
|
|
|
2014-05-17 01:05:49 +02:00
|
|
|
if (fontMetrics == null) {
|
|
|
|
int sz = super.getSize(paint, text, start, end, fm);
|
|
|
|
|
2014-07-03 00:39:05 +02:00
|
|
|
int offset = AndroidUtilities.dp(8);
|
|
|
|
int w = AndroidUtilities.dp(10);
|
2014-05-17 01:05:49 +02:00
|
|
|
fm.top = -w - offset;
|
|
|
|
fm.bottom = w - offset;
|
|
|
|
fm.ascent = -w - offset;
|
|
|
|
fm.leading = 0;
|
|
|
|
fm.descent = w - offset;
|
|
|
|
|
|
|
|
return sz;
|
|
|
|
} else {
|
|
|
|
if (fm != null) {
|
|
|
|
fm.ascent = fontMetrics.ascent;
|
|
|
|
fm.descent = fontMetrics.descent;
|
|
|
|
|
|
|
|
fm.top = fontMetrics.top;
|
|
|
|
fm.bottom = fontMetrics.bottom;
|
|
|
|
}
|
2014-06-15 10:03:43 +02:00
|
|
|
if (getDrawable() != null) {
|
|
|
|
getDrawable().setBounds(0, 0, size, size);
|
|
|
|
}
|
2014-05-17 01:05:49 +02:00
|
|
|
return size;
|
|
|
|
}
|
2013-10-25 17:19:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|