package org.thoughtcrime.securesms.stickers; import androidx.lifecycle.ViewModelProviders; import android.content.Context; import android.graphics.drawable.Drawable; import android.net.Uri; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentStatePagerAdapter; import androidx.appcompat.app.AppCompatActivity; import android.widget.ImageView; 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.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.ResUtil; import org.thoughtcrime.securesms.util.ThemeUtil; 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 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; public StickerKeyboardProvider(@NonNull AppCompatActivity activity, @NonNull StickerEventListener eventListener) { this.context = activity; this.eventListener = eventListener; this.pagerAdapter = new StickerPagerAdapter(activity.getSupportFragmentManager(), this); this.stickerThrottler = new Throttler(100); initViewModel(activity); } @Override public int getProviderIconView(boolean selected) { if (selected) { return ThemeUtil.isDarkTheme(context) ? R.layout.sticker_keyboard_icon_dark_selected : R.layout.sticker_keyboard_icon_light_selected; } else { return ThemeUtil.isDarkTheme(context) ? R.layout.sticker_keyboard_icon_dark : R.layout.sticker_keyboard_icon_light; } } @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, true); } } @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); } } private void initViewModel(@NonNull AppCompatActivity 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 = presenter.getCurrentPosition(); if (calculateStartingIndex) { startingIndex = !result.hasRecents() && result.getPacks().size() > 0 ? 1 : 0; } presenter.present(this, pagerAdapter, new IconProvider(context, result.getPacks()), 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 packs; private final Map 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 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 a, @NonNull List 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 packs; private IconProvider(@NonNull Context context, List packs) { this.context = context; this.packs = packs; } @Override public void loadCategoryTabIcon(@NonNull GlideRequests glideRequests, @NonNull ImageView imageView, int index) { if (index == 0) { Drawable icon = ResUtil.getDrawable(context, R.attr.emoji_category_recent); imageView.setImageDrawable(icon); } else { Uri uri = packs.get(index - 1).getCover().getUri(); glideRequests.load(new DecryptableStreamUriLoader.DecryptableUri(uri)) .into(imageView); } } } public interface StickerEventListener { void onStickerSelected(@NonNull StickerRecord sticker); void onStickerManagementClicked(); } }