kopia lustrzana https://github.com/ryukoposting/Signal-Android
Fix issue where gifs would load as images.
rodzic
bcbd365326
commit
cb9ab61b6b
|
@ -27,7 +27,7 @@ import org.signal.core.util.ThreadUtil;
|
|||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.components.emoji.parsing.EmojiDrawInfo;
|
||||
import org.thoughtcrime.securesms.components.emoji.parsing.EmojiParser;
|
||||
import org.thoughtcrime.securesms.emoji.EmojiBitmapDecoder;
|
||||
import org.thoughtcrime.securesms.emoji.EmojiPage;
|
||||
import org.thoughtcrime.securesms.emoji.EmojiSource;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.util.DeviceProperties;
|
||||
|
@ -99,10 +99,10 @@ class EmojiProvider {
|
|||
GlideApp.with(context)
|
||||
.asBitmap()
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.load(drawInfo.getPage().getModel())
|
||||
.load(drawInfo.getPage())
|
||||
.priority(Priority.HIGH)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.apply(new RequestOptions().set(EmojiBitmapDecoder.OPTION, lowMemoryDecodeScale))
|
||||
.apply(new RequestOptions().set(EmojiPage.IN_SAMPLE_SIZE, lowMemoryDecodeScale))
|
||||
.addListener(new RequestListener<Bitmap>() {
|
||||
@Override
|
||||
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Bitmap> target, boolean isFirstResource) {
|
||||
|
|
|
@ -3,19 +3,19 @@ package org.thoughtcrime.securesms.components.emoji.parsing;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.emoji.EmojiPageReference;
|
||||
import org.thoughtcrime.securesms.emoji.EmojiPage;
|
||||
|
||||
public class EmojiDrawInfo {
|
||||
|
||||
private final EmojiPageReference page;
|
||||
private final int index;
|
||||
private final EmojiPage page;
|
||||
private final int index;
|
||||
|
||||
public EmojiDrawInfo(final @NonNull EmojiPageReference page, final int index) {
|
||||
public EmojiDrawInfo(final @NonNull EmojiPage page, final int index) {
|
||||
this.page = page;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public @NonNull EmojiPageReference getPage() {
|
||||
public @NonNull EmojiPage getPage() {
|
||||
return page;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
package org.thoughtcrime.securesms.emoji
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import com.bumptech.glide.load.Option
|
||||
import com.bumptech.glide.load.Options
|
||||
import com.bumptech.glide.load.ResourceDecoder
|
||||
import com.bumptech.glide.load.engine.Resource
|
||||
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
|
||||
import com.bumptech.glide.load.resource.bitmap.BitmapResource
|
||||
import java.io.InputStream
|
||||
|
||||
/**
|
||||
* Allows fine grain control over how we decode Emoji pages via a scale factor.
|
||||
*
|
||||
* This can be set via RequestOptions on a Glide request:
|
||||
*
|
||||
* ```
|
||||
* .apply(RequestOptions().set(EmojiBitmapDecoder.OPTION, inSampleSize)
|
||||
* ```
|
||||
*/
|
||||
class EmojiBitmapDecoder(private val bitmapPool: BitmapPool) : ResourceDecoder<InputStream, Bitmap> {
|
||||
|
||||
override fun handles(source: InputStream, options: Options): Boolean {
|
||||
return options.get(OPTION)?.let { it > 1 } ?: false
|
||||
}
|
||||
|
||||
override fun decode(source: InputStream, width: Int, height: Int, options: Options): Resource<Bitmap>? {
|
||||
val bitmapOptions = BitmapFactory.Options()
|
||||
|
||||
bitmapOptions.inSampleSize = requireNotNull(options.get(OPTION))
|
||||
|
||||
return BitmapResource.obtain(BitmapFactory.decodeStream(source, null, bitmapOptions), bitmapPool)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val OPTION: Option<Int> = Option.memory("emoji_sample_size", 1)
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package org.thoughtcrime.securesms.emoji
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.jobs.DownloadLatestEmojiDataJob
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.service.PersistentAlarmManagerListener
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
private val INTERVAL_WITHOUT_REMOTE_DOWNLOAD = TimeUnit.DAYS.toMillis(1)
|
||||
private val INTERVAL_WITH_REMOTE_DOWNLOAD = TimeUnit.DAYS.toMillis(7)
|
||||
|
||||
class EmojiDownloadListener : PersistentAlarmManagerListener() {
|
||||
|
||||
override fun getNextScheduledExecutionTime(context: Context): Long = SignalStore.emojiValues().nextScheduledCheck
|
||||
|
||||
override fun onAlarm(context: Context, scheduledTime: Long): Long {
|
||||
ApplicationDependencies.getJobManager().add(DownloadLatestEmojiDataJob(false))
|
||||
|
||||
val nextTime: Long = System.currentTimeMillis() + if (EmojiFiles.Version.exists(context)) INTERVAL_WITH_REMOTE_DOWNLOAD else INTERVAL_WITHOUT_REMOTE_DOWNLOAD
|
||||
|
||||
SignalStore.emojiValues().nextScheduledCheck = nextTime
|
||||
|
||||
return nextTime
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun schedule(context: Context) {
|
||||
EmojiDownloadListener().onReceive(context, Intent())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
package org.thoughtcrime.securesms.emoji
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import com.bumptech.glide.Priority
|
||||
import com.bumptech.glide.load.DataSource
|
||||
import com.bumptech.glide.load.Key
|
||||
import com.bumptech.glide.load.Option
|
||||
import com.bumptech.glide.load.Options
|
||||
import com.bumptech.glide.load.data.DataFetcher
|
||||
import com.bumptech.glide.load.model.ModelLoader
|
||||
import com.bumptech.glide.load.model.ModelLoaderFactory
|
||||
import com.bumptech.glide.load.model.MultiModelLoaderFactory
|
||||
import org.thoughtcrime.securesms.mms.PartAuthority
|
||||
import java.io.InputStream
|
||||
import java.security.MessageDigest
|
||||
|
||||
typealias EmojiPageFactory = (Uri) -> EmojiPage
|
||||
|
||||
sealed class EmojiPage(private val uri: Uri) : Key {
|
||||
override fun updateDiskCacheKey(messageDigest: MessageDigest) {
|
||||
messageDigest.update("EmojiPage".encodeToByteArray())
|
||||
messageDigest.update(uri.toString().encodeToByteArray())
|
||||
}
|
||||
|
||||
data class Asset(private val uri: Uri) : EmojiPage(uri)
|
||||
data class Disk(private val uri: Uri) : EmojiPage(uri)
|
||||
|
||||
class Loader(private val context: Context) : ModelLoader<EmojiPage, Bitmap> {
|
||||
override fun buildLoadData(
|
||||
model: EmojiPage,
|
||||
width: Int,
|
||||
height: Int,
|
||||
options: Options
|
||||
): ModelLoader.LoadData<Bitmap> {
|
||||
return ModelLoader.LoadData(model, Fetcher(context, model, options.get(IN_SAMPLE_SIZE) ?: 1))
|
||||
}
|
||||
|
||||
override fun handles(model: EmojiPage): Boolean = true
|
||||
|
||||
class Factory(private val context: Context) : ModelLoaderFactory<EmojiPage, Bitmap> {
|
||||
override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader<EmojiPage, Bitmap> {
|
||||
return Loader(context)
|
||||
}
|
||||
|
||||
override fun teardown() = Unit
|
||||
}
|
||||
}
|
||||
|
||||
class Fetcher(private val context: Context, private val model: EmojiPage, private val inSampleSize: Int) : DataFetcher<Bitmap> {
|
||||
override fun loadData(priority: Priority, callback: DataFetcher.DataCallback<in Bitmap>) {
|
||||
try {
|
||||
val inputStream: InputStream = when (model) {
|
||||
is Asset -> context.assets.open(model.uri.toString().replace("file:///android_asset/", ""))
|
||||
is Disk -> EmojiFiles.openForReading(context, PartAuthority.getEmojiFilename(model.uri))
|
||||
}
|
||||
|
||||
val bitmapOptions = BitmapFactory.Options()
|
||||
bitmapOptions.inSampleSize = inSampleSize
|
||||
|
||||
callback.onDataReady(BitmapFactory.decodeStream(inputStream, null, bitmapOptions))
|
||||
} catch (e: Exception) {
|
||||
callback.onLoadFailed(e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun cleanup() = Unit
|
||||
override fun cancel() = Unit
|
||||
|
||||
override fun getDataClass(): Class<Bitmap> {
|
||||
return Bitmap::class.java
|
||||
}
|
||||
|
||||
override fun getDataSource(): DataSource {
|
||||
return DataSource.LOCAL
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val IN_SAMPLE_SIZE: Option<Int> = Option.memory("emoji_page_in_sample_size", 1)
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package org.thoughtcrime.securesms.emoji
|
||||
|
||||
import android.net.Uri
|
||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader
|
||||
|
||||
/**
|
||||
* Used by Emoji provider to set up a glide request.
|
||||
*/
|
||||
class EmojiPageReference {
|
||||
|
||||
val model: Any
|
||||
|
||||
constructor(uri: Uri) {
|
||||
model = uri
|
||||
}
|
||||
|
||||
constructor(decryptableUri: DecryptableStreamUriLoader.DecryptableUri) {
|
||||
model = decryptableUri
|
||||
}
|
||||
}
|
||||
|
||||
typealias EmojiPageReferenceFactory = (uri: Uri) -> EmojiPageReference
|
|
@ -9,7 +9,6 @@ import org.thoughtcrime.securesms.components.emoji.parsing.EmojiDrawInfo
|
|||
import org.thoughtcrime.securesms.components.emoji.parsing.EmojiTree
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader
|
||||
import org.thoughtcrime.securesms.util.ScreenDensity
|
||||
import java.io.InputStream
|
||||
import java.util.concurrent.CountDownLatch
|
||||
|
@ -21,7 +20,7 @@ import java.util.concurrent.atomic.AtomicReference
|
|||
class EmojiSource(
|
||||
val decodeScale: Float,
|
||||
private val emojiData: EmojiData,
|
||||
private val emojiPageReferenceFactory: EmojiPageReferenceFactory
|
||||
private val emojiPageFactory: EmojiPageFactory
|
||||
) : EmojiData by emojiData {
|
||||
|
||||
val variationMap: Map<String, String> by lazy {
|
||||
|
@ -50,9 +49,9 @@ class EmojiSource(
|
|||
dataPages
|
||||
.filter { it.spriteUri != null }
|
||||
.forEach { page ->
|
||||
val reference = emojiPageReferenceFactory(page.spriteUri!!)
|
||||
val emojiPage = emojiPageFactory(page.spriteUri!!)
|
||||
page.emoji.forEachIndexed { idx, emoji ->
|
||||
tree.add(emoji, EmojiDrawInfo(reference, idx))
|
||||
tree.add(emoji, EmojiDrawInfo(emojiPage, idx))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,7 +96,7 @@ class EmojiSource(
|
|||
val density = ScreenDensity.xhdpiRelativeDensityScaleFactor(version.density)
|
||||
|
||||
return emojiData?.let {
|
||||
EmojiSource(density, it) { uri: Uri -> EmojiPageReference(DecryptableStreamUriLoader.DecryptableUri(uri)) }
|
||||
EmojiSource(density, it) { uri: Uri -> EmojiPage.Disk(uri) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,7 +111,7 @@ class EmojiSource(
|
|||
displayPages = parsedData.displayPages + PAGE_EMOTICONS,
|
||||
dataPages = parsedData.dataPages + PAGE_EMOTICONS
|
||||
)
|
||||
) { uri: Uri -> EmojiPageReference(uri) }
|
||||
) { uri: Uri -> EmojiPage.Asset(uri) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ import org.thoughtcrime.securesms.blurhash.BlurHashResourceDecoder;
|
|||
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
|
||||
import org.thoughtcrime.securesms.crypto.AttachmentSecret;
|
||||
import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider;
|
||||
import org.thoughtcrime.securesms.emoji.EmojiBitmapDecoder;
|
||||
import org.thoughtcrime.securesms.emoji.EmojiPage;
|
||||
import org.thoughtcrime.securesms.giph.model.ChunkedImageUrl;
|
||||
import org.thoughtcrime.securesms.glide.ChunkedImageUrlLoader;
|
||||
import org.thoughtcrime.securesms.glide.ContactPhotoLoader;
|
||||
|
@ -83,7 +83,6 @@ public class SignalGlideModule extends AppGlideModule {
|
|||
ApngBufferCacheDecoder apngBufferCacheDecoder = new ApngBufferCacheDecoder();
|
||||
ApngStreamCacheDecoder apngStreamCacheDecoder = new ApngStreamCacheDecoder(apngBufferCacheDecoder);
|
||||
|
||||
registry.prepend(InputStream.class, Bitmap.class, new EmojiBitmapDecoder(glide.getBitmapPool()));
|
||||
registry.prepend(InputStream.class, APNGDecoder.class, apngStreamCacheDecoder);
|
||||
registry.prepend(ByteBuffer.class, APNGDecoder.class, apngBufferCacheDecoder);
|
||||
registry.prepend(APNGDecoder.class, new EncryptedApngCacheEncoder(secret));
|
||||
|
@ -92,6 +91,7 @@ public class SignalGlideModule extends AppGlideModule {
|
|||
|
||||
registry.prepend(BlurHash.class, Bitmap.class, new BlurHashResourceDecoder());
|
||||
|
||||
registry.append(EmojiPage.class, Bitmap.class, new EmojiPage.Loader.Factory(context));
|
||||
registry.append(ConversationShortcutPhoto.class, Bitmap.class, new ConversationShortcutPhoto.Loader.Factory(context));
|
||||
registry.append(ContactPhoto.class, InputStream.class, new ContactPhotoLoader.Factory(context));
|
||||
registry.append(DecryptableUri.class, InputStream.class, new DecryptableStreamUriLoader.Factory(context));
|
||||
|
|
|
@ -11,7 +11,7 @@ class EmojiSourceTest {
|
|||
@Test
|
||||
fun `Given a bunch of data pages with max value 100100, when I get the maxEmojiLength, then I expect 6`() {
|
||||
val emojiDataFake = ParsedEmojiData(EmojiMetrics(-1, -1, -1), listOf(), "png", listOf(), dataPages = generatePages(), listOf())
|
||||
val testSubject = EmojiSource(0f, emojiDataFake, ::EmojiPageReference)
|
||||
val testSubject = EmojiSource(0f, emojiDataFake) { uri -> EmojiPage.Disk(uri) }
|
||||
|
||||
Assert.assertEquals(6, testSubject.maxEmojiLength)
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue