From a3007c5906572efc695201696de37aa504012648 Mon Sep 17 00:00:00 2001 From: Jake McGinty Date: Thu, 21 May 2015 18:27:31 -0700 Subject: [PATCH] Fix flickering emoji and periodic emoji vanishings Fixes #3231 Closes #3233 // FREEBIE --- .../components/emoji/EmojiProvider.java | 48 +++++++++++-------- .../emoji/PostInvalidateCallback.java | 8 +++- .../securesms/util/BitmapUtil.java | 3 +- src/org/thoughtcrime/securesms/util/Util.java | 17 +++++-- 4 files changed, 49 insertions(+), 27 deletions(-) diff --git a/src/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java b/src/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java index b6cf381b43..2a6351962d 100644 --- a/src/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java +++ b/src/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java @@ -1,5 +1,6 @@ package org.thoughtcrime.securesms.components.emoji; +import android.annotation.TargetApi; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -10,8 +11,9 @@ 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.os.Build.VERSION; +import android.os.Build.VERSION_CODES; +import android.support.annotation.Nullable; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.util.Log; @@ -28,7 +30,6 @@ import org.thoughtcrime.securesms.util.Util; import java.io.IOException; import java.io.InputStream; import java.lang.ref.SoftReference; -import java.util.Arrays; import java.util.concurrent.Callable; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -36,8 +37,7 @@ import java.util.regex.Pattern; public class EmojiProvider { 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 static final Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG); private final SparseArray offsets = new SparseArray<>(); @@ -57,7 +57,6 @@ public class EmojiProvider { private final double drawWidth; private final double drawHeight; private final double verticalPad; - private final Handler handler = new Handler(Looper.getMainLooper()); public static EmojiProvider getInstance(Context context) { if (instance == null) { @@ -114,7 +113,7 @@ public class EmojiProvider { drawable.setBounds(0, 0, (int)(drawWidth * size), (int)(drawHeight * size)); drawInfo.page.get().addListener(new FutureTaskListener() { @Override public void onSuccess(final Bitmap result) { - handler.post(new Runnable() { + Util.runOnMain(new Runnable() { @Override public void run() { drawable.setBitmap(result); } @@ -129,10 +128,10 @@ public class EmojiProvider { } public class EmojiDrawable extends Drawable { - private final int index; - private final double width; - private final double height; - private Bitmap bmp; + private final DrawInfo info; + private final double width; + private final double height; + private Bitmap bmp; @Override public int getIntrinsicWidth() { return (int)width; @@ -143,33 +142,38 @@ public class EmojiProvider { } public EmojiDrawable(DrawInfo info, double width, double height) { - this.index = info.index; + this.info = info; this.width = width; this.height = height; } @Override public void draw(Canvas canvas) { - if (bmp == null) return; + if (bmp == null) { + Log.w(TAG, "no-op draw(" + info.page + ", " + info.index + ")"); + return; + } - Rect b = copyBounds(); - - final int row = index / EMOJI_PER_ROW; - final int row_index = index % EMOJI_PER_ROW; + final int row = info.index / EMOJI_PER_ROW; + 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)), - b, + getBounds(), paint); } + @TargetApi(VERSION_CODES.HONEYCOMB_MR1) public void setBitmap(Bitmap bitmap) { Util.assertMainThread(); - bmp = bitmap; - invalidateSelf(); + Log.w(TAG, "setBitmap(" + info.page + ", " + info.index + ")"); + if (VERSION.SDK_INT < VERSION_CODES.HONEYCOMB_MR1 || bmp == null || !bmp.sameAs(bitmap)) { + bmp = bitmap; + invalidateSelf(); + } } @Override @@ -264,5 +268,9 @@ public class EmojiProvider { throw new AssertionError("emoji sprite asset is corrupted or android decoding is broken"); } } + + @Override public String toString() { + return Integer.toString(page); + } } } diff --git a/src/org/thoughtcrime/securesms/components/emoji/PostInvalidateCallback.java b/src/org/thoughtcrime/securesms/components/emoji/PostInvalidateCallback.java index 8ec9cb1315..bb661afee8 100644 --- a/src/org/thoughtcrime/securesms/components/emoji/PostInvalidateCallback.java +++ b/src/org/thoughtcrime/securesms/components/emoji/PostInvalidateCallback.java @@ -4,6 +4,8 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable.Callback; import android.view.View; +import org.thoughtcrime.securesms.util.Util; + public class PostInvalidateCallback implements Callback { private final View view; @@ -12,7 +14,11 @@ public class PostInvalidateCallback implements Callback { } @Override public void invalidateDrawable(Drawable who) { - view.postInvalidate(); + Util.runOnMain(new Runnable() { + @Override public void run() { + view.invalidate(); + } + }); } @Override public void scheduleDrawable(Drawable who, Runnable what, long when) { diff --git a/src/org/thoughtcrime/securesms/util/BitmapUtil.java b/src/org/thoughtcrime/securesms/util/BitmapUtil.java index ffb730924b..9c8492eda9 100644 --- a/src/org/thoughtcrime/securesms/util/BitmapUtil.java +++ b/src/org/thoughtcrime/securesms/util/BitmapUtil.java @@ -296,8 +296,7 @@ public class BitmapUtil { } }; - if (Looper.myLooper() == Looper.getMainLooper()) runnable.run(); - else new Handler(Looper.getMainLooper()).post(runnable); + Util.runOnMain(runnable); synchronized (result) { while (!created.get()) Util.wait(result, 0); diff --git a/src/org/thoughtcrime/securesms/util/Util.java b/src/org/thoughtcrime/securesms/util/Util.java index 25eda004a3..d12830c42c 100644 --- a/src/org/thoughtcrime/securesms/util/Util.java +++ b/src/org/thoughtcrime/securesms/util/Util.java @@ -21,10 +21,10 @@ import android.annotation.TargetApi; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Typeface; -import android.net.Uri; import android.os.Build; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; +import android.os.Handler; import android.os.Looper; import android.provider.Telephony; import android.support.annotation.Nullable; @@ -60,10 +60,11 @@ import ws.com.google.android.mms.pdu.CharacterSets; import ws.com.google.android.mms.pdu.EncodedStringValue; public class Util { + public static Handler handler = new Handler(Looper.getMainLooper()); public static String join(Collection list, String delimiter) { StringBuilder result = new StringBuilder(); - int i=0; + int i = 0; for (String item : list) { result.append(item); @@ -301,12 +302,21 @@ public class Util { return (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) || OutgoingLegacyMmsConnection.isConnectionPossible(context); } + public static boolean isMainThread() { + return Looper.myLooper() == Looper.getMainLooper(); + } + public static void assertMainThread() { - if (Looper.myLooper() != Looper.getMainLooper()) { + if (!isMainThread()) { throw new AssertionError("Main-thread assertion failed."); } } + public static void runOnMain(Runnable runnable) { + if (isMainThread()) runnable.run(); + else handler.post(runnable); + } + public static boolean equals(@Nullable Object a, @Nullable Object b) { return a == b || (a != null && a.equals(b)); } @@ -314,5 +324,4 @@ public class Util { public static int hashCode(@Nullable Object... objects) { return Arrays.hashCode(objects); } - }