Fix layout designer deadlock.
This commit is contained in:
parent
dd934e0095
commit
d672857e82
11 changed files with 44 additions and 62 deletions
|
@ -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);
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue