Remove dead keyboard code after refresh.

This commit is contained in:
Cody Henthorne
2021-06-29 12:34:49 -04:00
committed by Greyson Parrelli
parent 7419570f94
commit c54c6018b2
45 changed files with 97 additions and 1545 deletions

View File

@@ -31,11 +31,10 @@ import org.signal.core.util.ThreadUtil;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.animation.AnimationCompleteListener;
import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider;
import org.thoughtcrime.securesms.components.emoji.EmojiEventListener;
import org.thoughtcrime.securesms.components.emoji.EmojiToggle;
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;
@@ -59,7 +58,7 @@ import java.util.concurrent.TimeUnit;
public class InputPanel extends LinearLayout
implements MicrophoneRecorderView.Listener,
KeyboardAwareLinearLayout.OnKeyboardShownListener,
EmojiKeyboardProvider.EmojiEventListener,
EmojiEventListener,
ConversationStickerSuggestionAdapter.EventListener
{

View File

@@ -0,0 +1,9 @@
package org.thoughtcrime.securesms.components.emoji;
import android.view.KeyEvent;
public interface EmojiEventListener {
void onEmojiSelected(String emoji);
void onKeyEvent(KeyEvent keyEvent);
}

View File

@@ -1,180 +0,0 @@
package org.thoughtcrime.securesms.components.emoji;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.viewpager.widget.PagerAdapter;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter.VariationSelectorListener;
import org.thoughtcrime.securesms.emoji.EmojiSource;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.util.ResUtil;
import java.util.LinkedList;
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,
MediaKeyboardProvider.BackspaceObserver,
VariationSelectorListener
{
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;
private final List<EmojiPageModel> models;
private final RecentEmojiPageModel recentModel;
private final EmojiPagerAdapter emojiPagerAdapter;
private final EmojiEventListener emojiEventListener;
private Controller controller;
private int currentPosition;
public EmojiKeyboardProvider(@NonNull Context context, @Nullable EmojiEventListener emojiEventListener) {
this.context = context;
this.emojiEventListener = emojiEventListener;
this.models = new LinkedList<>();
this.recentModel = new RecentEmojiPageModel(context, RECENT_STORAGE_KEY);
this.emojiPagerAdapter = new EmojiPagerAdapter(context, models, new EmojiEventListener() {
@Override
public void onEmojiSelected(String emoji) {
recentModel.onCodePointSelected(emoji);
SignalStore.emojiValues().setPreferredVariation(emoji);
if (emojiEventListener != null) {
emojiEventListener.onEmojiSelected(emoji);
}
}
@Override
public void onKeyEvent(KeyEvent keyEvent) {
if (emojiEventListener != null) {
emojiEventListener.onKeyEvent(keyEvent);
}
}
}, this);
models.add(recentModel);
models.addAll(EmojiSource.getLatest().getDisplayPages());
currentPosition = recentModel.getEmoji().size() > 0 ? 0 : 1;
}
@Override
public void requestPresentation(@NonNull Presenter presenter, boolean isSoloProvider) {
presenter.present(this, emojiPagerAdapter, this, this, null, null, currentPosition);
}
@Override
public void setCurrentPosition(int currentPosition) {
this.currentPosition = currentPosition;
}
@Override
public void setController(@Nullable Controller controller) {
this.controller = controller;
}
@Override
public int getProviderIconView(boolean selected) {
if (selected) {
return R.layout.emoji_keyboard_icon_selected;
} else {
return R.layout.emoji_keyboard_icon;
}
}
@Override
public void loadCategoryTabIcon(@NonNull GlideRequests glideRequests, @NonNull ImageView imageView, int index) {
Drawable drawable = ResUtil.getDrawable(context, models.get(index).getIconAttr());
imageView.setImageDrawable(drawable);
}
@Override
public void onBackspaceClicked() {
if (emojiEventListener != null) {
emojiEventListener.onKeyEvent(DELETE_KEY_EVENT);
}
}
@Override
public void onVariationSelectorStateChanged(boolean open) {
if (controller != null) {
controller.setViewPagerEnabled(!open);
}
}
@Override
public boolean equals(@Nullable Object obj) {
return obj instanceof EmojiKeyboardProvider;
}
private static class EmojiPagerAdapter extends PagerAdapter {
private Context context;
private List<EmojiPageModel> pages;
private EmojiEventListener emojiSelectionListener;
private VariationSelectorListener variationSelectorListener;
public EmojiPagerAdapter(@NonNull Context context,
@NonNull List<EmojiPageModel> pages,
@NonNull EmojiEventListener emojiSelectionListener,
@NonNull VariationSelectorListener variationSelectorListener)
{
super();
this.context = context;
this.pages = pages;
this.emojiSelectionListener = emojiSelectionListener;
this.variationSelectorListener = variationSelectorListener;
}
@Override
public int getCount() {
return pages.size();
}
@Override
public @NonNull Object instantiateItem(@NonNull ViewGroup container, int position) {
EmojiPageView page = new EmojiPageView(context, emojiSelectionListener, variationSelectorListener, true);
page.setModel(pages.get(position));
container.addView(page);
return page;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View)object);
}
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
EmojiPageView current = (EmojiPageView) object;
current.onSelected();
super.setPrimaryItem(container, position, object);
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
}
public interface EmojiEventListener {
void onEmojiSelected(String emoji);
void onKeyEvent(KeyEvent keyEvent);
}
}

View File

@@ -16,7 +16,6 @@ import androidx.recyclerview.widget.LinearSmoothScroller;
import androidx.recyclerview.widget.RecyclerView;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider.EmojiEventListener;
import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter.EmojiHeader;
import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter.EmojiNoResultsModel;
import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter.VariationSelectorListener;

View File

@@ -10,7 +10,6 @@ import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider.EmojiEventListener;
import org.thoughtcrime.securesms.util.MappingAdapter;
import org.thoughtcrime.securesms.util.MappingModel;
import org.thoughtcrime.securesms.util.MappingViewHolder;

View File

@@ -6,11 +6,9 @@ import android.util.AttributeSet;
import androidx.annotation.NonNull;
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;

View File

@@ -11,7 +11,6 @@ import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider.EmojiEventListener;
import java.util.List;

View File

