kopia lustrzana https://github.com/ryukoposting/Signal-Android
Disable mass APNG animation on low-memory devices.
rodzic
acbc17c909
commit
92b586c061
|
@ -20,7 +20,11 @@ public class ApngBufferCacheDecoder implements ResourceDecoder<ByteBuffer, APNGD
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean handles(@NonNull ByteBuffer source, @NonNull Options options) {
|
public boolean handles(@NonNull ByteBuffer source, @NonNull Options options) {
|
||||||
return APNGParser.isAPNG(new ByteBufferReader(source));
|
if (options.get(ApngOptions.ANIMATE)) {
|
||||||
|
return APNGParser.isAPNG(new ByteBufferReader(source));
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package org.thoughtcrime.securesms.glide.cache;
|
||||||
|
|
||||||
|
import com.bumptech.glide.load.Option;
|
||||||
|
|
||||||
|
import org.signal.core.util.Conversions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds options that can be used to alter how APNGs are decoded in Glide.
|
||||||
|
*/
|
||||||
|
public final class ApngOptions {
|
||||||
|
|
||||||
|
private static final String KEY = "org.signal.skip_apng";
|
||||||
|
|
||||||
|
public static Option<Boolean> ANIMATE = Option.disk(KEY, true, (keyBytes, value, messageDigest) -> {
|
||||||
|
messageDigest.update(keyBytes);
|
||||||
|
messageDigest.update(Conversions.intToByteArray(value ? 1 : 0));
|
||||||
|
});
|
||||||
|
|
||||||
|
private ApngOptions() {}
|
||||||
|
}
|
|
@ -26,7 +26,11 @@ public class ApngStreamCacheDecoder implements ResourceDecoder<InputStream, APNG
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean handles(@NonNull InputStream source, @NonNull Options options) {
|
public boolean handles(@NonNull InputStream source, @NonNull Options options) {
|
||||||
return APNGParser.isAPNG(new StreamReader(source));
|
if (options.get(ApngOptions.ANIMATE)) {
|
||||||
|
return APNGParser.isAPNG(new StreamReader(source));
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -14,6 +14,7 @@ import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.database.model.StickerRecord;
|
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.DecryptableStreamUriLoader.DecryptableUri;
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||||
|
|
||||||
|
@ -29,13 +30,15 @@ final class StickerKeyboardPageAdapter extends RecyclerView.Adapter<StickerKeybo
|
||||||
private final GlideRequests glideRequests;
|
private final GlideRequests glideRequests;
|
||||||
private final EventListener eventListener;
|
private final EventListener eventListener;
|
||||||
private final List<StickerRecord> stickers;
|
private final List<StickerRecord> stickers;
|
||||||
|
private final boolean allowApngAnimation;
|
||||||
|
|
||||||
private int stickerSize;
|
private int stickerSize;
|
||||||
|
|
||||||
StickerKeyboardPageAdapter(@NonNull GlideRequests glideRequests, @NonNull EventListener eventListener) {
|
StickerKeyboardPageAdapter(@NonNull GlideRequests glideRequests, @NonNull EventListener eventListener, boolean allowApngAnimation) {
|
||||||
this.glideRequests = glideRequests;
|
this.glideRequests = glideRequests;
|
||||||
this.eventListener = eventListener;
|
this.eventListener = eventListener;
|
||||||
this.stickers = new ArrayList<>();
|
this.allowApngAnimation = allowApngAnimation;
|
||||||
|
this.stickers = new ArrayList<>();
|
||||||
|
|
||||||
setHasStableIds(true);
|
setHasStableIds(true);
|
||||||
}
|
}
|
||||||
|
@ -52,7 +55,7 @@ final class StickerKeyboardPageAdapter extends RecyclerView.Adapter<StickerKeybo
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(@NonNull StickerKeyboardPageViewHolder viewHolder, int i) {
|
public void onBindViewHolder(@NonNull StickerKeyboardPageViewHolder viewHolder, int i) {
|
||||||
viewHolder.bind(glideRequests, eventListener, stickers.get(i), stickerSize);
|
viewHolder.bind(glideRequests, eventListener, stickers.get(i), stickerSize, allowApngAnimation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -93,7 +96,8 @@ final class StickerKeyboardPageAdapter extends RecyclerView.Adapter<StickerKeybo
|
||||||
public void bind(@NonNull GlideRequests glideRequests,
|
public void bind(@NonNull GlideRequests glideRequests,
|
||||||
@Nullable EventListener eventListener,
|
@Nullable EventListener eventListener,
|
||||||
@NonNull StickerRecord sticker,
|
@NonNull StickerRecord sticker,
|
||||||
@Px int size)
|
@Px int size,
|
||||||
|
boolean allowApngAnimation)
|
||||||
{
|
{
|
||||||
currentSticker = sticker;
|
currentSticker = sticker;
|
||||||
|
|
||||||
|
@ -102,6 +106,7 @@ final class StickerKeyboardPageAdapter extends RecyclerView.Adapter<StickerKeybo
|
||||||
itemView.requestLayout();
|
itemView.requestLayout();
|
||||||
|
|
||||||
glideRequests.load(new DecryptableUri(sticker.getUri()))
|
glideRequests.load(new DecryptableUri(sticker.getUri()))
|
||||||
|
.set(ApngOptions.ANIMATE, allowApngAnimation)
|
||||||
.transition(DrawableTransitionOptions.withCrossFade())
|
.transition(DrawableTransitionOptions.withCrossFade())
|
||||||
.into(image);
|
.into(image);
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||||
import org.thoughtcrime.securesms.stickers.StickerKeyboardPageAdapter.StickerKeyboardPageViewHolder;
|
import org.thoughtcrime.securesms.stickers.StickerKeyboardPageAdapter.StickerKeyboardPageViewHolder;
|
||||||
|
import org.thoughtcrime.securesms.util.DeviceProperties;
|
||||||
import org.whispersystems.libsignal.util.Pair;
|
import org.whispersystems.libsignal.util.Pair;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -69,7 +70,7 @@ public final class StickerKeyboardPageFragment extends Fragment implements Stick
|
||||||
GlideRequests glideRequests = GlideApp.with(this);
|
GlideRequests glideRequests = GlideApp.with(this);
|
||||||
|
|
||||||
this.list = view.findViewById(R.id.sticker_keyboard_list);
|
this.list = view.findViewById(R.id.sticker_keyboard_list);
|
||||||
this.adapter = new StickerKeyboardPageAdapter(glideRequests, this);
|
this.adapter = new StickerKeyboardPageAdapter(glideRequests, this, DeviceProperties.shouldAllowApngStickerAnimation(requireContext()));
|
||||||
this.layoutManager = new GridLayoutManager(requireContext(), 2);
|
this.layoutManager = new GridLayoutManager(requireContext(), 2);
|
||||||
this.listTouchListener = new StickerRolloverTouchListener(requireContext(), glideRequests, eventListener, this);
|
this.listTouchListener = new StickerRolloverTouchListener(requireContext(), glideRequests, eventListener, this);
|
||||||
this.packId = getArguments().getString(KEY_PACK_ID);
|
this.packId = getArguments().getString(KEY_PACK_ID);
|
||||||
|
|
|
@ -19,10 +19,12 @@ import org.thoughtcrime.securesms.components.emoji.MediaKeyboardProvider;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.model.StickerPackRecord;
|
import org.thoughtcrime.securesms.database.model.StickerPackRecord;
|
||||||
import org.thoughtcrime.securesms.database.model.StickerRecord;
|
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.DecryptableStreamUriLoader;
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||||
import org.thoughtcrime.securesms.stickers.StickerKeyboardPageFragment.EventListener;
|
import org.thoughtcrime.securesms.stickers.StickerKeyboardPageFragment.EventListener;
|
||||||
import org.thoughtcrime.securesms.stickers.StickerKeyboardRepository.PackListResult;
|
import org.thoughtcrime.securesms.stickers.StickerKeyboardRepository.PackListResult;
|
||||||
|
import org.thoughtcrime.securesms.util.DeviceProperties;
|
||||||
import org.thoughtcrime.securesms.util.Throttler;
|
import org.thoughtcrime.securesms.util.Throttler;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -147,7 +149,7 @@ public final class StickerKeyboardProvider implements MediaKeyboardProvider,
|
||||||
startingIndex = !result.hasRecents() && result.getPacks().size() > 0 ? 1 : 0;
|
startingIndex = !result.hasRecents() && result.getPacks().size() > 0 ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
presenter.present(this, pagerAdapter, new IconProvider(context, result.getPacks()), null, this, null, startingIndex);
|
presenter.present(this, pagerAdapter, new IconProvider(context, result.getPacks(), DeviceProperties.shouldAllowApngStickerAnimation(context)), null, this, null, startingIndex);
|
||||||
|
|
||||||
if (isSoloProvider && result.getPacks().isEmpty()) {
|
if (isSoloProvider && result.getPacks().isEmpty()) {
|
||||||
context.startActivity(StickerManagementActivity.getIntent(context));
|
context.startActivity(StickerManagementActivity.getIntent(context));
|
||||||
|
@ -238,10 +240,12 @@ public final class StickerKeyboardProvider implements MediaKeyboardProvider,
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final List<StickerPackRecord> packs;
|
private final List<StickerPackRecord> packs;
|
||||||
|
private final boolean allowApngAnimation;
|
||||||
|
|
||||||
private IconProvider(@NonNull Context context, List<StickerPackRecord> packs) {
|
private IconProvider(@NonNull Context context, List<StickerPackRecord> packs, boolean allowApngAnimation) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.packs = packs;
|
this.packs = packs;
|
||||||
|
this.allowApngAnimation = allowApngAnimation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -253,6 +257,7 @@ public final class StickerKeyboardProvider implements MediaKeyboardProvider,
|
||||||
Uri uri = packs.get(index - 1).getCover().getUri();
|
Uri uri = packs.get(index - 1).getCover().getUri();
|
||||||
|
|
||||||
glideRequests.load(new DecryptableStreamUriLoader.DecryptableUri(uri))
|
glideRequests.load(new DecryptableStreamUriLoader.DecryptableUri(uri))
|
||||||
|
.set(ApngOptions.ANIMATE, allowApngAnimation)
|
||||||
.into(imageView);
|
.into(imageView);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.PassphraseRequiredActivity;
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||||
import org.thoughtcrime.securesms.sharing.ShareActivity;
|
import org.thoughtcrime.securesms.sharing.ShareActivity;
|
||||||
|
import org.thoughtcrime.securesms.util.DeviceProperties;
|
||||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -93,7 +94,7 @@ public final class StickerManagementActivity extends PassphraseRequiredActivity
|
||||||
|
|
||||||
private void initView() {
|
private void initView() {
|
||||||
this.list = findViewById(R.id.sticker_management_list);
|
this.list = findViewById(R.id.sticker_management_list);
|
||||||
this.adapter = new StickerManagementAdapter(GlideApp.with(this), this);
|
this.adapter = new StickerManagementAdapter(GlideApp.with(this), this, DeviceProperties.shouldAllowApngStickerAnimation(this));
|
||||||
|
|
||||||
list.setLayoutManager(new LinearLayoutManager(this));
|
list.setLayoutManager(new LinearLayoutManager(this));
|
||||||
list.setAdapter(adapter);
|
list.setAdapter(adapter);
|
||||||
|
|
|
@ -21,6 +21,7 @@ import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
|
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
|
||||||
import org.thoughtcrime.securesms.database.model.StickerPackRecord;
|
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.DecryptableStreamUriLoader.DecryptableUri;
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||||
import org.thoughtcrime.securesms.util.adapter.SectionedRecyclerViewAdapter;
|
import org.thoughtcrime.securesms.util.adapter.SectionedRecyclerViewAdapter;
|
||||||
|
@ -38,6 +39,7 @@ final class StickerManagementAdapter extends SectionedRecyclerViewAdapter<String
|
||||||
|
|
||||||
private final GlideRequests glideRequests;
|
private final GlideRequests glideRequests;
|
||||||
private final EventListener eventListener;
|
private final EventListener eventListener;
|
||||||
|
private final boolean allowApngAnimation;
|
||||||
|
|
||||||
private final List<StickerSection> sections = new ArrayList<StickerSection>(3) {{
|
private final List<StickerSection> sections = new ArrayList<StickerSection>(3) {{
|
||||||
StickerSection yourStickers = new StickerSection(TAG_YOUR_STICKERS,
|
StickerSection yourStickers = new StickerSection(TAG_YOUR_STICKERS,
|
||||||
|
@ -55,9 +57,10 @@ final class StickerManagementAdapter extends SectionedRecyclerViewAdapter<String
|
||||||
add(messageStickers);
|
add(messageStickers);
|
||||||
}};
|
}};
|
||||||
|
|
||||||
StickerManagementAdapter(@NonNull GlideRequests glideRequests, @NonNull EventListener eventListener) {
|
StickerManagementAdapter(@NonNull GlideRequests glideRequests, @NonNull EventListener eventListener, boolean allowApngAnimation) {
|
||||||
this.glideRequests = glideRequests;
|
this.glideRequests = glideRequests;
|
||||||
this.eventListener = eventListener;
|
this.eventListener = eventListener;
|
||||||
|
this.allowApngAnimation = allowApngAnimation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -82,7 +85,7 @@ final class StickerManagementAdapter extends SectionedRecyclerViewAdapter<String
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, @NonNull StickerSection section, int localPosition) {
|
public void bindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, @NonNull StickerSection section, int localPosition) {
|
||||||
section.bindViewHolder(viewHolder, localPosition, glideRequests, eventListener);
|
section.bindViewHolder(viewHolder, localPosition, glideRequests, eventListener, allowApngAnimation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -198,14 +201,15 @@ final class StickerManagementAdapter extends SectionedRecyclerViewAdapter<String
|
||||||
void bindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder,
|
void bindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder,
|
||||||
int localPosition,
|
int localPosition,
|
||||||
@NonNull GlideRequests glideRequests,
|
@NonNull GlideRequests glideRequests,
|
||||||
@NonNull EventListener eventListener)
|
@NonNull EventListener eventListener,
|
||||||
|
boolean allowApngAnimation)
|
||||||
{
|
{
|
||||||
if (localPosition == 0) {
|
if (localPosition == 0) {
|
||||||
((HeaderViewHolder) viewHolder).bind(titleResId);
|
((HeaderViewHolder) viewHolder).bind(titleResId);
|
||||||
} else if (records.isEmpty()) {
|
} else if (records.isEmpty()) {
|
||||||
((EmptyViewHolder) viewHolder).bind(emptyResId);
|
((EmptyViewHolder) viewHolder).bind(emptyResId);
|
||||||
} else {
|
} else {
|
||||||
((StickerViewHolder) viewHolder).bind(glideRequests, eventListener, records.get(localPosition - 1), localPosition == records.size());
|
((StickerViewHolder) viewHolder).bind(glideRequests, eventListener, records.get(localPosition - 1), localPosition == records.size(), allowApngAnimation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,7 +258,8 @@ final class StickerManagementAdapter extends SectionedRecyclerViewAdapter<String
|
||||||
void bind(@NonNull GlideRequests glideRequests,
|
void bind(@NonNull GlideRequests glideRequests,
|
||||||
@NonNull EventListener eventListener,
|
@NonNull EventListener eventListener,
|
||||||
@NonNull StickerPackRecord stickerPack,
|
@NonNull StickerPackRecord stickerPack,
|
||||||
boolean lastInList)
|
boolean lastInList,
|
||||||
|
boolean allowApngAnimation)
|
||||||
{
|
{
|
||||||
title.setText(stickerPack.getTitle().or(itemView.getResources().getString(R.string.StickerManagementAdapter_untitled)));
|
title.setText(stickerPack.getTitle().or(itemView.getResources().getString(R.string.StickerManagementAdapter_untitled)));
|
||||||
author.setText(stickerPack.getAuthor().or(itemView.getResources().getString(R.string.StickerManagementAdapter_unknown)));
|
author.setText(stickerPack.getAuthor().or(itemView.getResources().getString(R.string.StickerManagementAdapter_unknown)));
|
||||||
|
@ -268,6 +273,7 @@ final class StickerManagementAdapter extends SectionedRecyclerViewAdapter<String
|
||||||
|
|
||||||
glideRequests.load(new DecryptableUri(stickerPack.getCover().getUri()))
|
glideRequests.load(new DecryptableUri(stickerPack.getCover().getUri()))
|
||||||
.transition(DrawableTransitionOptions.withCrossFade())
|
.transition(DrawableTransitionOptions.withCrossFade())
|
||||||
|
.set(ApngOptions.ANIMATE, allowApngAnimation)
|
||||||
.into(cover);
|
.into(cover);
|
||||||
|
|
||||||
if (stickerPack.isInstalled()) {
|
if (stickerPack.isInstalled()) {
|
||||||
|
|
|
@ -21,10 +21,12 @@ import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
|
||||||
import org.signal.core.util.logging.Log;
|
import org.signal.core.util.logging.Log;
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActivity;
|
import org.thoughtcrime.securesms.PassphraseRequiredActivity;
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.glide.cache.ApngOptions;
|
||||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
|
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||||
import org.thoughtcrime.securesms.sharing.ShareActivity;
|
import org.thoughtcrime.securesms.sharing.ShareActivity;
|
||||||
import org.thoughtcrime.securesms.stickers.StickerManifest.Sticker;
|
import org.thoughtcrime.securesms.stickers.StickerManifest.Sticker;
|
||||||
|
import org.thoughtcrime.securesms.util.DeviceProperties;
|
||||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
|
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
|
||||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||||
import org.whispersystems.libsignal.util.Pair;
|
import org.whispersystems.libsignal.util.Pair;
|
||||||
|
@ -140,7 +142,7 @@ public final class StickerPackPreviewActivity extends PassphraseRequiredActivity
|
||||||
this.shareButton = findViewById(R.id.sticker_install_share_button);
|
this.shareButton = findViewById(R.id.sticker_install_share_button);
|
||||||
this.shareButtonImage = findViewById(R.id.sticker_install_share_button_image);
|
this.shareButtonImage = findViewById(R.id.sticker_install_share_button_image);
|
||||||
|
|
||||||
this.adapter = new StickerPackPreviewAdapter(GlideApp.with(this), this);
|
this.adapter = new StickerPackPreviewAdapter(GlideApp.with(this), this, DeviceProperties.shouldAllowApngStickerAnimation(this));
|
||||||
this.layoutManager = new GridLayoutManager(this, 2);
|
this.layoutManager = new GridLayoutManager(this, 2);
|
||||||
this.touchListener = new StickerRolloverTouchListener(this, GlideApp.with(this), this, this);
|
this.touchListener = new StickerRolloverTouchListener(this, GlideApp.with(this), this, this);
|
||||||
onScreenWidthChanged(getScreenWidth());
|
onScreenWidthChanged(getScreenWidth());
|
||||||
|
@ -192,6 +194,7 @@ public final class StickerPackPreviewActivity extends PassphraseRequiredActivity
|
||||||
: new StickerRemoteUri(cover.getPackId(), cover.getPackKey(), cover.getId());
|
: new StickerRemoteUri(cover.getPackId(), cover.getPackKey(), cover.getId());
|
||||||
GlideApp.with(this).load(model)
|
GlideApp.with(this).load(model)
|
||||||
.transition(DrawableTransitionOptions.withCrossFade())
|
.transition(DrawableTransitionOptions.withCrossFade())
|
||||||
|
.set(ApngOptions.ANIMATE, DeviceProperties.shouldAllowApngStickerAnimation(this))
|
||||||
.into(coverImage);
|
.into(coverImage);
|
||||||
} else {
|
} else {
|
||||||
coverImage.setImageDrawable(null);
|
coverImage.setImageDrawable(null);
|
||||||
|
|
|
@ -12,6 +12,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||||
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
|
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.glide.cache.ApngOptions;
|
||||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
|
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||||
|
|
||||||
|
@ -23,11 +24,13 @@ public final class StickerPackPreviewAdapter extends RecyclerView.Adapter<Sticke
|
||||||
private final GlideRequests glideRequests;
|
private final GlideRequests glideRequests;
|
||||||
private final EventListener eventListener;
|
private final EventListener eventListener;
|
||||||
private final List<StickerManifest.Sticker> list;
|
private final List<StickerManifest.Sticker> list;
|
||||||
|
private final boolean allowApngAnimation;
|
||||||
|
|
||||||
public StickerPackPreviewAdapter(@NonNull GlideRequests glideRequests, @NonNull EventListener eventListener) {
|
public StickerPackPreviewAdapter(@NonNull GlideRequests glideRequests, @NonNull EventListener eventListener, boolean allowApngAnimation) {
|
||||||
this.glideRequests = glideRequests;
|
this.glideRequests = glideRequests;
|
||||||
this.eventListener = eventListener;
|
this.eventListener = eventListener;
|
||||||
this.list = new ArrayList<>();
|
this.allowApngAnimation = allowApngAnimation;
|
||||||
|
this.list = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -37,7 +40,7 @@ public final class StickerPackPreviewAdapter extends RecyclerView.Adapter<Sticke
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(@NonNull StickerViewHolder stickerViewHolder, int i) {
|
public void onBindViewHolder(@NonNull StickerViewHolder stickerViewHolder, int i) {
|
||||||
stickerViewHolder.bind(glideRequests, list.get(i), eventListener);
|
stickerViewHolder.bind(glideRequests, list.get(i), eventListener, allowApngAnimation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -68,12 +71,17 @@ public final class StickerPackPreviewAdapter extends RecyclerView.Adapter<Sticke
|
||||||
this.image = itemView.findViewById(R.id.sticker_install_item_image);
|
this.image = itemView.findViewById(R.id.sticker_install_item_image);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bind(@NonNull GlideRequests glideRequests, @NonNull StickerManifest.Sticker sticker, @NonNull EventListener eventListener) {
|
void bind(@NonNull GlideRequests glideRequests,
|
||||||
|
@NonNull StickerManifest.Sticker sticker,
|
||||||
|
@NonNull EventListener eventListener,
|
||||||
|
boolean allowApngAnimation)
|
||||||
|
{
|
||||||
currentEmoji = sticker.getEmoji();
|
currentEmoji = sticker.getEmoji();
|
||||||
currentGlideModel = sticker.getUri().isPresent() ? new DecryptableStreamUriLoader.DecryptableUri(sticker.getUri().get())
|
currentGlideModel = sticker.getUri().isPresent() ? new DecryptableStreamUriLoader.DecryptableUri(sticker.getUri().get())
|
||||||
: new StickerRemoteUri(sticker.getPackId(), sticker.getPackKey(), sticker.getId());
|
: new StickerRemoteUri(sticker.getPackId(), sticker.getPackKey(), sticker.getId());
|
||||||
glideRequests.load(currentGlideModel)
|
glideRequests.load(currentGlideModel)
|
||||||
.transition(DrawableTransitionOptions.withCrossFade())
|
.transition(DrawableTransitionOptions.withCrossFade())
|
||||||
|
.set(ApngOptions.ANIMATE, allowApngAnimation)
|
||||||
.into(image);
|
.into(image);
|
||||||
|
|
||||||
image.setOnLongClickListener(v -> {
|
image.setOnLongClickListener(v -> {
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package org.thoughtcrime.securesms.util;
|
||||||
|
|
||||||
|
import android.app.ActivityManager;
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Easy access to various properties of the device, typically to make performance-related decisions.
|
||||||
|
*/
|
||||||
|
public final class DeviceProperties {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not we believe the device has the performance capabilities to efficiently render
|
||||||
|
* large numbers of APNGs simultaneously.
|
||||||
|
*/
|
||||||
|
public static boolean shouldAllowApngStickerAnimation(@NonNull Context context) {
|
||||||
|
return !isLowMemoryDevice(context) && getMemoryClass(context) >= FeatureFlags.animatedStickerMinimumMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isLowMemoryDevice(@NonNull Context context) {
|
||||||
|
ActivityManager activityManager = ServiceUtil.getActivityManager(context);
|
||||||
|
return activityManager.isLowRamDevice();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getMemoryClass(@NonNull Context context) {
|
||||||
|
ActivityManager activityManager = ServiceUtil.getActivityManager(context);
|
||||||
|
return activityManager.getMemoryClass();
|
||||||
|
}
|
||||||
|
}
|
|
@ -72,6 +72,7 @@ public final class FeatureFlags {
|
||||||
private static final String DEFAULT_MAX_BACKOFF = "android.defaultMaxBackoff";
|
private static final String DEFAULT_MAX_BACKOFF = "android.defaultMaxBackoff";
|
||||||
private static final String OKHTTP_AUTOMATIC_RETRY = "android.okhttpAutomaticRetry";
|
private static final String OKHTTP_AUTOMATIC_RETRY = "android.okhttpAutomaticRetry";
|
||||||
private static final String SHARE_SELECTION_LIMIT = "android.share.limit";
|
private static final String SHARE_SELECTION_LIMIT = "android.share.limit";
|
||||||
|
private static final String ANIMATED_STICKER_MIN_MEMORY = "android.animatedStickerMinMemory";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We will only store remote values for flags in this set. If you want a flag to be controllable
|
* We will only store remote values for flags in this set. If you want a flag to be controllable
|
||||||
|
@ -100,7 +101,8 @@ public final class FeatureFlags {
|
||||||
AUTOMATIC_SESSION_INTERVAL,
|
AUTOMATIC_SESSION_INTERVAL,
|
||||||
DEFAULT_MAX_BACKOFF,
|
DEFAULT_MAX_BACKOFF,
|
||||||
OKHTTP_AUTOMATIC_RETRY,
|
OKHTTP_AUTOMATIC_RETRY,
|
||||||
SHARE_SELECTION_LIMIT
|
SHARE_SELECTION_LIMIT,
|
||||||
|
ANIMATED_STICKER_MIN_MEMORY
|
||||||
);
|
);
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
@ -139,7 +141,8 @@ public final class FeatureFlags {
|
||||||
AUTOMATIC_SESSION_INTERVAL,
|
AUTOMATIC_SESSION_INTERVAL,
|
||||||
DEFAULT_MAX_BACKOFF,
|
DEFAULT_MAX_BACKOFF,
|
||||||
OKHTTP_AUTOMATIC_RETRY,
|
OKHTTP_AUTOMATIC_RETRY,
|
||||||
SHARE_SELECTION_LIMIT
|
SHARE_SELECTION_LIMIT,
|
||||||
|
ANIMATED_STICKER_MIN_MEMORY
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -324,6 +327,11 @@ public final class FeatureFlags {
|
||||||
return getBoolean(OKHTTP_AUTOMATIC_RETRY, false);
|
return getBoolean(OKHTTP_AUTOMATIC_RETRY, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The minimum amount of memory required for rendering animated stickers in the keyboard and such */
|
||||||
|
public static int animatedStickerMinimumMemory() {
|
||||||
|
return getInteger(ANIMATED_STICKER_MIN_MEMORY, 193);
|
||||||
|
}
|
||||||
|
|
||||||
/** Only for rendering debug info. */
|
/** Only for rendering debug info. */
|
||||||
public static synchronized @NonNull Map<String, Object> getMemoryValues() {
|
public static synchronized @NonNull Map<String, Object> getMemoryValues() {
|
||||||
return new TreeMap<>(REMOTE_VALUES);
|
return new TreeMap<>(REMOTE_VALUES);
|
||||||
|
|
Ładowanie…
Reference in New Issue