Fix layout designer deadlock.

This commit is contained in:
Alex Hart 2021-05-07 15:25:30 -03:00 committed by Greyson Parrelli
parent dd934e0095
commit d672857e82
11 changed files with 44 additions and 62 deletions

View file

@ -19,7 +19,7 @@ public class EmojiFilter implements InputFilter {
char[] v = new char[end - start]; char[] v = new char[end - start];
TextUtils.getChars(source, start, end, v, 0); TextUtils.getChars(source, start, end, v, 0);
Spannable emojified = EmojiProvider.getInstance(view.getContext()).emojify(new String(v), view); Spannable emojified = EmojiProvider.emojify(new String(v), view);
if (source instanceof Spanned && emojified != null) { if (source instanceof Spanned && emojified != null) {
TextUtils.copySpansFrom((Spanned) source, start, end, null, emojified, 0); TextUtils.copySpansFrom((Spanned) source, start, end, null, emojified, 0);

View file

@ -5,6 +5,8 @@ import android.util.AttributeSet;
import androidx.appcompat.widget.AppCompatImageView; import androidx.appcompat.widget.AppCompatImageView;
import org.thoughtcrime.securesms.R;
public class EmojiImageView extends AppCompatImageView { public class EmojiImageView extends AppCompatImageView {
public EmojiImageView(Context context) { public EmojiImageView(Context context) {
super(context); super(context);
@ -15,6 +17,10 @@ public class EmojiImageView extends AppCompatImageView {
} }
public void setImageEmoji(CharSequence emoji) { public void setImageEmoji(CharSequence emoji) {
setImageDrawable(EmojiProvider.getInstance(getContext()).getEmojiDrawable(emoji)); if (isInEditMode()) {
setImageResource(R.drawable.ic_emoji);
} else {
setImageDrawable(EmojiProvider.getEmojiDrawable(getContext(), emoji));
}
} }
} }

View file

@ -40,8 +40,7 @@ public class EmojiPageView extends FrameLayout implements VariationSelectorListe
layoutManager = new GridLayoutManager(context, 8); layoutManager = new GridLayoutManager(context, 8);
scrollDisabler = new ScrollDisabler(); scrollDisabler = new ScrollDisabler();
popup = new EmojiVariationSelectorPopup(context, emojiSelectionListener); popup = new EmojiVariationSelectorPopup(context, emojiSelectionListener);
adapter = new EmojiPageViewGridAdapter(EmojiProvider.getInstance(context), adapter = new EmojiPageViewGridAdapter(popup,
popup,
emojiSelectionListener, emojiSelectionListener,
this, this,
allowVariations); allowVariations);

View file

@ -19,20 +19,17 @@ import java.util.List;
public class EmojiPageViewGridAdapter extends RecyclerView.Adapter<EmojiPageViewGridAdapter.EmojiViewHolder> implements PopupWindow.OnDismissListener { public class EmojiPageViewGridAdapter extends RecyclerView.Adapter<EmojiPageViewGridAdapter.EmojiViewHolder> implements PopupWindow.OnDismissListener {
private final List<Emoji> emojiList; private final List<Emoji> emojiList;
private final EmojiProvider emojiProvider;
private final EmojiVariationSelectorPopup popup; private final EmojiVariationSelectorPopup popup;
private final VariationSelectorListener variationSelectorListener; private final VariationSelectorListener variationSelectorListener;
private final EmojiEventListener emojiEventListener; private final EmojiEventListener emojiEventListener;
private final boolean allowVariations; private final boolean allowVariations;
public EmojiPageViewGridAdapter(@NonNull EmojiProvider emojiProvider, public EmojiPageViewGridAdapter(@NonNull EmojiVariationSelectorPopup popup,
@NonNull EmojiVariationSelectorPopup popup,
@NonNull EmojiEventListener emojiEventListener, @NonNull EmojiEventListener emojiEventListener,
@NonNull VariationSelectorListener variationSelectorListener, @NonNull VariationSelectorListener variationSelectorListener,
boolean allowVariations) boolean allowVariations)
{ {
this.emojiList = new ArrayList<>(); this.emojiList = new ArrayList<>();
this.emojiProvider = emojiProvider;
this.popup = popup; this.popup = popup;
this.emojiEventListener = emojiEventListener; this.emojiEventListener = emojiEventListener;
this.variationSelectorListener = variationSelectorListener; this.variationSelectorListener = variationSelectorListener;
@ -51,7 +48,7 @@ public class EmojiPageViewGridAdapter extends RecyclerView.Adapter<EmojiPageView
public void onBindViewHolder(@NonNull EmojiViewHolder viewHolder, int i) { public void onBindViewHolder(@NonNull EmojiViewHolder viewHolder, int i) {
Emoji emoji = emojiList.get(i); Emoji emoji = emojiList.get(i);
Drawable drawable = emojiProvider.getEmojiDrawable(emoji.getValue()); final Drawable drawable = EmojiProvider.getEmojiDrawable(viewHolder.imageView.getContext(), emoji.getValue());
if (drawable != null) { if (drawable != null) {
viewHolder.textView.setVisibility(View.GONE); viewHolder.textView.setVisibility(View.GONE);

View file

@ -28,45 +28,31 @@ import java.util.concurrent.ExecutionException;
class EmojiProvider { class EmojiProvider {
private static final String TAG = Log.tag(EmojiProvider.class); private static final String TAG = Log.tag(EmojiProvider.class);
private static volatile EmojiProvider instance = null; private static final Paint PAINT = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
private static final Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
private final Context context; static @Nullable EmojiParser.CandidateList getCandidates(@Nullable CharSequence text) {
public static EmojiProvider getInstance(Context context) {
if (instance == null) {
synchronized (EmojiProvider.class) {
if (instance == null) {
instance = new EmojiProvider(context);
}
}
}
return instance;
}
private EmojiProvider(Context context) {
this.context = context.getApplicationContext();
}
@Nullable EmojiParser.CandidateList getCandidates(@Nullable CharSequence text) {
if (text == null) return null; if (text == null) return null;
return new EmojiParser(EmojiSource.getLatest().getEmojiTree()).findCandidates(text); return new EmojiParser(EmojiSource.getLatest().getEmojiTree()).findCandidates(text);
} }
@Nullable Spannable emojify(@Nullable CharSequence text, @NonNull TextView tv) { static @Nullable Spannable emojify(@Nullable CharSequence text, @NonNull TextView tv) {
return emojify(getCandidates(text), text, tv); if (tv.isInEditMode()) {
return null;
} else {
return emojify(getCandidates(text), text, tv);
}
} }
@Nullable Spannable emojify(@Nullable EmojiParser.CandidateList matches, static @Nullable Spannable emojify(@Nullable EmojiParser.CandidateList matches,
@Nullable CharSequence text, @Nullable CharSequence text,
@NonNull TextView tv) @NonNull TextView tv)
{ {
if (matches == null || text == null) return null; if (matches == null || text == null || tv.isInEditMode()) return null;
SpannableStringBuilder builder = new SpannableStringBuilder(text); SpannableStringBuilder builder = new SpannableStringBuilder(text);
for (EmojiParser.Candidate candidate : matches) { for (EmojiParser.Candidate candidate : matches) {
Drawable drawable = getEmojiDrawable(candidate.getDrawInfo()); Drawable drawable = getEmojiDrawable(tv.getContext(), candidate.getDrawInfo());
if (drawable != null) { if (drawable != null) {
builder.setSpan(new EmojiSpan(drawable, tv), candidate.getStartIndex(), candidate.getEndIndex(), builder.setSpan(new EmojiSpan(drawable, tv), candidate.getStartIndex(), candidate.getEndIndex(),
@ -77,12 +63,12 @@ class EmojiProvider {
return builder; return builder;
} }
@Nullable Drawable getEmojiDrawable(CharSequence emoji) { static @Nullable Drawable getEmojiDrawable(@NonNull Context context, @Nullable CharSequence emoji) {
EmojiDrawInfo drawInfo = EmojiSource.getLatest().getEmojiTree().getEmoji(emoji, 0, emoji.length()); EmojiDrawInfo drawInfo = EmojiSource.getLatest().getEmojiTree().getEmoji(emoji, 0, emoji.length());
return getEmojiDrawable(drawInfo); return getEmojiDrawable(context, drawInfo);
} }
private @Nullable Drawable getEmojiDrawable(@Nullable EmojiDrawInfo drawInfo) { private static @Nullable Drawable getEmojiDrawable(@NonNull Context context, @Nullable EmojiDrawInfo drawInfo) {
if (drawInfo == null) { if (drawInfo == null) {
return null; return null;
} }
@ -151,7 +137,7 @@ class EmojiProvider {
canvas.drawBitmap(bmp, canvas.drawBitmap(bmp,
emojiBounds, emojiBounds,
getBounds(), getBounds(),
paint); PAINT);
} }
public void setBitmap(Bitmap bitmap) { public void setBitmap(Bitmap bitmap) {

View file

@ -91,8 +91,7 @@ public class EmojiTextView extends AppCompatTextView {
} }
@Override public void setText(@Nullable CharSequence text, BufferType type) { @Override public void setText(@Nullable CharSequence text, BufferType type) {
EmojiProvider provider = EmojiProvider.getInstance(getContext()); EmojiParser.CandidateList candidates = isInEditMode() ? null : EmojiProvider.getCandidates(text);
EmojiParser.CandidateList candidates = !isInEditMode() ? provider.getCandidates(text) : null;
if (scaleEmojis && candidates != null && candidates.allEmojis) { if (scaleEmojis && candidates != null && candidates.allEmojis) {
int emojis = candidates.size(); int emojis = candidates.size();
@ -124,7 +123,7 @@ public class EmojiTextView extends AppCompatTextView {
ellipsizeAnyTextForMaxLength(); ellipsizeAnyTextForMaxLength();
} }
} else { } else {
CharSequence emojified = provider.emojify(candidates, text, this); CharSequence emojified = EmojiProvider.emojify(candidates, text, this);
super.setText(new SpannableStringBuilder(emojified).append(Optional.fromNullable(overflowText).or("")), BufferType.SPANNABLE); super.setText(new SpannableStringBuilder(emojified).append(Optional.fromNullable(overflowText).or("")), BufferType.SPANNABLE);
// Android fails to ellipsize spannable strings. (https://issuetracker.google.com/issues/36991688) // Android fails to ellipsize spannable strings. (https://issuetracker.google.com/issues/36991688)
@ -165,12 +164,12 @@ public class EmojiTextView extends AppCompatTextView {
.append(ELLIPSIS) .append(ELLIPSIS)
.append(Util.emptyIfNull(overflowText)); .append(Util.emptyIfNull(overflowText));
EmojiParser.CandidateList newCandidates = EmojiProvider.getInstance(getContext()).getCandidates(newContent); EmojiParser.CandidateList newCandidates = isInEditMode() ? null : EmojiProvider.getCandidates(newContent);
if (useSystemEmoji || newCandidates == null || newCandidates.size() == 0) { if (useSystemEmoji || newCandidates == null || newCandidates.size() == 0) {
super.setText(newContent, BufferType.NORMAL); super.setText(newContent, BufferType.NORMAL);
} else { } else {
CharSequence emojified = EmojiProvider.getInstance(getContext()).emojify(newCandidates, newContent, this); CharSequence emojified = EmojiProvider.emojify(newCandidates, newContent, this);
super.setText(emojified, BufferType.SPANNABLE); super.setText(emojified, BufferType.SPANNABLE);
} }
} }
@ -199,8 +198,8 @@ public class EmojiTextView extends AppCompatTextView {
.append(ellipsized.subSequence(0, ellipsized.length())) .append(ellipsized.subSequence(0, ellipsized.length()))
.append(Optional.fromNullable(overflowText).or("")); .append(Optional.fromNullable(overflowText).or(""));
EmojiParser.CandidateList newCandidates = EmojiProvider.getInstance(getContext()).getCandidates(newContent); EmojiParser.CandidateList newCandidates = isInEditMode() ? null : EmojiProvider.getCandidates(newContent);
CharSequence emojified = EmojiProvider.getInstance(getContext()).emojify(newCandidates, newContent, this); CharSequence emojified = EmojiProvider.emojify(newCandidates, newContent, this);
super.setText(emojified, BufferType.SPANNABLE); super.setText(emojified, BufferType.SPANNABLE);
} }

View file

@ -12,10 +12,7 @@ import org.thoughtcrime.securesms.emoji.ObsoleteEmoji;
import org.thoughtcrime.securesms.util.StringUtil; import org.thoughtcrime.securesms.util.StringUtil;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -63,7 +60,7 @@ public final class EmojiUtil {
if (Util.isEmpty(emoji)) { if (Util.isEmpty(emoji)) {
return null; return null;
} else { } else {
return EmojiProvider.getInstance(context).getEmojiDrawable(emoji); return EmojiProvider.getEmojiDrawable(context, emoji);
} }
} }
@ -74,7 +71,7 @@ public final class EmojiUtil {
* followed by a more wide check for all of the valid emoji unicode ranges (which could lead to * followed by a more wide check for all of the valid emoji unicode ranges (which could lead to
* some false positives). YMMV. * some false positives). YMMV.
*/ */
public static boolean isEmoji(@NonNull Context context, @Nullable String text) { public static boolean isEmoji(@Nullable String text) {
if (Util.isEmpty(text)) { if (Util.isEmpty(text)) {
return false; return false;
} }
@ -83,7 +80,7 @@ public final class EmojiUtil {
return false; return false;
} }
EmojiParser.CandidateList candidates = EmojiProvider.getInstance(context).getCandidates(text); EmojiParser.CandidateList candidates = EmojiProvider.getCandidates(text);
return (candidates != null && candidates.size() > 0) || EMOJI_PATTERN.matcher(text).matches(); return (candidates != null && candidates.size() > 0) || EMOJI_PATTERN.matcher(text).matches();
} }

View file

@ -42,7 +42,7 @@ public class EmojiVariationSelectorPopup extends PopupWindow {
for (String variation : variations) { for (String variation : variations) {
ImageView imageView = (ImageView) LayoutInflater.from(context).inflate(R.layout.emoji_variation_selector_item, list, false); ImageView imageView = (ImageView) LayoutInflater.from(context).inflate(R.layout.emoji_variation_selector_item, list, false);
imageView.setImageDrawable(EmojiProvider.getInstance(context).getEmojiDrawable(variation)); imageView.setImageDrawable(EmojiProvider.getEmojiDrawable(context, variation));
imageView.setOnClickListener(v -> { imageView.setOnClickListener(v -> {
listener.onEmojiSelected(variation); listener.onEmojiSelected(variation);
dismiss(); dismiss();

View file

@ -106,7 +106,6 @@ import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Hex; import org.thoughtcrime.securesms.util.Hex;
import org.thoughtcrime.securesms.util.IdentityUtil; import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.MessageRecordUtil;
import org.thoughtcrime.securesms.util.RemoteDeleteUtil; import org.thoughtcrime.securesms.util.RemoteDeleteUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
@ -154,7 +153,6 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.UUID; import java.util.UUID;
/** /**
@ -719,7 +717,7 @@ public final class MessageContentProcessor {
private void handleReaction(@NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message) { private void handleReaction(@NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message) {
SignalServiceDataMessage.Reaction reaction = message.getReaction().get(); SignalServiceDataMessage.Reaction reaction = message.getReaction().get();
if (!EmojiUtil.isEmoji(context, reaction.getEmoji())) { if (!EmojiUtil.isEmoji(reaction.getEmoji())) {
Log.w(TAG, "Reaction text is not a valid emoji! Ignoring the message."); Log.w(TAG, "Reaction text is not a valid emoji! Ignoring the message.");
return; return;
} }

View file

@ -241,7 +241,7 @@ public class EditAboutFragment extends Fragment implements ManageProfileActivity
} }
public void bind(@NonNull AboutPreset preset) { public void bind(@NonNull AboutPreset preset) {
this.emoji.setImageDrawable(EmojiUtil.convertToDrawable(itemView.getContext(), preset.getEmoji())); this.emoji.setImageDrawable(EmojiUtil.convertToDrawable(requireContext(), preset.getEmoji()));
this.body.setText(preset.getBodyRes()); this.body.setText(preset.getBodyRes());
} }
} }

View file

@ -71,10 +71,10 @@ public class EmojiUtilTest_isEmoji {
@Test @Test
public void isEmoji() throws Exception { public void isEmoji() throws Exception {
Context context = ApplicationProvider.getApplicationContext(); Application application = ApplicationProvider.getApplicationContext();
PowerMockito.mockStatic(ApplicationDependencies.class); PowerMockito.mockStatic(ApplicationDependencies.class);
PowerMockito.when(ApplicationDependencies.getApplication()).thenReturn((Application) context); PowerMockito.when(ApplicationDependencies.getApplication()).thenReturn(application);
PowerMockito.mockStatic(AttachmentSecretProvider.class); PowerMockito.mockStatic(AttachmentSecretProvider.class);
PowerMockito.when(AttachmentSecretProvider.getInstance(any())).thenThrow(IOException.class); PowerMockito.when(AttachmentSecretProvider.getInstance(any())).thenThrow(IOException.class);
PowerMockito.whenNew(SignalStore.class).withAnyArguments().thenReturn(null); PowerMockito.whenNew(SignalStore.class).withAnyArguments().thenReturn(null);
@ -82,6 +82,6 @@ public class EmojiUtilTest_isEmoji {
PowerMockito.when(SignalStore.internalValues()).thenReturn(PowerMockito.mock(InternalValues.class)); PowerMockito.when(SignalStore.internalValues()).thenReturn(PowerMockito.mock(InternalValues.class));
EmojiSource.refresh(); EmojiSource.refresh();
assertEquals(output, EmojiUtil.isEmoji(context, input)); assertEquals(output, EmojiUtil.isEmoji(input));
} }
} }