@@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.components.emoji;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -13,7 +12,6 @@ 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 org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
@@ -22,13 +20,7 @@ import org.thoughtcrime.securesms.keyboard.KeyboardPage;
import org.thoughtcrime.securesms.keyboard.KeyboardPagerFragment;
import org.thoughtcrime.securesms.keyboard.emoji.search.EmojiSearchFragment;
import java.security.Key;
public class MediaKeyboard extends FrameLayout implements InputView,
MediaKeyboardProvider.Presenter,
MediaKeyboardProvider.Controller,
MediaKeyboardBottomTabAdapter.EventListener
{
public class MediaKeyboard extends FrameLayout implements InputView {
private static final String TAG = Log.tag(MediaKeyboard.class);
private static final String EMOJI_SEARCH = "emoji_search_fragment";
@@ -88,60 +80,6 @@ public class MediaKeyboard extends FrameLayout implements InputView,
keyboardPagerFragment.hide();
}
@Override
public void present(@NonNull MediaKeyboardProvider provider,
@NonNull PagerAdapter pagerAdapter,
@NonNull MediaKeyboardProvider.TabIconProvider tabIconProvider,
@Nullable MediaKeyboardProvider.BackspaceObserver backspaceObserver,
@Nullable MediaKeyboardProvider.AddObserver addObserver,
@Nullable MediaKeyboardProvider.SearchObserver searchObserver,
int startingIndex)
{
// 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 0;
}
@Override
public void requestDismissal() {
hide(true);
}
@Override
public boolean isVisible() {
return getVisibility() == View.VISIBLE;
}
@Override
public void onTabSelected(int index) {
// if (categoryPager != null) {
// categoryPager.setCurrentItem(index);
// categoryTabs.smoothScrollToPosition(index);
// }
}
@Override
public void setViewPagerEnabled(boolean enabled) {
// if (categoryPager != null) {
// categoryPager.setEnabled(enabled);
// }
}
public void onCloseEmojiSearch() {
onCloseEmojiSearchInternal(true);
}

View File

@@ -1,93 +0,0 @@
package org.thoughtcrime.securesms.components.emoji;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.emoji.MediaKeyboardProvider.TabIconProvider;
import org.thoughtcrime.securesms.mms.GlideRequests;
public class MediaKeyboardBottomTabAdapter extends RecyclerView.Adapter<MediaKeyboardBottomTabAdapter.MediaKeyboardBottomTabViewHolder> {
private final GlideRequests glideRequests;
private final EventListener eventListener;
private TabIconProvider tabIconProvider;
private int activePosition;
private int count;
public MediaKeyboardBottomTabAdapter(@NonNull GlideRequests glideRequests, @NonNull EventListener eventListener) {
this.glideRequests = glideRequests;
this.eventListener = eventListener;
}
@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));
}
@Override
public void onBindViewHolder(@NonNull MediaKeyboardBottomTabViewHolder viewHolder, int i) {
viewHolder.bind(glideRequests, eventListener, tabIconProvider, i, i == activePosition);
}
@Override
public void onViewRecycled(@NonNull MediaKeyboardBottomTabViewHolder holder) {
holder.recycle();
}
@Override
public int getItemCount() {
return count;
}
public void setTabIconProvider(@NonNull TabIconProvider iconProvider, int count) {
this.tabIconProvider = iconProvider;
this.count = count;
notifyDataSetChanged();
}
public void setActivePosition(int position) {
this.activePosition = position;
notifyDataSetChanged();
}
static class MediaKeyboardBottomTabViewHolder extends RecyclerView.ViewHolder {
private final ImageView image;
private final View imageSelected;
public MediaKeyboardBottomTabViewHolder(@NonNull View itemView) {
super(itemView);
this.image = itemView.findViewById(R.id.category_icon);
this.imageSelected = itemView.findViewById(R.id.category_icon_selected);
}
void bind(@NonNull GlideRequests glideRequests,
@NonNull EventListener eventListener,
@NonNull TabIconProvider tabIconProvider,
int index,
boolean selected)
{
tabIconProvider.loadCategoryTabIcon(glideRequests, image, index);
image.setAlpha(selected ? 1 : 0.5f);
imageSelected.setSelected(selected);
itemView.setOnClickListener(v -> eventListener.onTabSelected(index));
}
void recycle() {
itemView.setOnClickListener(null);
}
}
public interface EventListener {
void onTabSelected(int index);
}
}

View File

@@ -1,53 +0,0 @@
package org.thoughtcrime.securesms.components.emoji;
import android.widget.ImageView;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.viewpager.widget.PagerAdapter;
import org.thoughtcrime.securesms.mms.GlideRequests;
public interface MediaKeyboardProvider {
@LayoutRes int getProviderIconView(boolean selected);
/** @return True if the click was handled with provider-specific logic, otherwise false */
void requestPresentation(@NonNull Presenter presenter, boolean isSoloProvider);
void setController(@Nullable Controller controller);
void setCurrentPosition(int currentPosition);
interface BackspaceObserver {
void onBackspaceClicked();
}
interface AddObserver {
void onAddClicked();
}
interface SearchObserver {
void onSearchOpened();
void onSearchClosed();
void onSearchChanged(@NonNull String query);
}
interface Controller {
void setViewPagerEnabled(boolean enabled);
}
interface Presenter {
void present(@NonNull MediaKeyboardProvider provider,
@NonNull PagerAdapter pagerAdapter,
@NonNull TabIconProvider iconProvider,
@Nullable BackspaceObserver backspaceObserver,
@Nullable AddObserver addObserver,
@Nullable SearchObserver searchObserver,
int startingIndex);
int getCurrentPosition();
void requestDismissal();
boolean isVisible();
}
interface TabIconProvider {
void loadCategoryTabIcon(@NonNull GlideRequests glideRequests, @NonNull ImageView imageView, int index);
}
}

View File

@@ -116,7 +116,7 @@ import org.thoughtcrime.securesms.components.MaskView;
import org.thoughtcrime.securesms.components.SendButton;
import org.thoughtcrime.securesms.components.TooltipPopup;
import org.thoughtcrime.securesms.components.TypingStatusSender;
import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider;
import org.thoughtcrime.securesms.components.emoji.EmojiEventListener;
import org.thoughtcrime.securesms.components.emoji.EmojiStrings;
import org.thoughtcrime.securesms.components.emoji.MediaKeyboard;
import org.thoughtcrime.securesms.components.identity.UnverifiedBannerView;
@@ -244,7 +244,7 @@ import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingEncryptedMessage;
import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.thoughtcrime.securesms.stickers.StickerKeyboardProvider;
import org.thoughtcrime.securesms.stickers.StickerEventListener;
import org.thoughtcrime.securesms.stickers.StickerLocator;
import org.thoughtcrime.securesms.stickers.StickerManagementActivity;
import org.thoughtcrime.securesms.stickers.StickerPackInstallEvent;
@@ -318,14 +318,14 @@ public class ConversationActivity extends PassphraseRequiredActivity
InputPanel.MediaListener,
ComposeText.CursorPositionChangedListener,
ConversationSearchBottomBar.EventListener,
StickerKeyboardProvider.StickerEventListener,
StickerEventListener,
AttachmentKeyboard.Callback,
ConversationReactionOverlay.OnReactionSelectedListener,
ReactWithAnyEmojiBottomSheetDialogFragment.Callback,
SafetyNumberChangeDialog.Callback,
ReactionsBottomSheetDialogFragment.Callback,
MediaKeyboard.MediaKeyboardListener,
EmojiKeyboardProvider.EmojiEventListener,
EmojiEventListener,
GifKeyboardPageFragment.Host,
EmojiKeyboardPageFragment.Callback,
EmojiSearchFragment.Callback

View File

