kopia lustrzana https://github.com/ryukoposting/Signal-Android
355 wiersze
14 KiB
Java
355 wiersze
14 KiB
Java
package org.thoughtcrime.securesms.stickers;
|
|
|
|
import android.content.Context;
|
|
import android.graphics.PorterDuff;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.text.SpannableString;
|
|
import android.text.SpannableStringBuilder;
|
|
import android.text.style.ImageSpan;
|
|
import android.view.LayoutInflater;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.widget.ImageView;
|
|
import android.widget.TextView;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.StringRes;
|
|
import androidx.core.content.ContextCompat;
|
|
import androidx.recyclerview.widget.RecyclerView;
|
|
|
|
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
|
|
|
|
import org.thoughtcrime.securesms.R;
|
|
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
|
|
import org.thoughtcrime.securesms.database.model.StickerPackRecord;
|
|
import org.thoughtcrime.securesms.glide.cache.ApngOptions;
|
|
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
|
|
import org.thoughtcrime.securesms.mms.GlideRequests;
|
|
import org.thoughtcrime.securesms.util.adapter.SectionedRecyclerViewAdapter;
|
|
import org.thoughtcrime.securesms.util.adapter.StableIdGenerator;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
|
|
final class StickerManagementAdapter extends SectionedRecyclerViewAdapter<String, StickerManagementAdapter.StickerSection> {
|
|
|
|
private static final String TAG_YOUR_STICKERS = "YourStickers";
|
|
private static final String TAG_MESSAGE_STICKERS = "MessageStickers";
|
|
private static final String TAG_BLESSED_STICKERS = "BlessedStickers";
|
|
|
|
private final GlideRequests glideRequests;
|
|
private final EventListener eventListener;
|
|
private final boolean allowApngAnimation;
|
|
|
|
private final List<StickerSection> sections = new ArrayList<StickerSection>(3) {{
|
|
StickerSection yourStickers = new StickerSection(TAG_YOUR_STICKERS,
|
|
R.string.StickerManagementAdapter_installed_stickers,
|
|
R.string.StickerManagementAdapter_no_stickers_installed,
|
|
new ArrayList<>(),
|
|
0);
|
|
StickerSection messageStickers = new StickerSection(TAG_MESSAGE_STICKERS,
|
|
R.string.StickerManagementAdapter_stickers_you_received,
|
|
R.string.StickerManagementAdapter_stickers_from_incoming_messages_will_appear_here,
|
|
new ArrayList<>(),
|
|
yourStickers.size());
|
|
|
|
add(yourStickers);
|
|
add(messageStickers);
|
|
}};
|
|
|
|
StickerManagementAdapter(@NonNull GlideRequests glideRequests, @NonNull EventListener eventListener, boolean allowApngAnimation) {
|
|
this.glideRequests = glideRequests;
|
|
this.eventListener = eventListener;
|
|
this.allowApngAnimation = allowApngAnimation;
|
|
}
|
|
|
|
@Override
|
|
protected @NonNull List<StickerSection> getSections() {
|
|
return sections;
|
|
}
|
|
|
|
@Override
|
|
protected @NonNull RecyclerView.ViewHolder createHeaderViewHolder(@NonNull ViewGroup parent) {
|
|
return new HeaderViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.sticker_management_header_item, parent, false));
|
|
}
|
|
|
|
@Override
|
|
protected @NonNull RecyclerView.ViewHolder createContentViewHolder(@NonNull ViewGroup parent) {
|
|
return new StickerViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.sticker_management_sticker_item, parent, false));
|
|
}
|
|
|
|
@Override
|
|
protected @NonNull RecyclerView.ViewHolder createEmptyViewHolder(@NonNull ViewGroup viewGroup) {
|
|
return new EmptyViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.sticker_management_empty_item, viewGroup, false));
|
|
}
|
|
|
|
@Override
|
|
public void bindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, @NonNull StickerSection section, int localPosition) {
|
|
section.bindViewHolder(viewHolder, localPosition, glideRequests, eventListener, allowApngAnimation);
|
|
}
|
|
|
|
@Override
|
|
public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) {
|
|
if (holder instanceof StickerViewHolder) {
|
|
((StickerViewHolder) holder).recycle();
|
|
}
|
|
}
|
|
|
|
boolean onMove(int start, int end) {
|
|
StickerSection installed = sections.get(0);
|
|
|
|
if (!installed.isContent(start)) {
|
|
return false;
|
|
}
|
|
|
|
if (!installed.isContent(end)) {
|
|
return false;
|
|
}
|
|
|
|
installed.swap(start, end);
|
|
notifyItemMoved(start, end);
|
|
return true;
|
|
}
|
|
|
|
boolean isMovable(int position) {
|
|
return sections.get(0).isContent(position);
|
|
}
|
|
|
|
@NonNull List<StickerPackRecord> getInstalledPacksInOrder() {
|
|
return sections.get(0).records;
|
|
}
|
|
|
|
void setPackLists(@NonNull List<StickerPackRecord> installedPacks,
|
|
@NonNull List<StickerPackRecord> availablePacks,
|
|
@NonNull List<StickerPackRecord> blessedPacks)
|
|
{
|
|
StickerSection yourStickers = new StickerSection(TAG_YOUR_STICKERS,
|
|
R.string.StickerManagementAdapter_installed_stickers,
|
|
R.string.StickerManagementAdapter_no_stickers_installed,
|
|
installedPacks,
|
|
0);
|
|
StickerSection blessedStickers = new StickerSection(TAG_BLESSED_STICKERS,
|
|
R.string.StickerManagementAdapter_signal_artist_series,
|
|
0,
|
|
blessedPacks,
|
|
yourStickers.size());
|
|
StickerSection messageStickers = new StickerSection(TAG_MESSAGE_STICKERS,
|
|
R.string.StickerManagementAdapter_stickers_you_received,
|
|
R.string.StickerManagementAdapter_stickers_from_incoming_messages_will_appear_here,
|
|
availablePacks,
|
|
yourStickers.size() + (blessedPacks.isEmpty() ? 0 : blessedStickers.size()));
|
|
|
|
sections.clear();
|
|
sections.add(yourStickers);
|
|
|
|
if (!blessedPacks.isEmpty()) {
|
|
sections.add(blessedStickers);
|
|
}
|
|
|
|
sections.add(messageStickers);
|
|
|
|
notifyDataSetChanged();
|
|
}
|
|
|
|
public static class StickerSection extends SectionedRecyclerViewAdapter.Section<String> {
|
|
|
|
private static final String STABLE_ID_HEADER = "header";
|
|
private static final String STABLE_ID_TEXT = "text";
|
|
|
|
private final String tag;
|
|
private final int titleResId;
|
|
private final int emptyResId;
|
|
private final List<StickerPackRecord> records;
|
|
|
|
StickerSection(@NonNull String tag,
|
|
@StringRes int titleResId,
|
|
@StringRes int emptyResId,
|
|
@NonNull List<StickerPackRecord> records,
|
|
int offset)
|
|
{
|
|
super(offset);
|
|
|
|
this.tag = tag;
|
|
this.titleResId = titleResId;
|
|
this.emptyResId = emptyResId;
|
|
this.records = records;
|
|
}
|
|
|
|
@Override
|
|
public boolean hasEmptyState() {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public int getContentSize() {
|
|
return records.size();
|
|
}
|
|
|
|
@Override
|
|
public long getItemId(@NonNull StableIdGenerator<String> idGenerator, int globalPosition) {
|
|
int localPosition = getLocalPosition(globalPosition);
|
|
|
|
if (localPosition == 0) {
|
|
return idGenerator.getId(tag + "_" + STABLE_ID_HEADER);
|
|
} else if (records.isEmpty()) {
|
|
return idGenerator.getId(tag + "_" + STABLE_ID_TEXT);
|
|
} else {
|
|
return idGenerator.getId(records.get(localPosition - 1).getPackId());
|
|
}
|
|
}
|
|
|
|
void bindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder,
|
|
int localPosition,
|
|
@NonNull GlideRequests glideRequests,
|
|
@NonNull EventListener eventListener,
|
|
boolean allowApngAnimation)
|
|
{
|
|
if (localPosition == 0) {
|
|
((HeaderViewHolder) viewHolder).bind(titleResId);
|
|
} else if (records.isEmpty()) {
|
|
((EmptyViewHolder) viewHolder).bind(emptyResId);
|
|
} else {
|
|
((StickerViewHolder) viewHolder).bind(glideRequests, eventListener, records.get(localPosition - 1), localPosition == records.size(), allowApngAnimation);
|
|
}
|
|
}
|
|
|
|
void swap(int start, int end) {
|
|
int localStart = getLocalPosition(start) - 1;
|
|
int localEnd = getLocalPosition(end) - 1;
|
|
|
|
if (localStart < localEnd) {
|
|
for (int i = localStart; i < localEnd; i++) {
|
|
Collections.swap(records, i, i + 1);
|
|
}
|
|
} else {
|
|
for (int i = localStart; i > localEnd; i--) {
|
|
Collections.swap(records, i, i - 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static class StickerViewHolder extends RecyclerView.ViewHolder {
|
|
|
|
private final ImageView cover;
|
|
private final EmojiTextView title;
|
|
private final TextView author;
|
|
private final View divider;
|
|
private final View actionButton;
|
|
private final ImageView actionButtonImage;
|
|
private final View shareButton;
|
|
private final ImageView shareButtonImage;
|
|
private final CharSequence blessedBadge;
|
|
|
|
StickerViewHolder(@NonNull View itemView) {
|
|
super(itemView);
|
|
|
|
this.cover = itemView.findViewById(R.id.sticker_management_cover);
|
|
this.title = itemView.findViewById(R.id.sticker_management_title);
|
|
this.author = itemView.findViewById(R.id.sticker_management_author);
|
|
this.divider = itemView.findViewById(R.id.sticker_management_divider);
|
|
this.actionButton = itemView.findViewById(R.id.sticker_management_action_button);
|
|
this.actionButtonImage = itemView.findViewById(R.id.sticker_management_action_button_image);
|
|
this.shareButton = itemView.findViewById(R.id.sticker_management_share_button);
|
|
this.shareButtonImage = itemView.findViewById(R.id.sticker_management_share_button_image);
|
|
this.blessedBadge = buildBlessedBadge(itemView.getContext());
|
|
}
|
|
|
|
void bind(@NonNull GlideRequests glideRequests,
|
|
@NonNull EventListener eventListener,
|
|
@NonNull StickerPackRecord stickerPack,
|
|
boolean lastInList,
|
|
boolean allowApngAnimation)
|
|
{
|
|
SpannableStringBuilder titleBuilder = new SpannableStringBuilder(stickerPack.getTitle().orElse(itemView.getResources().getString(R.string.StickerManagementAdapter_untitled)));
|
|
if (BlessedPacks.contains(stickerPack.getPackId())) {
|
|
titleBuilder.append(blessedBadge);
|
|
}
|
|
|
|
title.setText(titleBuilder);
|
|
author.setText(stickerPack.getAuthor().orElse(itemView.getResources().getString(R.string.StickerManagementAdapter_unknown)));
|
|
divider.setVisibility(lastInList ? View.GONE : View.VISIBLE);
|
|
|
|
glideRequests.load(new DecryptableUri(stickerPack.getCover().getUri()))
|
|
.transition(DrawableTransitionOptions.withCrossFade())
|
|
.set(ApngOptions.ANIMATE, allowApngAnimation)
|
|
.into(cover);
|
|
|
|
if (stickerPack.isInstalled()) {
|
|
actionButtonImage.setImageResource(R.drawable.ic_x);
|
|
actionButton.setOnClickListener(v -> eventListener.onStickerPackUninstallClicked(stickerPack.getPackId(), stickerPack.getPackKey()));
|
|
|
|
shareButton.setVisibility(View.VISIBLE);
|
|
shareButtonImage.setVisibility(View.VISIBLE);
|
|
shareButton.setOnClickListener(v -> eventListener.onStickerPackShareClicked(stickerPack.getPackId(), stickerPack.getPackKey()));
|
|
} else {
|
|
actionButtonImage.setImageResource(R.drawable.ic_arrow_down);
|
|
actionButton.setOnClickListener(v -> eventListener.onStickerPackInstallClicked(stickerPack.getPackId(), stickerPack.getPackKey()));
|
|
|
|
shareButton.setVisibility(View.GONE);
|
|
shareButtonImage.setVisibility(View.GONE);
|
|
shareButton.setOnClickListener(null);
|
|
}
|
|
|
|
itemView.setOnClickListener(v -> eventListener.onStickerPackClicked(stickerPack.getPackId(), stickerPack.getPackKey()));
|
|
}
|
|
|
|
void recycle() {
|
|
actionButton.setOnClickListener(null);
|
|
shareButton.setOnClickListener(null);
|
|
itemView.setOnClickListener(null);
|
|
}
|
|
|
|
private static @NonNull CharSequence buildBlessedBadge(@NonNull Context context) {
|
|
SpannableString badgeSpan = new SpannableString(" ");
|
|
Drawable badge = ContextCompat.getDrawable(context, R.drawable.ic_check_circle_white_18dp);
|
|
|
|
badge.setBounds(0, 0, badge.getIntrinsicWidth(), badge.getIntrinsicHeight());
|
|
badge.setColorFilter(ContextCompat.getColor(context, R.color.core_ultramarine), PorterDuff.Mode.MULTIPLY);
|
|
badgeSpan.setSpan(new ImageSpan(badge), 1, badgeSpan.length(), 0);
|
|
|
|
return badgeSpan;
|
|
}
|
|
}
|
|
|
|
static class HeaderViewHolder extends RecyclerView.ViewHolder {
|
|
|
|
private final TextView titleView;
|
|
|
|
HeaderViewHolder(@NonNull View itemView) {
|
|
super(itemView);
|
|
|
|
this.titleView = itemView.findViewById(R.id.sticker_management_header);
|
|
}
|
|
|
|
void bind(@StringRes int title) {
|
|
titleView.setText(title);
|
|
}
|
|
}
|
|
|
|
static class EmptyViewHolder extends RecyclerView.ViewHolder {
|
|
|
|
private final TextView text;
|
|
|
|
EmptyViewHolder(@NonNull View itemView) {
|
|
super(itemView);
|
|
|
|
this.text = itemView.findViewById(R.id.sticker_management_empty_text);
|
|
}
|
|
|
|
void bind(@StringRes int title) {
|
|
text.setText(title);
|
|
}
|
|
}
|
|
|
|
interface EventListener {
|
|
void onStickerPackClicked(@NonNull String packId, @NonNull String packKey);
|
|
void onStickerPackUninstallClicked(@NonNull String packId, @NonNull String packKey);
|
|
void onStickerPackInstallClicked(@NonNull String packId, @NonNull String packKey);
|
|
void onStickerPackShareClicked(@NonNull String packId, @NonNull String packKey);
|
|
}
|
|
|
|
private static class NoSectionException extends IllegalStateException {}
|
|
}
|