mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-11-10 13:17:34 +01:00
Update to use lightcompressor library version that supports h265
Add h265 toggle to NewMediaView.kt and ImageVideoDescription.kt Touch many files to add optional h265 boolean (default is false)
This commit is contained in:
@@ -151,6 +151,12 @@ android {
|
|||||||
signingConfig = signingConfigs.debug
|
signingConfig = signingConfigs.debug
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// TODO: remove this when lightcompressor uses one MP4 parser only
|
||||||
|
packaging {
|
||||||
|
resources {
|
||||||
|
resources.pickFirsts.add('builddef.lst')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
flavorDimensions = ["channel"]
|
flavorDimensions = ["channel"]
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ class MediaCompressor {
|
|||||||
contentType: String?,
|
contentType: String?,
|
||||||
mediaQuality: CompressorQuality,
|
mediaQuality: CompressorQuality,
|
||||||
applicationContext: Context,
|
applicationContext: Context,
|
||||||
|
useH265: Boolean = false,
|
||||||
): MediaCompressorResult {
|
): MediaCompressorResult {
|
||||||
// Skip compression if user selected uncompressed
|
// Skip compression if user selected uncompressed
|
||||||
if (mediaQuality == CompressorQuality.UNCOMPRESSED) {
|
if (mediaQuality == CompressorQuality.UNCOMPRESSED) {
|
||||||
@@ -57,7 +58,7 @@ class MediaCompressor {
|
|||||||
// branch into compression based on content type
|
// branch into compression based on content type
|
||||||
return when {
|
return when {
|
||||||
contentType?.startsWith("video", ignoreCase = true) == true -> {
|
contentType?.startsWith("video", ignoreCase = true) == true -> {
|
||||||
VideoCompressionHelper.compressVideo(uri, contentType, applicationContext, mediaQuality)
|
VideoCompressionHelper.compressVideo(uri, contentType, applicationContext, mediaQuality, useH265)
|
||||||
}
|
}
|
||||||
contentType?.startsWith("image", ignoreCase = true) == true &&
|
contentType?.startsWith("image", ignoreCase = true) == true &&
|
||||||
!contentType.contains("gif") &&
|
!contentType.contains("gif") &&
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ class MultiOrchestrator(
|
|||||||
|
|
||||||
fun first() = list.first()
|
fun first() = list.first()
|
||||||
|
|
||||||
|
fun hasVideo() = list.any { it.media.mimeType?.startsWith("video", ignoreCase = true) == true }
|
||||||
|
|
||||||
suspend fun upload(
|
suspend fun upload(
|
||||||
alt: String?,
|
alt: String?,
|
||||||
contentWarningReason: String?,
|
contentWarningReason: String?,
|
||||||
@@ -53,6 +55,7 @@ class MultiOrchestrator(
|
|||||||
server: ServerName,
|
server: ServerName,
|
||||||
account: Account,
|
account: Account,
|
||||||
context: Context,
|
context: Context,
|
||||||
|
useH265: Boolean = false,
|
||||||
): Result {
|
): Result {
|
||||||
coroutineScope {
|
coroutineScope {
|
||||||
val jobs =
|
val jobs =
|
||||||
@@ -67,6 +70,7 @@ class MultiOrchestrator(
|
|||||||
server,
|
server,
|
||||||
account,
|
account,
|
||||||
context,
|
context,
|
||||||
|
useH265,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,6 +89,7 @@ class MultiOrchestrator(
|
|||||||
server: ServerName,
|
server: ServerName,
|
||||||
account: Account,
|
account: Account,
|
||||||
context: Context,
|
context: Context,
|
||||||
|
useH265: Boolean = false,
|
||||||
): Result {
|
): Result {
|
||||||
coroutineScope {
|
coroutineScope {
|
||||||
val jobs =
|
val jobs =
|
||||||
@@ -100,6 +105,7 @@ class MultiOrchestrator(
|
|||||||
server,
|
server,
|
||||||
account,
|
account,
|
||||||
context,
|
context,
|
||||||
|
useH265,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -288,9 +288,10 @@ class UploadOrchestrator {
|
|||||||
mimeType: String?,
|
mimeType: String?,
|
||||||
compressionQuality: CompressorQuality,
|
compressionQuality: CompressorQuality,
|
||||||
context: Context,
|
context: Context,
|
||||||
|
useH265: Boolean = false,
|
||||||
) = if (compressionQuality != CompressorQuality.UNCOMPRESSED) {
|
) = if (compressionQuality != CompressorQuality.UNCOMPRESSED) {
|
||||||
updateState(0.02, UploadingState.Compressing)
|
updateState(0.02, UploadingState.Compressing)
|
||||||
MediaCompressor().compress(uri, mimeType, compressionQuality, context.applicationContext)
|
MediaCompressor().compress(uri, mimeType, compressionQuality, context.applicationContext, useH265)
|
||||||
} else {
|
} else {
|
||||||
MediaCompressorResult(uri, mimeType, null)
|
MediaCompressorResult(uri, mimeType, null)
|
||||||
}
|
}
|
||||||
@@ -304,8 +305,9 @@ class UploadOrchestrator {
|
|||||||
server: ServerName,
|
server: ServerName,
|
||||||
account: Account,
|
account: Account,
|
||||||
context: Context,
|
context: Context,
|
||||||
|
useH265: Boolean = false,
|
||||||
): UploadingFinalState {
|
): UploadingFinalState {
|
||||||
val compressed = compressIfNeeded(uri, mimeType, compressionQuality, context)
|
val compressed = compressIfNeeded(uri, mimeType, compressionQuality, context, useH265)
|
||||||
|
|
||||||
return when (server.type) {
|
return when (server.type) {
|
||||||
ServerType.NIP95 -> uploadNIP95(compressed.uri, compressed.contentType, null, null, context)
|
ServerType.NIP95 -> uploadNIP95(compressed.uri, compressed.contentType, null, null, context)
|
||||||
@@ -324,8 +326,9 @@ class UploadOrchestrator {
|
|||||||
server: ServerName,
|
server: ServerName,
|
||||||
account: Account,
|
account: Account,
|
||||||
context: Context,
|
context: Context,
|
||||||
|
useH265: Boolean = false,
|
||||||
): UploadingFinalState {
|
): UploadingFinalState {
|
||||||
val compressed = compressIfNeeded(uri, mimeType, compressionQuality, context)
|
val compressed = compressIfNeeded(uri, mimeType, compressionQuality, context, useH265)
|
||||||
val encrypted = EncryptFiles().encryptFile(context, compressed.uri, encrypt)
|
val encrypted = EncryptFiles().encryptFile(context, compressed.uri, encrypt)
|
||||||
|
|
||||||
return when (server.type) {
|
return when (server.type) {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import android.text.format.Formatter.formatFileSize
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.abedelazizshe.lightcompressorlibrary.CompressionListener
|
import com.abedelazizshe.lightcompressorlibrary.CompressionListener
|
||||||
|
import com.abedelazizshe.lightcompressorlibrary.VideoCodec
|
||||||
import com.abedelazizshe.lightcompressorlibrary.VideoCompressor
|
import com.abedelazizshe.lightcompressorlibrary.VideoCompressor
|
||||||
import com.abedelazizshe.lightcompressorlibrary.config.AppSpecificStorageConfiguration
|
import com.abedelazizshe.lightcompressorlibrary.config.AppSpecificStorageConfiguration
|
||||||
import com.abedelazizshe.lightcompressorlibrary.config.Configuration
|
import com.abedelazizshe.lightcompressorlibrary.config.Configuration
|
||||||
@@ -141,6 +142,7 @@ object VideoCompressionHelper {
|
|||||||
contentType: String?,
|
contentType: String?,
|
||||||
applicationContext: Context,
|
applicationContext: Context,
|
||||||
mediaQuality: CompressorQuality,
|
mediaQuality: CompressorQuality,
|
||||||
|
useH265: Boolean = false,
|
||||||
timeoutMs: Long = 60_000L, // configurable, default 60s
|
timeoutMs: Long = 60_000L, // configurable, default 60s
|
||||||
): MediaCompressorResult {
|
): MediaCompressorResult {
|
||||||
val videoInfo = getVideoInfo(uri, applicationContext)
|
val videoInfo = getVideoInfo(uri, applicationContext)
|
||||||
@@ -186,6 +188,7 @@ object VideoCompressionHelper {
|
|||||||
resizer = resizer,
|
resizer = resizer,
|
||||||
videoNames = listOf(UUID.randomUUID().toString()),
|
videoNames = listOf(UUID.randomUUID().toString()),
|
||||||
isMinBitrateCheckEnabled = false,
|
isMinBitrateCheckEnabled = false,
|
||||||
|
videoCodec = if (useH265) VideoCodec.H265 else VideoCodec.H264,
|
||||||
),
|
),
|
||||||
listener =
|
listener =
|
||||||
object : CompressionListener {
|
object : CompressionListener {
|
||||||
|
|||||||
@@ -261,7 +261,7 @@ fun EditPostView(
|
|||||||
ImageVideoDescription(
|
ImageVideoDescription(
|
||||||
it,
|
it,
|
||||||
accountViewModel.account.settings.defaultFileServer,
|
accountViewModel.account.settings.defaultFileServer,
|
||||||
onAdd = { alt, server, sensitiveContent, mediaQuality ->
|
onAdd = { alt, server, sensitiveContent, mediaQuality, _ ->
|
||||||
postViewModel.upload(alt, sensitiveContent, mediaQuality, false, server, accountViewModel.toastManager::toast, context)
|
postViewModel.upload(alt, sensitiveContent, mediaQuality, false, server, accountViewModel.toastManager::toast, context)
|
||||||
if (server.type != ServerType.NIP95) {
|
if (server.type != ServerType.NIP95) {
|
||||||
accountViewModel.account.settings.changeDefaultFileServer(server)
|
accountViewModel.account.settings.changeDefaultFileServer(server)
|
||||||
|
|||||||
@@ -62,6 +62,9 @@ open class NewMediaModel : ViewModel() {
|
|||||||
// 0 = Low, 1 = Medium, 2 = High, 3=UNCOMPRESSED
|
// 0 = Low, 1 = Medium, 2 = High, 3=UNCOMPRESSED
|
||||||
var mediaQualitySlider by mutableIntStateOf(1)
|
var mediaQualitySlider by mutableIntStateOf(1)
|
||||||
|
|
||||||
|
// Codec selection: false = H264, true = H265
|
||||||
|
var useH265Codec by mutableStateOf(false)
|
||||||
|
|
||||||
open fun load(
|
open fun load(
|
||||||
account: Account,
|
account: Account,
|
||||||
uris: ImmutableList<SelectedMedia>,
|
uris: ImmutableList<SelectedMedia>,
|
||||||
@@ -111,6 +114,7 @@ open class NewMediaModel : ViewModel() {
|
|||||||
serverToUse,
|
serverToUse,
|
||||||
myAccount,
|
myAccount,
|
||||||
context,
|
context,
|
||||||
|
useH265Codec,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (results.allGood) {
|
if (results.allGood) {
|
||||||
|
|||||||
@@ -261,4 +261,18 @@ fun ImageVideoPost(
|
|||||||
steps = 2,
|
steps = 2,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only show H.265 codec option if there are videos in the upload
|
||||||
|
if (postViewModel.multiOrchestrator?.hasVideo() == true) {
|
||||||
|
SettingSwitchItem(
|
||||||
|
title = R.string.video_codec_h265_label,
|
||||||
|
description = R.string.video_codec_h265_description,
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(top = 8.dp),
|
||||||
|
checked = postViewModel.useH265Codec,
|
||||||
|
onCheckedChange = { postViewModel.useH265Codec = it },
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ import kotlinx.collections.immutable.toImmutableList
|
|||||||
fun ImageVideoDescription(
|
fun ImageVideoDescription(
|
||||||
uris: MultiOrchestrator,
|
uris: MultiOrchestrator,
|
||||||
defaultServer: ServerName,
|
defaultServer: ServerName,
|
||||||
onAdd: (String, ServerName, Boolean, Int) -> Unit,
|
onAdd: (String, ServerName, Boolean, Int, Boolean) -> Unit,
|
||||||
onDelete: (SelectedMediaProcessing) -> Unit,
|
onDelete: (SelectedMediaProcessing) -> Unit,
|
||||||
onCancel: () -> Unit,
|
onCancel: () -> Unit,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
@@ -91,7 +91,7 @@ fun ImageVideoDescription(
|
|||||||
uris: MultiOrchestrator,
|
uris: MultiOrchestrator,
|
||||||
defaultServer: ServerName,
|
defaultServer: ServerName,
|
||||||
includeNIP95: Boolean,
|
includeNIP95: Boolean,
|
||||||
onAdd: (String, ServerName, Boolean, Int) -> Unit,
|
onAdd: (String, ServerName, Boolean, Int, Boolean) -> Unit,
|
||||||
onDelete: (SelectedMediaProcessing) -> Unit,
|
onDelete: (SelectedMediaProcessing) -> Unit,
|
||||||
onCancel: () -> Unit,
|
onCancel: () -> Unit,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
@@ -128,6 +128,9 @@ fun ImageVideoDescription(
|
|||||||
// 0 = Low, 1 = Medium, 2 = High, 3=UNCOMPRESSED
|
// 0 = Low, 1 = Medium, 2 = High, 3=UNCOMPRESSED
|
||||||
var mediaQualitySlider by remember { mutableIntStateOf(1) }
|
var mediaQualitySlider by remember { mutableIntStateOf(1) }
|
||||||
|
|
||||||
|
// Codec selection: false = H264, true = H265
|
||||||
|
var useH265Codec by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
@@ -294,32 +297,40 @@ fun ImageVideoDescription(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Row(
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
Box(modifier = Modifier.fillMaxWidth()) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
Text(
|
||||||
) {
|
text =
|
||||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
when (mediaQualitySlider) {
|
||||||
Box(modifier = Modifier.fillMaxWidth()) {
|
0 -> stringRes(R.string.media_compression_quality_low)
|
||||||
Text(
|
1 -> stringRes(R.string.media_compression_quality_medium)
|
||||||
text =
|
2 -> stringRes(R.string.media_compression_quality_high)
|
||||||
when (mediaQualitySlider) {
|
3 -> stringRes(R.string.media_compression_quality_uncompressed)
|
||||||
0 -> stringRes(R.string.media_compression_quality_low)
|
else -> stringRes(R.string.media_compression_quality_medium)
|
||||||
1 -> stringRes(R.string.media_compression_quality_medium)
|
},
|
||||||
2 -> stringRes(R.string.media_compression_quality_high)
|
modifier = Modifier.align(Alignment.Center),
|
||||||
3 -> stringRes(R.string.media_compression_quality_uncompressed)
|
|
||||||
else -> stringRes(R.string.media_compression_quality_medium)
|
|
||||||
},
|
|
||||||
modifier = Modifier.align(Alignment.Center),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Slider(
|
|
||||||
value = mediaQualitySlider.toFloat(),
|
|
||||||
onValueChange = { mediaQualitySlider = it.toInt() },
|
|
||||||
valueRange = 0f..3f,
|
|
||||||
steps = 2,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Slider(
|
||||||
|
value = mediaQualitySlider.toFloat(),
|
||||||
|
onValueChange = { mediaQualitySlider = it.toInt() },
|
||||||
|
valueRange = 0f..3f,
|
||||||
|
steps = 2,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uris.first().media.isVideo() == true) {
|
||||||
|
SettingSwitchItem(
|
||||||
|
title = R.string.video_codec_h265_label,
|
||||||
|
description = R.string.video_codec_h265_description,
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(top = 8.dp),
|
||||||
|
checked = useH265Codec,
|
||||||
|
onCheckedChange = { useH265Codec = it },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Button(
|
Button(
|
||||||
@@ -327,7 +338,7 @@ fun ImageVideoDescription(
|
|||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(vertical = 10.dp),
|
.padding(vertical = 10.dp),
|
||||||
onClick = { onAdd(message, selectedServer, sensitiveContent, mediaQualitySlider) },
|
onClick = { onAdd(message, selectedServer, sensitiveContent, mediaQualitySlider, useH265Codec) },
|
||||||
shape = QuoteBorder,
|
shape = QuoteBorder,
|
||||||
colors =
|
colors =
|
||||||
ButtonDefaults.buttonColors(
|
ButtonDefaults.buttonColors(
|
||||||
|
|||||||
@@ -285,7 +285,7 @@ private fun GenericCommentPostBody(
|
|||||||
ImageVideoDescription(
|
ImageVideoDescription(
|
||||||
it,
|
it,
|
||||||
accountViewModel.account.settings.defaultFileServer,
|
accountViewModel.account.settings.defaultFileServer,
|
||||||
onAdd = { alt, server, sensitiveContent, mediaQuality ->
|
onAdd = { alt, server, sensitiveContent, mediaQuality, _ ->
|
||||||
postViewModel.upload(alt, if (sensitiveContent) "" else null, mediaQuality, server, accountViewModel.toastManager::toast, context)
|
postViewModel.upload(alt, if (sensitiveContent) "" else null, mediaQuality, server, accountViewModel.toastManager::toast, context)
|
||||||
if (server.type != ServerType.NIP95) {
|
if (server.type != ServerType.NIP95) {
|
||||||
accountViewModel.account.settings.changeDefaultFileServer(server)
|
accountViewModel.account.settings.changeDefaultFileServer(server)
|
||||||
|
|||||||
@@ -278,7 +278,7 @@ fun GroupDMScreenContent(
|
|||||||
ImageVideoDescription(
|
ImageVideoDescription(
|
||||||
selectedFiles,
|
selectedFiles,
|
||||||
accountViewModel.account.settings.defaultFileServer,
|
accountViewModel.account.settings.defaultFileServer,
|
||||||
onAdd = { alt, server, sensitiveContent, mediaQuality ->
|
onAdd = { alt, server, sensitiveContent, mediaQuality, _ ->
|
||||||
postViewModel.uploadAndHold(
|
postViewModel.uploadAndHold(
|
||||||
accountViewModel.toastManager::toast,
|
accountViewModel.toastManager::toast,
|
||||||
context,
|
context,
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ private fun NewProductBody(
|
|||||||
uris = it,
|
uris = it,
|
||||||
defaultServer = accountViewModel.account.settings.defaultFileServer,
|
defaultServer = accountViewModel.account.settings.defaultFileServer,
|
||||||
includeNIP95 = false,
|
includeNIP95 = false,
|
||||||
onAdd = { alt, server, sensitiveContent, mediaQuality ->
|
onAdd = { alt, server, sensitiveContent, mediaQuality, _ ->
|
||||||
postViewModel.upload(alt, if (sensitiveContent) "" else null, mediaQuality, server, accountViewModel.toastManager::toast, context)
|
postViewModel.upload(alt, if (sensitiveContent) "" else null, mediaQuality, server, accountViewModel.toastManager::toast, context)
|
||||||
if (server.type != ServerType.NIP95) {
|
if (server.type != ServerType.NIP95) {
|
||||||
accountViewModel.account.settings.changeDefaultFileServer(server)
|
accountViewModel.account.settings.changeDefaultFileServer(server)
|
||||||
|
|||||||
@@ -326,7 +326,7 @@ private fun NewPostScreenBody(
|
|||||||
ImageVideoDescription(
|
ImageVideoDescription(
|
||||||
it,
|
it,
|
||||||
accountViewModel.account.settings.defaultFileServer,
|
accountViewModel.account.settings.defaultFileServer,
|
||||||
onAdd = { alt, server, sensitiveContent, mediaQuality ->
|
onAdd = { alt, server, sensitiveContent, mediaQuality, _ ->
|
||||||
postViewModel.upload(alt, if (sensitiveContent) "" else null, mediaQuality, server, accountViewModel.toastManager::toast, context)
|
postViewModel.upload(alt, if (sensitiveContent) "" else null, mediaQuality, server, accountViewModel.toastManager::toast, context)
|
||||||
if (server.type != ServerType.NIP95) {
|
if (server.type != ServerType.NIP95) {
|
||||||
accountViewModel.account.settings.changeDefaultFileServer(server)
|
accountViewModel.account.settings.changeDefaultFileServer(server)
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ fun PublicMessageScreenContent(
|
|||||||
ImageVideoDescription(
|
ImageVideoDescription(
|
||||||
it,
|
it,
|
||||||
accountViewModel.account.settings.defaultFileServer,
|
accountViewModel.account.settings.defaultFileServer,
|
||||||
onAdd = { alt, server, sensitiveContent, mediaQuality ->
|
onAdd = { alt, server, sensitiveContent, mediaQuality, _ ->
|
||||||
postViewModel.upload(alt, if (sensitiveContent) "" else null, mediaQuality, server, accountViewModel.toastManager::toast, context)
|
postViewModel.upload(alt, if (sensitiveContent) "" else null, mediaQuality, server, accountViewModel.toastManager::toast, context)
|
||||||
if (server.type != ServerType.NIP95) {
|
if (server.type != ServerType.NIP95) {
|
||||||
accountViewModel.account.settings.changeDefaultFileServer(server)
|
accountViewModel.account.settings.changeDefaultFileServer(server)
|
||||||
|
|||||||
@@ -1039,6 +1039,8 @@
|
|||||||
<string name="media_compression_quality_medium">Medium</string>
|
<string name="media_compression_quality_medium">Medium</string>
|
||||||
<string name="media_compression_quality_high">High</string>
|
<string name="media_compression_quality_high">High</string>
|
||||||
<string name="media_compression_quality_uncompressed">Uncompressed</string>
|
<string name="media_compression_quality_uncompressed">Uncompressed</string>
|
||||||
|
<string name="video_codec_h265_label">Use H.265/HEVC Codec</string>
|
||||||
|
<string name="video_codec_h265_description">Better quality at smaller file sizes but not all devices support H.265 playback.</string>
|
||||||
|
|
||||||
<string name="edit_draft">Edit draft</string>
|
<string name="edit_draft">Edit draft</string>
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ languageId = "17.0.6"
|
|||||||
lazysodiumAndroid = "5.2.0"
|
lazysodiumAndroid = "5.2.0"
|
||||||
lazysodiumJava = "5.2.0"
|
lazysodiumJava = "5.2.0"
|
||||||
lifecycleRuntimeKtx = "2.9.4"
|
lifecycleRuntimeKtx = "2.9.4"
|
||||||
lightcompressor = "1.4.0"
|
lightcompressor = "51defaaa8d"
|
||||||
markdown = "e1151c8"
|
markdown = "e1151c8"
|
||||||
media3 = "1.8.0"
|
media3 = "1.8.0"
|
||||||
mockk = "1.14.5"
|
mockk = "1.14.5"
|
||||||
|
|||||||
Reference in New Issue
Block a user