diff --git a/app/proguard/proguard.cfg b/app/proguard/proguard.cfg index 9bad298310..2acd3d1fa4 100644 --- a/app/proguard/proguard.cfg +++ b/app/proguard/proguard.cfg @@ -8,6 +8,11 @@ -keep class org.thoughtcrime.securesms.** { *; } -keep class org.signal.donations.json.** { *; } -keep class org.signal.network.** { *; } + +-keep class org.signal.core.util.crypto.AttachmentSecret { *; } +-keep class org.signal.core.util.crypto.AttachmentSecret$* { *; } +-keep class org.signal.core.util.crypto.KeyStoreHelper$SealedData { *; } +-keep class org.signal.core.util.crypto.KeyStoreHelper$SealedData$* { *; } -keepclassmembers class ** { public void onEvent*(**); } diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/database/AttachmentTableTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/database/AttachmentTableTest.kt index af6fb3d298..2ff7171bd2 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/database/AttachmentTableTest.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/database/AttachmentTableTest.kt @@ -32,10 +32,10 @@ import org.thoughtcrime.securesms.attachments.Attachment import org.thoughtcrime.securesms.attachments.PointerAttachment import org.thoughtcrime.securesms.attachments.UriAttachment import org.thoughtcrime.securesms.backup.v2.ArchivedMediaObject +import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.mms.IncomingMessage import org.thoughtcrime.securesms.mms.MediaStream -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.testing.SignalActivityRule import org.thoughtcrime.securesms.util.MediaUtil import org.whispersystems.signalservice.api.crypto.AttachmentCipherOutputStream @@ -67,7 +67,7 @@ class AttachmentTableTest { @Test fun givenABlob_whenIInsert2AttachmentsForPreUpload_thenIExpectDistinctIdsButSameFileName() { - val blob = BlobProvider.getInstance().forData(byteArrayOf(1, 2, 3, 4, 5)).createForSingleSessionInMemory() + val blob = AppDependencies.blobs.forData(byteArrayOf(1, 2, 3, 4, 5)).createForSingleSessionInMemory() val highQualityProperties = createHighQualityTransformProperties() val highQualityImage = createAttachment(1, blob, highQualityProperties) val attachment = SignalDatabase.attachments.insertAttachmentForPreUpload(highQualityImage) @@ -80,7 +80,7 @@ class AttachmentTableTest { @FlakyTest @Test fun givenABlobAndDifferentTransformQuality_whenIInsert2AttachmentsForPreUpload_thenIExpectDifferentFileInfos() { - val blob = BlobProvider.getInstance().forData(byteArrayOf(1, 2, 3, 4, 5)).createForSingleSessionInMemory() + val blob = AppDependencies.blobs.forData(byteArrayOf(1, 2, 3, 4, 5)).createForSingleSessionInMemory() val highQualityProperties = createHighQualityTransformProperties() val highQualityImage = createAttachment(1, blob, highQualityProperties) val lowQualityImage = createAttachment(1, blob, TransformProperties.empty()) @@ -107,7 +107,7 @@ class AttachmentTableTest { @Ignore("test is flaky") @Test fun givenIdenticalAttachmentsInsertedForPreUpload_whenIUpdateAttachmentDataAndSpecifyOnlyModifyThisAttachment_thenIExpectDifferentFileInfos() { - val blob = BlobProvider.getInstance().forData(byteArrayOf(1, 2, 3, 4, 5)).createForSingleSessionInMemory() + val blob = AppDependencies.blobs.forData(byteArrayOf(1, 2, 3, 4, 5)).createForSingleSessionInMemory() val highQualityProperties = createHighQualityTransformProperties() val highQualityImage = createAttachment(1, blob, highQualityProperties) val attachment = SignalDatabase.attachments.insertAttachmentForPreUpload(highQualityImage) @@ -143,9 +143,9 @@ class AttachmentTableTest { val uncompressData = byteArrayOf(1, 2, 3, 4, 5) val compressedData = byteArrayOf(1, 2, 3) - val blobUncompressed = BlobProvider.getInstance().forData(uncompressData).createForSingleSessionInMemory() + val blobUncompressed = AppDependencies.blobs.forData(uncompressData).createForSingleSessionInMemory() - val previousAttachment = createAttachment(1, BlobProvider.getInstance().forData(compressedData).createForSingleSessionInMemory(), TransformProperties.empty()) + val previousAttachment = createAttachment(1, AppDependencies.blobs.forData(compressedData).createForSingleSessionInMemory(), TransformProperties.empty()) val previousDatabaseAttachmentId: AttachmentId = SignalDatabase.attachments.insertAttachmentsForMessage(1, listOf(previousAttachment), emptyList()).values.first() val standardQualityPreUpload = createAttachment(1, blobUncompressed, TransformProperties.empty()) @@ -178,7 +178,7 @@ class AttachmentTableTest { fun doNotDedupedFileIfUsedByAnotherAttachmentWithADifferentTransformProperties() { // GIVEN val uncompressData = byteArrayOf(1, 2, 3, 4, 5) - val blobUncompressed = BlobProvider.getInstance().forData(uncompressData).createForSingleSessionInMemory() + val blobUncompressed = AppDependencies.blobs.forData(uncompressData).createForSingleSessionInMemory() val standardQualityPreUpload = createAttachment(1, blobUncompressed, TransformProperties.empty()) val standardDatabaseAttachment = SignalDatabase.attachments.insertAttachmentForPreUpload(standardQualityPreUpload) @@ -204,7 +204,7 @@ class AttachmentTableTest { @Test fun resetArchiveTransferStateByPlaintextHashAndRemoteKey_singleMatch() { // Given an attachment with some plaintextHash+remoteKey - val blob = BlobProvider.getInstance().forData(byteArrayOf(1, 2, 3, 4, 5)).createForSingleSessionInMemory() + val blob = AppDependencies.blobs.forData(byteArrayOf(1, 2, 3, 4, 5)).createForSingleSessionInMemory() val attachment = createAttachment(1, blob, TransformProperties.empty()) val attachmentId = SignalDatabase.attachments.insertAttachmentsForMessage(-1L, listOf(attachment), emptyList()).values.first() SignalDatabase.attachments.finalizeAttachmentAfterUpload(attachmentId, AttachmentTableTestUtil.createUploadResult(attachmentId)) @@ -259,7 +259,7 @@ class AttachmentTableTest { fun givenAnAttachmentWithAMessageThatExpiresIn5Minutes_whenIGetAttachmentsThatNeedArchiveUpload_thenIDoNotExpectThatAttachment() { // GIVEN val uncompressData = byteArrayOf(1, 2, 3, 4, 5) - val blobUncompressed = BlobProvider.getInstance().forData(uncompressData).createForSingleSessionInMemory() + val blobUncompressed = AppDependencies.blobs.forData(uncompressData).createForSingleSessionInMemory() val attachment = createAttachment(1, blobUncompressed, TransformProperties.empty()) val message = createIncomingMessage(serverTime = 0.days, attachment = attachment, expiresIn = 5.minutes) val messageId = SignalDatabase.messages.insertMessageInbox(message).map { it.messageId }.get() @@ -278,7 +278,7 @@ class AttachmentTableTest { fun givenAnAttachmentWithAMessageThatExpiresIn5Days_whenIGetAttachmentsThatNeedArchiveUpload_thenIDoExpectThatAttachment() { // GIVEN val uncompressData = byteArrayOf(1, 2, 3, 4, 5) - val blobUncompressed = BlobProvider.getInstance().forData(uncompressData).createForSingleSessionInMemory() + val blobUncompressed = AppDependencies.blobs.forData(uncompressData).createForSingleSessionInMemory() val attachment = createAttachment(1, blobUncompressed, TransformProperties.empty()) val message = createIncomingMessage(serverTime = 0.days, attachment = attachment, expiresIn = 5.days) val messageId = SignalDatabase.messages.insertMessageInbox(message).map { it.messageId }.get() @@ -297,7 +297,7 @@ class AttachmentTableTest { fun givenAnAttachmentWithAMessageWithExpirationStartedThatExpiresIn5Days_whenIGetAttachmentsThatNeedArchiveUpload_thenIDoExpectThatAttachment() { // GIVEN val uncompressData = byteArrayOf(1, 2, 3, 4, 5) - val blobUncompressed = BlobProvider.getInstance().forData(uncompressData).createForSingleSessionInMemory() + val blobUncompressed = AppDependencies.blobs.forData(uncompressData).createForSingleSessionInMemory() val attachment = createAttachment(1, blobUncompressed, TransformProperties.empty()) val message = createIncomingMessage(serverTime = 0.days, attachment = attachment, expiresIn = 5.days) val messageId = SignalDatabase.messages.insertMessageInbox(message).map { it.messageId }.get() @@ -317,7 +317,7 @@ class AttachmentTableTest { fun givenAnAttachmentWithALongTextAttachment_whenIGetAttachmentsThatNeedArchiveUpload_thenIDoNotExpectThatAttachment() { // GIVEN val uncompressData = byteArrayOf(1, 2, 3, 4, 5) - val blobUncompressed = BlobProvider.getInstance().forData(uncompressData).createForSingleSessionInMemory() + val blobUncompressed = AppDependencies.blobs.forData(uncompressData).createForSingleSessionInMemory() val attachment = createAttachment(1, blobUncompressed, TransformProperties.empty(), contentType = MediaUtil.LONG_TEXT) val message = createIncomingMessage(serverTime = 0.days, attachment = attachment) val messageId = SignalDatabase.messages.insertMessageInbox(message).map { it.messageId }.get() diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/database/AttachmentTableTest_deduping.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/database/AttachmentTableTest_deduping.kt index 887349d0ef..6a85be3ae8 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/database/AttachmentTableTest_deduping.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/database/AttachmentTableTest_deduping.kt @@ -22,11 +22,11 @@ import org.signal.core.util.update import org.signal.mediasend.SentMediaQuality import org.thoughtcrime.securesms.attachments.Cdn import org.thoughtcrime.securesms.attachments.PointerAttachment +import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.mms.MediaStream import org.thoughtcrime.securesms.mms.OutgoingMessage import org.thoughtcrime.securesms.mms.QuoteModel -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.util.MediaUtil import org.whispersystems.signalservice.internal.crypto.PaddingInputStream @@ -671,7 +671,7 @@ class AttachmentTableTest_deduping { } fun insertWithData(data: ByteArray, transformProperties: TransformProperties = TransformProperties.empty()): AttachmentId { - val uri = BlobProvider.getInstance().forData(data).createForSingleSessionInMemory() + val uri = AppDependencies.blobs.forData(data).createForSingleSessionInMemory() val attachment = UriAttachmentBuilder.build( id = Random.nextLong(), diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJobTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJobTest.kt index 2d5622f55e..f66b62e02e 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJobTest.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJobTest.kt @@ -22,7 +22,6 @@ import org.thoughtcrime.securesms.database.UriAttachmentBuilder import org.thoughtcrime.securesms.database.transformPropertiesForSentMediaQuality import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.jobmanager.Job -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.testing.SignalActivityRule import org.thoughtcrime.securesms.util.MediaUtil import java.util.Optional @@ -40,7 +39,7 @@ class AttachmentCompressionJobTest { StreamUtil.readFully(it) } - val blob = BlobProvider.getInstance().forData(imageBytes).createForSingleSessionOnDisk(AppDependencies.application) + val blob = AppDependencies.blobs.forData(imageBytes).createForSingleSessionOnDisk(AppDependencies.application) val firstPreUpload = createAttachment(1, blob, TransformProperties.empty()) val firstDatabaseAttachment = SignalDatabase.attachments.insertAttachmentForPreUpload(firstPreUpload) diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/main/MainNavigationLaunchTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/main/MainNavigationLaunchTest.kt index 1cb2a5ac99..233b79b666 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/main/MainNavigationLaunchTest.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/main/MainNavigationLaunchTest.kt @@ -32,8 +32,8 @@ import org.thoughtcrime.securesms.conversation.ConversationIntents import org.thoughtcrime.securesms.conversation.v2.ConversationFragment import org.thoughtcrime.securesms.conversationlist.ConversationListArchiveFragment import org.thoughtcrime.securesms.conversationlist.ConversationListFragment +import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionActivity -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.stories.landing.StoriesLandingFragment @@ -571,7 +571,7 @@ class MainNavigationLaunchTest { } private fun realBlob(bytes: ByteArray, mimeType: String): Uri { - return BlobProvider.getInstance() + return AppDependencies.blobs .forData(bytes) .withMimeType(mimeType) .createForSingleSessionInMemory() diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/messages/MessageHelper.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/messages/MessageHelper.kt index 4076d58ce1..11cce87dee 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/messages/MessageHelper.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/messages/MessageHelper.kt @@ -18,10 +18,10 @@ import org.thoughtcrime.securesms.database.UriAttachmentBuilder import org.thoughtcrime.securesms.database.model.GroupsV2UpdateMessageConverter import org.thoughtcrime.securesms.database.model.databaseprotos.DecryptedGroupV2Context import org.thoughtcrime.securesms.database.model.databaseprotos.GV2UpdateDescription +import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.jobs.ThreadUpdateJob import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.mms.OutgoingMessage -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.testing.GroupTestingUtils @@ -120,7 +120,7 @@ class MessageHelper(private val harness: SignalActivityRule, var startTime: Long } fun outgoingAttachment(data: ByteArray, uuid: UUID? = UUID.randomUUID()): Attachment { - val uri: Uri = BlobProvider.getInstance().forData(data).createForSingleSessionInMemory() + val uri: Uri = AppDependencies.blobs.forData(data).createForSingleSessionInMemory() val attachment: UriAttachment = UriAttachmentBuilder.build( id = Random.nextLong(), diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index 89c64ae92d..144308e1ad 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -39,6 +39,7 @@ import org.signal.core.util.MemoryTracker; import org.signal.core.util.Util; import org.signal.core.util.concurrent.AnrDetector; import org.signal.core.util.concurrent.SignalExecutors; +import org.signal.core.util.crypto.AttachmentSecretProvider; import org.signal.core.util.logging.AndroidLogger; import org.signal.core.util.logging.Log; import org.signal.core.util.logging.Scrubber; @@ -51,10 +52,10 @@ import org.signal.ringrtc.CallManager; import org.thoughtcrime.securesms.apkupdate.ApkUpdateRefreshListener; import org.thoughtcrime.securesms.avatar.AvatarPickerStorage; import org.thoughtcrime.securesms.backup.v2.BackupRepository; -import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider; +import org.thoughtcrime.securesms.conversation.drafts.DraftBlobs; +import org.thoughtcrime.securesms.crypto.AppAttachmentSecretStore; import org.thoughtcrime.securesms.crypto.DatabaseSecretProvider; import org.thoughtcrime.securesms.database.LogDatabase; -import org.thoughtcrime.securesms.database.SQLiteDatabase; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.database.SqlCipherLibraryLoader; import org.thoughtcrime.securesms.dependencies.AppDependencies; @@ -99,7 +100,6 @@ import org.thoughtcrime.securesms.messageprocessingalarm.RoutineMessageFetchRece import org.thoughtcrime.securesms.messages.IncomingMessageObserver; import org.thoughtcrime.securesms.migrations.ApplicationMigrations; import org.thoughtcrime.securesms.mms.SignalGlideModule; -import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.ratelimit.RateLimitUtil; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.registration.util.RegistrationUtil; @@ -175,7 +175,7 @@ public class ApplicationContext extends Application implements AppForegroundObse SqlCipherLibraryLoader.load(); SignalDatabase.init(this, DatabaseSecretProvider.getOrCreateDatabaseSecret(this), - AttachmentSecretProvider.getInstance(this).getOrCreateAttachmentSecret()); + AttachmentSecretProvider.getInstance(this, AppAttachmentSecretStore.INSTANCE).getOrCreateAttachmentSecret()); Logger.setTarget(SqlCipherLogTarget.INSTANCE); }) .addBlocking("signal-store", () -> SignalStore.init(this)) @@ -578,7 +578,7 @@ public class ApplicationContext extends Application implements AppForegroundObse @WorkerThread private void initializeBlobProvider() { - BlobProvider.getInstance().initialize(this); + AppDependencies.getBlobs().initialize(this, DraftBlobs.INSTANCE::deleteOrphanedDraftFiles); } @WorkerThread diff --git a/app/src/main/java/org/thoughtcrime/securesms/audio/AudioRecorder.java b/app/src/main/java/org/thoughtcrime/securesms/audio/AudioRecorder.java index 583353f7b0..b3afe6da15 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/audio/AudioRecorder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/audio/AudioRecorder.java @@ -11,10 +11,11 @@ import androidx.annotation.Nullable; import org.signal.core.util.ThreadUtil; import org.signal.core.util.concurrent.SignalExecutors; +import org.signal.core.util.contentproviders.BlobProvider; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.components.voice.VoiceNoteDraft; +import org.thoughtcrime.securesms.dependencies.AppDependencies; import org.thoughtcrime.securesms.notifications.v2.InChatNotificationSoundSuppressor; -import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.util.MediaUtil; import java.io.IOException; @@ -88,9 +89,9 @@ public class AudioRecorder { ParcelFileDescriptor fds[] = ParcelFileDescriptor.createPipe(); - BlobProvider.BlobBuilder blobBuilder = BlobProvider.getInstance() - .forData(new ParcelFileDescriptor.AutoCloseInputStream(fds[0]), 0) - .withMimeType(MediaUtil.AUDIO_AAC); + BlobProvider.BlobBuilder blobBuilder = AppDependencies.getBlobs() + .forData(new ParcelFileDescriptor.AutoCloseInputStream(fds[0]), 0) + .withMimeType(MediaUtil.AUDIO_AAC); recordingUri = blobBuilder.buildUriForDraftAttachment(); recordingUriFuture = blobBuilder.createForDraftAttachmentAsync(context); diff --git a/app/src/main/java/org/thoughtcrime/securesms/avatar/AvatarRenderer.kt b/app/src/main/java/org/thoughtcrime/securesms/avatar/AvatarRenderer.kt index 996e4624e1..7f8347c178 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/avatar/AvatarRenderer.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/avatar/AvatarRenderer.kt @@ -11,9 +11,9 @@ import androidx.appcompat.content.res.AppCompatResources import com.airbnb.lottie.SimpleColorFilter import org.signal.core.models.media.Media import org.signal.core.util.concurrent.SignalExecutors +import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.mms.PartAuthority import org.thoughtcrime.securesms.profiles.AvatarHelper -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.util.MediaUtil import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream @@ -80,7 +80,7 @@ object AvatarRenderer { private fun renderPhoto(context: Context, avatar: Avatar.Photo, onAvatarRendered: (Media) -> Unit) { SignalExecutors.BOUNDED.execute { - val blob = BlobProvider.getInstance() + val blob = AppDependencies.blobs .forData(AvatarPickerStorage.read(context, PartAuthority.getAvatarPickerFilename(avatar.uri)), avatar.size) .createForSingleSessionOnDisk(context) @@ -124,7 +124,7 @@ object AvatarRenderer { val bytes = outStream.toByteArray() val inStream = ByteArrayInputStream(bytes) - val uri = BlobProvider.getInstance().forData(inStream, bytes.size.toLong()).createForSingleSessionOnDisk(context) + val uri = AppDependencies.blobs.forData(inStream, bytes.size.toLong()).createForSingleSessionOnDisk(context) onAvatarRendered(createMedia(uri, bytes.size.toLong())) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/avatar/photo/PhotoEditorFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/avatar/photo/PhotoEditorFragment.kt index 796c988664..25efa066f7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/avatar/photo/PhotoEditorFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/avatar/photo/PhotoEditorFragment.kt @@ -11,7 +11,7 @@ import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.avatar.AvatarBundler import org.thoughtcrime.securesms.avatar.AvatarPickerStorage import org.thoughtcrime.securesms.database.SignalDatabase -import org.thoughtcrime.securesms.providers.BlobProvider +import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.scribbles.ImageEditorFragment class PhotoEditorFragment : Fragment(R.layout.avatar_photo_editor_fragment), ImageEditorFragment.Controller { @@ -39,15 +39,15 @@ class PhotoEditorFragment : Fragment(R.layout.avatar_photo_editor_fragment), Ima SignalExecutors.BOUNDED.execute { val editedImageUri = imageEditorFragment.renderToSingleUseBlob() - val size = BlobProvider.getFileSize(editedImageUri) ?: 0 - val inputStream = BlobProvider.getInstance().getStream(applicationContext, editedImageUri) + val size = AppDependencies.blobs.getFileSize(editedImageUri) ?: 0 + val inputStream = AppDependencies.blobs.getStream(applicationContext, editedImageUri) val onDiskUri = AvatarPickerStorage.save(applicationContext, inputStream) val photo = AvatarBundler.extractPhoto(args.photoAvatar) val database = SignalDatabase.avatarPicker val newPhoto = photo.copy(uri = onDiskUri, size = size) database.update(newPhoto) - BlobProvider.getInstance().delete(requireContext(), photo.uri) + AppDependencies.blobs.delete(requireContext(), photo.uri) ThreadUtil.runOnMain { setFragmentResult(REQUEST_KEY_EDIT, AvatarBundler.bundlePhoto(newPhoto)) diff --git a/app/src/main/java/org/thoughtcrime/securesms/avatar/picker/AvatarPickerRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/avatar/picker/AvatarPickerRepository.kt index fd40da051f..755d256f9d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/avatar/picker/AvatarPickerRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/avatar/picker/AvatarPickerRepository.kt @@ -16,9 +16,9 @@ import org.thoughtcrime.securesms.avatar.AvatarRenderer import org.thoughtcrime.securesms.avatar.Avatars import org.thoughtcrime.securesms.conversation.colors.AvatarColor import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.groups.GroupId import org.thoughtcrime.securesms.profiles.AvatarHelper -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.util.NameUtil import org.whispersystems.signalservice.api.util.StreamDetails @@ -36,7 +36,7 @@ class AvatarPickerRepository(context: Context) { try { val bytes = StreamUtil.readFully(details.stream) Avatar.Photo( - BlobProvider.getInstance().forData(bytes).createForSingleSessionInMemory(), + AppDependencies.blobs.forData(bytes).createForSingleSessionInMemory(), details.length, Avatar.DatabaseId.DoNotPersist ) @@ -56,7 +56,7 @@ class AvatarPickerRepository(context: Context) { try { val bytes = AvatarHelper.getAvatarBytes(applicationContext, recipient.id) Avatar.Photo( - BlobProvider.getInstance().forData(bytes).createForSingleSessionInMemory(), + AppDependencies.blobs.forData(bytes).createForSingleSessionInMemory(), AvatarHelper.getAvatarLength(applicationContext, recipient.id), Avatar.DatabaseId.DoNotPersist ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/BackupPassphrase.java b/app/src/main/java/org/thoughtcrime/securesms/backup/BackupPassphrase.java index f3effaf30c..af1d52a181 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/BackupPassphrase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/BackupPassphrase.java @@ -6,7 +6,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import org.signal.core.util.logging.Log; -import org.thoughtcrime.securesms.crypto.KeyStoreHelper; +import org.signal.core.util.crypto.KeyStoreHelper; import org.thoughtcrime.securesms.util.TextSecurePreferences; /** diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java index 45e5f01781..a5198775c7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java @@ -23,9 +23,9 @@ import org.signal.core.models.database.AttachmentId; import org.thoughtcrime.securesms.backup.proto.KeyValue; import org.thoughtcrime.securesms.backup.proto.SharedPreference; import org.thoughtcrime.securesms.backup.proto.SqlStatement; -import org.thoughtcrime.securesms.crypto.AttachmentSecret; -import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream; -import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream; +import org.signal.core.util.crypto.AttachmentSecret; +import org.signal.core.util.crypto.ClassicDecryptingPartInputStream; +import org.signal.core.util.crypto.ModernDecryptingPartInputStream; import org.thoughtcrime.securesms.database.AttachmentTable; import org.thoughtcrime.securesms.database.BackupMediaSnapshotTable; import org.thoughtcrime.securesms.database.EmojiSearchTable; diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java index 1d925cd255..affb3a23fd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java @@ -24,8 +24,8 @@ import org.thoughtcrime.securesms.backup.proto.KeyValue; import org.thoughtcrime.securesms.backup.proto.SharedPreference; import org.thoughtcrime.securesms.backup.proto.SqlStatement; import org.thoughtcrime.securesms.backup.proto.Sticker; -import org.thoughtcrime.securesms.crypto.AttachmentSecret; -import org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream; +import org.signal.core.util.crypto.AttachmentSecret; +import org.signal.core.util.crypto.ModernEncryptingPartOutputStream; import org.thoughtcrime.securesms.database.AttachmentTable; import org.thoughtcrime.securesms.database.EmojiSearchTable; import org.thoughtcrime.securesms.database.KeyValueDatabase; diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt index e7b4c4101f..34ff4763a1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt @@ -47,6 +47,7 @@ import org.signal.core.util.bytes import org.signal.core.util.concurrent.LimitedWorker import org.signal.core.util.concurrent.SignalDispatchers import org.signal.core.util.concurrent.SignalExecutors +import org.signal.core.util.crypto.AttachmentSecretProvider import org.signal.core.util.decodeOrNull import org.signal.core.util.forceForeignKeyConstraintsEnabled import org.signal.core.util.fullWalCheckpoint @@ -93,7 +94,7 @@ import org.thoughtcrime.securesms.backup.v2.ui.BackupAlert import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity import org.thoughtcrime.securesms.components.settings.app.subscription.RecurringInAppPaymentRepository -import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider +import org.thoughtcrime.securesms.crypto.AppAttachmentSecretStore import org.thoughtcrime.securesms.crypto.DatabaseSecretProvider import org.thoughtcrime.securesms.database.AttachmentTable import org.thoughtcrime.securesms.database.BackupMediaSnapshotTable.ArchiveMediaItem @@ -140,7 +141,6 @@ import org.thoughtcrime.securesms.logsubmit.SubmitDebugLogRepository import org.thoughtcrime.securesms.net.SignalNetwork import org.thoughtcrime.securesms.notifications.NotificationChannels import org.thoughtcrime.securesms.notifications.NotificationIds -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.service.BackupMediaRestoreService @@ -717,7 +717,7 @@ object BackupRepository { SignalDatabase( context = context, databaseSecret = DatabaseSecretProvider.getOrCreateDatabaseSecret(context), - attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(), + attachmentSecret = AttachmentSecretProvider.getInstance(context, AppAttachmentSecretStore).getOrCreateAttachmentSecret(), name = "$baseName.db" ) } @@ -2235,7 +2235,7 @@ object BackupRepository { } Log.i(TAG, "[remoteRestore] Downloading backup") - val tempBackupFile = BlobProvider.getInstance().forNonAutoEncryptingSingleSessionOnDisk(AppDependencies.application) + val tempBackupFile = AppDependencies.blobs.forNonAutoEncryptingSingleSessionOnDisk(AppDependencies.application) when (val result = downloadBackupFile(tempBackupFile, progressListener)) { is NetworkResult.Success -> Log.i(TAG, "[remoteRestore] Download successful") else -> { @@ -2355,7 +2355,7 @@ object BackupRepository { } Log.i(TAG, "[restoreLinkAndSyncBackup] Downloading backup") - val tempBackupFile = BlobProvider.getInstance().forNonAutoEncryptingSingleSessionOnDisk(AppDependencies.application) + val tempBackupFile = AppDependencies.blobs.forNonAutoEncryptingSingleSessionOnDisk(AppDependencies.application) when (val result = AppDependencies.signalServiceMessageReceiver.retrieveLinkAndSyncBackup(response.cdn, response.key, tempBackupFile, progressListener)) { is NetworkResult.Success -> Log.i(TAG, "[restoreLinkAndSyncBackup] Download successful") else -> { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/account/export/ExportAccountDataRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/account/export/ExportAccountDataRepository.kt index 692f521b24..2ee48b6596 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/account/export/ExportAccountDataRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/account/export/ExportAccountDataRepository.kt @@ -7,8 +7,8 @@ import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.schedulers.Schedulers import org.signal.core.util.JsonUtils import org.signal.network.NetworkResult +import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.net.SignalNetwork -import org.thoughtcrime.securesms.providers.BlobProvider class ExportAccountDataRepository { @@ -44,7 +44,7 @@ class ExportAccountDataRepository { tree["text"].asText() } - val uri = BlobProvider.getInstance() + val uri = AppDependencies.blobs .forData(dataStr.encodeToByteArray()) .withMimeType(mimeType) .withFileName(fileName) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundViewModel.kt index 94e2ed99e3..9011d04009 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundViewModel.kt @@ -57,7 +57,6 @@ import org.thoughtcrime.securesms.jobs.LocalBackupJob import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.keyvalue.protos.LocalBackupCreationProgress import org.thoughtcrime.securesms.net.SignalNetwork -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.recipients.Recipient import java.io.FileOutputStream import java.io.IOException @@ -136,7 +135,7 @@ class InternalBackupPlaygroundViewModel : ViewModel() { fun validateBackup() { _state.value = _state.value.copy(statusMessage = "Exporting to a temporary file...") - val tempFile = BlobProvider.getInstance().forNonAutoEncryptingSingleSessionOnDisk(AppDependencies.application) + val tempFile = AppDependencies.blobs.forNonAutoEncryptingSingleSessionOnDisk(AppDependencies.application) disposables += Single .fromCallable { @@ -207,7 +206,7 @@ class InternalBackupPlaygroundViewModel : ViewModel() { SignalExecutors.BOUNDED_IO.execute { Log.d(TAG, "Downloading file...") - val tempBackupFile = BlobProvider.getInstance().forNonAutoEncryptingSingleSessionOnDisk(AppDependencies.application) + val tempBackupFile = AppDependencies.blobs.forNonAutoEncryptingSingleSessionOnDisk(AppDependencies.application) when (val result = BackupRepository.downloadBackupFile(tempBackupFile)) { is NetworkResult.Success -> Log.i(TAG, "Download successful") diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/receipts/ReceiptImageRenderer.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/receipts/ReceiptImageRenderer.kt index c06e2e6c09..6dd5cc90db 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/receipts/ReceiptImageRenderer.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/receipts/ReceiptImageRenderer.kt @@ -24,8 +24,8 @@ import kotlinx.coroutines.withContext import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.database.model.InAppPaymentReceiptRecord +import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.payments.FiatMoneyUtil -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.util.DateUtils import java.io.ByteArrayOutputStream import java.util.Locale @@ -75,7 +75,7 @@ object ReceiptImageRenderer { val bitmap = view.drawToBitmap() bitmap.compress(Bitmap.CompressFormat.PNG, 0, outputStream) - BlobProvider.getInstance() + AppDependencies.blobs .forData(outputStream.toByteArray()) .withMimeType("image/png") .withFileName("Signal-Donation-Receipt.png") diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/usernamelinks/main/UsernameLinkSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/usernamelinks/main/UsernameLinkSettingsFragment.kt index 2864a5c3d9..0ccb71a7c7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/usernamelinks/main/UsernameLinkSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/usernamelinks/main/UsernameLinkSettingsFragment.kt @@ -75,7 +75,7 @@ import org.thoughtcrime.securesms.components.settings.app.usernamelinks.QrCodeDa import org.thoughtcrime.securesms.components.settings.app.usernamelinks.QrCodeState import org.thoughtcrime.securesms.components.settings.app.usernamelinks.UsernameQrCodeColorScheme import org.thoughtcrime.securesms.components.settings.app.usernamelinks.main.UsernameLinkSettingsState.ActiveTab -import org.thoughtcrime.securesms.providers.BlobProvider +import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.util.CommunicationActions import java.io.ByteArrayOutputStream import java.util.UUID @@ -405,7 +405,7 @@ private fun shareQrBadge(activity: Activity, badge: Bitmap?) { badge.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream) byteArrayOutputStream.flush() val bytes = byteArrayOutputStream.toByteArray() - val shareUri = BlobProvider.getInstance() + val shareUri = AppDependencies.blobs .forData(bytes) .withMimeType("image/png") .withFileName("SignalUsernameQr.png") diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/InternalConversationSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/InternalConversationSettingsFragment.kt index 371088db49..d597b4a735 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/InternalConversationSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/InternalConversationSettingsFragment.kt @@ -39,7 +39,6 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.mms.IncomingMessage import org.thoughtcrime.securesms.mms.OutgoingMessage import org.thoughtcrime.securesms.profiles.AvatarHelper -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientForeverObserver import org.thoughtcrime.securesms.recipients.RecipientId @@ -88,7 +87,7 @@ class InternalConversationSettingsFragment : ComposeFragment(), InternalConversa ) val stream = BitmapUtil.toCompressedJpeg(bitmap) val bytes = stream.readBytes() - val uri = BlobProvider.getInstance().forData(bytes).createForSingleSessionOnDisk(requireContext()) + val uri = AppDependencies.blobs.forData(bytes).createForSingleSessionOnDisk(requireContext()) return UriAttachment( uri = uri, contentType = MediaUtil.IMAGE_JPEG, diff --git a/app/src/main/java/org/thoughtcrime/securesms/contactshare/SharedContactRepository.java b/app/src/main/java/org/thoughtcrime/securesms/contactshare/SharedContactRepository.java index 5090c0353e..91cd03bfc9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contactshare/SharedContactRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/contactshare/SharedContactRepository.java @@ -18,8 +18,8 @@ import org.thoughtcrime.securesms.contactshare.Contact.Email; import org.thoughtcrime.securesms.contactshare.Contact.Name; import org.thoughtcrime.securesms.contactshare.Contact.Phone; import org.thoughtcrime.securesms.contactshare.Contact.PostalAddress; +import org.thoughtcrime.securesms.dependencies.AppDependencies; import org.thoughtcrime.securesms.mms.PartAuthority; -import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.util.SignalE164Util; @@ -98,8 +98,8 @@ public class SharedContactRepository { Log.w(TAG, "Failed to parse the vcard.", e); } - if (BlobProvider.AUTHORITY.equals(uri.getAuthority())) { - BlobProvider.getInstance().delete(context, uri); + if (AppDependencies.getBlobs().getAuthority().equals(uri.getAuthority())) { + AppDependencies.getBlobs().delete(context, uri); } return contact; diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/drafts/DraftBlobs.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/drafts/DraftBlobs.kt new file mode 100644 index 0000000000..b935c968e3 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/drafts/DraftBlobs.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.conversation.drafts + +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.components.voice.VoiceNoteDraft +import org.thoughtcrime.securesms.database.SignalDatabase.Companion.drafts +import org.thoughtcrime.securesms.dependencies.AppDependencies +import java.io.File + +object DraftBlobs { + + private val TAG = Log.tag(DraftBlobs::class) + + fun deleteOrphanedDraftFiles(directory: File) { + val files = directory.listFiles() + + if (files == null || files.size == 0) { + Log.d(TAG, "No attachment drafts exist. Skipping.") + return + } + + val draftDatabase = drafts + val voiceNoteDrafts = draftDatabase.getAllVoiceNoteDrafts() + + val draftFileNames = voiceNoteDrafts + .asSequence() + .map { VoiceNoteDraft.fromDraft(it) } + .map(VoiceNoteDraft::uri) + .mapNotNull { AppDependencies.blobs.buildFileName(it) } + .toList() + + for (file in files) { + if (!draftFileNames.contains(file.getName())) { + if (file.delete()) { + Log.d(TAG, "Deleted orphaned attachment draft: " + file.getName()) + } else { + Log.d(TAG, "Failed to delete orphaned attachment draft: " + file.getName()) + } + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/drafts/DraftRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/drafts/DraftRepository.kt index 64dfd4487f..7ac7524064 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/drafts/DraftRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/drafts/DraftRepository.kt @@ -40,7 +40,6 @@ import org.thoughtcrime.securesms.mms.QuoteId import org.thoughtcrime.securesms.mms.Slide import org.thoughtcrime.securesms.mms.SlideFactory import org.thoughtcrime.securesms.mms.StickerSlide -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.util.MediaUtil import org.thoughtcrime.securesms.util.concurrent.SerialMonoLifoExecutor @@ -161,7 +160,7 @@ class DraftRepository( fun deleteVoiceNoteDraftData(draft: DraftTable.Draft?) { if (draft != null) { SignalExecutors.BOUNDED.execute { - BlobProvider.getInstance().delete(context, Uri.parse(draft.value).buildUpon().clearQuery().build()) + AppDependencies.blobs.delete(context, Uri.parse(draft.value).buildUpon().clearQuery().build()) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt index c1911597cf..6ce0d7a533 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt @@ -326,7 +326,6 @@ import org.thoughtcrime.securesms.polls.PollOption import org.thoughtcrime.securesms.polls.PollRecord import org.thoughtcrime.securesms.profiles.manage.EditProfileActivity import org.thoughtcrime.securesms.profiles.spoofing.ReviewCardDialogFragment -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.ratelimit.RecaptchaProofBottomSheetFragment import org.thoughtcrime.securesms.ratelimit.RecaptchaRequiredEvent import org.thoughtcrime.securesms.reactions.ReactionsBottomSheetDialogFragment @@ -1013,7 +1012,7 @@ class ConversationFragment : override fun onGifSelectSuccess(blobUri: Uri, width: Int, height: Int) { setMedia( uri = blobUri, - mediaType = SlideFactory.MediaType.from(BlobProvider.getMimeType(blobUri))!!, + mediaType = SlideFactory.MediaType.from(AppDependencies.blobs.getMimeType(blobUri))!!, width = width, height = height, videoGif = true diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationRepository.kt index 79e78857ca..f237fcc862 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationRepository.kt @@ -86,7 +86,6 @@ import org.thoughtcrime.securesms.mms.Slide import org.thoughtcrime.securesms.mms.SlideDeck import org.thoughtcrime.securesms.polls.Poll import org.thoughtcrime.securesms.profiles.spoofing.ReviewRecipient -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.recipients.RecipientUtil @@ -681,7 +680,7 @@ class ConversationRepository( val thumbnailUri = thumbnailSlide.uri ?: return@fromCallable null val inputStream = PartAuthority.getAttachmentStream(applicationContext, thumbnailUri) - val tempUri = BlobProvider.getInstance().forData(inputStream, thumbnailSlide.fileSize) + val tempUri = AppDependencies.blobs.forData(inputStream, thumbnailSlide.fileSize) .withMimeType(thumbnailSlide.contentType) .createForSingleSessionOnDisk(applicationContext) @@ -826,9 +825,9 @@ class ConversationRepository( SignalExecutors.BOUNDED_IO.execute { slides .mapNotNull(Slide::getUri) - .filter(BlobProvider::isAuthority) + .filter { AppDependencies.blobs.isAuthority(it) } .forEach { - BlobProvider.getInstance().delete(applicationContext, it) + AppDependencies.blobs.delete(applicationContext, it) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/AppAttachmentSecretStore.kt b/app/src/main/java/org/thoughtcrime/securesms/crypto/AppAttachmentSecretStore.kt new file mode 100644 index 0000000000..a76852ba3e --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/AppAttachmentSecretStore.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.crypto + +import android.content.Context +import org.signal.core.util.crypto.AttachmentSecretStore +import org.thoughtcrime.securesms.util.TextSecurePreferences + +object AppAttachmentSecretStore : AttachmentSecretStore { + override fun getAttachmentUnencryptedSecret(context: Context): String? { + return TextSecurePreferences.getAttachmentUnencryptedSecret(context) + } + + override fun getAttachmentEncryptedSecret(context: Context): String? { + return TextSecurePreferences.getAttachmentEncryptedSecret(context) + } + + override fun setAttachmentEncryptedSecret(context: Context, secret: String) { + TextSecurePreferences.setAttachmentEncryptedSecret(context, secret) + } + + override fun setAttachmentUnencryptedSecret(context: Context, secret: String?) { + TextSecurePreferences.setAttachmentUnencryptedSecret(context, secret) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/DatabaseSecretProvider.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/DatabaseSecretProvider.java index bd64474461..86d6dcfb24 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/DatabaseSecretProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/DatabaseSecretProvider.java @@ -5,6 +5,7 @@ import android.content.Context; import androidx.annotation.NonNull; +import org.signal.core.util.crypto.KeyStoreHelper; import org.thoughtcrime.securesms.util.TextSecurePreferences; import java.io.IOException; 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 250f553f60..4fbaa59cee 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt @@ -40,6 +40,10 @@ import org.signal.core.util.UuidUtil import org.signal.core.util.bitmaps.BitmapDecodingException import org.signal.core.util.copyTo import org.signal.core.util.count +import org.signal.core.util.crypto.AttachmentSecret +import org.signal.core.util.crypto.ClassicDecryptingPartInputStream +import org.signal.core.util.crypto.ModernDecryptingPartInputStream +import org.signal.core.util.crypto.ModernEncryptingPartOutputStream import org.signal.core.util.delete import org.signal.core.util.deleteAll import org.signal.core.util.drain @@ -79,10 +83,6 @@ import org.thoughtcrime.securesms.attachments.WallpaperAttachment 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.crypto.AttachmentSecret -import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream -import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream -import org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream import org.thoughtcrime.securesms.database.AttachmentTable.Companion.DATA_FILE import org.thoughtcrime.securesms.database.AttachmentTable.Companion.DATA_HASH_END import org.thoughtcrime.securesms.database.AttachmentTable.Companion.PREUPLOAD_MESSAGE_ID diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.kt index 5590971289..37da8d75d7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.kt @@ -6,9 +6,9 @@ import androidx.annotation.VisibleForTesting import androidx.sqlite.db.SupportSQLiteDatabase import net.zetetic.database.sqlcipher.SQLiteOpenHelper import org.signal.core.util.SqlUtil +import org.signal.core.util.crypto.AttachmentSecret import org.signal.core.util.logging.Log import org.signal.core.util.withinTransaction -import org.thoughtcrime.securesms.crypto.AttachmentSecret import org.thoughtcrime.securesms.crypto.DatabaseSecret import org.thoughtcrime.securesms.database.helpers.SignalDatabaseMigrations import org.thoughtcrime.securesms.database.model.AvatarPickerDatabase diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/StickerTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/StickerTable.kt index eb31e7133c..58f4c42187 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/StickerTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/StickerTable.kt @@ -5,6 +5,9 @@ import android.database.Cursor import androidx.core.content.contentValuesOf import org.greenrobot.eventbus.EventBus import org.signal.core.util.StreamUtil +import org.signal.core.util.crypto.AttachmentSecret +import org.signal.core.util.crypto.ModernDecryptingPartInputStream +import org.signal.core.util.crypto.ModernEncryptingPartOutputStream import org.signal.core.util.delete import org.signal.core.util.exists import org.signal.core.util.forEach @@ -25,9 +28,6 @@ import org.signal.core.util.toInt import org.signal.core.util.update import org.signal.core.util.withinTransaction import org.signal.glide.decryptableuri.DecryptableUri -import org.thoughtcrime.securesms.crypto.AttachmentSecret -import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream -import org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream import org.thoughtcrime.securesms.database.model.IncomingSticker import org.thoughtcrime.securesms.database.model.StickerPackId import org.thoughtcrime.securesms.database.model.StickerPackRecord diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppDependencies.kt b/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppDependencies.kt index d741e583e8..bccf6f556f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppDependencies.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/AppDependencies.kt @@ -11,6 +11,7 @@ import org.signal.core.util.CoreUtilDependencies import org.signal.core.util.billing.BillingApi import org.signal.core.util.concurrent.DeadlockDetector import org.signal.core.util.concurrent.LatestValueObservable +import org.signal.core.util.contentproviders.BlobProvider import org.signal.core.util.orNull import org.signal.core.util.resettableLazy import org.signal.glide.SignalGlideDependencies @@ -261,6 +262,11 @@ object AppDependencies { provider.provideBillingApi() } + @JvmStatic + val blobs: BlobProvider by lazy { + provider.provideBlobs() + } + private val _webSocketObserver: BehaviorSubject = BehaviorSubject.create() /** @@ -511,5 +517,6 @@ object AppDependencies { fun provideDonationsApi(authWebSocket: SignalWebSocket.AuthenticatedWebSocket, unauthWebSocket: SignalWebSocket.UnauthenticatedWebSocket): DonationsApi fun provideSvrBApi(libSignalNetwork: Network): SvrBApi fun provideKeyTransparencyApi(unauthWebSocket: SignalWebSocket.UnauthenticatedWebSocket): KeyTransparencyApi + fun provideBlobs(): BlobProvider } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java index 4c96d8a4ec..2787792224 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java @@ -21,6 +21,7 @@ import org.signal.core.util.UptimeSleepTimer; import org.signal.core.util.billing.BillingApi; import org.signal.core.util.concurrent.DeadlockDetector; import org.signal.core.util.concurrent.SignalExecutors; +import org.signal.core.util.contentproviders.BlobProvider; import org.signal.libsignal.net.Network; import org.signal.libsignal.protocol.SignalProtocolAddress; import org.signal.libsignal.zkgroup.GenericServerPublicParams; @@ -46,6 +47,7 @@ import org.signal.network.service.MessageService; import org.thoughtcrime.securesms.BuildConfig; import org.thoughtcrime.securesms.components.TypingStatusRepository; import org.thoughtcrime.securesms.components.TypingStatusSender; +import org.thoughtcrime.securesms.crypto.AppAttachmentSecretStore; import org.thoughtcrime.securesms.crypto.ReentrantSessionLock; import org.thoughtcrime.securesms.crypto.storage.SignalBaseIdentityKeyStore; import org.thoughtcrime.securesms.crypto.storage.SignalIdentityKeyStore; @@ -635,6 +637,10 @@ public class ApplicationDependencyProvider implements AppDependencies.Provider { return new KeyTransparencyApi(unauthWebSocket); } + @Override public @NotNull BlobProvider provideBlobs() { + return new BlobProvider(context, AppAttachmentSecretStore.INSTANCE); + } + @VisibleForTesting static class DynamicCredentialsProvider implements CredentialsProvider { diff --git a/app/src/main/java/org/thoughtcrime/securesms/devicetransfer/newdevice/NewDeviceServerTask.java b/app/src/main/java/org/thoughtcrime/securesms/devicetransfer/newdevice/NewDeviceServerTask.java index af417863b2..ab8e8f6d58 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/devicetransfer/newdevice/NewDeviceServerTask.java +++ b/app/src/main/java/org/thoughtcrime/securesms/devicetransfer/newdevice/NewDeviceServerTask.java @@ -9,6 +9,7 @@ import net.zetetic.database.sqlcipher.SQLiteDatabase; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; +import org.signal.core.util.crypto.AttachmentSecretProvider; import org.signal.core.util.logging.Log; import org.signal.devicetransfer.NewDeviceRestoreStatus; import org.signal.devicetransfer.ServerTask; @@ -16,12 +17,11 @@ import org.thoughtcrime.securesms.AppInitialization; import org.thoughtcrime.securesms.backup.BackupEvent; import org.thoughtcrime.securesms.backup.BackupPassphrase; import org.thoughtcrime.securesms.backup.FullBackupImporter; -import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider; +import org.thoughtcrime.securesms.crypto.AppAttachmentSecretStore; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.jobmanager.impl.DataRestoreConstraint; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.notifications.NotificationChannels; -import org.thoughtcrime.securesms.util.RemoteConfig; import java.io.IOException; import java.io.InputStream; @@ -52,7 +52,7 @@ public final class NewDeviceServerTask implements ServerTask { BackupPassphrase.set(context, passphrase); FullBackupImporter.importFile(context, - AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(), + AttachmentSecretProvider.getInstance(context, AppAttachmentSecretStore.INSTANCE).getOrCreateAttachmentSecret(), database, inputStream, passphrase, diff --git a/app/src/main/java/org/thoughtcrime/securesms/devicetransfer/olddevice/OldDeviceClientTask.java b/app/src/main/java/org/thoughtcrime/securesms/devicetransfer/olddevice/OldDeviceClientTask.java index 36e5f1c4f2..8d4ae53716 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/devicetransfer/olddevice/OldDeviceClientTask.java +++ b/app/src/main/java/org/thoughtcrime/securesms/devicetransfer/olddevice/OldDeviceClientTask.java @@ -7,15 +7,15 @@ import androidx.annotation.NonNull; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; +import org.signal.core.util.crypto.AttachmentSecretProvider; import org.signal.core.util.logging.Log; import org.signal.devicetransfer.ClientTask; import org.thoughtcrime.securesms.backup.BackupEvent; import org.thoughtcrime.securesms.backup.FullBackupExporter; -import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider; +import org.thoughtcrime.securesms.crypto.AppAttachmentSecretStore; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.net.DeviceTransferBlockingInterceptor; -import org.thoughtcrime.securesms.util.RemoteConfig; import java.io.IOException; import java.io.OutputStream; @@ -43,7 +43,7 @@ final class OldDeviceClientTask implements ClientTask { String passphrase = SignalStore.account().getAccountEntropyPool().getValue(); FullBackupExporter.transfer(context, - AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(), + AttachmentSecretProvider.getInstance(context, AppAttachmentSecretStore.INSTANCE).getOrCreateAttachmentSecret(), SignalDatabase.getBackupDatabase(), outputStream, passphrase); diff --git a/app/src/main/java/org/thoughtcrime/securesms/emoji/EmojiFiles.kt b/app/src/main/java/org/thoughtcrime/securesms/emoji/EmojiFiles.kt index 0838206e25..d0d40a60bd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/emoji/EmojiFiles.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/emoji/EmojiFiles.kt @@ -12,10 +12,11 @@ import okio.HashingSink import okio.blackholeSink import okio.buffer import okio.source +import org.signal.core.util.crypto.AttachmentSecretProvider +import org.signal.core.util.crypto.ModernDecryptingPartInputStream +import org.signal.core.util.crypto.ModernEncryptingPartOutputStream import org.signal.core.util.logging.Log -import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider -import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream -import org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream +import org.thoughtcrime.securesms.crypto.AppAttachmentSecretStore import org.thoughtcrime.securesms.mms.PartAuthority import java.io.File import java.io.IOException @@ -60,12 +61,12 @@ private fun Context.getJumboFile(versionUuid: UUID): File = File(File(getEmojiDi private fun getFilesUri(name: String, format: String): Uri = PartAuthority.getEmojiUri(name) private fun getOutputStream(context: Context, outputFile: File): OutputStream { - val attachmentSecret = AttachmentSecretProvider.getInstance(context).orCreateAttachmentSecret + val attachmentSecret = AttachmentSecretProvider.getInstance(context, AppAttachmentSecretStore).orCreateAttachmentSecret return ModernEncryptingPartOutputStream.createFor(attachmentSecret, outputFile, true).second } private fun getInputStream(context: Context, inputFile: File): InputStream { - val attachmentSecret = AttachmentSecretProvider.getInstance(context).orCreateAttachmentSecret + val attachmentSecret = AttachmentSecretProvider.getInstance(context, AppAttachmentSecretStore).orCreateAttachmentSecret return ModernDecryptingPartInputStream.createFor(attachmentSecret, inputFile, 0) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4Repository.java b/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4Repository.java index 40ae4fd487..9c747aa81b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4Repository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4Repository.java @@ -11,7 +11,6 @@ import org.thoughtcrime.securesms.dependencies.AppDependencies; import org.thoughtcrime.securesms.giph.model.GiphyImage; import org.thoughtcrime.securesms.net.ContentProxySelector; import org.thoughtcrime.securesms.net.StandardUserAgentInterceptor; -import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess; import org.thoughtcrime.securesms.util.MediaUtil; @@ -66,10 +65,10 @@ final class GiphyMp4Repository { try (Response response = client.newCall(request).execute()) { if (response.code() >= 200 && response.code() < 300) { - return BlobProvider.getInstance() - .forData(response.body().byteStream(), response.body().contentLength()) - .withMimeType(mime) - .createForSingleSessionOnDisk(AppDependencies.getApplication()); + return AppDependencies.getBlobs() + .forData(response.body().byteStream(), response.body().contentLength()) + .withMimeType(mime) + .createForSingleSessionOnDisk(AppDependencies.getApplication()); } else { throw new IOException("Unexpected response code: " + response.code()); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivity.java b/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivity.java index 9776cba487..b171647fcc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivity.java @@ -12,28 +12,27 @@ import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; +import org.signal.core.models.media.Media; import org.thoughtcrime.securesms.PassphraseRequiredActivity; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.conversation.MessageSendType; +import org.thoughtcrime.securesms.dependencies.AppDependencies; import org.thoughtcrime.securesms.giph.mp4.GiphyMp4Fragment; import org.thoughtcrime.securesms.giph.mp4.GiphyMp4SaveResult; import org.thoughtcrime.securesms.giph.mp4.GiphyMp4ViewModel; import org.thoughtcrime.securesms.keyboard.emoji.KeyboardPageSearchView; -import org.signal.core.models.media.Media; import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionActivity; import org.thoughtcrime.securesms.mms.SlideFactory; -import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme; import org.thoughtcrime.securesms.util.DynamicTheme; -import org.thoughtcrime.securesms.util.RemoteConfig; import org.thoughtcrime.securesms.util.MediaUtil; +import org.thoughtcrime.securesms.util.RemoteConfig; import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.views.SimpleProgressDialog; import java.util.Collections; import java.util.Objects; -import java.util.Optional; public class GiphyActivity extends PassphraseRequiredActivity implements KeyboardPageSearchView.Callbacks { @@ -120,7 +119,7 @@ public class GiphyActivity extends PassphraseRequiredActivity implements Keyboar } private void handleGiphyMp4SuccessfulResult(@NonNull GiphyMp4SaveResult.Success success) { - SlideFactory.MediaType mediaType = Objects.requireNonNull(SlideFactory.MediaType.from(BlobProvider.getMimeType(success.getBlobUri()))); + SlideFactory.MediaType mediaType = Objects.requireNonNull(SlideFactory.MediaType.from(AppDependencies.getBlobs().getMimeType(success.getBlobUri()))); String mimeType = MediaUtil.getMimeType(this, success.getBlobUri()); if (mimeType == null) { mimeType = mediaType.toFallbackMimeType(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/glide/DecryptableStreamLocalUriFetcher.java b/app/src/main/java/org/thoughtcrime/securesms/glide/DecryptableStreamLocalUriFetcher.java index f662818d49..d718f8c1e3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/glide/DecryptableStreamLocalUriFetcher.java +++ b/app/src/main/java/org/thoughtcrime/securesms/glide/DecryptableStreamLocalUriFetcher.java @@ -17,7 +17,7 @@ import org.signal.core.util.logging.Log; import org.signal.glide.common.io.GlideStreamConfig; import org.signal.core.models.database.AttachmentId; import org.thoughtcrime.securesms.mms.PartAuthority; -import org.thoughtcrime.securesms.providers.BlobProvider; +import org.signal.core.util.contentproviders.BlobProvider; import org.signal.core.util.bitmaps.BitmapDecodingException; import org.signal.core.util.bitmaps.BitmapUtil; import org.thoughtcrime.securesms.util.MediaUtil; 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 32fe4a4f54..bb84f54e88 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/glide/SignalGlideComponents.java +++ b/app/src/main/java/org/thoughtcrime/securesms/glide/SignalGlideComponents.java @@ -18,23 +18,26 @@ import com.bumptech.glide.load.resource.gif.StreamGifDecoder; import org.signal.apng.ApngDecoder; import org.signal.blurhash.BlurHash; -import org.signal.glide.load.resource.apng.decode.APNGDecoder; +import org.signal.core.util.crypto.AttachmentSecret; +import org.signal.core.util.crypto.AttachmentSecretProvider; import org.signal.glide.blurhash.BlurHashModelLoader; import org.signal.glide.blurhash.BlurHashResourceDecoder; import org.signal.glide.common.io.InputStreamFactory; +import org.signal.glide.decryptableuri.DecryptableUri; +import org.signal.glide.decryptableuri.DecryptableUriStreamLoader; +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.contacts.avatars.ContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoLoader; -import org.thoughtcrime.securesms.crypto.AttachmentSecret; -import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider; +import org.thoughtcrime.securesms.crypto.AppAttachmentSecretStore; import org.thoughtcrime.securesms.giph.model.ChunkedImageUrl; import org.thoughtcrime.securesms.glide.cache.ApngDrawableTranscoder; import org.thoughtcrime.securesms.glide.cache.ApngFrameDrawableTranscoder; import org.thoughtcrime.securesms.glide.cache.ApngInputStreamFactoryResourceDecoder; -import org.thoughtcrime.securesms.glide.cache.EncryptedApngCacheDecoder; import org.thoughtcrime.securesms.glide.cache.ByteBufferApngDecoder; +import org.thoughtcrime.securesms.glide.cache.EncryptedApngCacheDecoder; import org.thoughtcrime.securesms.glide.cache.EncryptedApngCacheEncoder; import org.thoughtcrime.securesms.glide.cache.EncryptedApngResourceEncoder; import org.thoughtcrime.securesms.glide.cache.EncryptedBitmapResourceEncoder; @@ -47,15 +50,13 @@ import org.thoughtcrime.securesms.glide.cache.StreamBitmapDecoder; import org.thoughtcrime.securesms.glide.cache.StreamFactoryApngDecoder; import org.thoughtcrime.securesms.glide.cache.StreamFactoryGifDecoder; import org.thoughtcrime.securesms.glide.cache.WebpSanDecoder; -import org.signal.glide.decryptableuri.DecryptableUri; -import org.signal.glide.decryptableuri.DecryptableUriStreamLoader; import org.thoughtcrime.securesms.mms.RegisterGlideComponents; import org.thoughtcrime.securesms.mms.SignalGlideModule; -import org.thoughtcrime.securesms.util.RemoteConfig; import org.thoughtcrime.securesms.stickers.StickerRemoteUri; import org.thoughtcrime.securesms.stickers.StickerRemoteUriLoader; import org.thoughtcrime.securesms.stories.StoryTextPostModel; import org.thoughtcrime.securesms.util.ConversationShortcutPhoto; +import org.thoughtcrime.securesms.util.RemoteConfig; import java.io.File; import java.io.InputStream; @@ -69,7 +70,7 @@ public class SignalGlideComponents implements RegisterGlideComponents { @Override public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) { - AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(); + AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context, AppAttachmentSecretStore.INSTANCE).getOrCreateAttachmentSecret(); byte[] secret = attachmentSecret.getModernKey(); registry.prepend(File.class, File.class, UnitModelLoader.Factory.getInstance()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJob.java index 73cb6515d2..a80decda50 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJob.java @@ -10,16 +10,22 @@ import androidx.annotation.WorkerThread; import androidx.media3.common.MimeTypes; import org.greenrobot.eventbus.EventBus; +import org.signal.core.models.database.AttachmentId; +import org.signal.core.models.media.TransformProperties; +import org.signal.core.util.MemoryFileDescriptor.MemoryFileException; +import org.signal.core.util.bitmaps.BitmapDecodingException; +import org.signal.core.util.crypto.AttachmentSecret; +import org.signal.core.util.crypto.AttachmentSecretProvider; +import org.signal.core.util.crypto.ModernDecryptingPartInputStream; +import org.signal.core.util.crypto.ModernEncryptingPartOutputStream; import org.signal.core.util.logging.Log; +import org.signal.glide.decryptableuri.DecryptableUri; +import org.signal.mediasend.MediaConstraints; +import org.signal.mediasend.SentMediaQuality; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.attachments.Attachment; -import org.signal.core.models.database.AttachmentId; import org.thoughtcrime.securesms.attachments.DatabaseAttachment; -import org.thoughtcrime.securesms.crypto.AttachmentSecret; -import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider; -import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream; -import org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream; -import org.signal.core.models.media.TransformProperties; +import org.thoughtcrime.securesms.crypto.AppAttachmentSecretStore; import org.thoughtcrime.securesms.database.AttachmentTable; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.events.PartProgressEvent; @@ -27,18 +33,13 @@ import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.JsonJobData; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; import org.thoughtcrime.securesms.jobmanager.persistence.JobSpec; -import org.signal.glide.decryptableuri.DecryptableUri; -import org.signal.mediasend.MediaConstraints; import org.thoughtcrime.securesms.mms.MediaStream; import org.thoughtcrime.securesms.mms.MmsException; -import org.signal.mediasend.SentMediaQuality; import org.thoughtcrime.securesms.mms.PushMediaConstraints; import org.thoughtcrime.securesms.service.AttachmentProgressService; import org.thoughtcrime.securesms.transport.UndeliverableMessageException; -import org.signal.core.util.bitmaps.BitmapDecodingException; import org.thoughtcrime.securesms.util.ImageCompressionUtil; import org.thoughtcrime.securesms.util.MediaUtil; -import org.signal.core.util.MemoryFileDescriptor.MemoryFileException; import org.thoughtcrime.securesms.util.RemoteConfig; import org.thoughtcrime.securesms.video.StreamingTranscoder; import org.thoughtcrime.securesms.video.TranscoderOptions; @@ -263,7 +264,7 @@ public final class AttachmentCompressionJob extends BaseJob { if (transcoder.isTranscodeRequired()) { Log.i(TAG, "Compressing with streaming muxer"); - AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(); + AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context, AppAttachmentSecretStore.INSTANCE).getOrCreateAttachmentSecret(); File file = AttachmentTable.newDataFile(context); file.deleteOnExit(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt index d2ba478e0f..ad5b38942c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupMessagesJob.kt @@ -48,7 +48,6 @@ import org.thoughtcrime.securesms.logsubmit.SubmitDebugLogActivity import org.thoughtcrime.securesms.net.SignalNetwork import org.thoughtcrime.securesms.notifications.NotificationChannels import org.thoughtcrime.securesms.notifications.NotificationIds -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.storage.StorageSyncHelper import org.thoughtcrime.securesms.util.MediaUtil @@ -463,9 +462,9 @@ class BackupMessagesJob private constructor( } } - BlobProvider.getInstance().clearTemporaryBackupsDirectory(AppDependencies.application) + AppDependencies.blobs.clearTemporaryBackupsDirectory(AppDependencies.application) - val tempBackupFile = BlobProvider.getInstance().forTemporaryBackup(AppDependencies.application) + val tempBackupFile = AppDependencies.blobs.forTemporaryBackup(AppDependencies.application) val outputStream = FileOutputStream(tempBackupFile) val backupKey = SignalStore.backup.messageBackupKey diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/CreateReleaseChannelJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/CreateReleaseChannelJob.kt index 667852f981..311baeaa5c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/CreateReleaseChannelJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/CreateReleaseChannelJob.kt @@ -7,11 +7,11 @@ import org.thoughtcrime.securesms.avatar.Avatar import org.thoughtcrime.securesms.avatar.AvatarRenderer import org.thoughtcrime.securesms.avatar.Avatars import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.jobmanager.Job import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.profiles.AvatarHelper import org.thoughtcrime.securesms.profiles.ProfileName -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.transport.RetryLaterException import java.util.concurrent.CountDownLatch @@ -92,7 +92,7 @@ class CreateReleaseChannelJob private constructor(parameters: Parameters) : Base Avatars.ColorPair(ContextCompat.getColor(context, R.color.notification_background_ultramarine), ContextCompat.getColor(context, R.color.core_white), "") ), onAvatarRendered = { media -> - AvatarHelper.setAvatar(context, id, BlobProvider.getInstance().getStream(context, media.uri)) + AvatarHelper.setAvatar(context, id, AppDependencies.blobs.getStream(context, media.uri)) SignalDatabase.recipients.setProfileAvatar(id, "local") latch.countDown() }, diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/FetchRemoteMegaphoneImageJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/FetchRemoteMegaphoneImageJob.kt index 34ccbc9aca..c7211b66ed 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/FetchRemoteMegaphoneImageJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/FetchRemoteMegaphoneImageJob.kt @@ -3,10 +3,10 @@ package org.thoughtcrime.securesms.jobs import okhttp3.ResponseBody import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.jobmanager.Job import org.thoughtcrime.securesms.jobmanager.JsonJobData import org.thoughtcrime.securesms.jobmanager.impl.AutoDownloadEmojiConstraint -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.s3.S3 import org.thoughtcrime.securesms.transport.RetryLaterException import java.io.IOException @@ -44,7 +44,7 @@ class FetchRemoteMegaphoneImageJob(parameters: Parameters, private val uuid: Str S3.getObject(imageUrl).use { response -> val body: ResponseBody? = response.body if (body != null) { - val uri = BlobProvider.getInstance() + val uri = AppDependencies.blobs .forData(body.byteStream(), body.contentLength()) .createForMultipleSessionsOnDisk(context) diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJob.java index 0d6870b5d1..0b78ad2c23 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJob.java @@ -9,7 +9,11 @@ import androidx.annotation.Nullable; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; +import org.signal.core.ui.permissions.Permissions; +import org.signal.core.ui.util.StorageUtil; +import org.signal.core.util.NoExternalStorageException; import org.signal.core.util.Stopwatch; +import org.signal.core.util.crypto.AttachmentSecretProvider; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.backup.BackupEvent; @@ -17,18 +21,15 @@ import org.thoughtcrime.securesms.backup.BackupFileIOError; import org.thoughtcrime.securesms.backup.BackupPassphrase; import org.thoughtcrime.securesms.backup.BackupVerifier; import org.thoughtcrime.securesms.backup.FullBackupExporter; -import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider; -import org.signal.core.util.NoExternalStorageException; +import org.thoughtcrime.securesms.crypto.AppAttachmentSecretStore; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.dependencies.AppDependencies; import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.JobManager; import org.thoughtcrime.securesms.notifications.NotificationChannels; -import org.signal.core.ui.permissions.Permissions; import org.thoughtcrime.securesms.service.GenericForegroundService; import org.thoughtcrime.securesms.service.NotificationController; import org.thoughtcrime.securesms.util.BackupUtil; -import org.signal.core.ui.util.StorageUtil; import java.io.File; import java.io.FileInputStream; @@ -143,7 +144,7 @@ public final class LocalBackupJob extends BaseJob { try { Stopwatch stopwatch = new Stopwatch("backup-export"); BackupEvent finishedEvent = FullBackupExporter.export(context, - AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(), + AttachmentSecretProvider.getInstance(context, AppAttachmentSecretStore.INSTANCE).getOrCreateAttachmentSecret(), SignalDatabase.getBackupDatabase(), tempFile, backupPassword, diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJobApi29.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJobApi29.java index 85b78629b8..9d4ed673dc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJobApi29.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJobApi29.java @@ -12,16 +12,17 @@ import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import org.signal.core.util.Stopwatch; +import org.signal.core.util.androidx.DocumentFileUtil; +import org.signal.core.util.androidx.DocumentFileUtil.OperationResult; +import org.signal.core.util.crypto.AttachmentSecretProvider; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.backup.BackupEvent; import org.thoughtcrime.securesms.backup.BackupFileIOError; import org.thoughtcrime.securesms.backup.BackupPassphrase; import org.thoughtcrime.securesms.backup.BackupVerifier; -import org.signal.core.util.androidx.DocumentFileUtil; -import org.signal.core.util.androidx.DocumentFileUtil.OperationResult; import org.thoughtcrime.securesms.backup.FullBackupExporter; -import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider; +import org.thoughtcrime.securesms.crypto.AppAttachmentSecretStore; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.keyvalue.SignalStore; @@ -130,7 +131,7 @@ public final class LocalBackupJobApi29 extends BaseJob { try { Stopwatch stopwatch = new Stopwatch("backup-export"); BackupEvent finishedEvent = FullBackupExporter.export(context, - AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(), + AttachmentSecretProvider.getInstance(context, AppAttachmentSecretStore.INSTANCE).getOrCreateAttachmentSecret(), SignalDatabase.getBackupDatabase(), temporaryFile, backupPassword, diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceContactSyncJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceContactSyncJob.kt index 72d5b4a8c2..dc4f4859a9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceContactSyncJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceContactSyncJob.kt @@ -10,7 +10,6 @@ import org.thoughtcrime.securesms.jobmanager.JsonJobData import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.net.NotPushRegisteredException import org.thoughtcrime.securesms.profiles.AvatarHelper -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.recipients.Recipient import org.whispersystems.signalservice.api.crypto.AttachmentCipherInputStream.IntegrityCheck import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer @@ -58,7 +57,7 @@ class MultiDeviceContactSyncJob(parameters: Parameters, private val attachmentPo val contactAttachment: SignalServiceAttachmentPointer = AttachmentPointerUtil.createSignalAttachmentPointer(attachmentPointer) try { - val contactsFile: File = BlobProvider.getInstance().forNonAutoEncryptingSingleSessionOnDisk(context) + val contactsFile: File = AppDependencies.blobs.forNonAutoEncryptingSingleSessionOnDisk(context) AppDependencies.signalServiceMessageReceiver .retrieveAttachment(contactAttachment, contactsFile, MAX_ATTACHMENT_SIZE, IntegrityCheck.forEncryptedDigest(contactAttachment.digest.get())) .use(this::processContactFile) diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java index 439dd3335e..543f5204dc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java @@ -10,15 +10,15 @@ import android.provider.ContactsContract; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.signal.core.ui.permissions.Permissions; +import org.signal.core.util.AppForegroundObserver; import org.signal.core.util.logging.Log; import org.signal.libsignal.protocol.IdentityKey; -import org.signal.libsignal.zkgroup.profiles.ProfileKey; -import org.thoughtcrime.securesms.conversation.colors.ChatColorsMapper; -import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; +import org.signal.network.exceptions.PushNetworkException; +import org.signal.network.service.CdnService; import org.thoughtcrime.securesms.database.RecipientTable; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.database.model.IdentityRecord; -import org.signal.network.service.CdnService; import org.thoughtcrime.securesms.dependencies.AppDependencies; import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.JsonJobData; @@ -26,17 +26,13 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; import org.thoughtcrime.securesms.jobmanager.impl.SealedSenderConstraint; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.net.NotPushRegisteredException; -import org.signal.core.ui.permissions.Permissions; -import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.recipients.RecipientUtil; -import org.signal.core.util.AppForegroundObserver; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.crypto.AttachmentCipherStreamUtil; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; -import org.whispersystems.signalservice.internal.crypto.PaddingInputStream; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; import org.whispersystems.signalservice.api.messages.multidevice.ContactsMessage; @@ -46,9 +42,9 @@ import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsO import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.signal.network.exceptions.PushNetworkException; import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException; import org.whispersystems.signalservice.api.util.InvalidNumberException; +import org.whispersystems.signalservice.internal.crypto.PaddingInputStream; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -177,10 +173,10 @@ public class MultiDeviceContactUpdateJob extends BaseJob { out.close(); updateUri = writeDetails.getUri(); - long length = BlobProvider.getInstance().calculateFileSize(context, updateUri); + long length = AppDependencies.getBlobs().calculateFileSize(context, updateUri); sendUpdate(AppDependencies.getSignalServiceMessageSender(), - BlobProvider.getInstance().getStream(context, updateUri), + AppDependencies.getBlobs().getStream(context, updateUri), length, false); @@ -188,7 +184,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob { Log.w(TAG, e); } finally { if (updateUri != null) { - BlobProvider.getInstance().delete(context, updateUri); + AppDependencies.getBlobs().delete(context, updateUri); } } } @@ -253,17 +249,17 @@ public class MultiDeviceContactUpdateJob extends BaseJob { updateUri = writeDetails.getUri(); - long length = BlobProvider.getInstance().calculateFileSize(context, updateUri); + long length = AppDependencies.getBlobs().calculateFileSize(context, updateUri); sendUpdate(AppDependencies.getSignalServiceMessageSender(), - BlobProvider.getInstance().getStream(context, updateUri), + AppDependencies.getBlobs().getStream(context, updateUri), length, true); } catch(InterruptedException e) { Log.w(TAG, e); } finally { if (updateUri != null) { - BlobProvider.getInstance().delete(context, updateUri); + AppDependencies.getBlobs().delete(context, updateUri); } } } @@ -377,7 +373,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob { private @NonNull WriteDetails createTempFile() throws IOException { ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(pipe[0]); - Future futureUri = BlobProvider.getInstance() + Future futureUri = AppDependencies.getBlobs() .forData(inputStream, 0) .withFileName("multidevice-contact-update") .createForSingleSessionOnDiskAsync(context); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveRemoteAnnouncementsJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveRemoteAnnouncementsJob.kt index 71e9859be9..48ca27ac4b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveRemoteAnnouncementsJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveRemoteAnnouncementsJob.kt @@ -25,7 +25,6 @@ import org.thoughtcrime.securesms.jobmanager.JsonJobData import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.notifications.v2.ConversationId -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.releasechannel.ReleaseChannel import org.thoughtcrime.securesms.s3.S3 @@ -361,7 +360,7 @@ class RetrieveRemoteAnnouncementsJob private constructor(private val force: Bool for ((uuid, megaphone) in megaphonesToDelete) { SignalDatabase.remoteMegaphones.clear(uuid) if (megaphone.imageUri != null) { - BlobProvider.getInstance().delete(context, megaphone.imageUri) + AppDependencies.blobs.delete(context, megaphone.imageUri) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceRepository.kt index 09eb1c8c7a..c694a9b246 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/linkdevice/LinkDeviceRepository.kt @@ -27,7 +27,6 @@ import org.thoughtcrime.securesms.jobs.LinkedDeviceInactiveCheckJob import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.linkdevice.LinkDeviceRepository.createAndUploadArchive import org.thoughtcrime.securesms.net.SignalNetwork -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.registration.secondary.DeviceNameCipher import org.whispersystems.signalservice.api.link.LinkedDeviceVerificationCodeResponse import org.whispersystems.signalservice.api.link.TransferArchiveError @@ -281,7 +280,7 @@ object LinkDeviceRepository { fun createAndUploadArchive(ephemeralMessageBackupKey: MessageBackupKey, deviceId: Int, deviceRegistrationId: Int, cancellationSignal: () -> Boolean): LinkUploadArchiveResult { Log.d(TAG, "[createAndUploadArchive] Beginning process.") val stopwatch = Stopwatch("link-archive") - val tempBackupFile = BlobProvider.getInstance().forNonAutoEncryptingSingleSessionOnDisk(AppDependencies.application) + val tempBackupFile = AppDependencies.blobs.forNonAutoEncryptingSingleSessionOnDisk(AppDependencies.application) val outputStream = FileOutputStream(tempBackupFile) try { diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewRepository.java b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewRepository.java index ce151b9e4a..bd621044ed 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreviewRepository.java @@ -12,8 +12,10 @@ import androidx.core.util.Consumer; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; +import org.signal.core.util.ByteUnit; import org.signal.core.util.Hex; import org.signal.core.util.Result; +import org.signal.core.util.bitmaps.BitmapDecodingException; import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.logging.Log; import org.signal.libsignal.protocol.InvalidMessageException; @@ -42,15 +44,12 @@ import org.thoughtcrime.securesms.net.LinkPreviewRedirectValidationInterceptor; import org.thoughtcrime.securesms.net.RequestController; import org.thoughtcrime.securesms.net.UserAgentInterceptor; import org.thoughtcrime.securesms.profiles.AvatarHelper; -import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.service.webrtc.links.CallLinkCredentials; import org.thoughtcrime.securesms.service.webrtc.links.ReadCallLinkResult; import org.thoughtcrime.securesms.stickers.StickerRemoteUri; import org.thoughtcrime.securesms.stickers.StickerUrl; import org.thoughtcrime.securesms.util.AvatarUtil; -import org.signal.core.util.bitmaps.BitmapDecodingException; -import org.signal.core.util.ByteUnit; import org.thoughtcrime.securesms.util.ImageCompressionUtil; import org.thoughtcrime.securesms.util.LinkUtil; import org.thoughtcrime.securesms.util.MediaUtil; @@ -68,11 +67,10 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.ExecutionException; -import kotlin.Pair; - import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.schedulers.Schedulers; +import kotlin.Pair; import okhttp3.CacheControl; import okhttp3.Call; import okhttp3.OkHttpClient; @@ -467,7 +465,7 @@ public class LinkPreviewRepository { int height, @NonNull String contentType) { - Uri uri = BlobProvider.getInstance().forData(bytes).createForSingleSessionInMemory(); + Uri uri = AppDependencies.getBlobs().forData(bytes).createForSingleSessionInMemory(); return new UriAttachment(uri, contentType, diff --git a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogRepository.java b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogRepository.java index dd309cad62..2e00175e07 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogRepository.java @@ -12,6 +12,7 @@ import androidx.annotation.WorkerThread; import org.json.JSONException; import org.json.JSONObject; +import org.signal.core.util.Stopwatch; import org.signal.core.util.StreamUtil; import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.logging.Log; @@ -21,10 +22,8 @@ import org.signal.debuglogsviewer.DebugLogsViewer; import org.thoughtcrime.securesms.database.LogDatabase; import org.thoughtcrime.securesms.dependencies.AppDependencies; import org.thoughtcrime.securesms.net.StandardUserAgentInterceptor; -import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess; import org.thoughtcrime.securesms.util.RemoteConfig; -import org.signal.core.util.Stopwatch; import java.io.IOException; import java.io.OutputStream; @@ -39,8 +38,8 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.regex.Pattern; -import java.util.stream.Stream; import java.util.stream.Collectors; +import java.util.stream.Stream; import java.util.zip.GZIPOutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -190,10 +189,10 @@ public class SubmitDebugLogRepository { try { ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe(); - Future futureUri = BlobProvider.getInstance() - .forData(new ParcelFileDescriptor.AutoCloseInputStream(fds[0]), 0) - .withMimeType("application/gzip") - .createForSingleSessionOnDiskAsync(context); + Future futureUri = AppDependencies.getBlobs() + .forData(new ParcelFileDescriptor.AutoCloseInputStream(fds[0]), 0) + .withMimeType("application/gzip") + .createForSingleSessionOnDiskAsync(context); OutputStream gzipOutput = new GZIPOutputStream(new ParcelFileDescriptor.AutoCloseOutputStream(fds[1])); @@ -224,12 +223,12 @@ public class SubmitDebugLogRepository { } @Override public long contentLength() { - return BlobProvider.getInstance().calculateFileSize(context, gzipUri); + return AppDependencies.getBlobs().calculateFileSize(context, gzipUri); } @Override public void writeTo(@NonNull BufferedSink sink) throws IOException { - Source source = Okio.source(BlobProvider.getInstance().getStream(context, gzipUri)); + Source source = Okio.source(AppDependencies.getBlobs().getStream(context, gzipUri)); sink.writeAll(source); } }); @@ -237,7 +236,7 @@ public class SubmitDebugLogRepository { stopwatch.split("upload"); stopwatch.stop(TAG); - BlobProvider.getInstance().delete(context, gzipUri); + AppDependencies.getBlobs().delete(context, gzipUri); return Optional.of(logUrl); } catch (IOException | RuntimeException | ExecutionException | InterruptedException e) { @@ -264,10 +263,10 @@ public class SubmitDebugLogRepository { Stopwatch stopwatch = new Stopwatch("log-upload"); ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe(); - Future futureUri = BlobProvider.getInstance() - .forData(new ParcelFileDescriptor.AutoCloseInputStream(fds[0]), 0) - .withMimeType("application/gzip") - .createForSingleSessionOnDiskAsync(context); + Future futureUri = AppDependencies.getBlobs() + .forData(new ParcelFileDescriptor.AutoCloseInputStream(fds[0]), 0) + .withMimeType("application/gzip") + .createForSingleSessionOnDiskAsync(context); OutputStream gzipOutput = new GZIPOutputStream(new ParcelFileDescriptor.AutoCloseOutputStream(fds[1])); @@ -297,12 +296,12 @@ public class SubmitDebugLogRepository { } @Override public long contentLength() { - return BlobProvider.getInstance().calculateFileSize(context, gzipUri); + return AppDependencies.getBlobs().calculateFileSize(context, gzipUri); } @Override public void writeTo(@NonNull BufferedSink sink) throws IOException { - Source source = Okio.source(BlobProvider.getInstance().getStream(context, gzipUri)); + Source source = Okio.source(AppDependencies.getBlobs().getStream(context, gzipUri)); sink.writeAll(source); } }); @@ -310,7 +309,7 @@ public class SubmitDebugLogRepository { stopwatch.split("upload"); stopwatch.stop(TAG); - BlobProvider.getInstance().delete(context, gzipUri); + AppDependencies.getBlobs().delete(context, gzipUri); return Optional.of(logUrl); } catch (IOException | RuntimeException | ExecutionException | InterruptedException e) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/maps/PlacePickerActivity.java b/app/src/main/java/org/thoughtcrime/securesms/maps/PlacePickerActivity.java index 0e81800a7a..0c62845917 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/maps/PlacePickerActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/maps/PlacePickerActivity.java @@ -31,12 +31,12 @@ import com.google.android.gms.maps.SupportMapFragment; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.MapStyleOptions; +import org.signal.core.util.bitmaps.BitmapUtil; import org.signal.core.util.concurrent.ListenableFuture; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.components.location.SignalMapView; -import org.thoughtcrime.securesms.providers.BlobProvider; -import org.signal.core.util.bitmaps.BitmapUtil; +import org.thoughtcrime.securesms.dependencies.AppDependencies; import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme; import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.MediaUtil; @@ -196,7 +196,7 @@ public final class PlacePickerActivity extends AppCompatActivity { public void onSuccess(Bitmap result) { dismissibleDialog.dismiss(); byte[] blob = BitmapUtil.toByteArray(result); - Uri uri = BlobProvider.getInstance() + Uri uri = AppDependencies.getBlobs() .forData(blob) .withMimeType(MediaUtil.IMAGE_JPEG) .createForSingleSessionInMemory(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/media/DecryptableUriMediaInput.kt b/app/src/main/java/org/thoughtcrime/securesms/media/DecryptableUriMediaInput.kt index 9191524e84..96220c2936 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/media/DecryptableUriMediaInput.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/media/DecryptableUriMediaInput.kt @@ -4,9 +4,10 @@ import android.content.Context import android.net.Uri import androidx.annotation.RequiresApi import org.thoughtcrime.securesms.database.SignalDatabase.Companion.attachments +import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.mms.PartAuthority import org.thoughtcrime.securesms.mms.PartUriParser -import org.thoughtcrime.securesms.providers.BlobProvider +import org.thoughtcrime.securesms.video.MediaDataSourceProvider import org.thoughtcrime.securesms.video.interfaces.MediaInput import org.thoughtcrime.securesms.video.interfaces.MediaInputFactory import org.thoughtcrime.securesms.video.videoconverter.mediadatasource.MediaDataSourceMediaInput @@ -19,8 +20,8 @@ import java.io.IOException object DecryptableUriMediaInput : MediaInputFactory { @Throws(IOException::class) override fun createForUri(context: Context, uri: Uri): MediaInput { - if (BlobProvider.isAuthority(uri)) { - return MediaDataSourceMediaInput(BlobProvider.getInstance().getMediaDataSource(context, uri)) + if (AppDependencies.blobs.isAuthority(uri)) { + return MediaDataSourceMediaInput(MediaDataSourceProvider.getMediaDataSource(context, uri)) } return if (PartAuthority.isLocalUri(uri)) { createForAttachmentUri(uri) diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/AvatarSelectionActivity.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/AvatarSelectionActivity.java index 45fb5d8ce1..1bba004d42 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/AvatarSelectionActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/AvatarSelectionActivity.java @@ -20,10 +20,11 @@ import org.signal.mediasend.MediaConstraints; import org.signal.mediasend.CameraFragment; import org.signal.mediasend.capture.CameraXFragment; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.dependencies.AppDependencies; import org.thoughtcrime.securesms.mediasend.v2.gallery.MediaGalleryFragment; import org.thoughtcrime.securesms.mms.PushMediaConstraints; import org.thoughtcrime.securesms.profiles.AvatarHelper; -import org.thoughtcrime.securesms.providers.BlobProvider; +import org.signal.core.util.contentproviders.BlobProvider; import org.thoughtcrime.securesms.scribbles.ImageEditorFragment; import org.thoughtcrime.securesms.util.MediaUtil; @@ -79,10 +80,10 @@ public class AvatarSelectionActivity extends AppCompatActivity implements Camera @Override public void onImageCaptured(@NonNull byte[] data, int width, int height) { - Uri blobUri = BlobProvider.getInstance() - .forData(data) - .withMimeType(MediaUtil.IMAGE_JPEG) - .createForSingleSessionInMemory(); + Uri blobUri = AppDependencies.getBlobs() + .forData(data) + .withMimeType(MediaUtil.IMAGE_JPEG) + .createForSingleSessionInMemory(); onMediaSelected(new Media(blobUri, MediaUtil.IMAGE_JPEG, diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/ImageEditorModelRenderMediaTransform.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/ImageEditorModelRenderMediaTransform.java index 8379445e13..5649c089a7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/ImageEditorModelRenderMediaTransform.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/ImageEditorModelRenderMediaTransform.java @@ -13,8 +13,9 @@ import org.signal.core.util.StreamUtil; import org.signal.core.util.logging.Log; import org.signal.core.models.media.Media; import org.signal.imageeditor.core.model.EditorModel; +import org.thoughtcrime.securesms.dependencies.AppDependencies; import org.thoughtcrime.securesms.fonts.FontTypefaceProvider; -import org.thoughtcrime.securesms.providers.BlobProvider; +import org.signal.core.util.contentproviders.BlobProvider; import org.thoughtcrime.securesms.util.MediaUtil; import java.io.ByteArrayOutputStream; @@ -45,10 +46,10 @@ public final class ImageEditorModelRenderMediaTransform implements MediaTransfor try { bitmap.compress(Bitmap.CompressFormat.JPEG, 80, outputStream); - Uri uri = BlobProvider.getInstance() - .forData(outputStream.toByteArray()) - .withMimeType(MediaUtil.IMAGE_JPEG) - .createForSingleSessionOnDisk(context); + Uri uri = AppDependencies.getBlobs() + .forData(outputStream.toByteArray()) + .withMimeType(MediaUtil.IMAGE_JPEG) + .createForSingleSessionOnDisk(context); return new Media(uri, MediaUtil.IMAGE_JPEG, media.getDate(), bitmap.getWidth(), bitmap.getHeight(), outputStream.size(), 0, false, false, media.getBucketId(), media.getCaption(), null, null); } catch (IOException e) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionRepository.kt index 02a3317214..7dec9ef8f7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionRepository.kt @@ -39,7 +39,6 @@ import org.thoughtcrime.securesms.mms.OutgoingMessage import org.thoughtcrime.securesms.mms.Slide import org.thoughtcrime.securesms.mms.SlideDeck import org.thoughtcrime.securesms.mms.VideoSlide -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.scribbles.ImageEditorFragment @@ -237,8 +236,8 @@ class MediaSelectionRepository(context: Context) { fun deleteBlobs(media: List) { media .map(Media::uri) - .filter(BlobProvider::isAuthority) - .forEach { BlobProvider.getInstance().delete(context, it) } + .filter { AppDependencies.blobs.isAuthority(it) } + .forEach { AppDependencies.blobs.delete(context, it) } } fun cleanUp(selectedMedia: List) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionViewModel.kt index 9e6f032fb7..dc9fb79a99 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionViewModel.kt @@ -21,6 +21,7 @@ import io.reactivex.rxjava3.subjects.PublishSubject import io.reactivex.rxjava3.subjects.Subject import org.signal.core.models.media.Media import org.signal.core.util.Util +import org.signal.core.util.contentproviders.BlobProvider import org.signal.core.util.getParcelableArrayListCompat import org.signal.core.util.getParcelableCompat import org.signal.core.util.logging.Log @@ -31,9 +32,9 @@ import org.thoughtcrime.securesms.components.mention.MentionAnnotation import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey import org.thoughtcrime.securesms.conversation.MessageSendType import org.thoughtcrime.securesms.conversation.MessageStyler +import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.mediasend.MediaSendActivityResult import org.thoughtcrime.securesms.mms.PushMediaConstraints -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.scribbles.ImageEditorFragment import org.thoughtcrime.securesms.stories.Stories @@ -472,7 +473,7 @@ class MediaSelectionViewModel( editorStates.forEach { it.writeToParcel(parcel, 0) } val serializedEditorState: ByteArray = parcel.marshall() parcel.recycle() - val blobUri = BlobProvider.getInstance().forData(serializedEditorState).createForSingleUseInMemory() + val blobUri = AppDependencies.blobs.forData(serializedEditorState).createForSingleUseInMemory() outState.putParcelable(STATE_EDITORS, blobUri) } } @@ -496,7 +497,7 @@ class MediaSelectionViewModel( val cameraFirstCapture: Media? = savedInstanceState.getParcelableCompat(STATE_CAMERA_FIRST_CAPTURE, Media::class.java) val editorCount: Int = savedInstanceState.getInt(STATE_EDITOR_COUNT, 0) val blobUri: Uri? = savedInstanceState.getParcelableCompat(STATE_EDITORS, Uri::class.java) - val blobProvider: BlobProvider = BlobProvider.getInstance() + val blobProvider: BlobProvider = AppDependencies.blobs val editorStates: List = if (editorCount > 0 && blobUri != null && blobProvider.hasStream(context, blobUri)) { val accumulator: MutableList = mutableListOf() val blob: ByteArray = ByteStreams.toByteArray(blobProvider.getStream(context, blobUri)) diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/capture/MediaCaptureRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/capture/MediaCaptureRepository.kt index b5be426127..9b9ab7ecdd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/capture/MediaCaptureRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/capture/MediaCaptureRepository.kt @@ -10,8 +10,9 @@ import androidx.annotation.WorkerThread import org.signal.core.models.media.Media import org.signal.core.util.CursorUtil import org.signal.core.util.concurrent.SignalExecutors +import org.signal.core.util.contentproviders.BlobProvider +import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.mediasend.MediaRepository -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.util.MediaUtil import org.thoughtcrime.securesms.video.videoconverter.utils.VideoConstants import java.io.FileDescriptor @@ -72,7 +73,7 @@ class MediaCaptureRepository(context: Context) { return try { val data: T = dataSupplier() val length: Long = getLength(data) - val uri: Uri = createBlobBuilder(BlobProvider.getInstance(), data, length) + val uri: Uri = createBlobBuilder(AppDependencies.blobs, data, length) .withMimeType(mimeType) .createForSingleSessionOnDisk(context) diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/text/send/TextStoryPostSendRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/text/send/TextStoryPostSendRepository.kt index 14bee5434b..0bdd74404f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/text/send/TextStoryPostSendRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/text/send/TextStoryPostSendRepository.kt @@ -11,6 +11,7 @@ import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.model.StoryType import org.thoughtcrime.securesms.database.model.databaseprotos.StoryTextPost +import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.fonts.TextFont import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.keyvalue.StorySend @@ -18,7 +19,6 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreview import org.thoughtcrime.securesms.mediasend.v2.UntrustedRecords import org.thoughtcrime.securesms.mediasend.v2.text.TextStoryPostCreationState import org.thoughtcrime.securesms.mms.OutgoingMessage -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.stories.Stories import java.io.ByteArrayOutputStream @@ -32,7 +32,7 @@ class TextStoryPostSendRepository { val outputStream = ByteArrayOutputStream() bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream) bitmap.recycle() - BlobProvider.getInstance().forData(outputStream.toByteArray()).createForSingleSessionInMemory() + AppDependencies.blobs.forData(outputStream.toByteArray()).createForSingleSessionInMemory() }.subscribeOn(Schedulers.computation()) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v3/MediaSendV3Repository.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v3/MediaSendV3Repository.kt index d4c57c1d70..6e67d6d4c1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v3/MediaSendV3Repository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v3/MediaSendV3Repository.kt @@ -40,7 +40,6 @@ import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionRepository import org.thoughtcrime.securesms.mediasend.v2.MediaValidator import org.thoughtcrime.securesms.mms.PartAuthority import org.thoughtcrime.securesms.mms.PushMediaConstraints -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.scribbles.ImageEditorFragment @@ -96,8 +95,8 @@ object MediaSendV3Repository : MediaSendRepository { override suspend fun deleteBlobs(media: List) { media .map(Media::uri) - .filter(BlobProvider::isAuthority) - .forEach { BlobProvider.getInstance().delete(appContext, it) } + .filter { AppDependencies.blobs.isAuthority(it) } + .forEach { AppDependencies.blobs.delete(appContext, it) } } override suspend fun send(request: SendRequest): SendResult = withContext(Dispatchers.IO) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepository.kt index cb8d597982..ca2d13ec51 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepository.kt @@ -19,7 +19,6 @@ import org.thoughtcrime.securesms.database.model.RemoteMegaphoneRecord import org.thoughtcrime.securesms.database.model.RemoteMegaphoneRecord.ActionId import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.keyvalue.SignalStore -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.util.LocaleRemoteConfig import org.thoughtcrime.securesms.util.RemoteConfig @@ -50,7 +49,7 @@ object RemoteMegaphoneRepository { SignalExecutors.BOUNDED_IO.execute { db.markFinished(remote.uuid) if (remote.imageUri != null) { - BlobProvider.getInstance().delete(context, remote.imageUri) + AppDependencies.blobs.delete(context, remote.imageUri) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/BlobStorageLocationMigrationJob.java b/app/src/main/java/org/thoughtcrime/securesms/migrations/BlobStorageLocationMigrationJob.java index b8399a3389..f4db61d877 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/migrations/BlobStorageLocationMigrationJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/BlobStorageLocationMigrationJob.java @@ -11,7 +11,7 @@ import org.thoughtcrime.securesms.jobmanager.Job; import java.io.File; /** - * We moved files stored by {@link org.thoughtcrime.securesms.providers.BlobProvider} from the cache + * We moved files stored by {@link org.signal.core.util.contentproviders.BlobProvider} from the cache * into internal storage, so we gotta move any existing multi-session files. */ public class BlobStorageLocationMigrationJob extends MigrationJob { diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java b/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java index a3461644fc..00455acb98 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java @@ -37,13 +37,17 @@ import com.bumptech.glide.Glide; import com.bumptech.glide.RequestManager; import com.google.android.material.dialog.MaterialAlertDialogBuilder; +import org.signal.core.ui.permissions.Permissions; +import org.signal.core.ui.view.Stub; +import org.signal.core.util.ExpiringProfileCredentialUtil; import org.signal.core.util.ThreadUtil; +import org.signal.core.util.bitmaps.BitmapUtil; import org.signal.core.util.concurrent.ListenableFuture; import org.signal.core.util.concurrent.ListenableFuture.Listener; import org.signal.core.util.concurrent.SettableFuture; import org.signal.core.util.concurrent.SimpleTask; import org.signal.core.util.logging.Log; -import org.signal.core.ui.view.Stub; +import org.signal.core.util.permissions.PermissionCompat; import org.signal.mediasend.MediaConstraints; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.components.AudioView; @@ -55,6 +59,7 @@ import org.thoughtcrime.securesms.components.location.SignalPlace; import org.thoughtcrime.securesms.conversation.MessageSendType; import org.thoughtcrime.securesms.database.MediaTable; import org.thoughtcrime.securesms.database.SignalDatabase; +import org.thoughtcrime.securesms.dependencies.AppDependencies; import org.thoughtcrime.securesms.giph.ui.GiphyActivity; import org.thoughtcrime.securesms.maps.PlacePickerActivity; import org.thoughtcrime.securesms.mediapreview.MediaIntentFactory; @@ -67,19 +72,14 @@ import org.thoughtcrime.securesms.payments.create.CreatePaymentFragmentArgs; import org.thoughtcrime.securesms.payments.preferences.PaymentsActivity; import org.thoughtcrime.securesms.payments.preferences.RecipientHasNotEnabledPaymentsDialog; import org.thoughtcrime.securesms.payments.preferences.model.PayeeParcelable; -import org.signal.core.util.permissions.PermissionCompat; -import org.signal.core.ui.permissions.Permissions; -import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.sms.MessageSender; -import org.signal.core.util.bitmaps.BitmapUtil; -import org.thoughtcrime.securesms.util.RemoteConfig; import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.ProfileUtil; +import org.thoughtcrime.securesms.util.RemoteConfig; import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener; -import org.signal.core.util.ExpiringProfileCredentialUtil; import java.io.IOException; import java.util.Collections; @@ -174,14 +174,14 @@ public class AttachmentManager { } private void cleanup(final @Nullable Uri uri) { - if (uri != null && BlobProvider.isAuthority(uri)) { + if (uri != null && AppDependencies.getBlobs().isAuthority(uri)) { Log.d(TAG, "cleaning up " + uri); - BlobProvider.getInstance().delete(context, uri); + AppDependencies.getBlobs().delete(context, uri); } } private void markGarbage(@Nullable Uri uri) { - if (uri != null && BlobProvider.isAuthority(uri)) { + if (uri != null && AppDependencies.getBlobs().isAuthority(uri)) { Log.d(TAG, "Marking garbage that needs cleaning: " + uri); garbage.add(uri); } @@ -215,10 +215,10 @@ public class AttachmentManager { @Override public void onSuccess(@NonNull Bitmap result) { byte[] blob = BitmapUtil.toByteArray(result); - Uri uri = BlobProvider.getInstance() - .forData(blob) - .withMimeType(MediaUtil.IMAGE_JPEG) - .createForSingleSessionInMemory(); + Uri uri = AppDependencies.getBlobs() + .forData(blob) + .withMimeType(MediaUtil.IMAGE_JPEG) + .createForSingleSessionInMemory(); LocationSlide locationSlide = new LocationSlide(context, uri, blob.length, place); ThreadUtil.runOnMain(() -> { @@ -241,7 +241,7 @@ public class AttachmentManager { attachmentViewStub.get().setVisibility(View.VISIBLE); removableMediaView.display(mapView, false); - LocationSlide locationSlide = new LocationSlide(context, thumbnailUri, BlobProvider.getFileSize(thumbnailUri), place); + LocationSlide locationSlide = new LocationSlide(context, thumbnailUri, AppDependencies.getBlobs().getFileSize(thumbnailUri), place); setSlide(locationSlide); attachmentListener.onAttachmentChanged(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/PartAuthority.java b/app/src/main/java/org/thoughtcrime/securesms/mms/PartAuthority.java index 9e9276a2b6..c2a69fe44c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/PartAuthority.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/PartAuthority.java @@ -12,14 +12,14 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.provider.DocumentsContractCompat; +import org.signal.core.models.database.AttachmentId; +import org.signal.core.models.media.TransformProperties; import org.thoughtcrime.securesms.BuildConfig; import org.thoughtcrime.securesms.attachments.Attachment; -import org.signal.core.models.database.AttachmentId; import org.thoughtcrime.securesms.avatar.AvatarPickerStorage; -import org.signal.core.models.media.TransformProperties; import org.thoughtcrime.securesms.database.SignalDatabase; +import org.thoughtcrime.securesms.dependencies.AppDependencies; import org.thoughtcrime.securesms.emoji.EmojiFiles; -import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.providers.PartProvider; import java.io.FileNotFoundException; @@ -58,7 +58,7 @@ public class PartAuthority { uriMatcher.addURI(AUTHORITY, "wallpaper/*", WALLPAPER_ROW); uriMatcher.addURI(AUTHORITY, "emoji/*", EMOJI_ROW); uriMatcher.addURI(AUTHORITY, "avatar_picker/*", AVATAR_PICKER_ROW); - uriMatcher.addURI(BlobProvider.AUTHORITY, BlobProvider.PATH, BLOB_ROW); + uriMatcher.addURI(AppDependencies.getBlobs().getAuthority(), AppDependencies.getBlobs().PATH, BLOB_ROW); } public static InputStream getAttachmentThumbnailStream(@NonNull Context context, @NonNull Uri uri) @@ -75,7 +75,7 @@ public class PartAuthority { switch (match) { case PART_ROW: return SignalDatabase.attachments().getAttachmentStream(new PartUriParser(uri).getPartId(), 0); case STICKER_ROW: return SignalDatabase.stickers().getStickerStream(ContentUris.parseId(uri)); - case BLOB_ROW: return BlobProvider.getInstance().getStream(context, uri); + case BLOB_ROW: return AppDependencies.getBlobs().getStream(context, uri); case EMOJI_ROW: return EmojiFiles.openForReading(context, getEmojiFilename(uri)); case AVATAR_PICKER_ROW: return AvatarPickerStorage.read(context, getAvatarPickerFilename(uri)); case THUMBNAIL_ROW: return SignalDatabase.attachments().getAttachmentThumbnailStream(new PartUriParser(uri).getPartId(), 0); @@ -96,7 +96,7 @@ public class PartAuthority { if (attachment != null) return attachment.fileName; else return null; case BLOB_ROW: - return BlobProvider.getFileName(uri); + return AppDependencies.getBlobs().getFileName(uri); default: return null; } @@ -112,7 +112,7 @@ public class PartAuthority { if (attachment != null) return attachment.size; else return null; case BLOB_ROW: - return BlobProvider.getFileSize(uri); + return AppDependencies.getBlobs().getFileSize(uri); default: return null; } @@ -128,7 +128,7 @@ public class PartAuthority { if (attachment != null) return attachment.contentType; else return null; case BLOB_ROW: - return BlobProvider.getMimeType(uri); + return AppDependencies.getBlobs().getMimeType(uri); default: return null; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationThumbnails.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationThumbnails.kt index fe43ab7b53..cd90e50ffa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationThumbnails.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationThumbnails.kt @@ -11,7 +11,6 @@ import org.signal.glide.decryptableuri.DecryptableUri import org.thoughtcrime.securesms.database.model.MessageId import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.mms.Slide -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.util.ImageCompressionUtil import org.thoughtcrime.securesms.util.RemoteConfig import org.thoughtcrime.securesms.util.kb @@ -127,8 +126,7 @@ object NotificationThumbnails { } if (result != null) { - val thumbnailUri = BlobProvider - .getInstance() + val thumbnailUri = AppDependencies.blobs .forData(result.data) .withMimeType(result.mimeType) .withFileName(result.hashCode().toString()) @@ -137,7 +135,7 @@ object NotificationThumbnails { synchronized(thumbnailCache) { if (thumbnailCache.size >= MAX_CACHE_SIZE) { thumbnailCache.remove(thumbnailCache.keys.first())?.uri?.let { - BlobProvider.getInstance().delete(context, it) + AppDependencies.blobs.delete(context, it) } } thumbnailCache[messageId] = CachedThumbnail(thumbnailUri, result.mimeType) diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/AvatarHelper.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/AvatarHelper.java index 5ae1f15be1..2a7c103b24 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/AvatarHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/AvatarHelper.java @@ -6,17 +6,18 @@ import android.content.Context; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.signal.core.util.ByteUnit; import org.signal.core.util.StreamUtil; +import org.signal.core.util.crypto.AttachmentSecret; +import org.signal.core.util.crypto.AttachmentSecretProvider; +import org.signal.core.util.crypto.ModernDecryptingPartInputStream; +import org.signal.core.util.crypto.ModernEncryptingPartOutputStream; import org.signal.core.util.logging.Log; -import org.thoughtcrime.securesms.crypto.AttachmentSecret; -import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider; -import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream; -import org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream; +import org.thoughtcrime.securesms.crypto.AppAttachmentSecretStore; import org.thoughtcrime.securesms.database.model.ProfileAvatarFileDetails; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; -import org.signal.core.util.ByteUnit; import org.thoughtcrime.securesms.util.MediaUtil; import org.whispersystems.signalservice.api.util.StreamDetails; @@ -117,7 +118,7 @@ public class AvatarHelper { * IOException. It is recommended to call {@link #hasAvatar(Context, RecipientId)} first. */ public static @NonNull InputStream getAvatar(@NonNull Context context, @NonNull RecipientId recipientId) throws IOException { - AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(); + AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context, AppAttachmentSecretStore.INSTANCE).getOrCreateAttachmentSecret(); File avatarFile = getAvatarFile(context, recipientId); return ModernDecryptingPartInputStream.createFor(attachmentSecret, avatarFile, 0); @@ -166,7 +167,7 @@ public class AvatarHelper { * recipient. Only intended to be used for backup. Otherwise, use {@link #setAvatar(Context, RecipientId, InputStream)}. */ public static @NonNull OutputStream getOutputStream(@NonNull Context context, @NonNull RecipientId recipientId, boolean isSyncAvatar) throws IOException { - AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(); + AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context, AppAttachmentSecretStore.INSTANCE).getOrCreateAttachmentSecret(); File targetFile = getAvatarFile(context, recipientId, isSyncAvatar); return ModernEncryptingPartOutputStream.createFor(attachmentSecret, targetFile, true).getSecond(); } @@ -215,7 +216,7 @@ public class AvatarHelper { private static void setAvatarInternal(@NonNull Context context, @NonNull RecipientId recipientId, @NonNull InputStream inputStream, boolean isSyncAvatar) throws IOException { - AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(); + AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context, AppAttachmentSecretStore.INSTANCE).getOrCreateAttachmentSecret(); File targetFile = getAvatarFile(context, recipientId, isSyncAvatar); File tempFile = new File(targetFile.getParent(), targetFile.getName() + ".tmp"); diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/CreateProfileFragment.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/CreateProfileFragment.java index 0805f2f6b1..44853fc4c3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/CreateProfileFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/CreateProfileFragment.java @@ -22,23 +22,22 @@ import com.airbnb.lottie.SimpleColorFilter; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; -import org.signal.core.ui.compose.SignalIcons; +import org.signal.core.models.media.Media; +import org.signal.core.ui.logging.LoggingFragment; import org.signal.core.util.EditTextUtil; import org.signal.core.util.StreamUtil; import org.signal.core.util.concurrent.SimpleTask; import org.signal.core.util.logging.Log; -import org.signal.core.ui.logging.LoggingFragment; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.avatar.Avatars; import org.thoughtcrime.securesms.avatar.picker.AvatarPickerFragment; import org.thoughtcrime.securesms.databinding.CreateProfileFragmentBinding; +import org.thoughtcrime.securesms.dependencies.AppDependencies; import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues; import org.thoughtcrime.securesms.keyvalue.SignalStore; -import org.signal.core.models.media.Media; import org.thoughtcrime.securesms.profiles.edit.pnp.WhoCanFindMeByPhoneNumberFragment; import org.thoughtcrime.securesms.profiles.manage.EditProfileNameFragment; -import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.util.CommunicationActions; import org.thoughtcrime.securesms.util.RemoteConfig; import org.thoughtcrime.securesms.util.ViewUtil; @@ -47,7 +46,6 @@ import org.thoughtcrime.securesms.util.text.AfterTextChanged; import java.io.IOException; import java.io.InputStream; -import java.util.Objects; import static org.thoughtcrime.securesms.profiles.edit.CreateProfileActivity.EXCLUDE_SYSTEM; import static org.thoughtcrime.securesms.profiles.edit.CreateProfileActivity.GROUP_ID; @@ -119,7 +117,7 @@ public class CreateProfileFragment extends LoggingFragment { private void handleMediaFromResult(@NonNull Media media) { SimpleTask.run(() -> { try { - InputStream stream = BlobProvider.getInstance().getStream(requireContext(), media.getUri()); + InputStream stream = AppDependencies.getBlobs().getStream(requireContext(), media.getUri()); return StreamUtil.readFully(stream); } catch (IOException ioException) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileViewModel.java index 3e92c81ff1..c9c5279abc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileViewModel.java @@ -10,6 +10,7 @@ import androidx.lifecycle.Transformations; import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModelProvider; +import org.signal.core.models.media.Media; import org.signal.core.util.StreamUtil; import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.logging.Log; @@ -17,10 +18,8 @@ import org.thoughtcrime.securesms.badges.models.Badge; import org.thoughtcrime.securesms.dependencies.AppDependencies; import org.thoughtcrime.securesms.jobs.RefreshOwnProfileJob; import org.thoughtcrime.securesms.keyvalue.SignalStore; -import org.signal.core.models.media.Media; import org.thoughtcrime.securesms.profiles.AvatarHelper; import org.thoughtcrime.securesms.profiles.ProfileName; -import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientForeverObserver; import org.thoughtcrime.securesms.util.DefaultValueLiveData; @@ -135,7 +134,7 @@ class EditProfileViewModel extends ViewModel { } else { SignalExecutors.BOUNDED.execute(() -> { try { - InputStream stream = BlobProvider.getInstance().getStream(context, media.getUri()); + InputStream stream = AppDependencies.getBlobs().getStream(context, media.getUri()); byte[] data = StreamUtil.readFully(stream); internalAvatarState.postValue(InternalAvatarState.loading(data)); diff --git a/app/src/main/java/org/thoughtcrime/securesms/providers/AvatarProvider.kt b/app/src/main/java/org/thoughtcrime/securesms/providers/AvatarProvider.kt index dd36c82a36..d455d8801a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/providers/AvatarProvider.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/providers/AvatarProvider.kt @@ -15,11 +15,13 @@ import android.graphics.Bitmap import android.net.Uri import android.os.ParcelFileDescriptor import org.signal.core.util.concurrent.SignalExecutors +import org.signal.core.util.contentproviders.BaseContentProvider +import org.signal.core.util.crypto.AttachmentSecretProvider import org.signal.core.util.logging.AndroidLogger import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.BuildConfig -import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider +import org.thoughtcrime.securesms.crypto.AppAttachmentSecretStore import org.thoughtcrime.securesms.crypto.DatabaseSecretProvider import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.SqlCipherLibraryLoader @@ -73,7 +75,7 @@ class AvatarProvider : BaseContentProvider() { SignalDatabase.init( application, DatabaseSecretProvider.getOrCreateDatabaseSecret(application), - AttachmentSecretProvider.getInstance(application).getOrCreateAttachmentSecret() + AttachmentSecretProvider.getInstance(application, AppAttachmentSecretStore).getOrCreateAttachmentSecret() ) SignalStore.init(application) diff --git a/app/src/main/java/org/thoughtcrime/securesms/providers/BlobContentProvider.java b/app/src/main/java/org/thoughtcrime/securesms/providers/BlobContentProvider.java index 312b670d0d..d447d63400 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/providers/BlobContentProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/providers/BlobContentProvider.java @@ -1,3 +1,8 @@ +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + package org.thoughtcrime.securesms.providers; import android.content.ContentValues; @@ -9,11 +14,13 @@ import android.os.ParcelFileDescriptor; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.signal.core.util.CoreUtilDependencies; +import org.signal.core.util.MemoryFileUtil; import org.signal.core.util.StreamUtil; +import org.signal.core.util.Util; +import org.signal.core.util.contentproviders.BaseContentProvider; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.dependencies.AppDependencies; -import org.thoughtcrime.securesms.util.MemoryFileUtil; -import org.signal.core.util.Util; import java.io.FileNotFoundException; import java.io.IOException; @@ -35,8 +42,8 @@ public final class BlobContentProvider extends BaseContentProvider { Log.i(TAG, "openFile() called: " + uri); try { - try (InputStream stream = BlobProvider.getInstance().getStream(AppDependencies.getApplication(), uri)) { - Long fileSize = BlobProvider.getFileSize(uri); + try (InputStream stream = AppDependencies.getBlobs().getStream(CoreUtilDependencies.getApplication(), uri)) { + Long fileSize = AppDependencies.getBlobs().getFileSize(uri); if (fileSize == null) { Log.w(TAG, "No file size available"); throw new FileNotFoundException(); @@ -64,9 +71,9 @@ public final class BlobContentProvider extends BaseContentProvider { public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { Log.i(TAG, "query() called: " + uri); - String mimeType = BlobProvider.getMimeType(uri); - String fileName = BlobProvider.getFileName(uri); - Long fileSize = BlobProvider.getFileSize(uri); + String mimeType = AppDependencies.getBlobs().getMimeType(uri); + String fileName = AppDependencies.getBlobs().getFileName(uri); + Long fileSize = AppDependencies.getBlobs().getFileSize(uri); if (fileSize == null) { Log.w(TAG, "No file size"); @@ -79,16 +86,16 @@ public final class BlobContentProvider extends BaseContentProvider { } if (fileName == null) { - fileName = createFileNameForMimeType(mimeType); + fileName = BaseContentProvider.createFileNameForMimeType(mimeType); } - return createCursor(projection, fileName, fileSize); + return BaseContentProvider.createCursor(projection, fileName, fileSize); } @Nullable @Override public String getType(@NonNull Uri uri) { - return BlobProvider.getMimeType(uri); + return AppDependencies.getBlobs().getMimeType(uri); } @Nullable diff --git a/app/src/main/java/org/thoughtcrime/securesms/providers/PartProvider.java b/app/src/main/java/org/thoughtcrime/securesms/providers/PartProvider.java index d66dada300..df3ed05ec3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/providers/PartProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/providers/PartProvider.java @@ -38,6 +38,7 @@ import androidx.annotation.RequiresApi; import org.signal.core.util.StreamUtil; import org.signal.core.util.ThreadUtil; import org.signal.core.util.concurrent.SignalExecutors; +import org.signal.core.util.contentproviders.BaseContentProvider; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.BuildConfig; import org.signal.core.models.database.AttachmentId; @@ -46,7 +47,7 @@ import org.thoughtcrime.securesms.database.AttachmentTable; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.mms.PartUriParser; import org.thoughtcrime.securesms.service.KeyCachingService; -import org.thoughtcrime.securesms.util.MemoryFileUtil; +import org.signal.core.util.MemoryFileUtil; import org.signal.core.util.Util; import java.io.FileNotFoundException; diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/sharablegrouplink/qr/GroupLinkShareQrDialogFragment.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/sharablegrouplink/qr/GroupLinkShareQrDialogFragment.java index 20eb0c57ca..b1d8bfbd62 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/sharablegrouplink/qr/GroupLinkShareQrDialogFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/sharablegrouplink/qr/GroupLinkShareQrDialogFragment.java @@ -18,14 +18,14 @@ import androidx.fragment.app.DialogFragment; import androidx.fragment.app.FragmentManager; import androidx.lifecycle.ViewModelProvider; +import org.signal.core.ui.BottomSheetUtil; +import org.signal.core.ui.util.ThemeUtil; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.components.qr.QrView; +import org.thoughtcrime.securesms.dependencies.AppDependencies; import org.thoughtcrime.securesms.groups.GroupId; -import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.qr.QrCodeUtil; -import org.signal.core.ui.BottomSheetUtil; -import org.signal.core.ui.util.ThemeUtil; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -131,11 +131,11 @@ public class GroupLinkShareQrDialogFragment extends DialogFragment { byte[] bytes = byteArrayOutputStream.toByteArray(); - return BlobProvider.getInstance() - .forData(bytes) - .withMimeType("image/png") - .withFileName("SignalGroupQr.png") - .createForSingleSessionInMemory(); + return AppDependencies.getBlobs() + .forData(bytes) + .withMimeType("image/png") + .withFileName("SignalGroupQr.png") + .createForSingleSessionInMemory(); } finally { qrBitmap.recycle(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/AppRegistrationStorageController.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/AppRegistrationStorageController.kt index cfc21f7c77..71acb97214 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/AppRegistrationStorageController.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/AppRegistrationStorageController.kt @@ -24,6 +24,7 @@ import org.signal.archive.LocalBackupRestoreProgress import org.signal.core.models.AccountEntropyPool import org.signal.core.models.MasterKey import org.signal.core.util.StreamUtil +import org.signal.core.util.crypto.AttachmentSecretProvider import org.signal.core.util.logging.Log import org.signal.registration.PreExistingRegistrationData import org.signal.registration.StorageController @@ -38,7 +39,7 @@ import org.thoughtcrime.securesms.backup.v2.RemoteRestoreResult import org.thoughtcrime.securesms.backup.v2.RestoreV2Event import org.thoughtcrime.securesms.backup.v2.local.LocalArchiver import org.thoughtcrime.securesms.backup.v2.local.SnapshotFileSystem -import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider +import org.thoughtcrime.securesms.crypto.AppAttachmentSecretStore import org.thoughtcrime.securesms.crypto.ProfileKeyUtil import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.model.databaseprotos.LocalRegistrationMetadata @@ -278,7 +279,7 @@ class AppRegistrationStorageController(private val context: Context) : StorageCo val database = SignalDatabase.backupDatabase FullBackupImporter.importFile( context, - AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(), + AttachmentSecretProvider.getInstance(context, AppAttachmentSecretStore).getOrCreateAttachmentSecret(), database, uri, passphrase, diff --git a/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreRepository.kt index fa12e0c575..a10ddf7493 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/restore/RestoreRepository.kt @@ -9,11 +9,12 @@ import android.content.Context import android.net.Uri import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import org.signal.core.util.crypto.AttachmentSecretProvider import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.AppInitialization import org.thoughtcrime.securesms.backup.BackupPassphrase import org.thoughtcrime.securesms.backup.FullBackupImporter -import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider +import org.thoughtcrime.securesms.crypto.AppAttachmentSecretStore import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.jobmanager.impl.DataRestoreConstraint @@ -59,7 +60,7 @@ object RestoreRepository { FullBackupImporter.importFile( context, - AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(), + AttachmentSecretProvider.getInstance(context, AppAttachmentSecretStore).getOrCreateAttachmentSecret(), database, backupFileUri, passphrase, diff --git a/app/src/main/java/org/thoughtcrime/securesms/revealable/ViewOnceMessageActivity.java b/app/src/main/java/org/thoughtcrime/securesms/revealable/ViewOnceMessageActivity.java index 26821bde39..6ebcae2c9a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/revealable/ViewOnceMessageActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/revealable/ViewOnceMessageActivity.java @@ -20,13 +20,12 @@ import com.bumptech.glide.Glide; import org.signal.core.util.logging.Log; import org.signal.glide.decryptableuri.DecryptableUri; +import org.signal.video.VideoPlayer; import org.thoughtcrime.securesms.PassphraseRequiredActivity; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.dependencies.AppDependencies; import org.thoughtcrime.securesms.mms.PartAuthority; -import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.util.MediaUtil; -import org.signal.video.VideoPlayer; import java.util.concurrent.TimeUnit; @@ -88,7 +87,7 @@ public class ViewOnceMessageActivity extends PassphraseRequiredActivity implemen super.onStop(); cancelDurationUpdate(); video.cleanup(); - BlobProvider.getInstance().delete(this, uri); + AppDependencies.getBlobs().delete(this, uri); finish(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/scribbles/ImageEditorFragment.java b/app/src/main/java/org/thoughtcrime/securesms/scribbles/ImageEditorFragment.java index 816ecc2b3d..870afa77f1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/scribbles/ImageEditorFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/scribbles/ImageEditorFragment.java @@ -31,7 +31,10 @@ import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.target.Target; import com.google.android.material.dialog.MaterialAlertDialogBuilder; +import org.signal.core.ui.permissions.Permissions; import org.signal.core.util.FontUtil; +import org.signal.core.util.ParcelUtil; +import org.signal.core.util.ThrottledDebouncer; import org.signal.core.util.concurrent.LifecycleDisposable; import org.signal.core.util.concurrent.SimpleTask; import org.signal.core.util.logging.Log; @@ -46,6 +49,8 @@ import org.signal.imageeditor.core.renderers.BezierDrawingRenderer; import org.signal.imageeditor.core.renderers.FaceBlurRenderer; import org.signal.imageeditor.core.renderers.MultiLineTextRenderer; import org.signal.imageeditor.core.renderers.UriGlideRenderer; +import org.signal.mediasend.MediaConstraints; +import org.signal.mediasend.SentMediaQuality; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.animation.ResizeAnimation; import org.thoughtcrime.securesms.attachments.AttachmentSaver; @@ -54,20 +59,14 @@ import org.thoughtcrime.securesms.fonts.FontTypefaceProvider; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.mediasend.MediaSendPageFragment; import org.thoughtcrime.securesms.mediasend.v2.MediaAnimations; -import org.signal.mediasend.MediaConstraints; import org.thoughtcrime.securesms.mms.PushMediaConstraints; -import org.signal.mediasend.SentMediaQuality; -import org.signal.core.ui.permissions.Permissions; -import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.scribbles.stickers.AnalogClockStickerRenderer; import org.thoughtcrime.securesms.scribbles.stickers.DigitalClockStickerRenderer; import org.thoughtcrime.securesms.scribbles.stickers.FeatureSticker; import org.thoughtcrime.securesms.scribbles.stickers.TappableRenderer; import org.thoughtcrime.securesms.util.MediaUtil; -import org.signal.core.util.ParcelUtil; import org.thoughtcrime.securesms.util.SaveAttachmentUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.signal.core.util.ThrottledDebouncer; import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.views.SimpleProgressDialog; @@ -76,10 +75,9 @@ import java.util.Collections; import java.util.List; import java.util.Objects; -import kotlin.Pair; - import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.schedulers.Schedulers; +import kotlin.Pair; import static android.app.Activity.RESULT_OK; @@ -813,10 +811,10 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu image.compress(Bitmap.CompressFormat.JPEG, 80, outputStream); image.recycle(); - return BlobProvider.getInstance() - .forData(outputStream.toByteArray()) - .withMimeType(MediaUtil.IMAGE_JPEG) - .createForSingleUseInMemory(); + return AppDependencies.getBlobs() + .forData(outputStream.toByteArray()) + .withMimeType(MediaUtil.IMAGE_JPEG) + .createForSingleUseInMemory(); } @WorkerThread @@ -827,10 +825,10 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu image.compress(Bitmap.CompressFormat.JPEG, 80, outputStream); image.recycle(); - return BlobProvider.getInstance() - .forData(outputStream.toByteArray()) - .withMimeType(MediaUtil.IMAGE_JPEG) - .createForSingleSessionInMemory(); + return AppDependencies.getBlobs() + .forData(outputStream.toByteArray()) + .withMimeType(MediaUtil.IMAGE_JPEG) + .createForSingleSessionInMemory(); } private void onDrawingChanged(boolean stillTouching, boolean isUserEdit) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/sharing/v2/ShareRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/sharing/v2/ShareRepository.kt index f85018ca83..855169bbda 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/sharing/v2/ShareRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/sharing/v2/ShareRepository.kt @@ -9,7 +9,7 @@ import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.schedulers.Schedulers import org.signal.core.models.media.Media import org.signal.core.util.logging.Log -import org.thoughtcrime.securesms.providers.BlobProvider +import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.util.MediaUtil import org.thoughtcrime.securesms.util.RemoteConfig import org.thoughtcrime.securesms.util.UriUtil @@ -49,7 +49,7 @@ class ShareRepository(context: Context) { } ?: return ResolvedShareData.Failure val blobUri: Uri = try { - BlobProvider.getInstance() + AppDependencies.blobs .forData(stream, size) .withMimeType(mimeType) .withFileName(name) @@ -94,7 +94,7 @@ class ShareRepository(context: Context) { val dimens: Pair = MediaUtil.getDimensions(appContext, mimeType, uri) val duration = 0L val blobUri = try { - BlobProvider.getInstance() + AppDependencies.blobs .forData(stream, size) .withMimeType(mimeType) .createForSingleSessionOnDisk(appContext) diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/dialogs/StoryContextMenu.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/dialogs/StoryContextMenu.kt index af0a429355..e0021e0ca3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/dialogs/StoryContextMenu.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/dialogs/StoryContextMenu.kt @@ -28,7 +28,7 @@ import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.database.model.databaseprotos.StoryTextPost -import org.thoughtcrime.securesms.providers.BlobProvider +import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.sharing.v2.ShareActivity import org.thoughtcrime.securesms.stories.StoryTextPostModel import org.thoughtcrime.securesms.stories.landing.StoriesLandingItem @@ -88,7 +88,7 @@ object StoryContextMenu { bitmap.recycle() SaveAttachmentUtil.SaveAttachment( - uri = BlobProvider.getInstance().forData(jpeg.readBytes()).createForSingleUseInMemory(), + uri = AppDependencies.blobs.forData(jpeg.readBytes()).createForSingleUseInMemory(), contentType = MediaUtil.IMAGE_JPEG, date = messageRecord.dateSent, fileName = null diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/EncryptedStreamUtils.kt b/app/src/main/java/org/thoughtcrime/securesms/util/EncryptedStreamUtils.kt index b1d53e139a..b8f419341e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/EncryptedStreamUtils.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/EncryptedStreamUtils.kt @@ -2,9 +2,10 @@ package org.thoughtcrime.securesms.util import android.content.Context import androidx.annotation.WorkerThread -import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider -import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream -import org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream +import org.signal.core.util.crypto.AttachmentSecretProvider +import org.signal.core.util.crypto.ModernDecryptingPartInputStream +import org.signal.core.util.crypto.ModernEncryptingPartOutputStream +import org.thoughtcrime.securesms.crypto.AppAttachmentSecretStore import java.io.File import java.io.InputStream import java.io.OutputStream @@ -15,13 +16,13 @@ import java.io.OutputStream object EncryptedStreamUtils { @WorkerThread fun getOutputStream(context: Context, outputFile: File): OutputStream { - val attachmentSecret = AttachmentSecretProvider.getInstance(context).orCreateAttachmentSecret + val attachmentSecret = AttachmentSecretProvider.getInstance(context, AppAttachmentSecretStore).orCreateAttachmentSecret return ModernEncryptingPartOutputStream.createFor(attachmentSecret, outputFile, true).second } @WorkerThread fun getInputStream(context: Context, inputFile: File): InputStream { - val attachmentSecret = AttachmentSecretProvider.getInstance(context).orCreateAttachmentSecret + val attachmentSecret = AttachmentSecretProvider.getInstance(context, AppAttachmentSecretStore).orCreateAttachmentSecret return ModernDecryptingPartInputStream.createFor(attachmentSecret, inputFile, 0) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java index 48be86cf3c..075ea997c3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java @@ -14,7 +14,6 @@ import android.media.ThumbnailUtils; import android.net.Uri; import android.provider.MediaStore; import android.text.TextUtils; -import kotlin.Pair; import android.webkit.MimeTypeMap; import androidx.annotation.NonNull; @@ -26,16 +25,17 @@ import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.resource.gif.GifDrawable; +import org.signal.core.models.database.AttachmentId; +import org.signal.core.models.media.Media; import org.signal.core.util.ContentTypeUtil; import org.signal.core.util.bitmaps.BitmapDecodingException; import org.signal.core.util.bitmaps.BitmapUtil; import org.signal.core.util.logging.Log; -import org.thoughtcrime.securesms.attachments.Attachment; -import org.signal.core.models.database.AttachmentId; -import org.thoughtcrime.securesms.database.SignalDatabase; -import org.signal.core.models.media.Media; -import org.thoughtcrime.securesms.mms.AudioSlide; import org.signal.glide.decryptableuri.DecryptableUri; +import org.thoughtcrime.securesms.attachments.Attachment; +import org.thoughtcrime.securesms.database.SignalDatabase; +import org.thoughtcrime.securesms.dependencies.AppDependencies; +import org.thoughtcrime.securesms.mms.AudioSlide; import org.thoughtcrime.securesms.mms.DocumentSlide; import org.thoughtcrime.securesms.mms.GifSlide; import org.thoughtcrime.securesms.mms.ImageSlide; @@ -46,7 +46,7 @@ import org.thoughtcrime.securesms.mms.StickerSlide; import org.thoughtcrime.securesms.mms.TextSlide; import org.thoughtcrime.securesms.mms.VideoSlide; import org.thoughtcrime.securesms.mms.ViewOnceSlide; -import org.thoughtcrime.securesms.providers.BlobProvider; +import org.thoughtcrime.securesms.video.MediaDataSourceProvider; import java.io.FileNotFoundException; import java.io.IOException; @@ -55,6 +55,8 @@ import java.net.URLConnection; import java.util.Optional; import java.util.concurrent.ExecutionException; +import kotlin.Pair; + public class MediaUtil { private static final String TAG = Log.tag(MediaUtil.class); @@ -293,8 +295,8 @@ public class MediaUtil { MediaMetadataRetriever retriever = new MediaMetadataRetriever(); try { - if (BlobProvider.isAuthority(uri) && MediaUtil.isVideo(BlobProvider.getMimeType(uri))) { - MediaDataSource source = BlobProvider.getInstance().getMediaDataSource(context, uri); + if (AppDependencies.getBlobs().isAuthority(uri) && MediaUtil.isVideo(AppDependencies.getBlobs().getMimeType(uri))) { + MediaDataSource source = MediaDataSourceProvider.getMediaDataSource(context, uri); retriever.setDataSource(source); } else if (PartAuthority.isAttachmentUri(uri)) { MediaDataSource source = SignalDatabase.attachments().mediaDataSourceFor(PartAuthority.requireAttachmentId(uri), false); @@ -471,7 +473,7 @@ public class MediaUtil { return false; } - if (BlobProvider.isAuthority(uri) && MediaUtil.isVideo(BlobProvider.getMimeType(uri))) { + if (AppDependencies.getBlobs().isAuthority(uri) && MediaUtil.isVideo(AppDependencies.getBlobs().getMimeType(uri))) { return true; } @@ -516,11 +518,11 @@ public class MediaUtil { MediaUtil.isVideo(URLConnection.guessContentTypeFromName(uri.toString()))) { return ThumbnailUtils.createVideoThumbnail(uri.toString().replace("file://", ""), MediaStore.Video.Thumbnails.MINI_KIND); - } else if (BlobProvider.isAuthority(uri) && - MediaUtil.isVideo(BlobProvider.getMimeType(uri))) + } else if (AppDependencies.getBlobs().isAuthority(uri) && + MediaUtil.isVideo(AppDependencies.getBlobs().getMimeType(uri))) { try { - MediaDataSource source = BlobProvider.getInstance().getMediaDataSource(context, uri); + MediaDataSource source = MediaDataSourceProvider.getMediaDataSource(context, uri); return extractFrame(source, timeUs); } catch (IOException e) { Log.w(TAG, "Failed to extract frame for URI: " + uri, e); diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/MessageUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/util/MessageUtil.kt index 684d4d5ba7..ca4bc99873 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/MessageUtil.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/MessageUtil.kt @@ -3,8 +3,8 @@ package org.thoughtcrime.securesms.util import android.content.Context import org.signal.core.util.kibiBytes import org.signal.core.util.splitByByteLength +import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.mms.TextSlide -import org.thoughtcrime.securesms.providers.BlobProvider import java.text.SimpleDateFormat import java.util.Date import java.util.Locale @@ -31,7 +31,7 @@ object MessageUtil { val textData = rawText.toByteArray() val timestamp = SimpleDateFormat("yyyy-MM-dd-HHmmss", Locale.US).format(Date()) val filename = String.format("signal-%s.txt", timestamp) - val textUri = BlobProvider.getInstance() + val textUri = AppDependencies.blobs .forData(textData) .withMimeType(MediaUtil.LONG_TEXT) .withFileName(filename) diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/storage/FileStorage.java b/app/src/main/java/org/thoughtcrime/securesms/util/storage/FileStorage.java index 3876dfea6f..c1be674c50 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/storage/FileStorage.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/storage/FileStorage.java @@ -6,10 +6,11 @@ import androidx.annotation.NonNull; import androidx.annotation.WorkerThread; import org.signal.core.util.StreamUtil; -import org.thoughtcrime.securesms.crypto.AttachmentSecret; -import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider; -import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream; -import org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream; +import org.signal.core.util.crypto.AttachmentSecret; +import org.signal.core.util.crypto.AttachmentSecretProvider; +import org.signal.core.util.crypto.ModernDecryptingPartInputStream; +import org.signal.core.util.crypto.ModernEncryptingPartOutputStream; +import org.thoughtcrime.securesms.crypto.AppAttachmentSecretStore; import java.io.File; import java.io.IOException; @@ -93,12 +94,12 @@ public final class FileStorage { } private static @NonNull OutputStream getOutputStream(@NonNull Context context, File outputFile) throws IOException { - AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(); + AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context, AppAttachmentSecretStore.INSTANCE).getOrCreateAttachmentSecret(); return ModernEncryptingPartOutputStream.createFor(attachmentSecret, outputFile, true).getSecond(); } private static @NonNull InputStream getInputStream(@NonNull Context context, File inputFile) throws IOException { - AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(); + AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context, AppAttachmentSecretStore.INSTANCE).getOrCreateAttachmentSecret(); return ModernDecryptingPartInputStream.createFor(attachmentSecret, inputFile, 0); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/video/ClassicEncryptedMediaDataSource.java b/app/src/main/java/org/thoughtcrime/securesms/video/ClassicEncryptedMediaDataSource.java index 4f5488f7f9..95d00caa63 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/video/ClassicEncryptedMediaDataSource.java +++ b/app/src/main/java/org/thoughtcrime/securesms/video/ClassicEncryptedMediaDataSource.java @@ -4,8 +4,8 @@ import android.media.MediaDataSource; import androidx.annotation.NonNull; -import org.thoughtcrime.securesms.crypto.AttachmentSecret; -import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream; +import org.signal.core.util.crypto.AttachmentSecret; +import org.signal.core.util.crypto.ClassicDecryptingPartInputStream; import org.signal.core.util.Util; import java.io.File; diff --git a/app/src/main/java/org/thoughtcrime/securesms/video/EncryptedMediaDataSource.java b/app/src/main/java/org/thoughtcrime/securesms/video/EncryptedMediaDataSource.java index 0328d8511d..9ca5cf6642 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/video/EncryptedMediaDataSource.java +++ b/app/src/main/java/org/thoughtcrime/securesms/video/EncryptedMediaDataSource.java @@ -5,7 +5,7 @@ import android.media.MediaDataSource; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.thoughtcrime.securesms.crypto.AttachmentSecret; +import org.signal.core.util.crypto.AttachmentSecret; import java.io.File; diff --git a/app/src/main/java/org/thoughtcrime/securesms/video/MediaDataSourceProvider.kt b/app/src/main/java/org/thoughtcrime/securesms/video/MediaDataSourceProvider.kt new file mode 100644 index 0000000000..b23ec35f54 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/video/MediaDataSourceProvider.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.video + +import android.content.Context +import android.media.MediaDataSource +import android.net.Uri +import com.squareup.wire.internal.JvmStatic +import org.signal.core.util.crypto.AttachmentSecretProvider +import org.thoughtcrime.securesms.crypto.AppAttachmentSecretStore +import org.thoughtcrime.securesms.dependencies.AppDependencies +import java.io.IOException + +/** + * Utilizes blob provider to load a blob as a media data source. + */ +object MediaDataSourceProvider { + @JvmStatic + @Suppress("MoveLambdaOutsideParentheses") + @Throws(IOException::class) + fun getMediaDataSource(context: Context, uri: Uri): MediaDataSource { + val attachmentSecret = AttachmentSecretProvider.getInstance(context, AppAttachmentSecretStore).getOrCreateAttachmentSecret() + return AppDependencies.blobs.getBlobRepresentation( + context, + uri, + ::ByteArrayMediaDataSource, + { EncryptedMediaDataSource.createForDiskBlob(attachmentSecret, it!!) } + ) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/video/ModernEncryptedMediaDataSource.java b/app/src/main/java/org/thoughtcrime/securesms/video/ModernEncryptedMediaDataSource.java index 59d3343225..18b2255eb7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/video/ModernEncryptedMediaDataSource.java +++ b/app/src/main/java/org/thoughtcrime/securesms/video/ModernEncryptedMediaDataSource.java @@ -5,8 +5,8 @@ import android.media.MediaDataSource; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.thoughtcrime.securesms.crypto.AttachmentSecret; -import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream; +import org.signal.core.util.crypto.AttachmentSecret; +import org.signal.core.util.crypto.ModernDecryptingPartInputStream; import org.thoughtcrime.securesms.video.videoconverter.mediadatasource.InputStreamMediaDataSource; import java.io.File; diff --git a/app/src/main/java/org/thoughtcrime/securesms/video/exo/BlobDataSource.java b/app/src/main/java/org/thoughtcrime/securesms/video/exo/BlobDataSource.java index 13e7ecd555..f0b0f65d38 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/video/exo/BlobDataSource.java +++ b/app/src/main/java/org/thoughtcrime/securesms/video/exo/BlobDataSource.java @@ -8,13 +8,11 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.OptIn; import androidx.media3.common.util.UnstableApi; -import androidx.media3.datasource.DataSource; - import androidx.media3.datasource.DataSource; import androidx.media3.datasource.DataSpec; import androidx.media3.datasource.TransferListener; -import org.thoughtcrime.securesms.providers.BlobProvider; +import org.thoughtcrime.securesms.dependencies.AppDependencies; import java.io.EOFException; import java.io.IOException; @@ -44,15 +42,15 @@ class BlobDataSource implements DataSource { @Override public long open(DataSpec dataSpec) throws IOException { this.dataSpec = dataSpec; - this.inputStream = BlobProvider.getInstance().getStream(context, dataSpec.uri, dataSpec.position); + this.inputStream = AppDependencies.getBlobs().getStream(context, dataSpec.uri, dataSpec.position); if (listener != null) { listener.onTransferStart(this, dataSpec, false); } - long size = unwrapLong(BlobProvider.getFileSize(dataSpec.uri)); + long size = unwrapLong(AppDependencies.getBlobs().getFileSize(dataSpec.uri)); if (size == 0) { - size = BlobProvider.getInstance().calculateFileSize(context, dataSpec.uri); + size = AppDependencies.getBlobs().calculateFileSize(context, dataSpec.uri); } if (size - dataSpec.position <= 0) throw new EOFException("No more data"); diff --git a/app/src/main/java/org/thoughtcrime/securesms/video/exo/SignalDataSource.java b/app/src/main/java/org/thoughtcrime/securesms/video/exo/SignalDataSource.java index 9033247417..df71adf984 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/video/exo/SignalDataSource.java +++ b/app/src/main/java/org/thoughtcrime/securesms/video/exo/SignalDataSource.java @@ -7,17 +7,14 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.OptIn; import androidx.media3.common.util.UnstableApi; -import androidx.media3.datasource.DataSource; -import androidx.media3.datasource.DefaultDataSource; - import androidx.media3.datasource.DataSource; import androidx.media3.datasource.DataSpec; import androidx.media3.datasource.DefaultDataSource; import androidx.media3.datasource.DefaultDataSourceFactory; import androidx.media3.datasource.TransferListener; +import org.thoughtcrime.securesms.dependencies.AppDependencies; import org.thoughtcrime.securesms.mms.PartAuthority; -import org.thoughtcrime.securesms.providers.BlobProvider; import java.io.IOException; import java.util.Collections; @@ -26,7 +23,7 @@ import java.util.Map; import okhttp3.OkHttpClient; - /** +/** * Go-to {@link DataSource} that handles all of our various types of video sources. * Will defer to other {@link DataSource}s depending on the URI. */ @@ -57,7 +54,7 @@ import okhttp3.OkHttpClient; @Override public long open(DataSpec dataSpec) throws IOException { - if (BlobProvider.isAuthority(dataSpec.uri)) { + if (AppDependencies.getBlobs().isAuthority(dataSpec.uri)) { dataSource = blobDataSource; } else if (PartAuthority.isLocalUri(dataSpec.uri)) { dataSource = partDataSource; diff --git a/app/src/spinner/java/org/thoughtcrime/securesms/BackupPlugin.kt b/app/src/spinner/java/org/thoughtcrime/securesms/BackupPlugin.kt index 86953b6b04..8888d4b9f8 100644 --- a/app/src/spinner/java/org/thoughtcrime/securesms/BackupPlugin.kt +++ b/app/src/spinner/java/org/thoughtcrime/securesms/BackupPlugin.kt @@ -11,6 +11,7 @@ import org.signal.archive.proto.BackupDebugInfo import org.signal.archive.proto.FilePointer import org.signal.archive.stream.EncryptedBackupReader import org.signal.core.util.bytes +import org.signal.core.util.contentproviders.BlobProvider import org.signal.core.util.decodeOrNull import org.signal.core.util.logging.Log import org.signal.libsignal.zkgroup.profiles.ProfileKey @@ -23,7 +24,6 @@ import org.thoughtcrime.securesms.backup.v2.local.SnapshotFileSystem import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.net.SignalNetwork -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.recipients.Recipient import java.io.IOException import java.text.SimpleDateFormat diff --git a/app/src/test/java/org/thoughtcrime/securesms/components/settings/app/account/export/ExportAccountDataTest.kt b/app/src/test/java/org/thoughtcrime/securesms/components/settings/app/account/export/ExportAccountDataTest.kt index 17ff8b4400..a05ca8f5c0 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/components/settings/app/account/export/ExportAccountDataTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/components/settings/app/account/export/ExportAccountDataTest.kt @@ -19,8 +19,8 @@ import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import org.signal.core.util.JsonUtils import org.signal.network.NetworkResult +import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.net.SignalNetwork -import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule import java.io.IOException @@ -82,7 +82,7 @@ class ExportAccountDataTest { .subscribe { txtReport -> assertEquals(txtReport.mimeType, "text/plain") assertThrows(JsonParseException::class.java) { - JsonUtils.getMapper().readTree(BlobProvider.getInstance().getMemoryBlob(txtReport.uri) as ByteArray) + JsonUtils.getMapper().readTree(AppDependencies.blobs.getMemoryBlob(txtReport.uri) as ByteArray) } } scheduler.triggerActions() @@ -91,7 +91,7 @@ class ExportAccountDataTest { .observeOn(scheduler) .subscribe { jsonReport -> assertEquals(jsonReport.mimeType, "application/json") - val json = JsonUtils.getMapper().readTree(BlobProvider.getInstance().getMemoryBlob(jsonReport.uri) as ByteArray) + val json = JsonUtils.getMapper().readTree(AppDependencies.blobs.getMemoryBlob(jsonReport.uri) as ByteArray) assertFalse(json.has("text")) assertTrue(json.has("data")) assertTrue(json.has("reportId")) diff --git a/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt b/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt index 8fa75ec8ad..8f68d1a2f0 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt @@ -4,6 +4,7 @@ import androidx.media3.exoplayer.ExoPlayer import io.mockk.mockk import org.signal.core.util.billing.BillingApi import org.signal.core.util.concurrent.DeadlockDetector +import org.signal.core.util.contentproviders.BlobProvider import org.signal.libsignal.net.Network import org.signal.libsignal.zkgroup.profiles.ClientZkProfileOperations import org.signal.libsignal.zkgroup.receipts.ClientZkReceiptOperations @@ -336,4 +337,8 @@ class MockApplicationDependencyProvider : AppDependencies.Provider { override fun provideKeyTransparencyApi(unauthWebSocket: SignalWebSocket.UnauthenticatedWebSocket): KeyTransparencyApi { return mockk(relaxed = true) } + + override fun provideBlobs(): BlobProvider { + return mockk(relaxed = true) + } } diff --git a/app/src/test/java/org/thoughtcrime/securesms/recipients/Recipient_getChatColorsTest.kt b/app/src/test/java/org/thoughtcrime/securesms/recipients/Recipient_getChatColorsTest.kt index c45e9d6496..67d4b7d97f 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/recipients/Recipient_getChatColorsTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/recipients/Recipient_getChatColorsTest.kt @@ -15,9 +15,9 @@ import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config +import org.signal.core.util.crypto.AttachmentSecretProvider import org.thoughtcrime.securesms.conversation.colors.ChatColors import org.thoughtcrime.securesms.conversation.colors.ChatColorsPalette -import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider import org.thoughtcrime.securesms.database.RecipientDatabaseTestUtils.createRecipient import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.keyvalue.ChatColorsValues @@ -42,7 +42,7 @@ class Recipient_getChatColorsTest { every { AppDependencies.application } returns ApplicationProvider.getApplicationContext() mockkStatic(AttachmentSecretProvider::class) - every { AttachmentSecretProvider.getInstance(any()) } throws RuntimeException("Whoa, nelly!") + every { AttachmentSecretProvider.getInstance(any(), any()) } throws RuntimeException("Whoa, nelly!") wallpaperValues = mockk() chatColorsValues = mockk() diff --git a/app/src/test/java/org/thoughtcrime/securesms/testing/TestSignalDatabase.kt b/app/src/test/java/org/thoughtcrime/securesms/testing/TestSignalDatabase.kt index f072e55eef..fdadd53492 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/testing/TestSignalDatabase.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/testing/TestSignalDatabase.kt @@ -3,7 +3,7 @@ package org.thoughtcrime.securesms.testing import android.app.Application import androidx.sqlite.db.SupportSQLiteDatabase import androidx.sqlite.db.SupportSQLiteOpenHelper -import org.thoughtcrime.securesms.crypto.AttachmentSecret +import org.signal.core.util.crypto.AttachmentSecret import org.thoughtcrime.securesms.crypto.DatabaseSecret import org.thoughtcrime.securesms.database.SignalDatabase import java.security.SecureRandom diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/IOFunction.java b/core/util-jvm/src/main/java/org/signal/core/util/IOFunction.java similarity index 64% rename from app/src/main/java/org/thoughtcrime/securesms/util/IOFunction.java rename to core/util-jvm/src/main/java/org/signal/core/util/IOFunction.java index 9f5d43389b..6ac2e413f4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/IOFunction.java +++ b/core/util-jvm/src/main/java/org/signal/core/util/IOFunction.java @@ -1,4 +1,9 @@ -package org.thoughtcrime.securesms.util; +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.signal.core.util; import java.io.IOException; diff --git a/core/util/src/main/java/org/signal/core/util/CoreUtilDependencies.kt b/core/util/src/main/java/org/signal/core/util/CoreUtilDependencies.kt index ce70e0063d..770e1f29fd 100644 --- a/core/util/src/main/java/org/signal/core/util/CoreUtilDependencies.kt +++ b/core/util/src/main/java/org/signal/core/util/CoreUtilDependencies.kt @@ -23,6 +23,7 @@ object CoreUtilDependencies { _buildInfo = buildInfo } + @JvmStatic val application: Application get() = _application diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/MemoryFileDescriptorProxy.java b/core/util/src/main/java/org/signal/core/util/MemoryFileDescriptorProxy.java similarity index 95% rename from app/src/main/java/org/thoughtcrime/securesms/util/MemoryFileDescriptorProxy.java rename to core/util/src/main/java/org/signal/core/util/MemoryFileDescriptorProxy.java index 0d0e5ba8f0..b1f43ab86d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/MemoryFileDescriptorProxy.java +++ b/core/util/src/main/java/org/signal/core/util/MemoryFileDescriptorProxy.java @@ -1,4 +1,9 @@ -package org.thoughtcrime.securesms.util; +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.signal.core.util; import android.content.Context; import android.os.Handler; diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/MemoryFileUtil.java b/core/util/src/main/java/org/signal/core/util/MemoryFileUtil.java similarity index 86% rename from app/src/main/java/org/thoughtcrime/securesms/util/MemoryFileUtil.java rename to core/util/src/main/java/org/signal/core/util/MemoryFileUtil.java index 787c187807..17c8914d30 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/MemoryFileUtil.java +++ b/core/util/src/main/java/org/signal/core/util/MemoryFileUtil.java @@ -1,4 +1,9 @@ -package org.thoughtcrime.securesms.util; +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.signal.core.util; import android.annotation.SuppressLint; import android.os.Build; @@ -7,8 +12,6 @@ import android.os.ParcelFileDescriptor; import androidx.annotation.NonNull; -import org.thoughtcrime.securesms.dependencies.AppDependencies; - import java.io.FileDescriptor; import java.io.IOException; import java.lang.reflect.Field; @@ -23,7 +26,7 @@ public final class MemoryFileUtil { throws IOException { if (Build.VERSION.SDK_INT >= 26) { - return MemoryFileDescriptorProxy.create(AppDependencies.getApplication(), file); + return MemoryFileDescriptorProxy.create(CoreUtilDependencies.getApplication(), file); } else { return getParcelFileDescriptorLegacy(file); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/providers/BaseContentProvider.java b/core/util/src/main/java/org/signal/core/util/contentproviders/BaseContentProvider.java similarity index 93% rename from app/src/main/java/org/thoughtcrime/securesms/providers/BaseContentProvider.java rename to core/util/src/main/java/org/signal/core/util/contentproviders/BaseContentProvider.java index 64a485d040..58355d1d16 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/providers/BaseContentProvider.java +++ b/core/util/src/main/java/org/signal/core/util/contentproviders/BaseContentProvider.java @@ -1,4 +1,9 @@ -package org.thoughtcrime.securesms.providers; +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.signal.core.util.contentproviders; import android.content.ContentProvider; import android.content.Context; diff --git a/app/src/main/java/org/thoughtcrime/securesms/providers/BlobProvider.java b/core/util/src/main/java/org/signal/core/util/contentproviders/BlobProvider.java similarity index 82% rename from app/src/main/java/org/thoughtcrime/securesms/providers/BlobProvider.java rename to core/util/src/main/java/org/signal/core/util/contentproviders/BlobProvider.java index 5553b4c165..b3271c8cf7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/providers/BlobProvider.java +++ b/core/util/src/main/java/org/signal/core/util/contentproviders/BlobProvider.java @@ -1,9 +1,13 @@ -package org.thoughtcrime.securesms.providers; +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.signal.core.util.contentproviders; import android.app.Application; import android.content.Context; import android.content.UriMatcher; -import android.media.MediaDataSource; import android.net.Uri; import androidx.annotation.AnyThread; @@ -13,21 +17,17 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.annotation.WorkerThread; +import org.signal.core.util.CoreUtilDependencies; +import org.signal.core.util.IOFunction; import org.signal.core.util.StreamUtil; -import org.signal.core.util.concurrent.SignalExecutors; -import org.signal.core.util.logging.Log; -import org.thoughtcrime.securesms.BuildConfig; -import org.thoughtcrime.securesms.components.voice.VoiceNoteDraft; -import org.thoughtcrime.securesms.crypto.AttachmentSecret; -import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider; -import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream; -import org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream; -import org.thoughtcrime.securesms.database.DraftTable; -import org.thoughtcrime.securesms.database.SignalDatabase; -import org.thoughtcrime.securesms.util.IOFunction; import org.signal.core.util.Util; -import org.thoughtcrime.securesms.video.ByteArrayMediaDataSource; -import org.thoughtcrime.securesms.video.EncryptedMediaDataSource; +import org.signal.core.util.concurrent.SignalExecutors; +import org.signal.core.util.crypto.AttachmentSecret; +import org.signal.core.util.crypto.AttachmentSecretProvider; +import org.signal.core.util.crypto.AttachmentSecretStore; +import org.signal.core.util.crypto.ModernDecryptingPartInputStream; +import org.signal.core.util.crypto.ModernEncryptingPartOutputStream; +import org.signal.core.util.logging.Log; import java.io.ByteArrayInputStream; import java.io.File; @@ -35,13 +35,11 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; -import java.util.stream.Collectors; +import java.util.function.Consumer; /** * Allows for the creation and retrieval of blobs. @@ -55,8 +53,6 @@ public class BlobProvider { private static final String SINGLE_SESSION_DIRECTORY = "single_session_blobs"; private static final String TEMP_BACKUPS_DIRECTORY = "temp_backups"; - public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".blob"; - public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/blob"); public static final String PATH = "blob/*/*/*/*/*"; private static final int STORAGE_TYPE_PATH_SEGMENT = 1; @@ -64,21 +60,30 @@ public class BlobProvider { private static final int FILENAME_PATH_SEGMENT = 3; private static final int FILESIZE_PATH_SEGMENT = 4; private static final int ID_PATH_SEGMENT = 5; + private static final int MATCH = 1; - private static final int MATCH = 1; - private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH) {{ - addURI(AUTHORITY, PATH, MATCH); - }}; + private final AttachmentSecretStore secretStore; - private static final BlobProvider INSTANCE = new BlobProvider(); + private final String authority; + private final Uri contentUri; + private final UriMatcher uriMatcher; private final Map memoryBlobs = new HashMap<>(); private volatile boolean initialized = false; + public BlobProvider(@NonNull Context appContext, @NonNull AttachmentSecretStore secretStore) { + this.secretStore = secretStore; - public static BlobProvider getInstance() { - return INSTANCE; + authority = appContext.getPackageName() + ".blob"; + contentUri = Uri.parse("content://" + authority + "/blob"); + uriMatcher = new UriMatcher(UriMatcher.NO_MATCH) {{ + addURI(authority, PATH, MATCH); + }}; + } + + public String getAuthority() { + return authority; } /** @@ -139,7 +144,6 @@ public class BlobProvider { * @throws IOException If the stream fails to open or the spec of the URI doesn't match. */ public synchronized @NonNull InputStream getStream(@NonNull Context context, @NonNull Uri uri, long position) throws IOException { - waitUntilInitialized(); return getBlobRepresentation(context, uri, bytes -> { @@ -154,20 +158,23 @@ public class BlobProvider { position)); } - public synchronized @NonNull MediaDataSource getMediaDataSource(@NonNull Context context, @NonNull Uri uri) throws IOException { - waitUntilInitialized(); - return getBlobRepresentation(context, - uri, - ByteArrayMediaDataSource::new, - file -> EncryptedMediaDataSource.createForDiskBlob(getAttachmentSecret(context), file)); - } - - private synchronized @NonNull T getBlobRepresentation(@NonNull Context context, - @NonNull Uri uri, - @NonNull IOFunction getByteRepresentation, - @NonNull IOFunction getFileRepresentation) + /** + * Retreives a representation of a blob given the byte and file representations. + * + * @param context The activity or application context for file access + * @param uri The URI to load the representation of + * @param getByteRepresentation A constructor for the byte representation + * @param getFileRepresentation A constructor for the file representation + * @return An instance of the desired object. + */ + public synchronized @NonNull T getBlobRepresentation(@NonNull Context context, + @NonNull Uri uri, + @NonNull IOFunction getByteRepresentation, + @NonNull IOFunction getFileRepresentation) throws IOException { + waitUntilInitialized(); + if (isAuthority(uri)) { StorageType storageType = StorageType.decode(uri.getPathSegments().get(STORAGE_TYPE_PATH_SEGMENT)); @@ -195,7 +202,7 @@ public class BlobProvider { } private synchronized AttachmentSecret getAttachmentSecret(@NonNull Context context) { - return AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(); + return AttachmentSecretProvider.getInstance(context, secretStore).getOrCreateAttachmentSecret(); } /** @@ -238,7 +245,7 @@ public class BlobProvider { * background thread, so callers don't have to worry about it. */ @AnyThread - public synchronized void initialize(@NonNull Context context) { + public synchronized void initialize(@NonNull Context context, @NonNull Consumer clearDrafts) { SignalExecutors.BOUNDED.execute(() -> { synchronized (this) { File directory = getOrCreateDirectory(context, SINGLE_SESSION_DIRECTORY); @@ -256,7 +263,9 @@ public class BlobProvider { Log.w(TAG, "Null directory listing!"); } - deleteOrphanedDraftFiles(context); + File draftDirectory = BlobProvider.getOrCreateDirectory(context, BlobProvider.DRAFT_ATTACHMENTS_DIRECTORY); + + clearDrafts.accept(draftDirectory); Log.i(TAG, "Initialized."); initialized = true; @@ -288,53 +297,21 @@ public class BlobProvider { return memoryBlobs.get(uri); } - private static void deleteOrphanedDraftFiles(@NonNull Context context) { - File directory = getOrCreateDirectory(context, DRAFT_ATTACHMENTS_DIRECTORY); - File[] files = directory.listFiles(); - - if (files == null || files.length == 0) { - Log.d(TAG, "No attachment drafts exist. Skipping."); - return; - } - - DraftTable draftDatabase = SignalDatabase.drafts(); - DraftTable.Drafts voiceNoteDrafts = draftDatabase.getAllVoiceNoteDrafts(); - - @SuppressWarnings("ConstantConditions") - List draftFileNames = voiceNoteDrafts.stream() - .map(VoiceNoteDraft::fromDraft) - .map(VoiceNoteDraft::getUri) - .map(BlobProvider::getId) - .filter(Objects::nonNull) - .map(BlobProvider::buildFileName) - .collect(Collectors.toList()); - - for (final File file : files) { - if (!draftFileNames.contains(file.getName())) { - if (file.delete()) { - Log.d(TAG, "Deleted orphaned attachment draft: " + file.getName()); - } else { - Log.d(TAG, "Failed to delete orphaned attachment draft: " + file.getName()); - } - } - } - } - - public static @Nullable String getMimeType(@NonNull Uri uri) { + public @Nullable String getMimeType(@NonNull Uri uri) { if (isAuthority(uri)) { return uri.getPathSegments().get(MIMETYPE_PATH_SEGMENT); } return null; } - public static @Nullable String getFileName(@NonNull Uri uri) { + public @Nullable String getFileName(@NonNull Uri uri) { if (isAuthority(uri)) { return uri.getPathSegments().get(FILENAME_PATH_SEGMENT); } return null; } - public static @Nullable Long getFileSize(@NonNull Uri uri) { + public @Nullable Long getFileSize(@NonNull Uri uri) { if (isAuthority(uri)) { try { return Long.parseLong(uri.getPathSegments().get(FILESIZE_PATH_SEGMENT)); @@ -345,7 +322,7 @@ public class BlobProvider { return null; } - private static @Nullable String getId(@NonNull Uri uri) { + private @Nullable String getId(@NonNull Uri uri) { if (isAuthority(uri)) { return Uri.encode(uri.getPathSegments().get(ID_PATH_SEGMENT)); } @@ -366,8 +343,15 @@ public class BlobProvider { } } - public static boolean isAuthority(@NonNull Uri uri) { - return URI_MATCHER.match(uri) == MATCH; + public boolean isAuthority(@NonNull Uri uri) { + return uriMatcher.match(uri) == MATCH; + } + + @Nullable + public String buildFileName(@NonNull Uri uri) { + String id = getId(uri); + + return id != null ? buildFileName(id) : null; } @WorkerThread @@ -394,7 +378,7 @@ public class BlobProvider { private synchronized @NonNull Future writeBlobSpecToDiskAsync(@NonNull Context context, @NonNull BlobSpec blobSpec) throws IOException { - AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(); + AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context, secretStore).getOrCreateAttachmentSecret(); String directory = getDirectory(blobSpec.getStorageType()); File outputFile = new File(getOrCreateDirectory(context, directory), buildFileName(blobSpec.id)); OutputStream outputStream = ModernEncryptingPartOutputStream.createFor(attachmentSecret, outputFile, true).getSecond(); @@ -440,14 +424,14 @@ public class BlobProvider { return storageType == StorageType.MULTI_SESSION_DISK ? MULTI_SESSION_DIRECTORY : SINGLE_SESSION_DIRECTORY; } - private static @NonNull Uri buildUri(@NonNull BlobSpec blobSpec) { - return CONTENT_URI.buildUpon() + private @NonNull Uri buildUri(@NonNull BlobSpec blobSpec) { + return contentUri.buildUpon() .appendPath(blobSpec.getStorageType().encode()) - .appendPath(blobSpec.getMimeType()) - .appendPath(blobSpec.getFileName()) - .appendEncodedPath(String.valueOf(blobSpec.getFileSize())) - .appendPath(blobSpec.getId()) - .build(); + .appendPath(blobSpec.getMimeType()) + .appendPath(blobSpec.getFileName()) + .appendEncodedPath(String.valueOf(blobSpec.getFileSize())) + .appendPath(blobSpec.getId()) + .build(); } private static File getOrCreateDirectory(@NonNull Context context, @NonNull String directory) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/AttachmentSecret.java b/core/util/src/main/java/org/signal/core/util/crypto/AttachmentSecret.java similarity index 96% rename from app/src/main/java/org/thoughtcrime/securesms/crypto/AttachmentSecret.java rename to core/util/src/main/java/org/signal/core/util/crypto/AttachmentSecret.java index 8987cf0378..c7ac6a25ed 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/AttachmentSecret.java +++ b/core/util/src/main/java/org/signal/core/util/crypto/AttachmentSecret.java @@ -1,4 +1,9 @@ -package org.thoughtcrime.securesms.crypto; +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.signal.core.util.crypto; import android.util.Base64; diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/AttachmentSecretProvider.java b/core/util/src/main/java/org/signal/core/util/crypto/AttachmentSecretProvider.java similarity index 66% rename from app/src/main/java/org/thoughtcrime/securesms/crypto/AttachmentSecretProvider.java rename to core/util/src/main/java/org/signal/core/util/crypto/AttachmentSecretProvider.java index 98ee6cca18..47eac4088d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/AttachmentSecretProvider.java +++ b/core/util/src/main/java/org/signal/core/util/crypto/AttachmentSecretProvider.java @@ -1,17 +1,20 @@ -package org.thoughtcrime.securesms.crypto; +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.signal.core.util.crypto; import android.content.Context; import androidx.annotation.NonNull; -import org.thoughtcrime.securesms.util.TextSecurePreferences; - import java.security.SecureRandom; /** * A provider that is responsible for creating or retrieving the AttachmentSecret model. - * + *

* On modern Android, the serialized secrets are themselves encrypted using a key that lives * in the system KeyStore, for whatever that is worth. */ @@ -19,24 +22,30 @@ public class AttachmentSecretProvider { private static AttachmentSecretProvider provider; - public static synchronized AttachmentSecretProvider getInstance(@NonNull Context context) { - if (provider == null) provider = new AttachmentSecretProvider(context.getApplicationContext()); + public static synchronized AttachmentSecretProvider getInstance(@NonNull Context context,@NonNull AttachmentSecretStore secretStore) { + if (provider == null) provider = new AttachmentSecretProvider(context.getApplicationContext(), secretStore); return provider; } - private final Context context; + private final Context context; + private final AttachmentSecretStore secretStore; private AttachmentSecret attachmentSecret; - private AttachmentSecretProvider(@NonNull Context context) { - this.context = context.getApplicationContext(); + private AttachmentSecretProvider(@NonNull Context context, @NonNull AttachmentSecretStore secretStore) { + this.context = context.getApplicationContext(); + this.secretStore = secretStore; } + /** + * Because we need this store when we initialize the database, we have a separate method here to allow the app to pass in the concrete [AttachmentSecretStore] + * so that we don't need to reply on [CoreUtilDependencies] which isn't initialized at the time. + */ public synchronized AttachmentSecret getOrCreateAttachmentSecret() { if (attachmentSecret != null) return attachmentSecret; - String unencryptedSecret = TextSecurePreferences.getAttachmentUnencryptedSecret(context); - String encryptedSecret = TextSecurePreferences.getAttachmentEncryptedSecret(context); + String unencryptedSecret = secretStore.getAttachmentUnencryptedSecret(context); + String encryptedSecret = secretStore.getAttachmentEncryptedSecret(context); if (unencryptedSecret != null) attachmentSecret = getUnencryptedAttachmentSecret(context, unencryptedSecret); else if (encryptedSecret != null) attachmentSecret = getEncryptedAttachmentSecret(encryptedSecret); @@ -45,24 +54,14 @@ public class AttachmentSecretProvider { return attachmentSecret; } - public synchronized AttachmentSecret setClassicKey(@NonNull Context context, @NonNull byte[] classicCipherKey, @NonNull byte[] classicMacKey) { - AttachmentSecret currentSecret = getOrCreateAttachmentSecret(); - currentSecret.setClassicCipherKey(classicCipherKey); - currentSecret.setClassicMacKey(classicMacKey); - - storeAttachmentSecret(context, attachmentSecret); - - return attachmentSecret; - } - private AttachmentSecret getUnencryptedAttachmentSecret(@NonNull Context context, @NonNull String unencryptedSecret) { AttachmentSecret attachmentSecret = AttachmentSecret.fromString(unencryptedSecret); KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(attachmentSecret.serialize().getBytes()); - TextSecurePreferences.setAttachmentEncryptedSecret(context, encryptedSecret.serialize()); - TextSecurePreferences.setAttachmentUnencryptedSecret(context, null); + secretStore.setAttachmentEncryptedSecret(context, encryptedSecret.serialize()); + secretStore.setAttachmentUnencryptedSecret(context, null); return attachmentSecret; } @@ -85,6 +84,6 @@ public class AttachmentSecretProvider { private void storeAttachmentSecret(@NonNull Context context, @NonNull AttachmentSecret attachmentSecret) { KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(attachmentSecret.serialize().getBytes()); - TextSecurePreferences.setAttachmentEncryptedSecret(context, encryptedSecret.serialize()); + secretStore.setAttachmentEncryptedSecret(context, encryptedSecret.serialize()); } } diff --git a/core/util/src/main/java/org/signal/core/util/crypto/AttachmentSecretStore.kt b/core/util/src/main/java/org/signal/core/util/crypto/AttachmentSecretStore.kt new file mode 100644 index 0000000000..4874448fe9 --- /dev/null +++ b/core/util/src/main/java/org/signal/core/util/crypto/AttachmentSecretStore.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.signal.core.util.crypto + +import android.content.Context + +interface AttachmentSecretStore { + fun getAttachmentUnencryptedSecret(context: Context): String? + fun getAttachmentEncryptedSecret(context: Context): String? + fun setAttachmentEncryptedSecret(context: Context, secret: String) + fun setAttachmentUnencryptedSecret(context: Context, secret: String?) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/ClassicDecryptingPartInputStream.java b/core/util/src/main/java/org/signal/core/util/crypto/ClassicDecryptingPartInputStream.java similarity index 86% rename from app/src/main/java/org/thoughtcrime/securesms/crypto/ClassicDecryptingPartInputStream.java rename to core/util/src/main/java/org/signal/core/util/crypto/ClassicDecryptingPartInputStream.java index 9f98faa8c4..a7ce08e410 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/ClassicDecryptingPartInputStream.java +++ b/core/util/src/main/java/org/signal/core/util/crypto/ClassicDecryptingPartInputStream.java @@ -1,20 +1,8 @@ -/** - * Copyright (C) 2011 Whisper Systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only */ -package org.thoughtcrime.securesms.crypto; +package org.signal.core.util.crypto; import androidx.annotation.NonNull; diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java b/core/util/src/main/java/org/signal/core/util/crypto/KeyStoreHelper.java similarity index 98% rename from app/src/main/java/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java rename to core/util/src/main/java/org/signal/core/util/crypto/KeyStoreHelper.java index 2870946a76..65a446e700 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java +++ b/core/util/src/main/java/org/signal/core/util/crypto/KeyStoreHelper.java @@ -1,4 +1,9 @@ -package org.thoughtcrime.securesms.crypto; +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.signal.core.util.crypto; import android.security.keystore.KeyGenParameterSpec; diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/ModernDecryptingPartInputStream.java b/core/util/src/main/java/org/signal/core/util/crypto/ModernDecryptingPartInputStream.java similarity index 88% rename from app/src/main/java/org/thoughtcrime/securesms/crypto/ModernDecryptingPartInputStream.java rename to core/util/src/main/java/org/signal/core/util/crypto/ModernDecryptingPartInputStream.java index 8e5bbee351..ca3c47c516 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/ModernDecryptingPartInputStream.java +++ b/core/util/src/main/java/org/signal/core/util/crypto/ModernDecryptingPartInputStream.java @@ -1,4 +1,9 @@ -package org.thoughtcrime.securesms.crypto; +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.signal.core.util.crypto; import androidx.annotation.NonNull; @@ -43,7 +48,7 @@ public class ModernDecryptingPartInputStream { private static InputStream createFor(@NonNull AttachmentSecret attachmentSecret, @NonNull byte[] random, @NonNull InputStream inputStream, long offset) throws IOException { try { - Mac mac = Mac.getInstance("HmacSHA256"); + Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(attachmentSecret.getModernKey(), "HmacSHA256")); byte[] iv = new byte[16]; @@ -74,12 +79,12 @@ public class ModernDecryptingPartInputStream { private static void readFully(InputStream in, byte[] buffer) throws IOException { int offset = 0; - for (;;) { - int read = in.read(buffer, offset, buffer.length-offset); + for (; ; ) { + int read = in.read(buffer, offset, buffer.length - offset); - if (read == -1) throw new IOException(PREMATURE_END_ERROR_MESSAGE); + if (read == -1) throw new IOException(PREMATURE_END_ERROR_MESSAGE); else if (read + offset < buffer.length) offset += read; - else return; + else return; } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/ModernEncryptingPartOutputStream.java b/core/util/src/main/java/org/signal/core/util/crypto/ModernEncryptingPartOutputStream.java similarity index 93% rename from app/src/main/java/org/thoughtcrime/securesms/crypto/ModernEncryptingPartOutputStream.java rename to core/util/src/main/java/org/signal/core/util/crypto/ModernEncryptingPartOutputStream.java index 0751c494d9..e067be03c2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/ModernEncryptingPartOutputStream.java +++ b/core/util/src/main/java/org/signal/core/util/crypto/ModernEncryptingPartOutputStream.java @@ -1,4 +1,9 @@ -package org.thoughtcrime.securesms.crypto; +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.signal.core.util.crypto; import kotlin.Pair;