Fix flickering emoji and periodic emoji vanishings

Fixes #3231
Closes #3233
// FREEBIE
This commit is contained in:
Jake McGinty 2015-05-21 18:27:31 -07:00 committed by Moxie Marlinspike
parent 1251629997
commit a3007c5906
4 changed files with 49 additions and 27 deletions

View File

@ -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<DrawInfo> 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<Bitmap>() {
@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);
}
}
}

View File

@ -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) {

View File

@ -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);

View File

@ -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<String> 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);
}
}