@@ -1,76 +0,0 @@
package org.thoughtcrime.securesms.giph.mp4;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.emoji.MediaKeyboardProvider;
import org.thoughtcrime.securesms.mms.GlideRequests;
/**
* MediaKeyboardProvider for MP4 Gifs
*/
@SuppressWarnings("unused")
public final class GiphyMp4MediaKeyboardProvider implements MediaKeyboardProvider {
private final GiphyMp4MediaKeyboardPagerAdapter pagerAdapter;
private Controller controller;
public GiphyMp4MediaKeyboardProvider(@NonNull FragmentActivity fragmentActivity, boolean isForMms) {
pagerAdapter = new GiphyMp4MediaKeyboardPagerAdapter(fragmentActivity.getSupportFragmentManager(), isForMms);
}
@Override
public int getProviderIconView(boolean selected) {
if (selected) {
return R.layout.giphy_mp4_keyboard_icon_selected;
} else {
return R.layout.giphy_mp4_keyboard_icon;
}
}
@Override
public void requestPresentation(@NonNull Presenter presenter, boolean isSoloProvider) {
presenter.present(this, pagerAdapter, new GiphyMp4MediaKeyboardTabIconProvider(), null, null, null, 0);
}
@Override
public void setController(@Nullable Controller controller) {
this.controller = controller;
}
@Override
public void setCurrentPosition(int currentPosition) {
// ignored.
}
private static final class GiphyMp4MediaKeyboardPagerAdapter extends FragmentStatePagerAdapter {
private final boolean isForMms;
public GiphyMp4MediaKeyboardPagerAdapter(@NonNull FragmentManager fm, boolean isForMms) {
super(fm, FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
this.isForMms = isForMms;
}
@Override
public @NonNull Fragment getItem(int position) {
return GiphyMp4Fragment.create(isForMms);
}
@Override public int getCount() {
return 1;
}
}
private static final class GiphyMp4MediaKeyboardTabIconProvider implements TabIconProvider {
@Override public void loadCategoryTabIcon(@NonNull GlideRequests glideRequests, @NonNull ImageView imageView, int index) { }
}
}

View File

@@ -11,7 +11,7 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE
import com.google.android.material.appbar.AppBarLayout
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider
import org.thoughtcrime.securesms.components.emoji.EmojiEventListener
import org.thoughtcrime.securesms.components.emoji.EmojiPageView
import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter
import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter.EmojiHeader
@@ -22,14 +22,14 @@ import java.util.Optional
private val DELETE_KEY_EVENT: KeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)
class EmojiKeyboardPageFragment : Fragment(R.layout.keyboard_pager_emoji_page_fragment), EmojiKeyboardProvider.EmojiEventListener, EmojiPageViewGridAdapter.VariationSelectorListener {
class EmojiKeyboardPageFragment : Fragment(R.layout.keyboard_pager_emoji_page_fragment), EmojiEventListener, EmojiPageViewGridAdapter.VariationSelectorListener {
private lateinit var viewModel: EmojiKeyboardPageViewModel
private lateinit var emojiPageView: EmojiPageView
private lateinit var searchView: View
private lateinit var emojiCategoriesRecycler: RecyclerView
private lateinit var backspaceView: View
private lateinit var eventListener: EmojiKeyboardProvider.EmojiEventListener
private lateinit var eventListener: EmojiEventListener
private lateinit var callback: Callback
private lateinit var categoriesAdapter: EmojiKeyboardPageCategoriesAdapter
private lateinit var searchBar: KeyboardPageSearchView

View File

@@ -2,15 +2,15 @@ package org.thoughtcrime.securesms.keyboard.emoji
import android.content.Context
import org.signal.core.util.concurrent.SignalExecutors
import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider
import org.thoughtcrime.securesms.components.emoji.EmojiPageModel
import org.thoughtcrime.securesms.components.emoji.RecentEmojiPageModel
import org.thoughtcrime.securesms.emoji.EmojiSource.Companion.latest
import org.thoughtcrime.securesms.util.TextSecurePreferences
import java.util.function.Consumer
class EmojiKeyboardPageRepository(context: Context) {
private val recentEmojiPageModel: RecentEmojiPageModel = RecentEmojiPageModel(context, EmojiKeyboardProvider.RECENT_STORAGE_KEY)
private val recentEmojiPageModel: RecentEmojiPageModel = RecentEmojiPageModel(context, TextSecurePreferences.RECENT_STORAGE_KEY)
fun getEmoji(consumer: Consumer<List<EmojiPageModel>>) {
SignalExecutors.BOUNDED.execute {

View File

@@ -6,7 +6,6 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider
import org.thoughtcrime.securesms.components.emoji.EmojiPageModel
import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter
import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter.EmojiHeader
@@ -19,6 +18,7 @@ import org.thoughtcrime.securesms.keyboard.emoji.EmojiKeyboardPageCategoryMappin
import org.thoughtcrime.securesms.util.DefaultValueLiveData
import org.thoughtcrime.securesms.util.MappingModel
import org.thoughtcrime.securesms.util.MappingModelList
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil
class EmojiKeyboardPageViewModel(repository: EmojiKeyboardPageRepository) : ViewModel() {
@@ -70,12 +70,12 @@ class EmojiKeyboardPageViewModel(repository: EmojiKeyboardPageRepository) : View
}
fun addToRecents(emoji: String) {
RecentEmojiPageModel(ApplicationDependencies.getApplication(), EmojiKeyboardProvider.RECENT_STORAGE_KEY).onCodePointSelected(emoji)
RecentEmojiPageModel(ApplicationDependencies.getApplication(), TextSecurePreferences.RECENT_STORAGE_KEY).onCodePointSelected(emoji)
}
companion object {
fun getStartingTab(): String {
return if (RecentEmojiPageModel.hasRecents(ApplicationDependencies.getApplication(), EmojiKeyboardProvider.RECENT_STORAGE_KEY)) {
return if (RecentEmojiPageModel.hasRecents(ApplicationDependencies.getApplication(), TextSecurePreferences.RECENT_STORAGE_KEY)) {
RecentEmojiPageModel.KEY
} else {
EmojiCategory.PEOPLE.key

View File

@@ -11,7 +11,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout
import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider
import org.thoughtcrime.securesms.components.emoji.EmojiEventListener
import org.thoughtcrime.securesms.components.emoji.EmojiPageView
import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter
import org.thoughtcrime.securesms.keyboard.emoji.KeyboardPageSearchView
@@ -36,7 +36,7 @@ class EmojiSearchFragment : Fragment(R.layout.emoji_search_fragment), EmojiPageV
viewModel = ViewModelProviders.of(this, factory)[EmojiSearchViewModel::class.java]
val keyboardAwareLinearLayout: KeyboardAwareLinearLayout = view.findViewById(R.id.kb_aware_layout)
val eventListener: EmojiKeyboardProvider.EmojiEventListener = requireNotNull(findListener())
val eventListener: EmojiEventListener = requireNotNull(findListener())
val searchBar: KeyboardPageSearchView = view.findViewById(R.id.emoji_search_view)
val resultsContainer: FrameLayout = view.findViewById(R.id.emoji_search_results_container)
val noResults: TextView = view.findViewById(R.id.emoji_search_empty)

View File

@@ -4,12 +4,12 @@ import android.content.Context
import android.net.Uri
import org.signal.core.util.concurrent.SignalExecutors
import org.thoughtcrime.securesms.components.emoji.Emoji
import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider
import org.thoughtcrime.securesms.components.emoji.EmojiPageModel
import org.thoughtcrime.securesms.components.emoji.RecentEmojiPageModel
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.EmojiSearchDatabase
import org.thoughtcrime.securesms.emoji.EmojiSource
import org.thoughtcrime.securesms.util.TextSecurePreferences
import java.util.function.Consumer
private const val MINIMUM_QUERY_THRESHOLD = 1
@@ -21,7 +21,7 @@ class EmojiSearchRepository(private val context: Context) {
fun submitQuery(query: String, includeRecents: Boolean, limit: Int = EMOJI_SEARCH_LIMIT, consumer: Consumer<EmojiPageModel>) {
if (query.length < MINIMUM_QUERY_THRESHOLD && includeRecents) {
consumer.accept(RecentEmojiPageModel(context, EmojiKeyboardProvider.RECENT_STORAGE_KEY))
consumer.accept(RecentEmojiPageModel(context, TextSecurePreferences.RECENT_STORAGE_KEY))
} else {
SignalExecutors.SERIAL.execute {
val emoji: List<String> = emojiSearchDatabase.query(query, limit)

View File

@@ -12,7 +12,7 @@ import org.thoughtcrime.securesms.util.MappingAdapter
import org.thoughtcrime.securesms.util.MappingModel
import org.thoughtcrime.securesms.util.MappingViewHolder
class StickerPackListAdapter(private val glideRequests: GlideRequests, private val allowApngAnimation: Boolean, private val onTabSelected: (StickerPack) -> Unit) : MappingAdapter() {
class KeyboardStickerPackListAdapter(private val glideRequests: GlideRequests, private val allowApngAnimation: Boolean, private val onTabSelected: (StickerPack) -> Unit) : MappingAdapter() {
init {
registerFactory(StickerPack::class.java, LayoutFactory(::StickerPackViewHolder, R.layout.keyboard_pager_category_icon))

View File

@@ -17,7 +17,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.keyboard.emoji.KeyboardPageSearchView
import org.thoughtcrime.securesms.keyboard.findListener
import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.stickers.StickerKeyboardProvider.StickerEventListener
import org.thoughtcrime.securesms.stickers.StickerEventListener
import org.thoughtcrime.securesms.stickers.StickerRolloverTouchListener
import org.thoughtcrime.securesms.stickers.StickerRolloverTouchListener.RolloverStickerRetriever
import org.thoughtcrime.securesms.util.DeviceProperties
@@ -36,12 +36,12 @@ class StickerKeyboardPageFragment :
View.OnLayoutChangeListener {
private lateinit var stickerList: RecyclerView
private lateinit var keyboardStickerListAdapter: KeyboardStickerListAdapter
private lateinit var stickerListAdapter: KeyboardStickerListAdapter
private lateinit var layoutManager: GridLayoutManager
private lateinit var listTouchListener: StickerRolloverTouchListener
private lateinit var stickerPacksRecycler: RecyclerView
private lateinit var appBarLayout: AppBarLayout
private lateinit var stickerPacksAdapter: StickerPackListAdapter
private lateinit var stickerPacksAdapter: KeyboardStickerPackListAdapter
private lateinit var viewModel: StickerKeyboardPageViewModel
@@ -53,11 +53,11 @@ class StickerKeyboardPageFragment :
super.onViewCreated(view, savedInstanceState)
val glideRequests = GlideApp.with(this)
keyboardStickerListAdapter = KeyboardStickerListAdapter(glideRequests, this, DeviceProperties.shouldAllowApngStickerAnimation(requireContext()))
stickerListAdapter = KeyboardStickerListAdapter(glideRequests, this, DeviceProperties.shouldAllowApngStickerAnimation(requireContext()))
layoutManager = GridLayoutManager(requireContext(), 2).apply {
spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
val model: Optional<MappingModel<*>> = keyboardStickerListAdapter.getModel(position)
val model: Optional<MappingModel<*>> = stickerListAdapter.getModel(position)
if (model.isPresent && model.get() is KeyboardStickerListAdapter.StickerHeader) {
return spanCount
}
@@ -69,13 +69,13 @@ class StickerKeyboardPageFragment :
stickerList = view.findViewById(R.id.sticker_keyboard_list)
stickerList.layoutManager = layoutManager
stickerList.adapter = keyboardStickerListAdapter
stickerList.adapter = stickerListAdapter
stickerList.addOnItemTouchListener(listTouchListener)
stickerList.addOnScrollListener(packIdSelectionOnScroll)
stickerPacksRecycler = view.findViewById(R.id.sticker_packs_recycler)
stickerPacksAdapter = StickerPackListAdapter(glideRequests, DeviceProperties.shouldAllowApngStickerAnimation(requireContext()), this::onTabSelected)
stickerPacksAdapter = KeyboardStickerPackListAdapter(glideRequests, DeviceProperties.shouldAllowApngStickerAnimation(requireContext()), this::onTabSelected)
stickerPacksRecycler.adapter = stickerPacksAdapter
appBarLayout = view.findViewById(R.id.sticker_keyboard_search_appbar)
@@ -106,21 +106,21 @@ class StickerKeyboardPageFragment :
viewModel = ViewModelProviders.of(requireActivity(), StickerKeyboardPageViewModel.Factory(requireContext()))
.get(StickerKeyboardPageViewModel::class.java)
viewModel.stickers.observe(viewLifecycleOwner, keyboardStickerListAdapter::submitList)
viewModel.stickers.observe(viewLifecycleOwner, stickerListAdapter::submitList)
viewModel.packs.observe(viewLifecycleOwner, stickerPacksAdapter::submitList)
viewModel.getSelectedPack().observe(viewLifecycleOwner, this::updateCategoryTab)
viewModel.refreshStickers()
}
private fun onTabSelected(stickerPack: StickerPackListAdapter.StickerPack) {
private fun onTabSelected(stickerPack: KeyboardStickerPackListAdapter.StickerPack) {
scrollTo(stickerPack.packRecord.packId)
viewModel.selectPack(stickerPack.packRecord.packId)
}
private fun updateCategoryTab(packId: String) {
stickerPacksRecycler.post {
val index: Int = stickerPacksAdapter.indexOfFirst(StickerPackListAdapter.StickerPack::class.java) { it.packRecord.packId == packId }
val index: Int = stickerPacksAdapter.indexOfFirst(KeyboardStickerPackListAdapter.StickerPack::class.java) { it.packRecord.packId == packId }
if (index != -1) {
stickerPacksRecycler.smoothScrollToPosition(index)
@@ -129,7 +129,7 @@ class StickerKeyboardPageFragment :
}
private fun scrollTo(packId: String) {
val index = keyboardStickerListAdapter.indexOfFirst(KeyboardStickerListAdapter.StickerHeader::class.java) { it.packId == packId }
val index = stickerListAdapter.indexOfFirst(KeyboardStickerListAdapter.StickerHeader::class.java) { it.packId == packId }
if (index != -1) {
appBarLayout.setExpanded(false, true)
packIdSelectionOnScroll.startAutoScrolling()
@@ -163,7 +163,7 @@ class StickerKeyboardPageFragment :
override fun getStickerDataFromView(view: View): Pair<Any, String>? {
val position: Int = stickerList.getChildAdapterPosition(view)
val model: Optional<MappingModel<*>> = keyboardStickerListAdapter.getModel(position)
val model: Optional<MappingModel<*>> = stickerListAdapter.getModel(position)
if (model.isPresent && model.get() is KeyboardStickerListAdapter.Sticker) {
val sticker = model.get() as KeyboardStickerListAdapter.Sticker
return Pair(sticker.uri, sticker.stickerRecord.emoji)
@@ -216,7 +216,7 @@ class StickerKeyboardPageFragment :
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
val index = layoutManager.findFirstCompletelyVisibleItemPosition()
val item: Optional<MappingModel<*>> = keyboardStickerListAdapter.getModel(index)
val item: Optional<MappingModel<*>> = stickerListAdapter.getModel(index)
if (item.isPresent && item.get() is KeyboardStickerListAdapter.HasPackId) {
viewModel.selectPack((item.get() as KeyboardStickerListAdapter.HasPackId).packId)
}

View File

@@ -6,7 +6,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.keyboard.sticker.StickerPackListAdapter.StickerPack
import org.thoughtcrime.securesms.keyboard.sticker.KeyboardStickerPackListAdapter.StickerPack
import org.thoughtcrime.securesms.util.MappingModelList
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil

View File

@@ -1,7 +1,5 @@
package org.thoughtcrime.securesms.keyboard.sticker
import android.content.res.Configuration
import android.graphics.Point
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -13,25 +11,23 @@ import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.database.model.StickerRecord
import org.thoughtcrime.securesms.keyboard.emoji.KeyboardPageSearchView
import org.thoughtcrime.securesms.keyboard.findListener
import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.stickers.StickerKeyboardPageAdapter
import org.thoughtcrime.securesms.stickers.StickerKeyboardProvider
import org.thoughtcrime.securesms.stickers.StickerEventListener
import org.thoughtcrime.securesms.util.DeviceProperties
import org.thoughtcrime.securesms.util.ViewUtil
/**
* Search dialog for finding stickers.
*/
class StickerSearchDialogFragment : DialogFragment(), StickerKeyboardPageAdapter.EventListener {
class StickerSearchDialogFragment : DialogFragment(), KeyboardStickerListAdapter.EventListener, View.OnLayoutChangeListener {
private lateinit var search: KeyboardPageSearchView
private lateinit var list: RecyclerView
private lateinit var noResults: View
private lateinit var adapter: StickerKeyboardPageAdapter
private lateinit var adapter: KeyboardStickerListAdapter
private lateinit var layoutManager: GridLayoutManager
override fun onCreate(savedInstanceState: Bundle?) {
@@ -49,19 +45,17 @@ class StickerSearchDialogFragment : DialogFragment(), StickerKeyboardPageAdapter
list = view.findViewById(R.id.sticker_search_list)
noResults = view.findViewById(R.id.sticker_search_no_results)
adapter = StickerKeyboardPageAdapter(GlideApp.with(this), this, DeviceProperties.shouldAllowApngStickerAnimation(requireContext()))
adapter = KeyboardStickerListAdapter(GlideApp.with(this), this, DeviceProperties.shouldAllowApngStickerAnimation(requireContext()))
layoutManager = GridLayoutManager(requireContext(), 2)
list.layoutManager = layoutManager
list.adapter = adapter
onScreenWidthChanged(getScreenWidth())
val viewModel: StickerSearchViewModel = ViewModelProviders.of(this, StickerSearchViewModel.Factory(requireContext())).get(StickerSearchViewModel::class.java)
viewModel.searchResults.observe(viewLifecycleOwner) { stickerRecords ->
adapter.setStickers(stickerRecords, calculateStickerSize(getScreenWidth()))
noResults.visibility = if (stickerRecords.isEmpty()) View.VISIBLE else View.GONE
viewModel.searchResults.observe(viewLifecycleOwner) { stickers ->
adapter.submitList(stickers)
noResults.visibility = if (stickers.isEmpty()) View.VISIBLE else View.GONE
}
search.enableBackNavigation()
@@ -77,22 +71,12 @@ class StickerSearchDialogFragment : DialogFragment(), StickerKeyboardPageAdapter
}
search.requestFocus()
view.addOnLayoutChangeListener(this)
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
onScreenWidthChanged(getScreenWidth())
}
private fun onScreenWidthChanged(@Px newWidth: Int) {
layoutManager.spanCount = calculateColumnCount(newWidth)
adapter.setStickerSize(calculateStickerSize(newWidth))
}
private fun getScreenWidth(): Int {
val size = Point()
requireActivity().windowManager.defaultDisplay.getSize(size)
return size.x
override fun onLayoutChange(v: View?, left: Int, top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int) {
layoutManager.spanCount = calculateColumnCount(view?.width ?: 0)
}
private fun calculateColumnCount(@Px screenWidth: Int): Int {
@@ -101,23 +85,17 @@ class StickerSearchDialogFragment : DialogFragment(), StickerKeyboardPageAdapter
return ((screenWidth - modifier) / divisor).toInt()
}
private fun calculateStickerSize(@Px screenWidth: Int): Int {
val multiplier = resources.getDimensionPixelOffset(R.dimen.sticker_page_item_multiplier).toFloat()
val columnCount = calculateColumnCount(screenWidth)
return ((screenWidth - (columnCount + 1) * multiplier) / columnCount).toInt()
override fun onStickerClicked(sticker: KeyboardStickerListAdapter.Sticker) {
ViewUtil.hideKeyboard(requireContext(), requireView())
findListener<StickerEventListener>()?.onStickerSelected(sticker.stickerRecord)
dismissAllowingStateLoss()
}
override fun onStickerLongClicked(sticker: KeyboardStickerListAdapter.Sticker) = Unit
companion object {
fun show(fragmentManager: FragmentManager) {
StickerSearchDialogFragment().show(fragmentManager, "TAG")
}
}
override fun onStickerClicked(sticker: StickerRecord) {
ViewUtil.hideKeyboard(requireContext(), requireView())
findListener<StickerKeyboardProvider.StickerEventListener>()?.onStickerSelected(sticker)
dismissAllowingStateLoss()
}
override fun onStickerLongClicked(targetView: View) = Unit
}

View File

@@ -5,14 +5,16 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import org.thoughtcrime.securesms.database.model.StickerRecord
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil
class StickerSearchViewModel(private val searchRepository: StickerSearchRepository) : ViewModel() {
private val searchQuery: MutableLiveData<String> = MutableLiveData("")
val searchResults: LiveData<List<StickerRecord>> = LiveDataUtil.mapAsync(searchQuery) { q -> searchRepository.search(q) }
val searchResults: LiveData<List<KeyboardStickerListAdapter.Sticker>> = LiveDataUtil.mapAsync(searchQuery) { q ->
searchRepository.search(q)
.map { KeyboardStickerListAdapter.Sticker(it.packId, it) }
}
fun query(query: String) {
searchQuery.postValue(query)

View File

@@ -46,7 +46,7 @@ import org.thoughtcrime.securesms.components.InputAwareLayout;
import org.thoughtcrime.securesms.components.SendButton;
import org.thoughtcrime.securesms.components.TooltipPopup;
import org.thoughtcrime.securesms.components.emoji.EmojiEditText;
import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider;
import org.thoughtcrime.securesms.components.emoji.EmojiEventListener;
import org.thoughtcrime.securesms.components.emoji.EmojiToggle;
import org.thoughtcrime.securesms.components.emoji.MediaKeyboard;
import org.thoughtcrime.securesms.components.mention.MentionAnnotation;
@@ -104,18 +104,18 @@ import java.util.Set;
* It will return the {@link Media} that the user decided to send.
*/
public class MediaSendActivity extends PassphraseRequiredActivity implements MediaPickerFolderFragment.Controller,
MediaPickerItemFragment.Controller,
ImageEditorFragment.Controller,
MediaSendVideoFragment.Controller,
CameraFragment.Controller,
CameraContactSelectionFragment.Controller,
ViewTreeObserver.OnGlobalLayoutListener,
MediaRailAdapter.RailItemListener,
InputAwareLayout.OnKeyboardShownListener,
InputAwareLayout.OnKeyboardHiddenListener,
EmojiKeyboardProvider.EmojiEventListener,
EmojiKeyboardPageFragment.Callback,
EmojiSearchFragment.Callback
MediaPickerItemFragment.Controller,
ImageEditorFragment.Controller,
MediaSendVideoFragment.Controller,
CameraFragment.Controller,
CameraContactSelectionFragment.Controller,
ViewTreeObserver.OnGlobalLayoutListener,
MediaRailAdapter.RailItemListener,
InputAwareLayout.OnKeyboardShownListener,
InputAwareLayout.OnKeyboardHiddenListener,
EmojiEventListener,
EmojiKeyboardPageFragment.Callback,
EmojiSearchFragment.Callback
{
private static final String TAG = Log.tag(MediaSendActivity.class);

View File

@@ -32,7 +32,7 @@ import com.google.android.material.shape.MaterialShapeDrawable;
import com.google.android.material.shape.ShapeAppearanceModel;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider;
import org.thoughtcrime.securesms.components.emoji.EmojiEventListener;
import org.thoughtcrime.securesms.components.emoji.EmojiPageView;
import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter;
import org.thoughtcrime.securesms.database.model.MessageRecord;
@@ -42,18 +42,19 @@ import org.thoughtcrime.securesms.keyboard.emoji.KeyboardPageSearchView;
import org.thoughtcrime.securesms.reactions.ReactionsLoader;
import org.thoughtcrime.securesms.reactions.edit.EditReactionsActivity;
import org.thoughtcrime.securesms.util.MappingModel;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ViewUtil;
import java.util.Optional;
import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
public final class ReactWithAnyEmojiBottomSheetDialogFragment extends BottomSheetDialogFragment implements EmojiKeyboardProvider.EmojiEventListener,
public final class ReactWithAnyEmojiBottomSheetDialogFragment extends BottomSheetDialogFragment implements EmojiEventListener,
EmojiPageViewGridAdapter.VariationSelectorListener
{
private static final String REACTION_STORAGE_KEY = "reactions_recent_emoji";
private static final String ABOUT_STORAGE_KEY = EmojiKeyboardProvider.RECENT_STORAGE_KEY;
private static final String ABOUT_STORAGE_KEY = TextSecurePreferences.RECENT_STORAGE_KEY;
private static final String ARG_MESSAGE_ID = "arg_message_id";
private static final String ARG_IS_MMS = "arg_is_mms";

View File

@@ -10,7 +10,6 @@ import androidx.lifecycle.Transformations;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider;
import org.thoughtcrime.securesms.components.emoji.EmojiPageModel;
import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter;
import org.thoughtcrime.securesms.components.emoji.RecentEmojiPageModel;
@@ -21,6 +20,7 @@ import org.thoughtcrime.securesms.keyboard.emoji.search.EmojiSearchRepository;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.reactions.ReactionsLoader;
import org.thoughtcrime.securesms.util.MappingModelList;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
import java.util.List;
@@ -133,7 +133,7 @@ public final class ReactWithAnyEmojiViewModel extends ViewModel {
}
private static @NonNull String getStartingKey() {
if (RecentEmojiPageModel.hasRecents(ApplicationDependencies.getApplication(), EmojiKeyboardProvider.RECENT_STORAGE_KEY)) {
if (RecentEmojiPageModel.hasRecents(ApplicationDependencies.getApplication(), TextSecurePreferences.RECENT_STORAGE_KEY)) {
return RecentEmojiPageModel.KEY;
} else {
return EmojiCategory.PEOPLE.getKey();

View File

@@ -18,11 +18,11 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.keyboard.KeyboardPage;
import org.thoughtcrime.securesms.keyboard.KeyboardPagerViewModel;
import org.thoughtcrime.securesms.stickers.StickerKeyboardProvider;
import org.thoughtcrime.securesms.stickers.StickerEventListener;
import org.thoughtcrime.securesms.stickers.StickerManagementActivity;
import org.thoughtcrime.securesms.util.ViewUtil;
public final class ImageEditorStickerSelectActivity extends AppCompatActivity implements StickerKeyboardProvider.StickerEventListener, MediaKeyboard.MediaKeyboardListener {
public final class ImageEditorStickerSelectActivity extends AppCompatActivity implements StickerEventListener, MediaKeyboard.MediaKeyboardListener {
@Override
protected void attachBaseContext(@NonNull Context newBase) {

View File

@@ -0,0 +1,11 @@
package org.thoughtcrime.securesms.stickers;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.database.model.StickerRecord;
public interface StickerEventListener {
void onStickerSelected(@NonNull StickerRecord sticker);
void onStickerManagementClicked();
}

View File

@@ -1,138 +0,0 @@
package org.thoughtcrime.securesms.stickers;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.glide.cache.ApngOptions;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
import org.thoughtcrime.securesms.mms.GlideRequests;
import java.util.ArrayList;
import java.util.List;
/**
* Adapter for a specific page in the sticker keyboard. Shows the stickers in a grid.
* @see StickerKeyboardPageFragment
*/
public final class StickerKeyboardPageAdapter extends RecyclerView.Adapter<StickerKeyboardPageAdapter.StickerKeyboardPageViewHolder> {
private final GlideRequests glideRequests;
private final EventListener eventListener;
private final List<StickerRecord> stickers;
private final boolean allowApngAnimation;
private int stickerSize;
public StickerKeyboardPageAdapter(@NonNull GlideRequests glideRequests, @NonNull EventListener eventListener, boolean allowApngAnimation) {
this.glideRequests = glideRequests;
this.eventListener = eventListener;
this.allowApngAnimation = allowApngAnimation;
this.stickers = new ArrayList<>();
setHasStableIds(true);
}
@Override
public long getItemId(int position) {
return stickers.get(position).getRowId();
}
@Override
public @NonNull StickerKeyboardPageViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
return new StickerKeyboardPageViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.sticker_keyboard_page_list_item, viewGroup, false));
}
@Override
public void onBindViewHolder(@NonNull StickerKeyboardPageViewHolder viewHolder, int i) {
viewHolder.bind(glideRequests, eventListener, stickers.get(i), stickerSize, allowApngAnimation);
}
@Override
public void onViewRecycled(@NonNull StickerKeyboardPageViewHolder holder) {
holder.recycle();
}
@Override
public int getItemCount() {
return stickers.size();
}
public void setStickers(@NonNull List<StickerRecord> stickers, @Px int stickerSize) {
this.stickers.clear();
this.stickers.addAll(stickers);
this.stickerSize = stickerSize;
notifyDataSetChanged();
}
public void setStickerSize(@Px int stickerSize) {
this.stickerSize = stickerSize;
notifyDataSetChanged();
}
static class StickerKeyboardPageViewHolder extends RecyclerView.ViewHolder {
private final ImageView image;
private StickerRecord currentSticker;
public StickerKeyboardPageViewHolder(@NonNull View itemView) {
super(itemView);
image = itemView.findViewById(R.id.sticker_keyboard_page_image);
}
public void bind(@NonNull GlideRequests glideRequests,
@Nullable EventListener eventListener,
@NonNull StickerRecord sticker,
@Px int size,
boolean allowApngAnimation)
{
currentSticker = sticker;
itemView.getLayoutParams().height = size;
itemView.getLayoutParams().width = size;
itemView.requestLayout();
glideRequests.load(new DecryptableUri(sticker.getUri()))
.set(ApngOptions.ANIMATE, allowApngAnimation)
.transition(DrawableTransitionOptions.withCrossFade())
.into(image);
if (eventListener != null) {
image.setOnClickListener(v -> eventListener.onStickerClicked(sticker));
image.setOnLongClickListener(v -> {
eventListener.onStickerLongClicked(v);
return true;
});
} else {
image.setOnClickListener(null);
image.setOnLongClickListener(null);
}
}
void recycle() {
image.setOnClickListener(null);
}
@Nullable StickerRecord getCurrentSticker() {
return currentSticker;
}
}
public interface EventListener {
void onStickerClicked(@NonNull StickerRecord sticker);
void onStickerLongClicked(@NonNull View targetView);
}
}

View File

@@ -1,166 +0,0 @@
package org.thoughtcrime.securesms.stickers;
import android.content.res.Configuration;
import android.graphics.Point;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.stickers.StickerKeyboardPageAdapter.StickerKeyboardPageViewHolder;
import org.thoughtcrime.securesms.util.DeviceProperties;
import org.whispersystems.libsignal.util.Pair;
/**
* An individual page of stickers in the {@link StickerKeyboardProvider}.
*/
public final class StickerKeyboardPageFragment extends Fragment implements StickerKeyboardPageAdapter.EventListener,
StickerRolloverTouchListener.RolloverStickerRetriever
{
private static final String TAG = Log.tag(StickerKeyboardPageFragment.class);
private static final String KEY_PACK_ID = "pack_id";
public static final String RECENT_PACK_ID = StickerKeyboardPageViewModel.RECENT_PACK_ID;
private RecyclerView list;
private StickerKeyboardPageAdapter adapter;
private GridLayoutManager layoutManager;
private StickerKeyboardPageViewModel viewModel;
private EventListener eventListener;
private StickerRolloverTouchListener listTouchListener;
private String packId;
public static StickerKeyboardPageFragment newInstance(@NonNull String packId) {
Bundle args = new Bundle();
args.putString(KEY_PACK_ID, packId);
StickerKeyboardPageFragment fragment = new StickerKeyboardPageFragment();
fragment.setArguments(args);
fragment.packId = packId;
return fragment;
}
@Override
public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.sticker_keyboard_page, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
GlideRequests glideRequests = GlideApp.with(this);
this.list = view.findViewById(R.id.sticker_keyboard_list);
this.adapter = new StickerKeyboardPageAdapter(glideRequests, this, DeviceProperties.shouldAllowApngStickerAnimation(requireContext()));
this.layoutManager = new GridLayoutManager(requireContext(), 2);
this.listTouchListener = new StickerRolloverTouchListener(requireContext(), glideRequests, eventListener, this);
this.packId = getArguments().getString(KEY_PACK_ID);
list.setLayoutManager(layoutManager);
list.setAdapter(adapter);
list.addOnItemTouchListener(listTouchListener);
initViewModel(packId);
onScreenWidthChanged(getScreenWidth());
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
onScreenWidthChanged(getScreenWidth());
}
@Override
public void onStickerClicked(@NonNull StickerRecord sticker) {
if (eventListener != null) {
eventListener.onStickerSelected(sticker);
}
}
@Override
public void onStickerLongClicked(@NonNull View targetView) {
if (listTouchListener != null) {
listTouchListener.enterHoverMode(list, targetView);
}
}
@Override
public @Nullable Pair<Object, String> getStickerDataFromView(@NonNull View view) {
if (list != null) {
StickerKeyboardPageViewHolder holder = (StickerKeyboardPageViewHolder) list.getChildViewHolder(view);
if (holder != null && holder.getCurrentSticker() != null) {
return new Pair<>(new DecryptableStreamUriLoader.DecryptableUri(holder.getCurrentSticker().getUri()),
holder.getCurrentSticker().getEmoji());
}
}
return null;
}
public void setEventListener(@NonNull EventListener eventListener) {
this.eventListener = eventListener;
}
public @NonNull String getPackId() {
return packId;
}
private void initViewModel(@NonNull String packId) {
StickerKeyboardRepository repository = new StickerKeyboardRepository(DatabaseFactory.getStickerDatabase(requireContext()));
viewModel = ViewModelProviders.of(this, new StickerKeyboardPageViewModel.Factory(requireActivity().getApplication(), repository)).get(StickerKeyboardPageViewModel.class);
viewModel.getStickers(packId).observe(getViewLifecycleOwner(), stickerRecords -> {
if (stickerRecords == null) return;
adapter.setStickers(stickerRecords, calculateStickerSize(getScreenWidth()));
});
}
private void onScreenWidthChanged(@Px int newWidth) {
if (layoutManager != null) {
layoutManager.setSpanCount(calculateColumnCount(newWidth));
adapter.setStickerSize(calculateStickerSize(newWidth));
}
}
private int getScreenWidth() {
Point size = new Point();
requireActivity().getWindowManager().getDefaultDisplay().getSize(size);
return size.x;
}
private int calculateColumnCount(@Px int screenWidth) {
float modifier = getResources().getDimensionPixelOffset(R.dimen.sticker_page_item_padding);
float divisor = getResources().getDimensionPixelOffset(R.dimen.sticker_page_item_divisor);
return (int) ((screenWidth - modifier) / divisor);
}
private int calculateStickerSize(@Px int screenWidth) {
float multiplier = getResources().getDimensionPixelOffset(R.dimen.sticker_page_item_multiplier);
int columnCount = calculateColumnCount(screenWidth);
return (int) ((screenWidth - ((columnCount + 1) * multiplier)) / columnCount);
}
interface EventListener extends StickerRolloverTouchListener.RolloverEventListener {
void onStickerSelected(@NonNull StickerRecord sticker);
}
}

View File

@@ -1,79 +0,0 @@
package org.thoughtcrime.securesms.stickers;
import android.app.Application;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import org.thoughtcrime.securesms.database.DatabaseContentProviders;
import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.util.Throttler;
import java.util.List;
final class StickerKeyboardPageViewModel extends ViewModel {
static final String RECENT_PACK_ID = "RECENT";
private final Application application;
private final StickerKeyboardRepository repository;
private final MutableLiveData<List<StickerRecord>> stickers;
private final Throttler observerThrottler;
private final ContentObserver observer;
private String packId;
private StickerKeyboardPageViewModel(@NonNull Application application, @NonNull StickerKeyboardRepository repository) {
this.application = application;
this.repository = repository;
this.stickers = new MutableLiveData<>();
this.observerThrottler = new Throttler(500);
this.observer = new ContentObserver(new Handler(Looper.getMainLooper())) {
@Override
public void onChange(boolean selfChange) {
observerThrottler.publish(() -> getStickers(packId));
}
};
application.getContentResolver().registerContentObserver(DatabaseContentProviders.Sticker.CONTENT_URI, true, observer);
}
LiveData<List<StickerRecord>> getStickers(@NonNull String packId) {
this.packId = packId;
if (RECENT_PACK_ID.equals(packId)) {
repository.getRecentStickers(stickers::postValue);
} else {
repository.getStickersForPack(packId, stickers::postValue);
}
return stickers;
}
@Override
protected void onCleared() {
application.getContentResolver().unregisterContentObserver(observer);
}
static class Factory extends ViewModelProvider.NewInstanceFactory {
private final Application application;
private final StickerKeyboardRepository repository;
Factory(@NonNull Application application, @NonNull StickerKeyboardRepository repository) {
this.application = application;
this.repository = repository;
}
@Override
public @NonNull<T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection ConstantConditions
return modelClass.cast(new StickerKeyboardPageViewModel(application, repository));
}
}
}

View File

@@ -1,270 +0,0 @@
package org.thoughtcrime.securesms.stickers;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.lifecycle.ViewModelProviders;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.emoji.MediaKeyboardProvider;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.model.StickerPackRecord;
import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.glide.cache.ApngOptions;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.stickers.StickerKeyboardPageFragment.EventListener;
import org.thoughtcrime.securesms.stickers.StickerKeyboardRepository.PackListResult;
import org.thoughtcrime.securesms.util.DeviceProperties;
import org.thoughtcrime.securesms.util.Throttler;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A provider to select stickers in the {@link org.thoughtcrime.securesms.components.emoji.MediaKeyboard}.
*/
public final class StickerKeyboardProvider implements MediaKeyboardProvider,
MediaKeyboardProvider.AddObserver,
StickerKeyboardPageFragment.EventListener
{
private static final int UNSET = -1;
private final Context context;
private final StickerEventListener eventListener;
private final StickerPagerAdapter pagerAdapter;
private final Throttler stickerThrottler;
private Controller controller;
private Presenter presenter;
private boolean isSoloProvider;
private StickerKeyboardViewModel viewModel;
private int currentPosition;
public StickerKeyboardProvider(@NonNull FragmentActivity activity,
@NonNull StickerEventListener eventListener)
{
this.context = activity;
this.eventListener = eventListener;
this.pagerAdapter = new StickerPagerAdapter(activity.getSupportFragmentManager(), this);
this.stickerThrottler = new Throttler(100);
this.currentPosition = UNSET;
initViewModel(activity);
}
@Override
public int getProviderIconView(boolean selected) {
if (selected) {
return R.layout.sticker_keyboard_icon_selected;
} else {
return R.layout.sticker_keyboard_icon;
}
}
@Override
public void requestPresentation(@NonNull Presenter presenter, boolean isSoloProvider) {
this.presenter = presenter;
this.isSoloProvider = isSoloProvider;
PackListResult result = viewModel.getPacks().getValue();
if (result != null) {
present(presenter, result, false);
}
}
@Override
public void setController(@Nullable Controller controller) {
this.controller = controller;
}
@Override
public void onAddClicked() {
eventListener.onStickerManagementClicked();
}
@Override
public void onStickerSelected(@NonNull StickerRecord sticker) {
stickerThrottler.publish(() -> eventListener.onStickerSelected(sticker));
}
@Override
public void onStickerPopupStarted() {
if (controller != null) {
controller.setViewPagerEnabled(false);
}
}
@Override
public void onStickerPopupEnded() {
if (controller != null) {
controller.setViewPagerEnabled(true);
}
}
@Override
public void setCurrentPosition(int currentPosition) {
this.currentPosition = currentPosition;
}
private void initViewModel(@NonNull FragmentActivity activity) {
StickerKeyboardRepository repository = new StickerKeyboardRepository(DatabaseFactory.getStickerDatabase(activity));
viewModel = ViewModelProviders.of(activity, new StickerKeyboardViewModel.Factory(activity.getApplication(), repository)).get(StickerKeyboardViewModel.class);
viewModel.getPacks().observe(activity, result -> {
if (result == null) return;
int previousCount = pagerAdapter.getCount();
pagerAdapter.setPacks(result.getPacks());
if (presenter != null) {
present(presenter, result, previousCount != pagerAdapter.getCount());
}
});
}
private void present(@NonNull Presenter presenter, @NonNull PackListResult result, boolean calculateStartingIndex) {
if (result.getPacks().isEmpty() && presenter.isVisible()) {
context.startActivity(StickerManagementActivity.getIntent(context));
presenter.requestDismissal();
return;
}
int startingIndex = currentPosition;
if (calculateStartingIndex || startingIndex == UNSET) {
startingIndex = !result.hasRecents() && result.getPacks().size() > 0 ? 1 : 0;
}
presenter.present(this, pagerAdapter, new IconProvider(context, result.getPacks(), DeviceProperties.shouldAllowApngStickerAnimation(context)), null, this, null, startingIndex);
if (isSoloProvider && result.getPacks().isEmpty()) {
context.startActivity(StickerManagementActivity.getIntent(context));
}
}
@Override
public boolean equals(@Nullable Object obj) {
return obj instanceof StickerKeyboardProvider;
}
private static class StickerPagerAdapter extends FragmentStatePagerAdapter {
private final List<StickerPackRecord> packs;
private final Map<String, Integer> itemPositions;
private final EventListener eventListener;
public StickerPagerAdapter(@NonNull FragmentManager fm, @NonNull EventListener eventListener) {
super(fm);
this.eventListener = eventListener;
this.packs = new ArrayList<>();
this.itemPositions = new HashMap<>();
}
@Override
public int getItemPosition(@NonNull Object object) {
String packId = ((StickerKeyboardPageFragment) object).getPackId();
if (itemPositions.containsKey(packId)) {
//noinspection ConstantConditions
return itemPositions.get(packId);
}
return POSITION_NONE;
}
@Override
public Fragment getItem(int i) {
StickerKeyboardPageFragment fragment;
if (i == 0) {
fragment = StickerKeyboardPageFragment.newInstance(StickerKeyboardPageFragment.RECENT_PACK_ID);
} else {
StickerPackRecord pack = packs.get(i - 1);
fragment = StickerKeyboardPageFragment.newInstance(pack.getPackId());
}
fragment.setEventListener(eventListener);
return fragment;
}
@Override
public int getCount() {
return packs.isEmpty() ? 0 : packs.size() + 1;
}
void setPacks(@NonNull List<StickerPackRecord> packs) {
itemPositions.clear();
if (areListsEqual(this.packs, packs)) {
itemPositions.put(StickerKeyboardPageFragment.RECENT_PACK_ID, 0);
for (int i = 0; i < packs.size(); i++) {
itemPositions.put(packs.get(i).getPackId(), i + 1);
}
}
this.packs.clear();
this.packs.addAll(packs);
notifyDataSetChanged();
}
boolean areListsEqual(@NonNull List<StickerPackRecord> a, @NonNull List<StickerPackRecord> b) {
if (a.size() != b.size()) return false;
for (int i = 0; i < a.size(); i++) {
if (!a.get(i).equals(b.get(i))) {
return false;
}
}
return true;
}
}
private static class IconProvider implements TabIconProvider {
private final Context context;
private final List<StickerPackRecord> packs;
private final boolean allowApngAnimation;
private IconProvider(@NonNull Context context, List<StickerPackRecord> packs, boolean allowApngAnimation) {
this.context = context;
this.packs = packs;
this.allowApngAnimation = allowApngAnimation;
}
@Override
public void loadCategoryTabIcon(@NonNull GlideRequests glideRequests, @NonNull ImageView imageView, int index) {
if (index == 0) {
Drawable icon = ContextCompat.getDrawable(context, R.drawable.ic_recent_20);
imageView.setImageDrawable(icon);
} else {
Uri uri = packs.get(index - 1).getCover().getUri();
glideRequests.load(new DecryptableStreamUriLoader.DecryptableUri(uri))
.set(ApngOptions.ANIMATE, allowApngAnimation)
.into(imageView);
}
}
}
public interface StickerEventListener {
void onStickerSelected(@NonNull StickerRecord sticker);
void onStickerManagementClicked();
}
}

View File

@@ -1,100 +0,0 @@
package org.thoughtcrime.securesms.stickers;
import android.database.Cursor;
import androidx.annotation.NonNull;
import org.signal.core.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.database.StickerDatabase;
import org.thoughtcrime.securesms.database.StickerDatabase.StickerPackRecordReader;
import org.thoughtcrime.securesms.database.StickerDatabase.StickerRecordReader;
import org.thoughtcrime.securesms.database.model.StickerPackRecord;
import org.thoughtcrime.securesms.database.model.StickerRecord;
import java.util.ArrayList;
import java.util.List;
final class StickerKeyboardRepository {
private static final int RECENT_LIMIT = 24;
private final StickerDatabase stickerDatabase;
StickerKeyboardRepository(@NonNull StickerDatabase stickerDatabase) {
this.stickerDatabase = stickerDatabase;
}
void getPackList(@NonNull Callback<PackListResult> callback) {
SignalExecutors.BOUNDED.execute(() -> {
List<StickerPackRecord> packs = new ArrayList<>();
try (StickerPackRecordReader reader = new StickerPackRecordReader(stickerDatabase.getInstalledStickerPacks())) {
StickerPackRecord pack;
while ((pack = reader.getNext()) != null) {
packs.add(pack);
}
}
boolean hasRecents;
try (Cursor recentsCursor = stickerDatabase.getRecentlyUsedStickers(1)) {
hasRecents = recentsCursor != null && recentsCursor.moveToFirst();
}
callback.onComplete(new PackListResult(packs, hasRecents));
});
}
void getStickersForPack(@NonNull String packId, @NonNull Callback<List<StickerRecord>> callback) {
SignalExecutors.BOUNDED.execute(() -> {
List<StickerRecord> stickers = new ArrayList<>();
try (StickerRecordReader reader = new StickerRecordReader(stickerDatabase.getStickersForPack(packId))) {
StickerRecord sticker;
while ((sticker = reader.getNext()) != null) {
stickers.add(sticker);
}
}
callback.onComplete(stickers);
});
}
void getRecentStickers(@NonNull Callback<List<StickerRecord>> callback) {
SignalExecutors.BOUNDED.execute(() -> {
List<StickerRecord> stickers = new ArrayList<>();
try (StickerRecordReader reader = new StickerRecordReader(stickerDatabase.getRecentlyUsedStickers(RECENT_LIMIT))) {
StickerRecord sticker;
while ((sticker = reader.getNext()) != null) {
stickers.add(sticker);
}
}
callback.onComplete(stickers);
});
}
static class PackListResult {
private final List<StickerPackRecord> packs;
private final boolean hasRecents;
PackListResult(List<StickerPackRecord> packs, boolean hasRecents) {
this.packs = packs;
this.hasRecents = hasRecents;
}
List<StickerPackRecord> getPacks() {
return packs;
}
boolean hasRecents() {
return hasRecents;
}
}
interface Callback<T> {
void onComplete(T result);
}
}

View File

@@ -1,64 +0,0 @@
package org.thoughtcrime.securesms.stickers;
import android.app.Application;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import org.thoughtcrime.securesms.database.DatabaseContentProviders;
import org.thoughtcrime.securesms.stickers.StickerKeyboardRepository.PackListResult;
import org.thoughtcrime.securesms.util.Throttler;
final class StickerKeyboardViewModel extends ViewModel {
private final Application application;
private final MutableLiveData<PackListResult> packs;
private final Throttler observerThrottler;
private final ContentObserver observer;
private StickerKeyboardViewModel(@NonNull Application application, @NonNull StickerKeyboardRepository repository) {
this.application = application;
this.packs = new MutableLiveData<>();
this.observerThrottler = new Throttler(500);
this.observer = new ContentObserver(new Handler(Looper.getMainLooper())) {
@Override
public void onChange(boolean selfChange) {
observerThrottler.publish(() -> repository.getPackList(packs::postValue));
}
};
repository.getPackList(packs::postValue);
application.getContentResolver().registerContentObserver(DatabaseContentProviders.StickerPack.CONTENT_URI, true, observer);
}
@NonNull LiveData<PackListResult> getPacks() {
return packs;
}
@Override
protected void onCleared() {
application.getContentResolver().unregisterContentObserver(observer);
}
public static final class Factory extends ViewModelProvider.NewInstanceFactory {
private final Application application;
private final StickerKeyboardRepository repository;
public Factory(@NonNull Application application, @NonNull StickerKeyboardRepository repository) {
this.application = application;
this.repository = repository;
}
@Override
public @NonNull<T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection ConstantConditions
return modelClass.cast(new StickerKeyboardViewModel(application, repository));
}
}
}

View File

@@ -197,6 +197,7 @@ public class TextSecurePreferences {
private static final String SEEN_STICKER_INTRO_TOOLTIP = "pref_seen_sticker_intro_tooltip";
private static final String MEDIA_KEYBOARD_MODE = "pref_media_keyboard_mode";
public static final String RECENT_STORAGE_KEY = "pref_recent_emoji2";
private static final String VIEW_ONCE_TOOLTIP_SEEN = "pref_revealable_message_tooltip_seen";