From cf420de65fa45a551b0a9c15b42db57a0d26db4a Mon Sep 17 00:00:00 2001 From: Jake McGinty Date: Thu, 14 May 2015 21:08:37 -0700 Subject: [PATCH] fix recent emoji pane 1) Make recent list properly update and invalidate. 2) Show most-recently-used first. 3) Refactoring Closes #3171 // FREEBIE --- .../components/emoji/EmojiDrawer.java | 6 +- .../components/emoji/EmojiEditText.java | 11 +- .../components/emoji/EmojiPageFragment.java | 13 +- .../components/emoji/EmojiPageModel.java | 17 +- .../components/emoji/EmojiProvider.java | 240 +++++++++--------- .../components/emoji/EmojiTextView.java | 27 +- .../emoji/InvalidatingDrawableSpan.java | 12 + .../emoji/PostInvalidateCallback.java | 25 ++ .../emoji/RecentEmojiPageModel.java | 20 +- .../emoji/StaticEmojiPageModel.java | 4 +- src/org/thoughtcrime/securesms/util/Util.java | 7 + 11 files changed, 210 insertions(+), 172 deletions(-) create mode 100644 src/org/thoughtcrime/securesms/components/emoji/InvalidatingDrawableSpan.java create mode 100644 src/org/thoughtcrime/securesms/components/emoji/PostInvalidateCallback.java diff --git a/src/org/thoughtcrime/securesms/components/emoji/EmojiDrawer.java b/src/org/thoughtcrime/securesms/components/emoji/EmojiDrawer.java index 577e19984f..d7ace2d1f7 100644 --- a/src/org/thoughtcrime/securesms/components/emoji/EmojiDrawer.java +++ b/src/org/thoughtcrime/securesms/components/emoji/EmojiDrawer.java @@ -16,7 +16,6 @@ import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.RelativeLayout; import com.astuetz.PagerSlidingTabStrip; @@ -38,6 +37,7 @@ public class EmojiDrawer extends Fragment { private KeyboardAwareLinearLayout container; private ViewPager pager; private PagerSlidingTabStrip strip; + private RecentEmojiPageModel recentModel; public static EmojiDrawer newInstance(@ArrayRes int categories, @ArrayRes int icons) { final EmojiDrawer fragment = new EmojiDrawer(); @@ -104,6 +104,7 @@ public class EmojiDrawer extends Fragment { getArguments().getInt("icons")), new EmojiSelectionListener() { @Override public void onEmojiSelected(int emojiCode) { + recentModel.onCodePointSelected(emojiCode); composeText.insertEmoji(emojiCode); } })); @@ -114,7 +115,8 @@ public class EmojiDrawer extends Fragment { final int[] icons = ResUtil.getResourceIds(getActivity(), iconsRes); final int[] pages = ResUtil.getResourceIds(getActivity(), pagesRes); final List models = new LinkedList<>(); - models.add(new RecentEmojiPageModel(getActivity())); + recentModel = new RecentEmojiPageModel(getActivity()); + models.add(recentModel); for (int i = 0; i < icons.length; i++) { models.add(new StaticEmojiPageModel(icons[i], getResources().getIntArray(pages[i]))); } diff --git a/src/org/thoughtcrime/securesms/components/emoji/EmojiEditText.java b/src/org/thoughtcrime/securesms/components/emoji/EmojiEditText.java index baa3b811c6..1407782e7c 100644 --- a/src/org/thoughtcrime/securesms/components/emoji/EmojiEditText.java +++ b/src/org/thoughtcrime/securesms/components/emoji/EmojiEditText.java @@ -3,29 +3,26 @@ package org.thoughtcrime.securesms.components.emoji; import android.content.Context; import android.support.v7.widget.AppCompatEditText; import android.util.AttributeSet; -import android.util.Log; -import org.thoughtcrime.securesms.components.emoji.EmojiProvider.InvalidatingPageLoadedListener; public class EmojiEditText extends AppCompatEditText { public EmojiEditText(Context context) { super(context); - init(); } public EmojiEditText(Context context, AttributeSet attrs) { super(context, attrs); - init(); } public EmojiEditText(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - init(); } - private void init() { + @Override public void setText(CharSequence text, BufferType type) { + super.setText(EmojiProvider.getInstance(getContext()).emojify(text, EmojiProvider.EMOJI_SMALL, new PostInvalidateCallback(this)), + BufferType.SPANNABLE); } public void insertEmoji(int codePoint) { @@ -34,7 +31,7 @@ public class EmojiEditText extends AppCompatEditText { final char[] chars = Character.toChars(codePoint); final CharSequence text = EmojiProvider.getInstance(getContext()).emojify(new String(chars), EmojiProvider.EMOJI_SMALL, - new InvalidatingPageLoadedListener(this)); + new PostInvalidateCallback(this)); getText().replace(Math.min(start, end), Math.max(start, end), text); setSelection(end + chars.length); diff --git a/src/org/thoughtcrime/securesms/components/emoji/EmojiPageFragment.java b/src/org/thoughtcrime/securesms/components/emoji/EmojiPageFragment.java index c9e7f33d39..40789713be 100644 --- a/src/org/thoughtcrime/securesms/components/emoji/EmojiPageFragment.java +++ b/src/org/thoughtcrime/securesms/components/emoji/EmojiPageFragment.java @@ -17,7 +17,7 @@ import android.widget.GridView; import android.widget.ImageView; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.components.emoji.EmojiProvider.InvalidatingPageLoadedListener; +import org.thoughtcrime.securesms.components.emoji.EmojiPageModel.OnModelChangedListener; public class EmojiPageFragment extends Fragment { private static final String TAG = EmojiPageFragment.class.getSimpleName(); @@ -42,11 +42,16 @@ public class EmojiPageFragment extends Fragment { grid.setColumnWidth(getResources().getDimensionPixelSize(R.dimen.emoji_drawer_size) + 2 * getResources().getDimensionPixelSize(R.dimen.emoji_drawer_item_padding)); grid.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { - model.onCodePointSelected((Integer)view.getTag()); if (listener != null) listener.onEmojiSelected((Integer)view.getTag()); } }); grid.setAdapter(new EmojiGridAdapter(getActivity(), model)); + model.setOnModelChangedListener(new OnModelChangedListener() { + @Override public void onModelChanged() { + ((EmojiGridAdapter)grid.getAdapter()).notifyDataSetChanged(); + } + }); + return view; } @@ -99,9 +104,7 @@ public class EmojiPageFragment extends Fragment { final Integer unicodeTag = model.getCodePoints()[position]; final EmojiProvider provider = EmojiProvider.getInstance(context); - final Drawable drawable = provider.getEmojiDrawable(unicodeTag, - EmojiProvider.EMOJI_HUGE, - new InvalidatingPageLoadedListener(view)); + final Drawable drawable = provider.getEmojiDrawable(unicodeTag, EmojiProvider.EMOJI_HUGE); view.setImageDrawable(drawable); view.setPadding(pad, pad, pad, pad); diff --git a/src/org/thoughtcrime/securesms/components/emoji/EmojiPageModel.java b/src/org/thoughtcrime/securesms/components/emoji/EmojiPageModel.java index efe0b4eee3..7c4f4b25ba 100644 --- a/src/org/thoughtcrime/securesms/components/emoji/EmojiPageModel.java +++ b/src/org/thoughtcrime/securesms/components/emoji/EmojiPageModel.java @@ -1,7 +1,16 @@ package org.thoughtcrime.securesms.components.emoji; -public interface EmojiPageModel { - int getIconRes(); - int[] getCodePoints(); - void onCodePointSelected(int codePoint); +public abstract class EmojiPageModel { + protected OnModelChangedListener listener; + + public abstract int getIconRes(); + public abstract int[] getCodePoints(); + + public void setOnModelChangedListener(OnModelChangedListener listener) { + this.listener = listener; + } + + interface OnModelChangedListener { + void onModelChanged(); + } } diff --git a/src/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java b/src/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java index 858a3f7d14..50a6d09c70 100644 --- a/src/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java +++ b/src/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java @@ -8,18 +8,20 @@ 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.Handler; import android.os.Looper; import android.text.Spannable; import android.text.SpannableStringBuilder; -import android.text.style.ImageSpan; import android.util.Log; import android.util.SparseArray; -import android.view.View; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.util.BitmapDecodingException; import org.thoughtcrime.securesms.util.BitmapUtil; +import org.thoughtcrime.securesms.util.FutureTaskListener; +import org.thoughtcrime.securesms.util.ListenableFutureTask; import org.thoughtcrime.securesms.util.ResUtil; import org.thoughtcrime.securesms.util.Util; @@ -27,17 +29,14 @@ import java.io.IOException; import java.io.InputStream; import java.lang.ref.SoftReference; import java.util.Arrays; -import java.util.concurrent.ExecutorService; +import java.util.concurrent.Callable; import java.util.regex.Matcher; import java.util.regex.Pattern; public class EmojiProvider { - private static final String TAG = EmojiProvider.class.getSimpleName(); - private static final ExecutorService executor = Util.newSingleThreadedLifoExecutor(); - private static volatile EmojiProvider instance = null; - private static final SparseArray> bitmaps = new SparseArray<>(); - private static final Paint paint = new Paint(); - private static final Handler handler = new Handler(Looper.getMainLooper()); + private static final String TAG = EmojiProvider.class.getSimpleName(); + private static volatile EmojiProvider instance = null; + private static final Paint paint = new Paint(); static { paint.setFilterBitmap(true); } private final SparseArray offsets = new SparseArray<>(); @@ -55,7 +54,7 @@ public class EmojiProvider { private final Context context; private final int bigDrawSize; - private final int[] pages; + private final Handler handler = new Handler(Looper.getMainLooper()); public static EmojiProvider getInstance(Context context) { if (instance == null) { @@ -69,72 +68,28 @@ public class EmojiProvider { } private EmojiProvider(Context context) { - this.context = context.getApplicationContext(); + int[] pages = ResUtil.getResourceIds(context, R.array.emoji_categories); + + this.context = context.getApplicationContext(); this.bigDrawSize = context.getResources().getDimensionPixelSize(R.dimen.emoji_drawer_size); - this.pages = ResUtil.getResourceIds(context, R.array.emoji_categories); for (int i = 0; i < pages.length; i++) { - final int[] page = context.getResources().getIntArray(pages[i]); - for (int j = 0; j < page.length; j++) { - offsets.put(page[j], new DrawInfo(i, j)); + final EmojiPageBitmap page = new EmojiPageBitmap(i); + final int[] codePoints = context.getResources().getIntArray(pages[i]); + for (int j = 0; j < codePoints.length; j++) { + offsets.put(codePoints[j], new DrawInfo(page, j)); } } } - 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(TAG, 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.get(page) != null && bitmaps.get(page).get() != null) return; - - try { - final String file = "emoji_" + page + "_wrapped.png"; - final InputStream measureStream = context.getAssets().open(file); - final InputStream bitmapStream = context.getAssets().open(file); - final Bitmap bitmap = BitmapUtil.createScaledBitmap(measureStream, bitmapStream, (float) bigDrawSize / (float) EMOJI_RAW_SIZE); - bitmaps.put(page, new SoftReference<>(bitmap)); - Log.w(TAG, "onPageLoaded(" + page + ")"); - } catch (IOException ioe) { - Log.w(TAG, ioe); - throw ioe; - } catch (BitmapDecodingException bde) { - Log.w(TAG, bde); - throw new AssertionError("emoji sprite asset is corrupted or android decoding is broken"); - } - } - - public CharSequence emojify(CharSequence text, PageLoadedListener pageLoadedListener) { - return emojify(text, EMOJI_LARGE, pageLoadedListener); - } - - public CharSequence emojify(CharSequence text, double size, PageLoadedListener pageLoadedListener) { + public CharSequence emojify(CharSequence text, double size, Callback callback) { Matcher matches = EMOJI_RANGE.matcher(text); SpannableStringBuilder builder = new SpannableStringBuilder(text); while (matches.find()) { int codePoint = matches.group().codePointAt(0); - Drawable drawable = getEmojiDrawable(codePoint, size, pageLoadedListener); + Drawable drawable = getEmojiDrawable(codePoint, size); if (drawable != null) { - ImageSpan imageSpan = new ImageSpan(drawable, ImageSpan.ALIGN_BOTTOM); - char[] chars = new char[matches.end() - matches.start()]; - Arrays.fill(chars, ' '); - builder.setSpan(imageSpan, matches.start(), matches.end(), + builder.setSpan(new InvalidatingDrawableSpan(drawable, callback), matches.start(), matches.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } @@ -142,51 +97,52 @@ public class EmojiProvider { return builder; } - public Drawable getEmojiDrawable(int emojiCode, double size, PageLoadedListener pageLoadedListener) { - return getEmojiDrawable(offsets.get(emojiCode), size, pageLoadedListener); + public Drawable getEmojiDrawable(int emojiCode, double size) { + return getEmojiDrawable(offsets.get(emojiCode), size); } - private Drawable getEmojiDrawable(DrawInfo drawInfo, double size, PageLoadedListener pageLoadedListener) { - if (drawInfo == null) { - return null; - } - final Drawable drawable = new EmojiDrawable(drawInfo, bigDrawSize); + private Drawable getEmojiDrawable(DrawInfo drawInfo, double size) { + if (drawInfo == null) return null; + + final EmojiDrawable drawable = new EmojiDrawable(drawInfo, bigDrawSize); drawable.setBounds(0, 0, (int)((double)bigDrawSize * size), (int)((double)bigDrawSize * size)); - if (bitmaps.get(drawInfo.page) == null || bitmaps.get(drawInfo.page).get() == null) { - preloadPage(drawInfo.page, pageLoadedListener); - } + drawInfo.page.get().addListener(new FutureTaskListener() { + @Override public void onSuccess(final Bitmap result) { + handler.post(new Runnable() { + @Override public void run() { + drawable.setBitmap(result); + } + }); + } + + @Override public void onFailure(Throwable error) { + Log.w(TAG, error); + } + }); return drawable; } public class EmojiDrawable extends Drawable { private final int index; - private final int page; private final int emojiSize; private Bitmap bmp; + @Override public int getIntrinsicWidth() { + return emojiSize; + } + + @Override public int getIntrinsicHeight() { + return emojiSize; + } + 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.get(page) == null || bitmaps.get(page).get() == null) { - preloadPage(page, new PageLoadedListener() { - @Override public void onPageLoaded() { - handler.post(new Runnable() { - @Override public void run() { - invalidateSelf(); - } - }); - } - }); - return; - } - if (bmp == null) { - bmp = bitmaps.get(page).get(); - } + if (bmp == null) return; Rect b = copyBounds(); @@ -202,6 +158,12 @@ public class EmojiProvider { paint); } + public void setBitmap(Bitmap bitmap) { + Util.assertMainThread(); + bmp = bitmap; + invalidateSelf(); + } + @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; @@ -212,39 +174,13 @@ public class EmojiProvider { @Override public void setColorFilter(ColorFilter cf) { } - - @Override - public String toString() { - return "EmojiDrawable{" + - "page=" + page + - ", index=" + index + - '}'; - } - } - - public static class InvalidatingPageLoadedListener implements PageLoadedListener { - private final View view; - - public InvalidatingPageLoadedListener(final View view) { - this.view = view; - } - - @Override - public void onPageLoaded() { - view.postInvalidate(); - } - - @Override - public String toString() { - return "InvalidatingPageLoadedListener{}"; - } } class DrawInfo { - int page; - int index; + EmojiPageBitmap page; + int index; - public DrawInfo(final int page, final int index) { + public DrawInfo(final EmojiPageBitmap page, final int index) { this.page = page; this.index = index; } @@ -258,7 +194,67 @@ public class EmojiProvider { } } - interface PageLoadedListener { - void onPageLoaded(); + private class EmojiPageBitmap { + private int page; + private SoftReference bitmapReference; + private ListenableFutureTask task; + + public EmojiPageBitmap(int page) { + this.page = page; + } + + private ListenableFutureTask get() { + Util.assertMainThread(); + + if (bitmapReference != null && bitmapReference.get() != null) { + return new ListenableFutureTask<>(bitmapReference.get()); + } else if (task != null) { + return task; + } else { + Callable callable = new Callable() { + @Override public Bitmap call() throws Exception { + try { + Log.w(TAG, "loading page " + page); + return loadPage(); + } catch (IOException ioe) { + Log.w(TAG, ioe); + } + return null; + } + }; + task = new ListenableFutureTask<>(callable); + new AsyncTask() { + @Override protected Void doInBackground(Void... params) { + task.run(); + return null; + } + + @Override protected void onPostExecute(Void aVoid) { + task = null; + } + }.execute(); + } + return task; + } + + private Bitmap loadPage() throws IOException { + if (bitmapReference != null && bitmapReference.get() != null) return bitmapReference.get(); + + try { + final String file = "emoji_" + page + "_wrapped.png"; + final InputStream measureStream = context.getAssets().open(file); + final InputStream bitmapStream = context.getAssets().open(file); + final Bitmap bitmap = BitmapUtil.createScaledBitmap(measureStream, bitmapStream, (float)bigDrawSize / (float)EMOJI_RAW_SIZE); + bitmapReference = new SoftReference<>(bitmap); + Log.w(TAG, "onPageLoaded(" + page + ")"); + return bitmap; + } catch (IOException ioe) { + Log.w(TAG, ioe); + throw ioe; + } catch (BitmapDecodingException bde) { + Log.w(TAG, bde); + throw new AssertionError("emoji sprite asset is corrupted or android decoding is broken"); + } + } } } diff --git a/src/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java b/src/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java index e5fe1fbb72..4fc5c40ac9 100644 --- a/src/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java +++ b/src/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java @@ -1,45 +1,26 @@ package org.thoughtcrime.securesms.components.emoji; -import android.annotation.TargetApi; import android.content.Context; -import android.graphics.Rect; -import android.os.Build.VERSION_CODES; import android.support.v7.widget.AppCompatTextView; -import android.text.method.TransformationMethod; import android.util.AttributeSet; -import android.view.View; - -import org.thoughtcrime.securesms.components.emoji.EmojiProvider.InvalidatingPageLoadedListener; public class EmojiTextView extends AppCompatTextView { public EmojiTextView(Context context) { super(context); - init(); } public EmojiTextView(Context context, AttributeSet attrs) { super(context, attrs); - init(); } public EmojiTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - init(); } - private void init() { - setTransformationMethod(new EmojiTransformationMethod()); - } - - private static class EmojiTransformationMethod implements TransformationMethod { - - @Override public CharSequence getTransformation(CharSequence source, View view) { - return EmojiProvider.getInstance(view.getContext()).emojify(source, + @Override public void setText(CharSequence text, BufferType type) { + super.setText(EmojiProvider.getInstance(getContext()).emojify(text, EmojiProvider.EMOJI_SMALL, - new InvalidatingPageLoadedListener(view)); - } - - @Override public void onFocusChanged(View view, CharSequence sourceText, boolean focused, - int direction, Rect previouslyFocusedRect) { } + new PostInvalidateCallback(this)), + BufferType.SPANNABLE); } } diff --git a/src/org/thoughtcrime/securesms/components/emoji/InvalidatingDrawableSpan.java b/src/org/thoughtcrime/securesms/components/emoji/InvalidatingDrawableSpan.java new file mode 100644 index 0000000000..62d4fa987c --- /dev/null +++ b/src/org/thoughtcrime/securesms/components/emoji/InvalidatingDrawableSpan.java @@ -0,0 +1,12 @@ +package org.thoughtcrime.securesms.components.emoji; + +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) { + super(drawable, ALIGN_BOTTOM); + drawable.setCallback(callback); + } +} diff --git a/src/org/thoughtcrime/securesms/components/emoji/PostInvalidateCallback.java b/src/org/thoughtcrime/securesms/components/emoji/PostInvalidateCallback.java new file mode 100644 index 0000000000..8ec9cb1315 --- /dev/null +++ b/src/org/thoughtcrime/securesms/components/emoji/PostInvalidateCallback.java @@ -0,0 +1,25 @@ +package org.thoughtcrime.securesms.components.emoji; + +import android.graphics.drawable.Drawable; +import android.graphics.drawable.Drawable.Callback; +import android.view.View; + +public class PostInvalidateCallback implements Callback { + private final View view; + + public PostInvalidateCallback(View view) { + this.view = view; + } + + @Override public void invalidateDrawable(Drawable who) { + view.postInvalidate(); + } + + @Override public void scheduleDrawable(Drawable who, Runnable what, long when) { + + } + + @Override public void unscheduleDrawable(Drawable who, Runnable what) { + + } +} diff --git a/src/org/thoughtcrime/securesms/components/emoji/RecentEmojiPageModel.java b/src/org/thoughtcrime/securesms/components/emoji/RecentEmojiPageModel.java index fc7b1e2cd0..2b256559c0 100644 --- a/src/org/thoughtcrime/securesms/components/emoji/RecentEmojiPageModel.java +++ b/src/org/thoughtcrime/securesms/components/emoji/RecentEmojiPageModel.java @@ -18,13 +18,14 @@ import java.io.IOException; import java.util.Iterator; import java.util.LinkedHashSet; -public class RecentEmojiPageModel implements EmojiPageModel { +public class RecentEmojiPageModel extends EmojiPageModel { private static final String TAG = RecentEmojiPageModel.class.getSimpleName(); private static final String EMOJI_LRU_PREFERENCE = "pref_recent_emoji"; private static final int EMOJI_LRU_SIZE = 50; private final SharedPreferences prefs; private final LinkedHashSet recentlyUsed; + private OnModelChangedListener listener; public RecentEmojiPageModel(Context context) { this.prefs = PreferenceManager.getDefaultSharedPreferences(context); @@ -50,10 +51,11 @@ public class RecentEmojiPageModel implements EmojiPageModel { } @Override public int[] getCodePoints() { - return toPrimitiveArray(recentlyUsed); + return toReversePrimitiveArray(recentlyUsed); } - @Override public void onCodePointSelected(int codePoint) { + public void onCodePointSelected(int codePoint) { + Log.w(TAG, "onCodePointSelected(" + codePoint + ")"); recentlyUsed.remove(codePoint); recentlyUsed.add(codePoint); @@ -80,6 +82,12 @@ public class RecentEmojiPageModel implements EmojiPageModel { return null; } }.execute(); + + if (listener != null) listener.onModelChanged(); + } + + @Override public void setOnModelChangedListener(OnModelChangedListener listener) { + this.listener = listener; } private LinkedHashSet fromHexString(@Nullable LinkedHashSet stringSet) { @@ -100,11 +108,11 @@ public class RecentEmojiPageModel implements EmojiPageModel { return stringSet; } - private int[] toPrimitiveArray(@NonNull LinkedHashSet integerSet) { + private int[] toReversePrimitiveArray(@NonNull LinkedHashSet integerSet) { int[] ints = new int[integerSet.size()]; - int i = 0; + int i = integerSet.size() - 1; for (Integer integer : integerSet) { - ints[i++] = integer; + ints[i--] = integer; } return ints; } diff --git a/src/org/thoughtcrime/securesms/components/emoji/StaticEmojiPageModel.java b/src/org/thoughtcrime/securesms/components/emoji/StaticEmojiPageModel.java index 3175c61b4a..ec0cc3af14 100644 --- a/src/org/thoughtcrime/securesms/components/emoji/StaticEmojiPageModel.java +++ b/src/org/thoughtcrime/securesms/components/emoji/StaticEmojiPageModel.java @@ -3,7 +3,7 @@ package org.thoughtcrime.securesms.components.emoji; import android.support.annotation.DrawableRes; import android.support.annotation.NonNull; -public class StaticEmojiPageModel implements EmojiPageModel { +public class StaticEmojiPageModel extends EmojiPageModel { @DrawableRes private final int icon; @NonNull private final int[] codePoints; @@ -19,6 +19,4 @@ public class StaticEmojiPageModel implements EmojiPageModel { @NonNull public int[] getCodePoints() { return codePoints; } - - @Override public void onCodePointSelected(int codePoint) { } } diff --git a/src/org/thoughtcrime/securesms/util/Util.java b/src/org/thoughtcrime/securesms/util/Util.java index cc7bb295c1..5c3275d52b 100644 --- a/src/org/thoughtcrime/securesms/util/Util.java +++ b/src/org/thoughtcrime/securesms/util/Util.java @@ -24,6 +24,7 @@ import android.graphics.Typeface; import android.os.Build; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; +import android.os.Looper; import android.provider.Telephony; import android.telephony.TelephonyManager; import android.text.Spannable; @@ -296,4 +297,10 @@ public class Util { public static boolean isMmsCapable(Context context) { return (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) || OutgoingLegacyMmsConnection.isConnectionPossible(context); } + + public static void assertMainThread() { + if (Looper.myLooper() != Looper.getMainLooper()) { + throw new AssertionError("Main-thread assertion failed."); + } + } }