mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-08-06 05:14:50 +01:00
Add ability to take a photo in media-send feature module.
This commit is contained in:
@@ -1171,7 +1171,7 @@ class MainActivity :
|
||||
} else if (SignalStore.internal.useNewMediaActivity) {
|
||||
mediaSendLauncher.launch(
|
||||
MediaSendActivityContract.Args(
|
||||
isCameraFirst = false,
|
||||
isCameraFirst = true,
|
||||
isStory = destination == MainNavigationListLocation.STORIES
|
||||
)
|
||||
)
|
||||
|
||||
+7
@@ -6,11 +6,14 @@
|
||||
package org.thoughtcrime.securesms.dependencies
|
||||
|
||||
import androidx.media3.exoplayer.ExoPlayer
|
||||
import org.signal.core.util.contentproviders.BlobProvider
|
||||
import org.signal.mediasend.MediaSendDependencies
|
||||
import org.signal.mediasend.MediaSendQrRepository
|
||||
import org.signal.mediasend.MediaSendRepository
|
||||
import org.signal.mediasend.preupload.PreUploadRepository
|
||||
import org.signal.video.exo.ExoPlayerPool
|
||||
import org.thoughtcrime.securesms.mediasend.v3.MediaSendV3PreUploadRepository
|
||||
import org.thoughtcrime.securesms.mediasend.v3.MediaSendV3QrRepository
|
||||
import org.thoughtcrime.securesms.mediasend.v3.MediaSendV3Repository
|
||||
|
||||
object MediaSendDependenciesProvider : MediaSendDependencies.Provider {
|
||||
@@ -18,5 +21,9 @@ object MediaSendDependenciesProvider : MediaSendDependencies.Provider {
|
||||
|
||||
override fun providePreUploadRepository(): PreUploadRepository = MediaSendV3PreUploadRepository
|
||||
|
||||
override fun provideQrRepository(): MediaSendQrRepository = MediaSendV3QrRepository
|
||||
|
||||
override fun provideExoPlayerPool(): ExoPlayerPool<ExoPlayer> = AppDependencies.exoPlayerPool
|
||||
|
||||
override fun provideBlobs(): BlobProvider = AppDependencies.blobs
|
||||
}
|
||||
|
||||
+2
-82
@@ -1,24 +1,16 @@
|
||||
package org.thoughtcrime.securesms.mediasend.v2.capture
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ContentUris
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.MediaStore
|
||||
import androidx.annotation.WorkerThread
|
||||
import org.signal.core.models.media.Media
|
||||
import org.signal.core.util.CursorUtil
|
||||
import org.signal.core.util.ContentTypeUtil
|
||||
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.util.MediaUtil
|
||||
import org.thoughtcrime.securesms.video.videoconverter.utils.VideoConstants
|
||||
import java.io.FileDescriptor
|
||||
import java.io.FileInputStream
|
||||
import java.io.IOException
|
||||
import java.util.LinkedList
|
||||
|
||||
class MediaCaptureRepository(context: Context) {
|
||||
|
||||
@@ -30,7 +22,7 @@ class MediaCaptureRepository(context: Context) {
|
||||
dataSupplier = { data },
|
||||
getLength = { data.size.toLong() },
|
||||
createBlobBuilder = { blobProvider, bytes, _ -> blobProvider.forData(bytes) },
|
||||
mimeType = MediaUtil.IMAGE_JPEG,
|
||||
mimeType = ContentTypeUtil.IMAGE_JPEG,
|
||||
width = width,
|
||||
height = height
|
||||
)
|
||||
@@ -96,76 +88,4 @@ class MediaCaptureRepository(context: Context) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@SuppressLint("VisibleForTests")
|
||||
@WorkerThread
|
||||
private fun getMediaInBucket(context: Context, bucketId: String, contentUri: Uri, isImage: Boolean): List<Media> {
|
||||
val media: MutableList<Media> = LinkedList()
|
||||
var selection: String? = MediaStore.Images.Media.BUCKET_ID + " = ? AND " + isNotPending()
|
||||
var selectionArgs: Array<String>? = arrayOf(bucketId)
|
||||
val sortBy = MediaStore.Images.Media.DATE_MODIFIED + " DESC"
|
||||
|
||||
val projection: Array<String> = if (isImage) {
|
||||
arrayOf(MediaStore.Images.Media._ID, MediaStore.Images.Media.MIME_TYPE, MediaStore.Images.Media.DATE_MODIFIED, MediaStore.Images.Media.ORIENTATION, MediaStore.Images.Media.WIDTH, MediaStore.Images.Media.HEIGHT, MediaStore.Images.Media.SIZE)
|
||||
} else {
|
||||
arrayOf(MediaStore.Images.Media._ID, MediaStore.Images.Media.MIME_TYPE, MediaStore.Images.Media.DATE_MODIFIED, MediaStore.Images.Media.WIDTH, MediaStore.Images.Media.HEIGHT, MediaStore.Images.Media.SIZE, MediaStore.Video.Media.DURATION)
|
||||
}
|
||||
|
||||
if (Media.ALL_MEDIA_BUCKET_ID == bucketId) {
|
||||
selection = isNotPending()
|
||||
selectionArgs = null
|
||||
}
|
||||
|
||||
context.contentResolver.query(contentUri, projection, selection, selectionArgs, sortBy).use { cursor ->
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
val rowId = CursorUtil.requireLong(cursor, projection[0])
|
||||
val uri = ContentUris.withAppendedId(contentUri, rowId)
|
||||
val mimetype = CursorUtil.requireString(cursor, MediaStore.Images.Media.MIME_TYPE)
|
||||
val date = CursorUtil.requireLong(cursor, MediaStore.Images.Media.DATE_MODIFIED)
|
||||
val orientation = if (isImage) CursorUtil.requireInt(cursor, MediaStore.Images.Media.ORIENTATION) else 0
|
||||
val width = CursorUtil.requireInt(cursor, getWidthColumn(orientation))
|
||||
val height = CursorUtil.requireInt(cursor, getHeightColumn(orientation))
|
||||
val size = CursorUtil.requireLong(cursor, MediaStore.Images.Media.SIZE)
|
||||
val duration = if (!isImage) CursorUtil.requireInt(cursor, MediaStore.Video.Media.DURATION).toLong() else 0.toLong()
|
||||
media.add(
|
||||
MediaRepository.fixMimeType(
|
||||
context,
|
||||
Media(
|
||||
uri = uri,
|
||||
contentType = mimetype,
|
||||
date = date,
|
||||
width = width,
|
||||
height = height,
|
||||
size = size,
|
||||
duration = duration,
|
||||
isBorderless = false,
|
||||
isVideoGif = false,
|
||||
bucketId = bucketId,
|
||||
caption = null,
|
||||
transformProperties = null,
|
||||
fileName = null
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return media
|
||||
}
|
||||
|
||||
private fun getWidthColumn(orientation: Int): String {
|
||||
return if (orientation == 0 || orientation == 180) MediaStore.Images.Media.WIDTH else MediaStore.Images.Media.HEIGHT
|
||||
}
|
||||
|
||||
private fun getHeightColumn(orientation: Int): String {
|
||||
return if (orientation == 0 || orientation == 180) MediaStore.Images.Media.HEIGHT else MediaStore.Images.Media.WIDTH
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun isNotPending(): String {
|
||||
return if (Build.VERSION.SDK_INT <= 28) MediaStore.Images.Media.DATA + " NOT NULL" else MediaStore.MediaColumns.IS_PENDING + " != 1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,9 @@ class MediaSendV3Activity : PassphraseRequiredActivity() {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// TODO - QR HudCommands (GoToConversation, GoToLinkedDevices, GoToQuickTransfer) not yet wired up.
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2026 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.mediasend.v3
|
||||
|
||||
import kotlinx.coroutines.rx3.await
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.mediasend.MediaRecipientId
|
||||
import org.signal.mediasend.MediaSendQrRepository
|
||||
import org.thoughtcrime.securesms.profiles.manage.UsernameRepository
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.registration.data.QuickRegistrationRepository
|
||||
|
||||
object MediaSendV3QrRepository : MediaSendQrRepository {
|
||||
|
||||
private val TAG = Log.tag(MediaSendV3QrRepository::class)
|
||||
|
||||
override suspend fun checkQrData(qrData: String): MediaSendQrRepository.QrCheckResult {
|
||||
return when {
|
||||
UsernameRepository.isValidLink(qrData) -> handleUsernameLink(qrData)
|
||||
qrData.startsWith("sgnl://linkdevice") -> handleLinkDevice()
|
||||
qrData.startsWith("sgnl://rereg") && QuickRegistrationRepository.isValidReRegistrationQr(qrData) -> handleReReg(qrData)
|
||||
else -> MediaSendQrRepository.QrCheckResult.None
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun handleUsernameLink(qrData: String): MediaSendQrRepository.QrCheckResult {
|
||||
return when (val result = UsernameRepository.fetchUsernameAndAciFromLink(qrData).await()) {
|
||||
is UsernameRepository.UsernameLinkConversionResult.Success -> {
|
||||
val username = result.username.toString()
|
||||
val recipient = Recipient.externalUsername(result.aci, result.username.toString())
|
||||
|
||||
MediaSendQrRepository.QrCheckResult.Username(
|
||||
recipientId = MediaRecipientId(recipient.id.toLong()),
|
||||
username = username
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
Log.w(TAG, "Failed to scan QR code")
|
||||
MediaSendQrRepository.QrCheckResult.None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleLinkDevice(): MediaSendQrRepository.QrCheckResult {
|
||||
return MediaSendQrRepository.QrCheckResult.LinkDevice
|
||||
}
|
||||
|
||||
private fun handleReReg(qrData: String): MediaSendQrRepository.QrCheckResult {
|
||||
return MediaSendQrRepository.QrCheckResult.ReRegistration(qrData)
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,8 @@ import org.thoughtcrime.securesms.util.RemoteConfig
|
||||
import java.io.InputStream
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
/**
|
||||
@@ -195,6 +197,12 @@ object MediaSendV3Repository : MediaSendRepository {
|
||||
return !RemoteConfig.cameraXMixedModelBlocklist.asListContains(Build.MODEL)
|
||||
}
|
||||
|
||||
override fun getMediaConstraints(): MediaConstraints {
|
||||
return PushMediaConstraints(null)
|
||||
}
|
||||
|
||||
override var storyMaxVideoDuration: Duration = Stories.MAX_VIDEO_DURATION_MILLIS.milliseconds
|
||||
|
||||
private fun resolveSendType(sendType: Int): MessageSendType {
|
||||
return when (sendType) {
|
||||
else -> MessageSendType.SignalMessageSendType
|
||||
|
||||
Reference in New Issue
Block a user