Files
Android/src/org/thoughtcrime/securesms/util/Emoji.java
Rhodey Orbits c6a4877391 Fix recent emoji sort order
The recent emoji tab was sorting emoji as last in last out instead
of last in first out. Also, if an emoji was already in the recent
emoji cache its order would not be updated on reinsert.

Fixes #2967
Closes #2946

// FREEBIE
2015-04-22 11:40:37 -07:00

475 lines
19 KiB
Java

package org.thoughtcrime.securesms.util;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Build;
import android.preference.PreferenceManager;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ImageSpan;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
import android.view.View;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.thoughtcrime.securesms.R;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.concurrent.ExecutorService;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Emoji {
private static final String TAG = Emoji.class.getSimpleName();
private static ExecutorService executor = Util.newSingleThreadedLifoExecutor();
public static final int[][] PAGES = {
{
0x263a, 0x1f60a, 0x1f600, 0x1f601, 0x1f602, 0x1f603, 0x1f604, 0x1f605,
0x1f606, 0x1f607, 0x1f608, 0x1f609, 0x1f62f, 0x1f610, 0x1f611, 0x1f615,
0x1f620, 0x1f62c, 0x1f621, 0x1f622, 0x1f634, 0x1f62e, 0x1f623, 0x1f624,
0x1f625, 0x1f626, 0x1f627, 0x1f628, 0x1f629, 0x1f630, 0x1f61f, 0x1f631,
0x1f632, 0x1f633, 0x1f635, 0x1f636, 0x1f637, 0x1f61e, 0x1f612, 0x1f60d,
0x1f61b, 0x1f61c, 0x1f61d, 0x1f60b, 0x1f617, 0x1f619, 0x1f618, 0x1f61a,
0x1f60e, 0x1f62d, 0x1f60c, 0x1f616, 0x1f614, 0x1f62a, 0x1f60f, 0x1f613,
0x1f62b, 0x1f64b, 0x1f64c, 0x1f64d, 0x1f645, 0x1f646, 0x1f647, 0x1f64e,
0x1f64f, 0x1f63a, 0x1f63c, 0x1f638, 0x1f639, 0x1f63b, 0x1f63d, 0x1f63f,
0x1f63e, 0x1f640, 0x1f648, 0x1f649, 0x1f64a, 0x1f4a9, 0x1f476, 0x1f466,
0x1f467, 0x1f468, 0x1f469, 0x1f474, 0x1f475, 0x1f48f, 0x1f491, 0x1f46a,
0x1f46b, 0x1f46c, 0x1f46d, 0x1f464, 0x1f465, 0x1f46e, 0x1f477, 0x1f481,
0x1f482, 0x1f46f, 0x1f470, 0x1f478, 0x1f385, 0x1f47c, 0x1f471, 0x1f472,
0x1f473, 0x1f483, 0x1f486, 0x1f487, 0x1f485, 0x1f47b, 0x1f479, 0x1f47a,
0x1f47d, 0x1f47e, 0x1f47f, 0x1f480, 0x1f4aa, 0x1f440, 0x1f442, 0x1f443,
0x1f463, 0x1f444, 0x1f445, 0x1f48b, 0x2764, 0x1f499, 0x1f49a, 0x1f49b,
0x1f49c, 0x1f493, 0x1f494, 0x1f495, 0x1f496, 0x1f497, 0x1f498, 0x1f49d,
0x1f49e, 0x1f49f, 0x1f44d, 0x1f44e, 0x1f44c, 0x270a, 0x270c, 0x270b,
0x1f44a, 0x261d, 0x1f446, 0x1f447, 0x1f448, 0x1f449, 0x1f44b, 0x1f44f,
0x1f450
},
{
0x1f530, 0x1f484, 0x1f45e, 0x1f45f, 0x1f451, 0x1f452, 0x1f3a9, 0x1f393,
0x1f453, 0x231a, 0x1f454, 0x1f455, 0x1f456, 0x1f457, 0x1f458, 0x1f459,
0x1f460, 0x1f461, 0x1f462, 0x1f45a, 0x1f45c, 0x1f4bc, 0x1f392, 0x1f45d,
0x1f45b, 0x1f4b0, 0x1f4b3, 0x1f4b2, 0x1f4b5, 0x1f4b4, 0x1f4b6, 0x1f4b7,
0x1f4b8, 0x1f4b1, 0x1f4b9, 0x1f52b, 0x1f52a, 0x1f4a3, 0x1f489, 0x1f48a,
0x1f6ac, 0x1f514, 0x1f515, 0x1f6aa, 0x1f52c, 0x1f52d, 0x1f52e, 0x1f526,
0x1f50b, 0x1f50c, 0x1f4dc, 0x1f4d7, 0x1f4d8, 0x1f4d9, 0x1f4da, 0x1f4d4,
0x1f4d2, 0x1f4d1, 0x1f4d3, 0x1f4d5, 0x1f4d6, 0x1f4f0, 0x1f4db, 0x1f383,
0x1f384, 0x1f380, 0x1f381, 0x1f382, 0x1f388, 0x1f386, 0x1f387, 0x1f389,
0x1f38a, 0x1f38d, 0x1f38f, 0x1f38c, 0x1f390, 0x1f38b, 0x1f38e, 0x1f4f1,
0x1f4f2, 0x1f4df, 0x260e, 0x1f4de, 0x1f4e0, 0x1f4e6, 0x2709, 0x1f4e8,
0x1f4e9, 0x1f4ea, 0x1f4eb, 0x1f4ed, 0x1f4ec, 0x1f4ee, 0x1f4e4, 0x1f4e5,
0x1f4ef, 0x1f4e2, 0x1f4e3, 0x1f4e1, 0x1f4ac, 0x1f4ad, 0x2712, 0x270f,
0x1f4dd, 0x1f4cf, 0x1f4d0, 0x1f4cd, 0x1f4cc, 0x1f4ce, 0x2702, 0x1f4ba,
0x1f4bb, 0x1f4bd, 0x1f4be, 0x1f4bf, 0x1f4c6, 0x1f4c5, 0x1f4c7, 0x1f4cb,
0x1f4c1, 0x1f4c2, 0x1f4c3, 0x1f4c4, 0x1f4ca, 0x1f4c8, 0x1f4c9, 0x26fa,
0x1f3a1, 0x1f3a2, 0x1f3a0, 0x1f3aa, 0x1f3a8, 0x1f3ac, 0x1f3a5, 0x1f4f7,
0x1f4f9, 0x1f3a6, 0x1f3ad, 0x1f3ab, 0x1f3ae, 0x1f3b2, 0x1f3b0, 0x1f0cf,
0x1f3b4, 0x1f004, 0x1f3af, 0x1f4fa, 0x1f4fb, 0x1f4c0, 0x1f4fc, 0x1f3a7,
0x1f3a4, 0x1f3b5, 0x1f3b6, 0x1f3bc, 0x1f3bb, 0x1f3b9, 0x1f3b7, 0x1f3ba,
0x1f3b8, 0x303d
},
{
0x1f415, 0x1f436, 0x1f429, 0x1f408, 0x1f431, 0x1f400, 0x1f401, 0x1f42d,
0x1f439, 0x1f422, 0x1f407, 0x1f430, 0x1f413, 0x1f414, 0x1f423, 0x1f424,
0x1f425, 0x1f426, 0x1f40f, 0x1f411, 0x1f410, 0x1f43a, 0x1f403, 0x1f402,
0x1f404, 0x1f42e, 0x1f434, 0x1f417, 0x1f416, 0x1f437, 0x1f43d, 0x1f438,
0x1f40d, 0x1f43c, 0x1f427, 0x1f418, 0x1f428, 0x1f412, 0x1f435, 0x1f406,
0x1f42f, 0x1f43b, 0x1f42b, 0x1f42a, 0x1f40a, 0x1f433, 0x1f40b, 0x1f41f,
0x1f420, 0x1f421, 0x1f419, 0x1f41a, 0x1f42c, 0x1f40c, 0x1f41b, 0x1f41c,
0x1f41d, 0x1f41e, 0x1f432, 0x1f409, 0x1f43e, 0x1f378, 0x1f37a, 0x1f37b,
0x1f377, 0x1f379, 0x1f376, 0x2615, 0x1f375, 0x1f37c, 0x1f374, 0x1f368,
0x1f367, 0x1f366, 0x1f369, 0x1f370, 0x1f36a, 0x1f36b, 0x1f36c, 0x1f36d,
0x1f36e, 0x1f36f, 0x1f373, 0x1f354, 0x1f35f, 0x1f35d, 0x1f355, 0x1f356,
0x1f357, 0x1f364, 0x1f363, 0x1f371, 0x1f35e, 0x1f35c, 0x1f359, 0x1f35a,
0x1f35b, 0x1f372, 0x1f365, 0x1f362, 0x1f361, 0x1f358, 0x1f360, 0x1f34c,
0x1f34e, 0x1f34f, 0x1f34a, 0x1f34b, 0x1f344, 0x1f345, 0x1f346, 0x1f347,
0x1f348, 0x1f349, 0x1f350, 0x1f351, 0x1f352, 0x1f353, 0x1f34d, 0x1f330,
0x1f331, 0x1f332, 0x1f333, 0x1f334, 0x1f335, 0x1f337, 0x1f338, 0x1f339,
0x1f340, 0x1f341, 0x1f342, 0x1f343, 0x1f33a, 0x1f33b, 0x1f33c, 0x1f33d,
0x1f33e, 0x1f33f, 0x2600, 0x1f308, 0x26c5, 0x2601, 0x1f301, 0x1f302,
0x2614, 0x1f4a7, 0x26a1, 0x1f300, 0x2744, 0x26c4, 0x1f319, 0x1f31e,
0x1f31d, 0x1f31a, 0x1f31b, 0x1f31c, 0x1f311, 0x1f312, 0x1f313, 0x1f314,
0x1f315, 0x1f316, 0x1f317, 0x1f318, 0x1f391, 0x1f304, 0x1f305, 0x1f307,
0x1f306, 0x1f303, 0x1f30c, 0x1f309, 0x1f30a, 0x1f30b, 0x1f30e, 0x1f30f,
0x1f30d, 0x1f310
},
{
0x1f3e0, 0x1f3e1, 0x1f3e2, 0x1f3e3, 0x1f3e4, 0x1f3e5, 0x1f3e6, 0x1f3e7,
0x1f3e8, 0x1f3e9, 0x1f3ea, 0x1f3eb, 0x26ea, 0x26f2, 0x1f3ec, 0x1f3ef,
0x1f3f0, 0x1f3ed, 0x1f5fb, 0x1f5fc, 0x1f5fd, 0x1f5fe, 0x1f5ff, 0x2693,
0x1f3ee, 0x1f488, 0x1f527, 0x1f528, 0x1f529, 0x1f6bf, 0x1f6c1, 0x1f6c0,
0x1f6bd, 0x1f6be, 0x1f3bd, 0x1f3a3, 0x1f3b1, 0x1f3b3, 0x26be, 0x26f3,
0x1f3be, 0x26bd, 0x1f3bf, 0x1f3c0, 0x1f3c1, 0x1f3c2, 0x1f3c3, 0x1f3c4,
0x1f3c6, 0x1f3c7, 0x1f40e, 0x1f3c8, 0x1f3c9, 0x1f3ca, 0x1f682, 0x1f683,
0x1f684, 0x1f685, 0x1f686, 0x1f687, 0x24c2, 0x1f688, 0x1f68a, 0x1f68b,
0x1f68c, 0x1f68d, 0x1f68e, 0x1f68f, 0x1f690, 0x1f691, 0x1f692, 0x1f693,
0x1f694, 0x1f695, 0x1f696, 0x1f697, 0x1f698, 0x1f699, 0x1f69a, 0x1f69b,
0x1f69c, 0x1f69d, 0x1f69e, 0x1f69f, 0x1f6a0, 0x1f6a1, 0x1f6a2, 0x1f6a3,
0x1f681, 0x2708, 0x1f6c2, 0x1f6c3, 0x1f6c4, 0x1f6c5, 0x26f5, 0x1f6b2,
0x1f6b3, 0x1f6b4, 0x1f6b5, 0x1f6b7, 0x1f6b8, 0x1f689, 0x1f680, 0x1f6a4,
0x1f6b6, 0x26fd, 0x1f17f, 0x1f6a5, 0x1f6a6, 0x1f6a7, 0x1f6a8, 0x2668,
0x1f48c, 0x1f48d, 0x1f48e, 0x1f490, 0x1f492, 0xfe4e5, 0xfe4e6, 0xfe4e7,
0xfe4e8, 0xfe4e9, 0xfe4ea, 0xfe4eb, 0xfe4ec, 0xfe4ed, 0xfe4ee
},
{
0x1f51d, 0x1f519, 0x1f51b, 0x1f51c, 0x1f51a, 0x23f3, 0x231b, 0x23f0,
0x2648, 0x2649, 0x264a, 0x264b, 0x264c, 0x264d, 0x264e, 0x264f,
0x2650, 0x2651, 0x2652, 0x2653, 0x26ce, 0x1f531, 0x1f52f, 0x1f6bb,
0x1f6ae, 0x1f6af, 0x1f6b0, 0x1f6b1, 0x1f170, 0x1f171, 0x1f18e, 0x1f17e,
0x1f4ae, 0x1f4af, 0x1f520, 0x1f521, 0x1f522, 0x1f523, 0x1f524, 0x27bf,
0x1f4f6, 0x1f4f3, 0x1f4f4, 0x1f4f5, 0x1f6b9, 0x1f6ba, 0x1f6bc, 0x267f,
0x267b, 0x1f6ad, 0x1f6a9, 0x26a0, 0x1f201, 0x1f51e, 0x26d4, 0x1f192,
0x1f197, 0x1f195, 0x1f198, 0x1f199, 0x1f193, 0x1f196, 0x1f19a, 0x1f232,
0x1f233, 0x1f234, 0x1f235, 0x1f236, 0x1f237, 0x1f238, 0x1f239, 0x1f202,
0x1f23a, 0x1f250, 0x1f251, 0x3299, 0x00ae, 0x00a9, 0x2122, 0x1f21a,
0x1f22f, 0x3297, 0x2b55, 0x274c, 0x274e, 0x2139, 0x1f6ab, 0x2705,
0x2714, 0x1f517, 0x2734, 0x2733, 0x2795, 0x2796, 0x2716, 0x2797,
0x1f4a0, 0x1f4a1, 0x1f4a4, 0x1f4a2, 0x1f525, 0x1f4a5, 0x1f4a8, 0x1f4a6,
0x1f4ab, 0x1f55b, 0x1f567, 0x1f550, 0x1f55c, 0x1f551, 0x1f55d, 0x1f552,
0x1f55e, 0x1f553, 0x1f55f, 0x1f554, 0x1f560, 0x1f555, 0x1f561, 0x1f556,
0x1f562, 0x1f557, 0x1f563, 0x1f558, 0x1f564, 0x1f559, 0x1f565, 0x1f55a,
0x1f566, 0x2195, 0x2b06, 0x2197, 0x27a1, 0x2198, 0x2b07, 0x2199,
0x2b05, 0x2196, 0x2194, 0x2934, 0x2935, 0x23ea, 0x23eb, 0x23ec,
0x23e9, 0x25c0, 0x25b6, 0x1f53d, 0x1f53c, 0x2747, 0x2728, 0x1f534,
0x1f535, 0x26aa, 0x26ab, 0x1f533, 0x1f532, 0x2b50, 0x1f31f, 0x1f320,
0x25ab, 0x25aa, 0x25fd, 0x25fe, 0x25fb, 0x25fc, 0x2b1c, 0x2b1b,
0x1f538, 0x1f539, 0x1f536, 0x1f537, 0x1f53a, 0x1f53b, 0x1f51f, /*0x20e3,*/
0x2754, 0x2753, 0x2755, 0x2757, 0x203c, 0x2049, 0x3030, 0x27b0,
0x2660, 0x2665, 0x2663, 0x2666, 0x1f194, 0x1f511, 0x21a9, 0x1f191,
0x1f50d, 0x1f512, 0x1f513, 0x21aa, 0x1f510, 0x2611, 0x1f518, 0x1f50e,
0x1f516, 0x1f50f, 0x1f503, 0x1f500, 0x1f501, 0x1f502, 0x1f504, 0x1f4e7,
0x1f505, 0x1f506, 0x1f507, 0x1f508, 0x1f509, 0x1f50a
}
};
private static final SparseArray<DrawInfo> offsets;
static {
offsets = new SparseArray<DrawInfo>();
for (int i = 0; i < PAGES.length; i++) {
for (int j = 0; j < PAGES[i].length; j++) {
offsets.put(PAGES[i][j], new DrawInfo(i, j));
}
}
}
private static Bitmap[] bitmaps = new Bitmap[PAGES.length];
private static Emoji instance = null;
public synchronized static Emoji getInstance(Context context) {
if (instance == null) {
instance = new Emoji(context);
}
return instance;
}
@SuppressWarnings("MalformedRegex")
// 0x20a0-0x32ff 0x1f00-0x1fff 0xfe4e5-0xfe4ee
// |==== 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_HUGE = 1.00;
public static final double EMOJI_LARGE = 0.75;
public static final double EMOJI_SMALL = 0.50;
public static final int EMOJI_RAW_SIZE = 128;
public static final int EMOJI_PER_ROW = 16;
private final Context context;
private final int bigDrawSize;
private Emoji(Context context) {
this.context = context.getApplicationContext();
this.bigDrawSize = context.getResources().getDimensionPixelSize(R.dimen.emoji_drawer_size);
}
private void preloadPage(final int page, final PageLoadedListener pageLoadListener) {
executor.submit(new Runnable() {
@Override
public void run() {
try {
loadPage(page);
if (pageLoadListener != null) pageLoadListener.onPageLoaded();
} catch (IOException ioe) {
Log.w("Emoji", ioe);
}
}
});
}
private void loadPage(int page) throws IOException {
if (page < 0 || page >= PAGES.length) {
throw new IndexOutOfBoundsException("can't load page that doesn't exist");
}
if (bitmaps[page] != null) return;
try {
final String file = "emoji_" + page + "_wrapped.png";
final InputStream measureStream = context.getAssets().open(file);
final InputStream bitmapStream = context.getAssets().open(file);
bitmaps[page] = BitmapUtil.createScaledBitmap(measureStream, bitmapStream, (float) bigDrawSize / (float) EMOJI_RAW_SIZE);
} catch (IOException ioe) {
Log.w("Emoji", ioe);
throw ioe;
} catch (BitmapDecodingException bde) {
Log.w("Emoji", bde);
throw new AssertionError("emoji sprite asset is corrupted or android decoding is broken");
}
}
public SpannableString emojify(String text, PageLoadedListener pageLoadedListener) {
return emojify(new SpannableString(text), pageLoadedListener);
}
public SpannableString emojify(SpannableString text, PageLoadedListener pageLoadedListener) {
return emojify(text, EMOJI_LARGE, pageLoadedListener);
}
public SpannableString emojify(SpannableString text, double size, PageLoadedListener pageLoadedListener) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) return text;
Matcher matches = EMOJI_RANGE.matcher(text);
while (matches.find()) {
String resource = Integer.toHexString(matches.group().codePointAt(0));
Drawable drawable = getEmojiDrawable(resource, size, pageLoadedListener);
if (drawable != null) {
ImageSpan imageSpan = new ImageSpan(drawable, ImageSpan.ALIGN_BOTTOM);
text.setSpan(imageSpan, matches.start(), matches.end(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
return text;
}
public Pair<Integer, Drawable> getRecentlyUsed(int position, double size, PageLoadedListener pageLoadedListener) {
String[] recentlyUsed = EmojiLRU.getRecentlyUsed(context);
String code = recentlyUsed[recentlyUsed.length - 1 - position];
return new Pair<Integer, Drawable>(Integer.parseInt(code, 16), getEmojiDrawable(code, size, pageLoadedListener));
}
public void setRecentlyUsed(String emojiCode) {
EmojiLRU.putRecentlyUsed(context, emojiCode);
}
public int getRecentlyUsedAssetCount() {
return EmojiLRU.getRecentlyUsedCount(context);
}
public Drawable getEmojiDrawable(String emojiCode, double size, PageLoadedListener pageLoadedListener) {
return getEmojiDrawable(offsets.get(Integer.parseInt(emojiCode, 16)), size, pageLoadedListener);
}
public Drawable getEmojiDrawable(DrawInfo drawInfo, double size, PageLoadedListener pageLoadedListener) {
if (drawInfo == null) {
return null;
}
final Drawable drawable = new EmojiDrawable(drawInfo, bigDrawSize);
drawable.setBounds(0, 0, (int) ((double) bigDrawSize * size), (int) ((double) bigDrawSize * size));
if (bitmaps[drawInfo.page] == null) {
preloadPage(drawInfo.page, pageLoadedListener);
}
return drawable;
}
private static class EmojiLRU {
private static SharedPreferences prefs = null;
private static LinkedHashSet<String> recentlyUsed = null;
private static final String EMOJI_LRU_PREFERENCE = "pref_recent_emoji";
private static final int EMOJI_LRU_SIZE = 50;
private static void initializeCache(Context context) {
if (prefs == null) {
prefs = PreferenceManager.getDefaultSharedPreferences(context);
}
String serialized = prefs.getString(EMOJI_LRU_PREFERENCE, "[]");
try {
recentlyUsed = JsonUtils.getMapper().readValue(serialized, TypeFactory.defaultInstance()
.constructCollectionType(LinkedHashSet.class, String.class));
} catch (IOException e) {
Log.w(TAG, e);
recentlyUsed = new LinkedHashSet<>();
}
}
public static String[] getRecentlyUsed(Context context) {
if (recentlyUsed == null) initializeCache(context);
return recentlyUsed.toArray(new String[recentlyUsed.size()]);
}
public static int getRecentlyUsedCount(Context context) {
if (recentlyUsed == null) initializeCache(context);
return recentlyUsed.size();
}
public static void putRecentlyUsed(Context context, String asset) {
if (recentlyUsed == null) initializeCache(context);
if (prefs == null) {
prefs = PreferenceManager.getDefaultSharedPreferences(context);
}
recentlyUsed.remove(asset);
recentlyUsed.add(asset);
if (recentlyUsed.size() > EMOJI_LRU_SIZE) {
Iterator<String> iterator = recentlyUsed.iterator();
iterator.next();
iterator.remove();
}
final LinkedHashSet<String> latestRecentlyUsed = new LinkedHashSet<String>(recentlyUsed);
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
try {
String serialized = JsonUtils.toJson(latestRecentlyUsed);
prefs.edit()
.putString(EMOJI_LRU_PREFERENCE, serialized)
.apply();
} catch (IOException e) {
Log.w(TAG, e);
}
return null;
}
}.execute();
}
}
public static class EmojiDrawable extends Drawable {
private final int index;
private final int page;
private final int emojiSize;
private static final Paint paint;
private Bitmap bmp;
static {
paint = new Paint();
paint.setFilterBitmap(true);
}
public EmojiDrawable(DrawInfo info, int emojiSize) {
this.index = info.index;
this.page = info.page;
this.emojiSize = emojiSize;
}
@Override
public void draw(Canvas canvas) {
if (bitmaps[page] == null) {
Log.w("Emoji", "bitmap for this page was null");
return;
}
if (bmp == null) {
bmp = bitmaps[page];
}
Rect b = copyBounds();
// int cX = b.centerX(), cY = b.centerY();
// b.left = cX - emojiSize / 2;
// b.right = cX + emojiSize / 2;
// b.top = cY - emojiSize / 2;
// b.bottom = cY + emojiSize / 2;
final int row = index / EMOJI_PER_ROW;
final int row_index = index % EMOJI_PER_ROW;
canvas.drawBitmap(bmp,
new Rect(row_index * emojiSize,
row * emojiSize,
(row_index + 1) * emojiSize,
(row + 1) * emojiSize),
b,
paint);
}
@Override
public int getOpacity() {
return 0;
}
@Override
public void setAlpha(int alpha) { }
@Override
public void setColorFilter(ColorFilter cf) { }
@Override
public String toString() {
return "EmojiDrawable{" +
"page=" + page +
", index=" + index +
'}';
}
}
public static interface PageLoadedListener {
public void onPageLoaded();
}
public static class InvalidatingPageLoadedListener implements PageLoadedListener {
private final View view;
public InvalidatingPageLoadedListener(final View view) {
this.view = view;
}
@Override
public void onPageLoaded() {
view.post(new Runnable() {
@Override
public void run() {
view.invalidate();
}
});
}
@Override
public String toString() {
return "InvalidatingPageLoadedListener{}";
}
}
public static class DrawInfo {
int page;
int index;
public DrawInfo(final int page, final int index) {
this.page = page;
this.index = index;
}
@Override
public String toString() {
return "DrawInfo{" +
"page=" + page +
", index=" + index +
'}';
}
}
}