diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8ec73438ad..190a3d5be1 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -532,6 +532,7 @@ dependencies { implementation(project(":lib:qr")) implementation(project(":lib:sticky-header-grid")) implementation(project(":lib:photoview")) + implementation(project(":lib:blurhash")) implementation(project(":core:ui")) implementation(project(":core:models")) implementation(project(":core:models-jvm")) diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/database/UriAttachmentBuilder.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/database/UriAttachmentBuilder.kt index 2fdd34ff92..5752e809fc 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/database/UriAttachmentBuilder.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/database/UriAttachmentBuilder.kt @@ -1,10 +1,10 @@ package org.thoughtcrime.securesms.database import android.net.Uri +import org.signal.blurhash.BlurHash import org.signal.core.models.media.TransformProperties import org.thoughtcrime.securesms.attachments.UriAttachment import org.thoughtcrime.securesms.audio.AudioHash -import org.thoughtcrime.securesms.blurhash.BlurHash import org.thoughtcrime.securesms.stickers.StickerLocator import java.util.UUID diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/ArchivedAttachment.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/ArchivedAttachment.kt index e99cdb1aaf..53117a4fd0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/ArchivedAttachment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/ArchivedAttachment.kt @@ -7,8 +7,8 @@ package org.thoughtcrime.securesms.attachments import android.net.Uri import android.os.Parcel +import org.signal.blurhash.BlurHash import org.signal.core.util.Base64 -import org.thoughtcrime.securesms.blurhash.BlurHash import org.thoughtcrime.securesms.database.AttachmentTable import org.thoughtcrime.securesms.stickers.StickerLocator import java.util.UUID diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.kt index 8da6586650..3a9866cc01 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.kt @@ -8,10 +8,10 @@ import android.net.Uri import android.os.Parcel import android.os.Parcelable import androidx.core.os.ParcelCompat +import org.signal.blurhash.BlurHash import org.signal.core.models.media.TransformProperties import org.signal.core.util.UuidUtil import org.thoughtcrime.securesms.audio.AudioHash -import org.thoughtcrime.securesms.blurhash.BlurHash import org.thoughtcrime.securesms.database.AttachmentTable import org.thoughtcrime.securesms.stickers.StickerLocator import org.thoughtcrime.securesms.util.ParcelUtil diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentUploadUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentUploadUtil.kt index 87c114dc05..f6e4b1c495 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentUploadUtil.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentUploadUtil.kt @@ -7,10 +7,10 @@ package org.thoughtcrime.securesms.attachments import android.content.Context import android.graphics.Bitmap +import org.signal.blurhash.BlurHashEncoder import org.signal.core.util.logging.Log import org.signal.core.util.mebiBytes import org.signal.protos.resumableuploads.ResumableUpload -import org.thoughtcrime.securesms.blurhash.BlurHashEncoder import org.thoughtcrime.securesms.mms.PartAuthority import org.thoughtcrime.securesms.util.MediaUtil import org.whispersystems.signalservice.api.messages.SignalServiceAttachment diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.kt index 8818a662d8..da7ed4455c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.kt @@ -3,9 +3,9 @@ package org.thoughtcrime.securesms.attachments import android.net.Uri import android.os.Parcel import androidx.core.os.ParcelCompat +import org.signal.blurhash.BlurHash import org.signal.core.models.media.TransformProperties import org.thoughtcrime.securesms.audio.AudioHash -import org.thoughtcrime.securesms.blurhash.BlurHash import org.thoughtcrime.securesms.database.AttachmentTable import org.thoughtcrime.securesms.mms.PartAuthority import org.thoughtcrime.securesms.stickers.StickerLocator diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.kt index d46916566b..5159c7e1c8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.kt @@ -3,8 +3,8 @@ package org.thoughtcrime.securesms.attachments import android.net.Uri import android.os.Parcel import androidx.annotation.VisibleForTesting +import org.signal.blurhash.BlurHash import org.signal.core.util.Base64 -import org.thoughtcrime.securesms.blurhash.BlurHash import org.thoughtcrime.securesms.database.AttachmentTable import org.thoughtcrime.securesms.stickers.StickerLocator import org.whispersystems.signalservice.api.InvalidMessageStructureException diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/TombstoneAttachment.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/TombstoneAttachment.kt index aee79af34b..0b7ffe10b5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/TombstoneAttachment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/TombstoneAttachment.kt @@ -2,7 +2,7 @@ package org.thoughtcrime.securesms.attachments import android.net.Uri import android.os.Parcel -import org.thoughtcrime.securesms.blurhash.BlurHash +import org.signal.blurhash.BlurHash import org.thoughtcrime.securesms.database.AttachmentTable import org.thoughtcrime.securesms.stickers.StickerLocator import org.thoughtcrime.securesms.util.MediaUtil diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.kt index 7f38903355..d0f6de8135 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.kt @@ -3,9 +3,9 @@ package org.thoughtcrime.securesms.attachments import android.net.Uri import android.os.Parcel import androidx.core.os.ParcelCompat +import org.signal.blurhash.BlurHash import org.signal.core.models.media.TransformProperties import org.thoughtcrime.securesms.audio.AudioHash -import org.thoughtcrime.securesms.blurhash.BlurHash import org.thoughtcrime.securesms.stickers.StickerLocator import java.util.Objects import java.util.UUID diff --git a/app/src/main/java/org/thoughtcrime/securesms/blurhash/BlurHash.java b/app/src/main/java/org/thoughtcrime/securesms/blurhash/BlurHash.java deleted file mode 100644 index 64933e7378..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/blurhash/BlurHash.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.thoughtcrime.securesms.blurhash; - -import android.os.Parcel; -import android.os.Parcelable; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.util.Objects; - -/** - * A BlurHash is a compact string representation of a blurred image that we can use to show fast - * image previews. - */ -public class BlurHash implements Parcelable { - - private final String hash; - - private BlurHash(@NonNull String hash) { - this.hash = hash; - } - - protected BlurHash(Parcel in) { - hash = in.readString(); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(hash); - } - - @Override - public int describeContents() { - return 0; - } - - public static @Nullable BlurHash parseOrNull(@Nullable String hash) { - if (Base83.isValid(hash)) { - return new BlurHash(hash); - } - return null; - } - - public @NonNull String getHash() { - return hash; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - BlurHash blurHash = (BlurHash) o; - return Objects.equals(hash, blurHash.hash); - } - - @Override - public int hashCode() { - return Objects.hash(hash); - } - - public static final Creator CREATOR = new Creator() { - @Override - public BlurHash createFromParcel(Parcel in) { - return new BlurHash(in); - } - - @Override - public BlurHash[] newArray(int size) { - return new BlurHash[size]; - } - }; -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/blurhash/BlurHashUtil.java b/app/src/main/java/org/thoughtcrime/securesms/blurhash/BlurHashUtil.java deleted file mode 100644 index 0012c18e24..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/blurhash/BlurHashUtil.java +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Source: https://github.com/hsch/blurhash-java - * - * Copyright (c) 2019 Hendrik Schnepel - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - * associated documentation files (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, publish, distribute, - * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT - * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package org.thoughtcrime.securesms.blurhash; - -final class BlurHashUtil { - - static double sRGBToLinear(long value) { - double v = value / 255.0; - if (v <= 0.04045) { - return v / 12.92; - } else { - return Math.pow((v + 0.055) / 1.055, 2.4); - } - } - - static long linearTosRGB(double value) { - double v = Math.max(0, Math.min(1, value)); - if (v <= 0.0031308) { - return (long)(v * 12.92 * 255 + 0.5); - } else { - return (long)((1.055 * Math.pow(v, 1 / 2.4) - 0.055) * 255 + 0.5); - } - } - - static double signPow(double val, double exp) { - return Math.copySign(Math.pow(Math.abs(val), exp), val); - } - - static double max(double[][] values, int from, int endExclusive) { - double result = Double.NEGATIVE_INFINITY; - for (int i = from; i < endExclusive; i++) { - for (int j = 0; j < values[i].length; j++) { - double value = values[i][j]; - if (value > result) { - result = value; - } - } - } - return result; - } - - private BlurHashUtil() { - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java b/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java index 73a2e5b89c..6a8673968c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java @@ -38,17 +38,17 @@ import com.bumptech.glide.request.RequestOptions; import org.signal.core.util.concurrent.ListenableFuture; import org.signal.core.util.concurrent.SettableFuture; import org.signal.core.util.logging.Log; +import org.signal.blurhash.BlurHash; +import org.signal.glide.decryptableuri.DecryptableUri; import org.signal.glide.load.SignalDownsampleStrategy; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.DatabaseAttachment; -import org.thoughtcrime.securesms.blurhash.BlurHash; import org.thoughtcrime.securesms.components.transfercontrols.TransferControlView; import org.thoughtcrime.securesms.database.AttachmentTable; import org.thoughtcrime.securesms.glide.targets.GlideBitmapListeningTarget; import org.thoughtcrime.securesms.glide.targets.GlideDrawableListeningTarget; import org.thoughtcrime.securesms.keyvalue.SignalStore; -import org.signal.glide.decryptableuri.DecryptableUri; import org.thoughtcrime.securesms.mms.ImageSlide; import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.mms.Slide; diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt index 1061e56a51..5404cbebc6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt @@ -28,6 +28,7 @@ import com.bumptech.glide.Glide import okio.ByteString.Companion.toByteString import org.json.JSONArray import org.json.JSONException +import org.signal.blurhash.BlurHash import org.signal.core.models.backup.MediaId import org.signal.core.models.backup.MediaName import org.signal.core.models.media.TransformProperties @@ -77,7 +78,6 @@ import org.thoughtcrime.securesms.audio.AudioHash import org.thoughtcrime.securesms.backup.v2.ArchivedMediaObject import org.thoughtcrime.securesms.backup.v2.exporters.ChatItemArchiveExporter import org.thoughtcrime.securesms.backup.v2.proto.BackupDebugInfo -import org.thoughtcrime.securesms.blurhash.BlurHash import org.thoughtcrime.securesms.crypto.AttachmentSecret import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream diff --git a/app/src/main/java/org/thoughtcrime/securesms/glide/SignalGlideComponents.java b/app/src/main/java/org/thoughtcrime/securesms/glide/SignalGlideComponents.java index cdb0de037e..b1ac9ebe57 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/glide/SignalGlideComponents.java +++ b/app/src/main/java/org/thoughtcrime/securesms/glide/SignalGlideComponents.java @@ -16,14 +16,14 @@ import com.bumptech.glide.load.resource.gif.ByteBufferGifDecoder; import com.bumptech.glide.load.resource.gif.GifDrawable; import com.bumptech.glide.load.resource.gif.StreamGifDecoder; +import org.signal.blurhash.BlurHash; +import org.signal.glide.blurhash.BlurHashModelLoader; +import org.signal.glide.blurhash.BlurHashResourceDecoder; import org.signal.glide.common.io.InputStreamFactory; import org.signal.glide.load.resource.apng.decode.APNGDecoder; import org.thoughtcrime.securesms.badges.load.BadgeLoader; import org.thoughtcrime.securesms.badges.load.GiftBadgeModel; import org.thoughtcrime.securesms.badges.models.Badge; -import org.thoughtcrime.securesms.blurhash.BlurHash; -import org.thoughtcrime.securesms.blurhash.BlurHashModelLoader; -import org.thoughtcrime.securesms.blurhash.BlurHashResourceDecoder; import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoLoader; import org.thoughtcrime.securesms.crypto.AttachmentSecret; diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java index 632b0952d7..8c4d27d659 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java @@ -21,11 +21,11 @@ import org.signal.libsignal.metadata.certificate.InvalidCertificateException; import org.signal.libsignal.metadata.certificate.SenderCertificate; import org.signal.libsignal.zkgroup.InvalidInputException; import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialPresentation; +import org.signal.blurhash.BlurHash; import org.thoughtcrime.securesms.TextSecureExpiredException; import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.AttachmentId; import org.thoughtcrime.securesms.attachments.DatabaseAttachment; -import org.thoughtcrime.securesms.blurhash.BlurHash; import org.thoughtcrime.securesms.contactshare.Contact; import org.thoughtcrime.securesms.contactshare.ContactModelMapper; import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/ImageSlide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/ImageSlide.java index 15acfbf92e..32f0f96b59 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/ImageSlide.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/ImageSlide.java @@ -24,11 +24,11 @@ import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.signal.blurhash.BlurHash; +import org.signal.core.models.media.TransformProperties; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.blurhash.BlurHash; -import org.signal.core.models.media.TransformProperties; import org.thoughtcrime.securesms.util.MediaUtil; public class ImageSlide extends Slide { diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java index 6ed502d7ea..f127a4a3cb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java @@ -25,11 +25,11 @@ import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.signal.blurhash.BlurHash; +import org.signal.core.models.media.TransformProperties; import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.UriAttachment; import org.thoughtcrime.securesms.audio.AudioHash; -import org.thoughtcrime.securesms.blurhash.BlurHash; -import org.signal.core.models.media.TransformProperties; import org.thoughtcrime.securesms.database.AttachmentTable; import org.thoughtcrime.securesms.stickers.StickerLocator; import org.thoughtcrime.securesms.util.MediaUtil; diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/SlideFactory.java b/app/src/main/java/org/thoughtcrime/securesms/mms/SlideFactory.java index 024c416bdf..d8d93f4889 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/SlideFactory.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/SlideFactory.java @@ -10,14 +10,12 @@ import kotlin.Pair; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.signal.core.util.logging.Log; -import org.thoughtcrime.securesms.blurhash.BlurHash; +import org.signal.blurhash.BlurHash; import org.signal.core.models.media.TransformProperties; -import org.thoughtcrime.securesms.database.AttachmentTable; +import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.util.MediaUtil; import java.io.IOException; -import java.util.Optional; /** * SlideFactory encapsulates logic related to constructing slides from a set of paramaeters as defined diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/StoryFirstTimeNavigationView.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/StoryFirstTimeNavigationView.kt index 1ccdebc5c7..c886a410fa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/StoryFirstTimeNavigationView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/StoryFirstTimeNavigationView.kt @@ -17,8 +17,8 @@ import com.bumptech.glide.load.DataSource import com.bumptech.glide.load.engine.GlideException import com.bumptech.glide.request.RequestListener import com.bumptech.glide.request.target.Target +import org.signal.blurhash.BlurHash import org.thoughtcrime.securesms.R -import org.thoughtcrime.securesms.blurhash.BlurHash import org.thoughtcrime.securesms.util.ContextUtil import org.thoughtcrime.securesms.util.visible diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/StorySlateView.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/StorySlateView.kt index 6e55e782af..e9fd987f43 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/StorySlateView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/StorySlateView.kt @@ -9,9 +9,9 @@ import android.widget.FrameLayout import android.widget.ImageView import android.widget.TextView import com.bumptech.glide.Glide +import org.signal.blurhash.BlurHash import org.signal.core.util.getParcelableCompat import org.thoughtcrime.securesms.R -import org.thoughtcrime.securesms.blurhash.BlurHash import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint import org.thoughtcrime.securesms.recipients.Recipient diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/StoryViewerArgs.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/StoryViewerArgs.kt index abbdcfe582..e480a31b4c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/StoryViewerArgs.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/StoryViewerArgs.kt @@ -3,7 +3,7 @@ package org.thoughtcrime.securesms.stories import android.net.Uri import android.os.Parcelable import kotlinx.parcelize.Parcelize -import org.thoughtcrime.securesms.blurhash.BlurHash +import org.signal.blurhash.BlurHash import org.thoughtcrime.securesms.recipients.RecipientId /** diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/StoryViewerState.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/StoryViewerState.kt index fb55d2d7b5..55f056ad1f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/StoryViewerState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/StoryViewerState.kt @@ -1,7 +1,7 @@ package org.thoughtcrime.securesms.stories.viewer import android.net.Uri -import org.thoughtcrime.securesms.blurhash.BlurHash +import org.signal.blurhash.BlurHash import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.stories.StoryTextPostModel diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/post/StoryBlurLoader.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/post/StoryBlurLoader.kt index f3fde8c19c..a110bde841 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/post/StoryBlurLoader.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/post/StoryBlurLoader.kt @@ -11,8 +11,8 @@ import com.bumptech.glide.load.DataSource import com.bumptech.glide.load.engine.GlideException import com.bumptech.glide.request.RequestListener import com.bumptech.glide.request.target.Target +import org.signal.blurhash.BlurHash import org.signal.core.util.logging.Log -import org.thoughtcrime.securesms.blurhash.BlurHash import org.thoughtcrime.securesms.stories.viewer.page.StoryCache import org.thoughtcrime.securesms.stories.viewer.page.StoryDisplay diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/post/StoryPostState.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/post/StoryPostState.kt index 0cdb23d588..7f439b78f7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/post/StoryPostState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/post/StoryPostState.kt @@ -2,7 +2,7 @@ package org.thoughtcrime.securesms.stories.viewer.post import android.graphics.Typeface import android.net.Uri -import org.thoughtcrime.securesms.blurhash.BlurHash +import org.signal.blurhash.BlurHash import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList import org.thoughtcrime.securesms.database.model.databaseprotos.StoryTextPost import org.thoughtcrime.securesms.linkpreview.LinkPreview diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/StoriesSharedElementCrossFaderView.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/StoriesSharedElementCrossFaderView.kt index 0f2640bba4..9906d542ae 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/StoriesSharedElementCrossFaderView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/StoriesSharedElementCrossFaderView.kt @@ -12,11 +12,11 @@ import com.bumptech.glide.load.engine.GlideException import com.bumptech.glide.request.RequestListener import com.bumptech.glide.request.target.Target import com.google.android.material.card.MaterialCardView +import org.signal.blurhash.BlurHash import org.signal.core.util.DimensionUnit import org.signal.glide.decryptableuri.DecryptableUri import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.animation.transitions.CrossfaderTransition -import org.thoughtcrime.securesms.blurhash.BlurHash import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.stories.StoryTextPostModel import kotlin.reflect.KProperty diff --git a/app/src/test/java/org/thoughtcrime/securesms/stories/StoryFirstTimeNavigationViewTest.kt b/app/src/test/java/org/thoughtcrime/securesms/stories/StoryFirstTimeNavigationViewTest.kt index 78257ded1f..844da0b67d 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/stories/StoryFirstTimeNavigationViewTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/stories/StoryFirstTimeNavigationViewTest.kt @@ -22,8 +22,8 @@ import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.Shadows.shadowOf import org.robolectric.annotation.Config +import org.signal.blurhash.BlurHash import org.thoughtcrime.securesms.R -import org.thoughtcrime.securesms.blurhash.BlurHash import org.thoughtcrime.securesms.util.visible @RunWith(RobolectricTestRunner::class) diff --git a/app/src/test/java/org/thoughtcrime/securesms/testutil/UriAttachmentBuilder.kt b/app/src/test/java/org/thoughtcrime/securesms/testutil/UriAttachmentBuilder.kt index e5d93dbed9..8a4f0623a2 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/testutil/UriAttachmentBuilder.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/testutil/UriAttachmentBuilder.kt @@ -1,10 +1,10 @@ package org.thoughtcrime.securesms.testutil import android.net.Uri +import org.signal.blurhash.BlurHash import org.signal.core.models.media.TransformProperties import org.thoughtcrime.securesms.attachments.UriAttachment import org.thoughtcrime.securesms.audio.AudioHash -import org.thoughtcrime.securesms.blurhash.BlurHash import org.thoughtcrime.securesms.database.AttachmentTable import org.thoughtcrime.securesms.stickers.StickerLocator diff --git a/app/src/testShared/org/thoughtcrime/securesms/database/FakeMessageRecords.kt b/app/src/testShared/org/thoughtcrime/securesms/database/FakeMessageRecords.kt index 799892cb48..265225a6f3 100644 --- a/app/src/testShared/org/thoughtcrime/securesms/database/FakeMessageRecords.kt +++ b/app/src/testShared/org/thoughtcrime/securesms/database/FakeMessageRecords.kt @@ -1,11 +1,11 @@ package org.thoughtcrime.securesms.database +import org.signal.blurhash.BlurHash import org.signal.core.models.media.TransformProperties import org.thoughtcrime.securesms.attachments.AttachmentId import org.thoughtcrime.securesms.attachments.Cdn import org.thoughtcrime.securesms.attachments.DatabaseAttachment import org.thoughtcrime.securesms.audio.AudioHash -import org.thoughtcrime.securesms.blurhash.BlurHash import org.thoughtcrime.securesms.contactshare.Contact import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch import org.thoughtcrime.securesms.database.documents.NetworkFailure diff --git a/lib/blurhash/build.gradle.kts b/lib/blurhash/build.gradle.kts new file mode 100644 index 0000000000..666dfc15eb --- /dev/null +++ b/lib/blurhash/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + id("signal-library") + id("kotlin-parcelize") +} + +android { + namespace = "org.signal.blurhash" +} diff --git a/lib/blurhash/src/main/AndroidManifest.xml b/lib/blurhash/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..3752c114f7 --- /dev/null +++ b/lib/blurhash/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + diff --git a/app/src/main/java/org/thoughtcrime/securesms/blurhash/Base83.java b/lib/blurhash/src/main/java/org/signal/blurhash/Base83.java similarity index 98% rename from app/src/main/java/org/thoughtcrime/securesms/blurhash/Base83.java rename to lib/blurhash/src/main/java/org/signal/blurhash/Base83.java index e65d1802e7..cf14fde11f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/blurhash/Base83.java +++ b/lib/blurhash/src/main/java/org/signal/blurhash/Base83.java @@ -18,7 +18,7 @@ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package org.thoughtcrime.securesms.blurhash; +package org.signal.blurhash; import androidx.annotation.Nullable; diff --git a/lib/blurhash/src/main/java/org/signal/blurhash/BlurHash.kt b/lib/blurhash/src/main/java/org/signal/blurhash/BlurHash.kt new file mode 100644 index 0000000000..4b3a62757b --- /dev/null +++ b/lib/blurhash/src/main/java/org/signal/blurhash/BlurHash.kt @@ -0,0 +1,20 @@ +package org.signal.blurhash + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +/** + * A BlurHash is a compact string representation of a blurred image that we can use to show fast + * image previews. + */ +@Parcelize +data class BlurHash(val hash: String) : Parcelable { + companion object { + fun parseOrNull(hash: String?): BlurHash? { + if (Base83.isValid(hash)) { + return BlurHash(hash!!) + } + return null + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/blurhash/BlurHashDecoder.java b/lib/blurhash/src/main/java/org/signal/blurhash/BlurHashDecoder.java similarity index 90% rename from app/src/main/java/org/thoughtcrime/securesms/blurhash/BlurHashDecoder.java rename to lib/blurhash/src/main/java/org/signal/blurhash/BlurHashDecoder.java index 4012c3822d..53a6331532 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/blurhash/BlurHashDecoder.java +++ b/lib/blurhash/src/main/java/org/signal/blurhash/BlurHashDecoder.java @@ -18,20 +18,20 @@ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package org.thoughtcrime.securesms.blurhash; +package org.signal.blurhash; import android.graphics.Bitmap; import android.graphics.Color; import androidx.annotation.Nullable; -import static org.thoughtcrime.securesms.blurhash.BlurHashUtil.linearTosRGB; -import static org.thoughtcrime.securesms.blurhash.BlurHashUtil.sRGBToLinear; -import static org.thoughtcrime.securesms.blurhash.BlurHashUtil.signPow; +import static org.signal.blurhash.BlurHashUtil.linearTosRGB; +import static org.signal.blurhash.BlurHashUtil.sRGBToLinear; +import static org.signal.blurhash.BlurHashUtil.signPow; -class BlurHashDecoder { +public class BlurHashDecoder { - static @Nullable Bitmap decode(@Nullable String blurHash, int width, int height) { + public static @Nullable Bitmap decode(@Nullable String blurHash, int width, int height) { return decode(blurHash, width, height, 1f); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/blurhash/BlurHashEncoder.java b/lib/blurhash/src/main/java/org/signal/blurhash/BlurHashEncoder.java similarity index 94% rename from app/src/main/java/org/thoughtcrime/securesms/blurhash/BlurHashEncoder.java rename to lib/blurhash/src/main/java/org/signal/blurhash/BlurHashEncoder.java index 6034819104..f10a377a29 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/blurhash/BlurHashEncoder.java +++ b/lib/blurhash/src/main/java/org/signal/blurhash/BlurHashEncoder.java @@ -19,7 +19,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package org.thoughtcrime.securesms.blurhash; +package org.signal.blurhash; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -29,10 +29,10 @@ import androidx.annotation.Nullable; import java.io.InputStream; -import static org.thoughtcrime.securesms.blurhash.BlurHashUtil.linearTosRGB; -import static org.thoughtcrime.securesms.blurhash.BlurHashUtil.max; -import static org.thoughtcrime.securesms.blurhash.BlurHashUtil.sRGBToLinear; -import static org.thoughtcrime.securesms.blurhash.BlurHashUtil.signPow; +import static org.signal.blurhash.BlurHashUtil.linearTosRGB; +import static org.signal.blurhash.BlurHashUtil.max; +import static org.signal.blurhash.BlurHashUtil.sRGBToLinear; +import static org.signal.blurhash.BlurHashUtil.signPow; public final class BlurHashEncoder { diff --git a/lib/blurhash/src/main/java/org/signal/blurhash/BlurHashUtil.java b/lib/blurhash/src/main/java/org/signal/blurhash/BlurHashUtil.java new file mode 100644 index 0000000000..c4c5e098d9 --- /dev/null +++ b/lib/blurhash/src/main/java/org/signal/blurhash/BlurHashUtil.java @@ -0,0 +1,46 @@ +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ +package org.signal.blurhash; + +final class BlurHashUtil { + + static double sRGBToLinear(long value) { + double v = value / 255.0; + if (v <= 0.04045) { + return v / 12.92; + } else { + return Math.pow((v + 0.055) / 1.055, 2.4); + } + } + + static long linearTosRGB(double value) { + double v = Math.max(0, Math.min(1, value)); + if (v <= 0.0031308) { + return (long)(v * 12.92 * 255 + 0.5); + } else { + return (long)((1.055 * Math.pow(v, 1 / 2.4) - 0.055) * 255 + 0.5); + } + } + + static double signPow(double val, double exp) { + return Math.copySign(Math.pow(Math.abs(val), exp), val); + } + + static double max(double[][] values, int from, int endExclusive) { + double result = Double.NEGATIVE_INFINITY; + for (int i = from; i < endExclusive; i++) { + for (int j = 0; j < values[i].length; j++) { + double value = values[i][j]; + if (value > result) { + result = value; + } + } + } + return result; + } + + private BlurHashUtil() { + } +} diff --git a/lib/glide/build.gradle.kts b/lib/glide/build.gradle.kts index ca0ddc5442..27cf3d0d4b 100644 --- a/lib/glide/build.gradle.kts +++ b/lib/glide/build.gradle.kts @@ -14,6 +14,7 @@ android { dependencies { implementation(project(":core:util")) + implementation(project(":lib:blurhash")) api(libs.glide.glide) ksp(libs.glide.ksp) diff --git a/app/src/main/java/org/thoughtcrime/securesms/blurhash/BlurHashModelLoader.java b/lib/glide/src/main/java/org/signal/glide/blurhash/BlurHashModelLoader.java similarity index 92% rename from app/src/main/java/org/thoughtcrime/securesms/blurhash/BlurHashModelLoader.java rename to lib/glide/src/main/java/org/signal/glide/blurhash/BlurHashModelLoader.java index 0dfb8ed704..ab4c544b00 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/blurhash/BlurHashModelLoader.java +++ b/lib/glide/src/main/java/org/signal/glide/blurhash/BlurHashModelLoader.java @@ -1,4 +1,9 @@ -package org.thoughtcrime.securesms.blurhash; +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.signal.glide.blurhash; import androidx.annotation.NonNull; @@ -11,6 +16,8 @@ import com.bumptech.glide.load.model.ModelLoaderFactory; import com.bumptech.glide.load.model.MultiModelLoaderFactory; import com.bumptech.glide.signature.ObjectKey; +import org.signal.blurhash.BlurHash; + public final class BlurHashModelLoader implements ModelLoader { private BlurHashModelLoader() {} diff --git a/app/src/main/java/org/thoughtcrime/securesms/blurhash/BlurHashResourceDecoder.java b/lib/glide/src/main/java/org/signal/glide/blurhash/BlurHashResourceDecoder.java similarity index 85% rename from app/src/main/java/org/thoughtcrime/securesms/blurhash/BlurHashResourceDecoder.java rename to lib/glide/src/main/java/org/signal/glide/blurhash/BlurHashResourceDecoder.java index a4464b9337..20727a9fb6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/blurhash/BlurHashResourceDecoder.java +++ b/lib/glide/src/main/java/org/signal/glide/blurhash/BlurHashResourceDecoder.java @@ -1,4 +1,9 @@ -package org.thoughtcrime.securesms.blurhash; +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.signal.glide.blurhash; import android.graphics.Bitmap; @@ -10,6 +15,9 @@ import com.bumptech.glide.load.ResourceDecoder; import com.bumptech.glide.load.engine.Resource; import com.bumptech.glide.load.resource.SimpleResource; +import org.signal.blurhash.BlurHash; +import org.signal.blurhash.BlurHashDecoder; + import java.io.IOException; public class BlurHashResourceDecoder implements ResourceDecoder { diff --git a/settings.gradle.kts b/settings.gradle.kts index ce9aaec2a2..80f76bb3a9 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -86,6 +86,7 @@ include(":lib:spinner") include(":lib:video") include(":lib:image-editor") include(":lib:debuglogs-viewer") +include(":lib:blurhash") // Feature modules include(":feature:registration")