mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-25 11:20:47 +01:00
Add Emoji Search, Sticker Search, and GIF Keyboard.
Co-authored-by: Alex Hart <alex@signal.org> Co-authored-by: Cody Henthorne <cody@signal.org> Co-authored-by: Greyson Parrelli<greyson@signal.org>
This commit is contained in:
committed by
Cody Henthorne
parent
66c3b1388a
commit
08e86b8c82
@@ -26,8 +26,8 @@ public class InputAwareLayout extends KeyboardAwareLinearLayout implements OnKey
|
||||
addOnKeyboardShownListener(this);
|
||||
}
|
||||
|
||||
@Override public void onKeyboardShown() {
|
||||
hideAttachedInput(true);
|
||||
@Override
|
||||
public void onKeyboardShown() {
|
||||
}
|
||||
|
||||
public void show(@NonNull final EditText imeTarget, @NonNull final InputView input) {
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.thoughtcrime.securesms.components.emoji.MediaKeyboard;
|
||||
import org.thoughtcrime.securesms.conversation.ConversationStickerSuggestionAdapter;
|
||||
import org.thoughtcrime.securesms.conversation.colors.Colorizer;
|
||||
import org.thoughtcrime.securesms.database.model.StickerRecord;
|
||||
import org.thoughtcrime.securesms.keyboard.KeyboardPage;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
|
||||
@@ -279,8 +280,8 @@ public class InputPanel extends LinearLayout
|
||||
mediaKeyboard.setVisibility(show ? View.VISIBLE : GONE);
|
||||
}
|
||||
|
||||
public void setMediaKeyboardToggleMode(boolean isSticker) {
|
||||
mediaKeyboard.setStickerMode(isSticker);
|
||||
public void setMediaKeyboardToggleMode(@NonNull KeyboardPage page) {
|
||||
mediaKeyboard.setStickerMode(page);
|
||||
}
|
||||
|
||||
public boolean isStickerMode() {
|
||||
@@ -291,6 +292,10 @@ public class InputPanel extends LinearLayout
|
||||
return mediaKeyboard;
|
||||
}
|
||||
|
||||
public MediaKeyboard.MediaKeyboardListener getMediaKeyboardListener() {
|
||||
return mediaKeyboard;
|
||||
}
|
||||
|
||||
public void setWallpaperEnabled(boolean enabled) {
|
||||
if (enabled) {
|
||||
setBackground(new ColorDrawable(getContext().getResources().getColor(R.color.wallpaper_compose_background)));
|
||||
|
||||
@@ -23,6 +23,8 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* A provider to select emoji in the {@link org.thoughtcrime.securesms.components.emoji.MediaKeyboard}.
|
||||
*
|
||||
* TODO [alex] -- Are we still using any of this?
|
||||
*/
|
||||
public class EmojiKeyboardProvider implements MediaKeyboardProvider,
|
||||
MediaKeyboardProvider.TabIconProvider,
|
||||
@@ -31,6 +33,7 @@ public class EmojiKeyboardProvider implements MediaKeyboardProvider,
|
||||
{
|
||||
private static final KeyEvent DELETE_KEY_EVENT = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL);
|
||||
|
||||
// TODO [alex] -- We are using this.
|
||||
public static final String RECENT_STORAGE_KEY = "pref_recent_emoji2";
|
||||
|
||||
private final Context context;
|
||||
@@ -146,7 +149,7 @@ public class EmojiKeyboardProvider implements MediaKeyboardProvider,
|
||||
|
||||
@Override
|
||||
public @NonNull Object instantiateItem(@NonNull ViewGroup container, int position) {
|
||||
EmojiPageView page = new EmojiPageView(context, emojiSelectionListener, variationSelectorListener, true);
|
||||
EmojiPageView page = new EmojiPageView(context, emojiSelectionListener, variationSelectorListener, true, null);
|
||||
page.setModel(pages.get(position));
|
||||
container.addView(page);
|
||||
return page;
|
||||
|
||||
@@ -6,58 +6,111 @@ import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider.EmojiEventListener;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter.VariationSelectorListener;
|
||||
import org.thoughtcrime.securesms.keyboard.emoji.KeyboardPageSearchView;
|
||||
import org.thoughtcrime.securesms.util.MappingModelList;
|
||||
|
||||
public class EmojiPageView extends FrameLayout implements VariationSelectorListener {
|
||||
private static final String TAG = Log.tag(EmojiPageView.class);
|
||||
|
||||
private EmojiPageModel model;
|
||||
private EmojiPageViewGridAdapter adapter;
|
||||
private AdapterFactory adapterFactory;
|
||||
private RecyclerView recyclerView;
|
||||
private GridLayoutManager layoutManager;
|
||||
private RecyclerView.LayoutManager layoutManager;
|
||||
private RecyclerView.OnItemTouchListener scrollDisabler;
|
||||
private VariationSelectorListener variationSelectorListener;
|
||||
private EmojiVariationSelectorPopup popup;
|
||||
private boolean searchEnabled;
|
||||
private SpanSizeLookup spanSizeLookup;
|
||||
|
||||
public EmojiPageView(@NonNull Context context,
|
||||
@NonNull EmojiEventListener emojiSelectionListener,
|
||||
@NonNull VariationSelectorListener variationSelectorListener,
|
||||
boolean allowVariations)
|
||||
boolean allowVariations,
|
||||
@Nullable KeyboardPageSearchView.Callbacks searchCallbacks)
|
||||
{
|
||||
this(context, emojiSelectionListener, variationSelectorListener, allowVariations, searchCallbacks, new GridLayoutManager(context, 8), R.layout.emoji_display_item);
|
||||
}
|
||||
|
||||
public EmojiPageView(@NonNull Context context,
|
||||
@NonNull EmojiEventListener emojiSelectionListener,
|
||||
@NonNull VariationSelectorListener variationSelectorListener,
|
||||
boolean allowVariations,
|
||||
@Nullable KeyboardPageSearchView.Callbacks searchCallbacks,
|
||||
@NonNull RecyclerView.LayoutManager layoutManager,
|
||||
@LayoutRes int displayItemLayoutResId)
|
||||
{
|
||||
super(context);
|
||||
final View view = LayoutInflater.from(getContext()).inflate(R.layout.emoji_grid_layout, this, true);
|
||||
|
||||
this.variationSelectorListener = variationSelectorListener;
|
||||
|
||||
recyclerView = view.findViewById(R.id.emoji);
|
||||
layoutManager = new GridLayoutManager(context, 8);
|
||||
scrollDisabler = new ScrollDisabler();
|
||||
popup = new EmojiVariationSelectorPopup(context, emojiSelectionListener);
|
||||
adapter = new EmojiPageViewGridAdapter(popup,
|
||||
emojiSelectionListener,
|
||||
this,
|
||||
allowVariations);
|
||||
this.recyclerView = view.findViewById(R.id.emoji);
|
||||
this.layoutManager = layoutManager;
|
||||
this.scrollDisabler = new ScrollDisabler();
|
||||
this.popup = new EmojiVariationSelectorPopup(context, emojiSelectionListener);
|
||||
this.adapterFactory = () -> new EmojiPageViewGridAdapter(popup,
|
||||
emojiSelectionListener,
|
||||
this,
|
||||
allowVariations,
|
||||
displayItemLayoutResId,
|
||||
searchCallbacks);
|
||||
|
||||
if (layoutManager instanceof GridLayoutManager) {
|
||||
spanSizeLookup = new SpanSizeLookup();
|
||||
((GridLayoutManager) layoutManager).setSpanSizeLookup(spanSizeLookup);
|
||||
}
|
||||
|
||||
recyclerView.setLayoutManager(layoutManager);
|
||||
recyclerView.setAdapter(adapter);
|
||||
recyclerView.setItemAnimator(null);
|
||||
}
|
||||
|
||||
public void onSelected() {
|
||||
if (model.isDynamic() && adapter != null) {
|
||||
adapter.notifyDataSetChanged();
|
||||
if (model.isDynamic() && recyclerView.getAdapter() != null) {
|
||||
recyclerView.getAdapter().notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void setModel(EmojiPageModel model) {
|
||||
public void setModel(@Nullable EmojiPageModel model) {
|
||||
this.model = model;
|
||||
adapter.setEmoji(model.getDisplayEmoji());
|
||||
|
||||
EmojiPageViewGridAdapter adapter = adapterFactory.create();
|
||||
recyclerView.setAdapter(adapter);
|
||||
adapter.submitList(getMappingModelList());
|
||||
}
|
||||
|
||||
public void bindSearchableAdapter(@Nullable EmojiPageModel model) {
|
||||
this.searchEnabled = true;
|
||||
this.model = model;
|
||||
|
||||
EmojiPageViewGridAdapter adapter = adapterFactory.create();
|
||||
recyclerView.setAdapter(adapter);
|
||||
adapter.submitList(getMappingModelList(), () -> layoutManager.scrollToPosition(1));
|
||||
}
|
||||
|
||||
private @NonNull MappingModelList getMappingModelList() {
|
||||
MappingModelList mappingModels = new MappingModelList();
|
||||
|
||||
if (searchEnabled) {
|
||||
mappingModels.add(new EmojiPageViewGridAdapter.SearchModel());
|
||||
}
|
||||
|
||||
if (model != null) {
|
||||
mappingModels.addAll(Stream.of(model.getDisplayEmoji()).map(EmojiPageViewGridAdapter.EmojiModel::new).toList());
|
||||
}
|
||||
|
||||
return mappingModels;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -69,8 +122,13 @@ public class EmojiPageView extends FrameLayout implements VariationSelectorListe
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
int idealWidth = getContext().getResources().getDimensionPixelOffset(R.dimen.emoji_drawer_item_width);
|
||||
layoutManager.setSpanCount(Math.max(w / idealWidth, 1));
|
||||
if (layoutManager instanceof GridLayoutManager) {
|
||||
int idealWidth = getContext().getResources().getDimensionPixelOffset(R.dimen.emoji_drawer_item_width);
|
||||
int spanCount = Math.max(w / idealWidth, 1);
|
||||
|
||||
spanSizeLookup.setSpansPerRow(spanCount);
|
||||
((GridLayoutManager) layoutManager).setSpanCount(spanCount);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -102,4 +160,22 @@ public class EmojiPageView extends FrameLayout implements VariationSelectorListe
|
||||
@Override
|
||||
public void onRequestDisallowInterceptTouchEvent(boolean b) { }
|
||||
}
|
||||
|
||||
private class SpanSizeLookup extends GridLayoutManager.SpanSizeLookup {
|
||||
|
||||
private int spansPerRow;
|
||||
|
||||
public void setSpansPerRow(int spansPerRow) {
|
||||
this.spansPerRow = spansPerRow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpanSize(int position) {
|
||||
return position == 0 && searchEnabled ? spansPerRow : 1;
|
||||
}
|
||||
}
|
||||
|
||||
private interface AdapterFactory {
|
||||
EmojiPageViewGridAdapter create();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,95 +1,42 @@
|
||||
package org.thoughtcrime.securesms.components.emoji;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.PopupWindow;
|
||||
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider.EmojiEventListener;
|
||||
import org.thoughtcrime.securesms.keyboard.emoji.KeyboardPageSearchView;
|
||||
import org.thoughtcrime.securesms.util.MappingAdapter;
|
||||
import org.thoughtcrime.securesms.util.MappingModel;
|
||||
import org.thoughtcrime.securesms.util.MappingViewHolder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
public class EmojiPageViewGridAdapter extends MappingAdapter implements PopupWindow.OnDismissListener {
|
||||
|
||||
public class EmojiPageViewGridAdapter extends RecyclerView.Adapter<EmojiPageViewGridAdapter.EmojiViewHolder> implements PopupWindow.OnDismissListener {
|
||||
|
||||
private final List<Emoji> emojiList;
|
||||
private final EmojiVariationSelectorPopup popup;
|
||||
private final VariationSelectorListener variationSelectorListener;
|
||||
private final EmojiEventListener emojiEventListener;
|
||||
private final boolean allowVariations;
|
||||
|
||||
public EmojiPageViewGridAdapter(@NonNull EmojiVariationSelectorPopup popup,
|
||||
@NonNull EmojiEventListener emojiEventListener,
|
||||
@NonNull VariationSelectorListener variationSelectorListener,
|
||||
boolean allowVariations)
|
||||
boolean allowVariations,
|
||||
@LayoutRes int displayItemLayoutResId,
|
||||
@Nullable KeyboardPageSearchView.Callbacks callbacks)
|
||||
{
|
||||
this.emojiList = new ArrayList<>();
|
||||
this.popup = popup;
|
||||
this.emojiEventListener = emojiEventListener;
|
||||
this.variationSelectorListener = variationSelectorListener;
|
||||
this.allowVariations = allowVariations;
|
||||
|
||||
popup.setOnDismissListener(this);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public EmojiViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
|
||||
return new EmojiViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.emoji_display_item, viewGroup, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull EmojiViewHolder viewHolder, int i) {
|
||||
Emoji emoji = emojiList.get(i);
|
||||
|
||||
final Drawable drawable = EmojiProvider.getEmojiDrawable(viewHolder.imageView.getContext(), emoji.getValue());
|
||||
|
||||
if (drawable != null) {
|
||||
viewHolder.textView.setVisibility(View.GONE);
|
||||
viewHolder.imageView.setVisibility(View.VISIBLE);
|
||||
|
||||
viewHolder.imageView.setImageDrawable(drawable);
|
||||
} else {
|
||||
viewHolder.textView.setVisibility(View.VISIBLE);
|
||||
viewHolder.imageView.setVisibility(View.GONE);
|
||||
|
||||
viewHolder.textView.setEmoji(emoji.getValue());
|
||||
}
|
||||
|
||||
viewHolder.itemView.setOnClickListener(v -> {
|
||||
emojiEventListener.onEmojiSelected(emoji.getValue());
|
||||
});
|
||||
|
||||
if (allowVariations && emoji.getVariations().size() > 1) {
|
||||
viewHolder.itemView.setOnLongClickListener(v -> {
|
||||
popup.dismiss();
|
||||
popup.setVariations(emoji.getVariations());
|
||||
popup.showAsDropDown(viewHolder.itemView, 0, -(2 * viewHolder.itemView.getHeight()));
|
||||
variationSelectorListener.onVariationSelectorStateChanged(true);
|
||||
return true;
|
||||
});
|
||||
viewHolder.hintCorner.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
viewHolder.itemView.setOnLongClickListener(null);
|
||||
viewHolder.hintCorner.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return emojiList.size();
|
||||
}
|
||||
|
||||
public void setEmoji(@NonNull List<Emoji> emojiList) {
|
||||
this.emojiList.clear();
|
||||
this.emojiList.addAll(emojiList);
|
||||
notifyDataSetChanged();
|
||||
registerFactory(SearchModel.class, new LayoutFactory<>(v -> {
|
||||
((KeyboardPageSearchView) v).setCallbacks(callbacks);
|
||||
return new SearchViewHolder(v);
|
||||
}, R.layout.emoji_page_view_search));
|
||||
registerFactory(EmojiModel.class, new LayoutFactory<>(v -> new EmojiViewHolder(v, emojiEventListener, variationSelectorListener, popup, allowVariations), displayItemLayoutResId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -97,18 +44,110 @@ public class EmojiPageViewGridAdapter extends RecyclerView.Adapter<EmojiPageView
|
||||
variationSelectorListener.onVariationSelectorStateChanged(false);
|
||||
}
|
||||
|
||||
static class EmojiViewHolder extends RecyclerView.ViewHolder {
|
||||
static class SearchModel implements MappingModel<SearchModel> {
|
||||
@Override
|
||||
public boolean areItemsTheSame(@NonNull @NotNull SearchModel newItem) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(@NonNull @NotNull SearchModel newItem) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static class SearchViewHolder extends MappingViewHolder<SearchModel> {
|
||||
public SearchViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(@NonNull @NotNull SearchModel model) {
|
||||
}
|
||||
}
|
||||
|
||||
static class EmojiModel implements MappingModel<EmojiModel> {
|
||||
|
||||
private final Emoji emoji;
|
||||
|
||||
EmojiModel(@NonNull Emoji emoji) {
|
||||
this.emoji = emoji;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areItemsTheSame(@NonNull @NotNull EmojiModel newItem) {
|
||||
return newItem.emoji.getValue().equals(emoji.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(@NonNull @NotNull EmojiModel newItem) {
|
||||
return areItemsTheSame(newItem);
|
||||
}
|
||||
}
|
||||
|
||||
static class EmojiViewHolder extends MappingViewHolder<EmojiModel> {
|
||||
|
||||
private final EmojiVariationSelectorPopup popup;
|
||||
private final VariationSelectorListener variationSelectorListener;
|
||||
private final EmojiEventListener emojiEventListener;
|
||||
private final boolean allowVariations;
|
||||
|
||||
private final ImageView imageView;
|
||||
private final AsciiEmojiView textView;
|
||||
private final ImageView hintCorner;
|
||||
|
||||
public EmojiViewHolder(@NonNull View itemView) {
|
||||
public EmojiViewHolder(@NonNull View itemView,
|
||||
@NonNull EmojiEventListener emojiEventListener,
|
||||
@NonNull VariationSelectorListener variationSelectorListener,
|
||||
@NonNull EmojiVariationSelectorPopup popup,
|
||||
boolean allowVariations)
|
||||
{
|
||||
super(itemView);
|
||||
|
||||
this.popup = popup;
|
||||
this.variationSelectorListener = variationSelectorListener;
|
||||
this.emojiEventListener = emojiEventListener;
|
||||
this.allowVariations = allowVariations;
|
||||
|
||||
this.imageView = itemView.findViewById(R.id.emoji_image);
|
||||
this.textView = itemView.findViewById(R.id.emoji_text);
|
||||
this.hintCorner = itemView.findViewById(R.id.emoji_variation_hint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(@NonNull @NotNull EmojiModel model) {
|
||||
final Drawable drawable = EmojiProvider.getEmojiDrawable(imageView.getContext(), model.emoji.getValue());
|
||||
|
||||
if (drawable != null) {
|
||||
textView.setVisibility(View.GONE);
|
||||
imageView.setVisibility(View.VISIBLE);
|
||||
|
||||
imageView.setImageDrawable(drawable);
|
||||
} else {
|
||||
textView.setVisibility(View.VISIBLE);
|
||||
imageView.setVisibility(View.GONE);
|
||||
|
||||
textView.setEmoji(model.emoji.getValue());
|
||||
}
|
||||
|
||||
itemView.setOnClickListener(v -> {
|
||||
emojiEventListener.onEmojiSelected(model.emoji.getValue());
|
||||
});
|
||||
|
||||
if (allowVariations && model.emoji.getVariations().size() > 1) {
|
||||
itemView.setOnLongClickListener(v -> {
|
||||
popup.dismiss();
|
||||
popup.setVariations(model.emoji.getVariations());
|
||||
popup.showAsDropDown(itemView, 0, -(2 * itemView.getHeight()));
|
||||
variationSelectorListener.onVariationSelectorStateChanged(true);
|
||||
return true;
|
||||
});
|
||||
hintCorner.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
itemView.setOnLongClickListener(null);
|
||||
hintCorner.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface VariationSelectorListener {
|
||||
|
||||
@@ -9,13 +9,16 @@ import androidx.appcompat.widget.AppCompatImageButton;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.keyboard.KeyboardPage;
|
||||
import org.thoughtcrime.securesms.stickers.StickerKeyboardProvider;
|
||||
import org.thoughtcrime.securesms.util.ContextUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
public class EmojiToggle extends AppCompatImageButton implements MediaKeyboard.MediaKeyboardListener {
|
||||
|
||||
private Drawable emojiToggle;
|
||||
private Drawable stickerToggle;
|
||||
private Drawable gifToggle;
|
||||
|
||||
private Drawable mediaToggle;
|
||||
private Drawable imeToggle;
|
||||
@@ -45,9 +48,10 @@ public class EmojiToggle extends AppCompatImageButton implements MediaKeyboard.M
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
this.emojiToggle = ContextCompat.getDrawable(getContext(), R.drawable.ic_emoji_smiley_24);
|
||||
this.stickerToggle = ContextCompat.getDrawable(getContext(), R.drawable.ic_sticker_24);
|
||||
this.imeToggle = ContextCompat.getDrawable(getContext(), R.drawable.ic_keyboard_24);
|
||||
this.emojiToggle = ContextUtil.requireDrawable(getContext(), R.drawable.keyboard_pager_fragment_emoji_icon);
|
||||
this.stickerToggle = ContextUtil.requireDrawable(getContext(), R.drawable.keyboard_pager_fragment_sticker_icon);
|
||||
this.gifToggle = ContextUtil.requireDrawable(getContext(), R.drawable.keyboard_pager_fragment_gif_icon);
|
||||
this.imeToggle = ContextUtil.requireDrawable(getContext(), R.drawable.ic_keyboard_24);
|
||||
this.mediaToggle = emojiToggle;
|
||||
|
||||
setToMedia();
|
||||
@@ -57,8 +61,18 @@ public class EmojiToggle extends AppCompatImageButton implements MediaKeyboard.M
|
||||
drawer.setKeyboardListener(this);
|
||||
}
|
||||
|
||||
public void setStickerMode(boolean stickerMode) {
|
||||
this.mediaToggle = stickerMode ? stickerToggle : emojiToggle;
|
||||
public void setStickerMode(@NonNull KeyboardPage page) {
|
||||
switch (page) {
|
||||
case EMOJI:
|
||||
mediaToggle = emojiToggle;
|
||||
break;
|
||||
case STICKER:
|
||||
mediaToggle = stickerToggle;
|
||||
break;
|
||||
case GIF:
|
||||
mediaToggle = gifToggle;
|
||||
break;
|
||||
}
|
||||
|
||||
if (getDrawable() != imeToggle) {
|
||||
setToMedia();
|
||||
@@ -78,9 +92,18 @@ public class EmojiToggle extends AppCompatImageButton implements MediaKeyboard.M
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onKeyboardProviderChanged(@NonNull MediaKeyboardProvider provider) {
|
||||
setStickerMode(provider instanceof StickerKeyboardProvider);
|
||||
TextSecurePreferences.setMediaKeyboardMode(getContext(), (provider instanceof StickerKeyboardProvider) ? TextSecurePreferences.MediaKeyboardMode.STICKER
|
||||
: TextSecurePreferences.MediaKeyboardMode.EMOJI);
|
||||
public void onKeyboardChanged(@NonNull KeyboardPage page) {
|
||||
setStickerMode(page);
|
||||
switch (page) {
|
||||
case EMOJI:
|
||||
TextSecurePreferences.setMediaKeyboardMode(getContext(), TextSecurePreferences.MediaKeyboardMode.EMOJI);
|
||||
break;
|
||||
case STICKER:
|
||||
TextSecurePreferences.setMediaKeyboardMode(getContext(), TextSecurePreferences.MediaKeyboardMode.STICKER);
|
||||
break;
|
||||
case GIF:
|
||||
TextSecurePreferences.setMediaKeyboardMode(getContext(), TextSecurePreferences.MediaKeyboardMode.GIF);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.thoughtcrime.securesms.components.emoji;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -10,18 +9,20 @@ import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.InputAwareLayout.InputView;
|
||||
import org.thoughtcrime.securesms.components.RepeatableImageKey;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.keyboard.KeyboardPage;
|
||||
import org.thoughtcrime.securesms.keyboard.KeyboardPagerFragment;
|
||||
import org.thoughtcrime.securesms.keyboard.emoji.search.EmojiSearchFragment;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.security.Key;
|
||||
|
||||
public class MediaKeyboard extends FrameLayout implements InputView,
|
||||
MediaKeyboardProvider.Presenter,
|
||||
@@ -29,22 +30,15 @@ public class MediaKeyboard extends FrameLayout implements InputView,
|
||||
MediaKeyboardBottomTabAdapter.EventListener
|
||||
{
|
||||
|
||||
private static final String TAG = Log.tag(MediaKeyboard.class);
|
||||
private static final String TAG = Log.tag(MediaKeyboard.class);
|
||||
private static final String EMOJI_SEARCH = "emoji_search_fragment";
|
||||
|
||||
private RecyclerView categoryTabs;
|
||||
private ViewPager categoryPager;
|
||||
private ViewGroup providerTabs;
|
||||
private RepeatableImageKey backspaceButton;
|
||||
private RepeatableImageKey backspaceButtonBackup;
|
||||
private View searchButton;
|
||||
private View addButton;
|
||||
@Nullable private MediaKeyboardListener keyboardListener;
|
||||
private MediaKeyboardProvider[] providers;
|
||||
private int providerIndex;
|
||||
|
||||
private final boolean tabsAtBottom;
|
||||
|
||||
private MediaKeyboardBottomTabAdapter categoryTabAdapter;
|
||||
@Nullable private MediaKeyboardListener keyboardListener;
|
||||
private boolean isInitialised;
|
||||
private int latestKeyboardHeight;
|
||||
private State keyboardState;
|
||||
private KeyboardPagerFragment keyboardPagerFragment;
|
||||
private FragmentManager fragmentManager;
|
||||
|
||||
public MediaKeyboard(Context context) {
|
||||
this(context, null);
|
||||
@@ -52,23 +46,6 @@ public class MediaKeyboard extends FrameLayout implements InputView,
|
||||
|
||||
public MediaKeyboard(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MediaKeyboard, 0, 0);
|
||||
|
||||
try {
|
||||
tabsAtBottom = typedArray.getInt(R.styleable.MediaKeyboard_tabs_gravity, 0) == 0;
|
||||
} finally {
|
||||
typedArray.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
public void setProviders(int startIndex, MediaKeyboardProvider... providers) {
|
||||
if (!Arrays.equals(this.providers, providers)) {
|
||||
this.providers = providers;
|
||||
this.providerIndex = startIndex;
|
||||
|
||||
requestPresent(providers, providerIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setKeyboardListener(@Nullable MediaKeyboardListener listener) {
|
||||
@@ -82,10 +59,12 @@ public class MediaKeyboard extends FrameLayout implements InputView,
|
||||
|
||||
@Override
|
||||
public void show(int height, boolean immediate) {
|
||||
if (this.categoryPager == null) initView();
|
||||
if (!isInitialised) initView();
|
||||
|
||||
latestKeyboardHeight = height;
|
||||
|
||||
ViewGroup.LayoutParams params = getLayoutParams();
|
||||
params.height = height;
|
||||
params.height = (keyboardState == State.NORMAL) ? latestKeyboardHeight : ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
Log.i(TAG, "showing emoji drawer with height " + params.height);
|
||||
setLayoutParams(params);
|
||||
|
||||
@@ -93,19 +72,20 @@ public class MediaKeyboard extends FrameLayout implements InputView,
|
||||
}
|
||||
|
||||
public void show() {
|
||||
if (this.categoryPager == null) initView();
|
||||
if (!isInitialised) initView();
|
||||
|
||||
setVisibility(VISIBLE);
|
||||
if (keyboardListener != null) keyboardListener.onShown();
|
||||
|
||||
requestPresent(providers, providerIndex);
|
||||
keyboardPagerFragment.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide(boolean immediate) {
|
||||
setVisibility(GONE);
|
||||
onCloseEmojiSearchInternal(false);
|
||||
if (keyboardListener != null) keyboardListener.onHidden();
|
||||
Log.i(TAG, "hide()");
|
||||
keyboardPagerFragment.hide();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -117,30 +97,29 @@ public class MediaKeyboard extends FrameLayout implements InputView,
|
||||
@Nullable MediaKeyboardProvider.SearchObserver searchObserver,
|
||||
int startingIndex)
|
||||
{
|
||||
if (categoryPager == null) return;
|
||||
if (!provider.equals(providers[providerIndex])) return;
|
||||
if (keyboardListener != null) keyboardListener.onKeyboardProviderChanged(provider);
|
||||
|
||||
boolean isSolo = providers.length == 1;
|
||||
|
||||
presentProviderStrip(isSolo);
|
||||
presentCategoryPager(pagerAdapter, tabIconProvider, startingIndex);
|
||||
presentProviderTabs(providers, providerIndex);
|
||||
presentSearchButton(searchObserver);
|
||||
presentBackspaceButton(backspaceObserver, isSolo);
|
||||
presentAddButton(addObserver);
|
||||
// if (categoryPager == null) return;
|
||||
// if (!provider.equals(providers[providerIndex])) return;
|
||||
// if (keyboardListener != null) keyboardListener.onKeyboardChanged(provider);
|
||||
//
|
||||
// boolean isSolo = providers.length == 1;
|
||||
//
|
||||
// presentProviderStrip(isSolo);
|
||||
// presentCategoryPager(pagerAdapter, tabIconProvider, startingIndex);
|
||||
// presentProviderTabs(providers, providerIndex);
|
||||
// presentSearchButton(searchObserver);
|
||||
// presentBackspaceButton(backspaceObserver, isSolo);
|
||||
// presentAddButton(addObserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentPosition() {
|
||||
return categoryPager != null ? categoryPager.getCurrentItem() : 0;
|
||||
// return categoryPager != null ? categoryPager.getCurrentItem() : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestDismissal() {
|
||||
hide(true);
|
||||
providerIndex = 0;
|
||||
if (keyboardListener != null) keyboardListener.onKeyboardProviderChanged(providers[providerIndex]);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -150,148 +129,82 @@ public class MediaKeyboard extends FrameLayout implements InputView,
|
||||
|
||||
@Override
|
||||
public void onTabSelected(int index) {
|
||||
if (categoryPager != null) {
|
||||
categoryPager.setCurrentItem(index);
|
||||
categoryTabs.smoothScrollToPosition(index);
|
||||
}
|
||||
// if (categoryPager != null) {
|
||||
// categoryPager.setCurrentItem(index);
|
||||
// categoryTabs.smoothScrollToPosition(index);
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setViewPagerEnabled(boolean enabled) {
|
||||
if (categoryPager != null) {
|
||||
categoryPager.setEnabled(enabled);
|
||||
// if (categoryPager != null) {
|
||||
// categoryPager.setEnabled(enabled);
|
||||
// }
|
||||
}
|
||||
|
||||
public void onCloseEmojiSearch() {
|
||||
onCloseEmojiSearchInternal(true);
|
||||
}
|
||||
|
||||
private void onCloseEmojiSearchInternal(boolean showAfterCommit) {
|
||||
if (keyboardState == State.NORMAL) {
|
||||
return;
|
||||
}
|
||||
|
||||
keyboardState = State.NORMAL;
|
||||
|
||||
Fragment emojiSearch = fragmentManager.findFragmentByTag(EMOJI_SEARCH);
|
||||
if (emojiSearch == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
FragmentTransaction transaction = fragmentManager.beginTransaction()
|
||||
.remove(emojiSearch)
|
||||
.show(keyboardPagerFragment)
|
||||
.setCustomAnimations(R.anim.fade_in, R.anim.fade_out);
|
||||
|
||||
if (showAfterCommit) {
|
||||
transaction.runOnCommit(() -> show(latestKeyboardHeight, false));
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
public void onOpenEmojiSearch() {
|
||||
if (keyboardState == State.EMOJI_SEARCH) {
|
||||
return;
|
||||
}
|
||||
|
||||
keyboardState = State.EMOJI_SEARCH;
|
||||
|
||||
fragmentManager.beginTransaction()
|
||||
.hide(keyboardPagerFragment)
|
||||
.add(R.id.media_keyboard_fragment_container, new EmojiSearchFragment(), EMOJI_SEARCH)
|
||||
.runOnCommit(() -> show(latestKeyboardHeight, true))
|
||||
.setCustomAnimations(R.anim.fade_in, R.anim.fade_out)
|
||||
.commit();
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
final View view = LayoutInflater.from(getContext()).inflate(R.layout.media_keyboard, this, true);
|
||||
if (!isInitialised) {
|
||||
LayoutInflater.from(getContext()).inflate(R.layout.media_keyboard, this, true);
|
||||
|
||||
RecyclerView categoryTabsTop = view.findViewById(R.id.media_keyboard_tabs_top);
|
||||
RecyclerView categoryTabsBottom = view.findViewById(R.id.media_keyboard_tabs);
|
||||
|
||||
this.categoryTabs = tabsAtBottom ? categoryTabsBottom : categoryTabsTop;
|
||||
this.categoryPager = view.findViewById(R.id.media_keyboard_pager);
|
||||
this.providerTabs = view.findViewById(R.id.media_keyboard_provider_tabs);
|
||||
this.backspaceButton = view.findViewById(R.id.media_keyboard_backspace);
|
||||
this.backspaceButtonBackup = view.findViewById(R.id.media_keyboard_backspace_backup);
|
||||
this.searchButton = view.findViewById(R.id.media_keyboard_search);
|
||||
this.addButton = view.findViewById(R.id.media_keyboard_add);
|
||||
|
||||
this.categoryTabAdapter = new MediaKeyboardBottomTabAdapter(GlideApp.with(this), this, tabsAtBottom);
|
||||
|
||||
categoryTabs.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false));
|
||||
categoryTabs.setAdapter(categoryTabAdapter);
|
||||
categoryTabs.setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
private void requestPresent(@NonNull MediaKeyboardProvider[] providers, int newIndex) {
|
||||
providers[providerIndex].setController(null);
|
||||
providerIndex = newIndex;
|
||||
|
||||
providers[providerIndex].setController(this);
|
||||
providers[providerIndex].requestPresentation(this, providers.length == 1);
|
||||
}
|
||||
|
||||
|
||||
private void presentCategoryPager(@NonNull PagerAdapter pagerAdapter,
|
||||
@NonNull MediaKeyboardProvider.TabIconProvider iconProvider,
|
||||
int startingIndex) {
|
||||
if (categoryPager.getAdapter() != pagerAdapter) {
|
||||
categoryPager.setAdapter(pagerAdapter);
|
||||
keyboardState = State.NORMAL;
|
||||
latestKeyboardHeight = -1;
|
||||
isInitialised = true;
|
||||
fragmentManager = ((FragmentActivity) getContext()).getSupportFragmentManager();
|
||||
keyboardPagerFragment = (KeyboardPagerFragment) fragmentManager.findFragmentById(R.id.media_keyboard_fragment_container);
|
||||
}
|
||||
|
||||
categoryPager.setCurrentItem(startingIndex);
|
||||
|
||||
categoryPager.clearOnPageChangeListeners();
|
||||
categoryPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||
@Override
|
||||
public void onPageScrolled(int i, float v, int i1) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int i) {
|
||||
categoryTabAdapter.setActivePosition(i);
|
||||
categoryTabs.smoothScrollToPosition(i);
|
||||
providers[providerIndex].setCurrentPosition(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int i) {
|
||||
}
|
||||
});
|
||||
|
||||
categoryTabAdapter.setTabIconProvider(iconProvider, pagerAdapter.getCount());
|
||||
categoryTabAdapter.setActivePosition(startingIndex);
|
||||
}
|
||||
|
||||
private void presentProviderTabs(@NonNull MediaKeyboardProvider[] providers, int selected) {
|
||||
providerTabs.removeAllViews();
|
||||
|
||||
LayoutInflater inflater = LayoutInflater.from(getContext());
|
||||
|
||||
for (int i = 0; i < providers.length; i++) {
|
||||
MediaKeyboardProvider provider = providers[i];
|
||||
View view = inflater.inflate(provider.getProviderIconView(i == selected), providerTabs, false);
|
||||
|
||||
view.setTag(provider);
|
||||
|
||||
final int index = i;
|
||||
view.setOnClickListener(v -> {
|
||||
requestPresent(providers, index);
|
||||
});
|
||||
|
||||
providerTabs.addView(view);
|
||||
}
|
||||
}
|
||||
|
||||
private void presentBackspaceButton(@Nullable MediaKeyboardProvider.BackspaceObserver backspaceObserver,
|
||||
boolean useBackupPosition)
|
||||
{
|
||||
if (backspaceObserver != null) {
|
||||
if (useBackupPosition) {
|
||||
backspaceButton.setVisibility(INVISIBLE);
|
||||
backspaceButton.setOnKeyEventListener(null);
|
||||
backspaceButtonBackup.setVisibility(VISIBLE);
|
||||
backspaceButtonBackup.setOnKeyEventListener(backspaceObserver::onBackspaceClicked);
|
||||
} else {
|
||||
backspaceButton.setVisibility(VISIBLE);
|
||||
backspaceButton.setOnKeyEventListener(backspaceObserver::onBackspaceClicked);
|
||||
backspaceButtonBackup.setVisibility(GONE);
|
||||
backspaceButtonBackup.setOnKeyEventListener(null);
|
||||
}
|
||||
} else {
|
||||
backspaceButton.setVisibility(INVISIBLE);
|
||||
backspaceButton.setOnKeyEventListener(null);
|
||||
backspaceButtonBackup.setVisibility(GONE);
|
||||
backspaceButton.setOnKeyEventListener(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void presentAddButton(@Nullable MediaKeyboardProvider.AddObserver addObserver) {
|
||||
if (addObserver != null) {
|
||||
addButton.setVisibility(VISIBLE);
|
||||
addButton.setOnClickListener(v -> addObserver.onAddClicked());
|
||||
} else {
|
||||
addButton.setVisibility(GONE);
|
||||
addButton.setOnClickListener(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void presentSearchButton(@Nullable MediaKeyboardProvider.SearchObserver searchObserver) {
|
||||
searchButton.setVisibility(searchObserver != null ? VISIBLE : INVISIBLE);
|
||||
}
|
||||
|
||||
private void presentProviderStrip(boolean isSolo) {
|
||||
int visibility = isSolo ? View.GONE : View.VISIBLE;
|
||||
|
||||
searchButton.setVisibility(visibility);
|
||||
backspaceButton.setVisibility(visibility);
|
||||
providerTabs.setVisibility(visibility);
|
||||
}
|
||||
|
||||
public interface MediaKeyboardListener {
|
||||
void onShown();
|
||||
void onHidden();
|
||||
void onKeyboardProviderChanged(@NonNull MediaKeyboardProvider provider);
|
||||
void onKeyboardChanged(@NonNull KeyboardPage page);
|
||||
}
|
||||
|
||||
private enum State {
|
||||
NORMAL,
|
||||
EMOJI_SEARCH
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,22 +16,19 @@ public class MediaKeyboardBottomTabAdapter extends RecyclerView.Adapter<MediaKey
|
||||
|
||||
private final GlideRequests glideRequests;
|
||||
private final EventListener eventListener;
|
||||
private final boolean highlightTop;
|
||||
|
||||
private TabIconProvider tabIconProvider;
|
||||
private int activePosition;
|
||||
private int count;
|
||||
|
||||
public MediaKeyboardBottomTabAdapter(@NonNull GlideRequests glideRequests, @NonNull EventListener eventListener, boolean highlightTop) {
|
||||
public MediaKeyboardBottomTabAdapter(@NonNull GlideRequests glideRequests, @NonNull EventListener eventListener) {
|
||||
this.glideRequests = glideRequests;
|
||||
this.eventListener = eventListener;
|
||||
this.highlightTop = highlightTop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull MediaKeyboardBottomTabViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
|
||||
return new MediaKeyboardBottomTabViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.media_keyboard_bottom_tab_item, viewGroup, false),
|
||||
highlightTop);
|
||||
return new MediaKeyboardBottomTabViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.media_keyboard_bottom_tab_item, viewGroup, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -64,18 +61,12 @@ public class MediaKeyboardBottomTabAdapter extends RecyclerView.Adapter<MediaKey
|
||||
static class MediaKeyboardBottomTabViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private final ImageView image;
|
||||
private final View indicator;
|
||||
private final View imageSelected;
|
||||
|
||||
public MediaKeyboardBottomTabViewHolder(@NonNull View itemView, boolean highlightTop) {
|
||||
public MediaKeyboardBottomTabViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
|
||||
View indicatorTop = itemView.findViewById(R.id.media_keyboard_top_tab_indicator);
|
||||
View indicatorBottom = itemView.findViewById(R.id.media_keyboard_bottom_tab_indicator);
|
||||
|
||||
this.image = itemView.findViewById(R.id.media_keyboard_bottom_tab_image);
|
||||
this.indicator = highlightTop ? indicatorTop : indicatorBottom;
|
||||
|
||||
this.indicator.setVisibility(View.VISIBLE);
|
||||
this.image = itemView.findViewById(R.id.category_icon);
|
||||
this.imageSelected = itemView.findViewById(R.id.category_icon_selected);
|
||||
}
|
||||
|
||||
void bind(@NonNull GlideRequests glideRequests,
|
||||
@@ -86,9 +77,7 @@ public class MediaKeyboardBottomTabAdapter extends RecyclerView.Adapter<MediaKey
|
||||
{
|
||||
tabIconProvider.loadCategoryTabIcon(glideRequests, image, index);
|
||||
image.setAlpha(selected ? 1 : 0.5f);
|
||||
image.setSelected(selected);
|
||||
|
||||
indicator.setVisibility(selected ? View.VISIBLE : View.INVISIBLE);
|
||||
imageSelected.setSelected(selected);
|
||||
|
||||
itemView.setOnClickListener(v -> eventListener.onTabSelected(index));
|
||||
}
|
||||
@@ -98,7 +87,7 @@ public class MediaKeyboardBottomTabAdapter extends RecyclerView.Adapter<MediaKey
|
||||
}
|
||||
}
|
||||
|
||||
interface EventListener {
|
||||
public interface EventListener {
|
||||
void onTabSelected(int index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,10 @@ public class RecentEmojiPageModel implements EmojiPageModel {
|
||||
private final String preferenceName;
|
||||
private final LinkedHashSet<String> recentlyUsed;
|
||||
|
||||
public static boolean hasRecents(Context context, @NonNull String preferenceName) {
|
||||
return PreferenceManager.getDefaultSharedPreferences(context).contains(preferenceName);
|
||||
}
|
||||
|
||||
public RecentEmojiPageModel(Context context, @NonNull String preferenceName) {
|
||||
this.prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
this.preferenceName = preferenceName;
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.components.settings.app.appearance
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import org.thoughtcrime.securesms.jobs.EmojiSearchIndexDownloadJob
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.util.livedata.Store
|
||||
|
||||
@@ -28,6 +29,7 @@ class AppearanceSettingsViewModel : ViewModel() {
|
||||
fun setLanguage(language: String) {
|
||||
store.update { it.copy(language = language) }
|
||||
SignalStore.settings().language = language
|
||||
EmojiSearchIndexDownloadJob.scheduleImmediately()
|
||||
}
|
||||
|
||||
fun setMessageFontSize(size: Int) {
|
||||
|
||||
Reference in New Issue
Block a user