mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-10-04 20:42:48 +02:00
Reverting the spotlight on the Save button
This commit is contained in:
@@ -29,6 +29,8 @@ import android.os.Environment
|
||||
import android.provider.MediaStore
|
||||
import android.webkit.MimeTypeMap
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.net.toFile
|
||||
import androidx.core.net.toUri
|
||||
import com.vitorpamplona.amethyst.BuildConfig
|
||||
import com.vitorpamplona.amethyst.service.HttpClientManager
|
||||
import kotlinx.coroutines.CancellationException
|
||||
@@ -45,6 +47,33 @@ import java.io.File
|
||||
import java.util.UUID
|
||||
|
||||
object ImageSaver {
|
||||
fun saveImage(
|
||||
videoUri: String?,
|
||||
mimeType: String?,
|
||||
localContext: Context,
|
||||
onSuccess: () -> Any?,
|
||||
onError: (Throwable) -> Any?,
|
||||
) {
|
||||
if (videoUri != null) {
|
||||
if (!videoUri.startsWith("file")) {
|
||||
saveImage(
|
||||
context = localContext,
|
||||
url = videoUri,
|
||||
onSuccess = onSuccess,
|
||||
onError = onError,
|
||||
)
|
||||
} else {
|
||||
saveImage(
|
||||
context = localContext,
|
||||
localFile = videoUri.toUri().toFile(),
|
||||
mimeType = mimeType,
|
||||
onSuccess = onSuccess,
|
||||
onError = onError,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the image to the gallery. May require a storage permission.
|
||||
*
|
||||
|
@@ -20,10 +20,12 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.components
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
@@ -44,6 +46,7 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Download
|
||||
import androidx.compose.material.icons.filled.Share
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
@@ -89,12 +92,15 @@ import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.session.MediaController
|
||||
import androidx.media3.ui.AspectRatioFrameLayout
|
||||
import androidx.media3.ui.PlayerView
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.isGranted
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
import com.linc.audiowaveform.infiniteLinearGradient
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.compose.GenericBaseCache
|
||||
import com.vitorpamplona.amethyst.commons.compose.produceCachedState
|
||||
import com.vitorpamplona.amethyst.commons.richtext.BaseMediaContent
|
||||
import com.vitorpamplona.amethyst.service.playback.PlaybackClientController
|
||||
import com.vitorpamplona.amethyst.ui.actions.ImageSaver
|
||||
import com.vitorpamplona.amethyst.ui.note.DownloadForOfflineIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.LyricsIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.LyricsOffIcon
|
||||
@@ -103,6 +109,7 @@ import com.vitorpamplona.amethyst.ui.note.MutedIcon
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.theme.PinBottomIconSize
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size110dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size165dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size22Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size50Modifier
|
||||
@@ -789,14 +796,13 @@ private fun RenderVideoPlayer(
|
||||
keepPlaying.value = newKeepPlaying
|
||||
}
|
||||
|
||||
AnimatedSaveAndShareButton(
|
||||
videoUri = videoUri,
|
||||
mimeType = mimeType,
|
||||
nostrUriCallback = nostrUriCallback,
|
||||
controllerVisible = controllerVisible,
|
||||
modifier = Modifier.align(Alignment.TopEnd).padding(end = Size110dp),
|
||||
accountViewModel = accountViewModel,
|
||||
)
|
||||
AnimatedSaveButton(controllerVisible, Modifier.align(Alignment.TopEnd).padding(end = Size110dp)) { context ->
|
||||
saveImage(videoUri, mimeType, context, accountViewModel)
|
||||
}
|
||||
|
||||
AnimatedShareButton(controllerVisible, Modifier.align(Alignment.TopEnd).padding(end = Size165dp)) { popupExpanded, toggle ->
|
||||
ShareImageAction(popupExpanded, videoUri, nostrUriCallback, toggle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1055,31 +1061,23 @@ private fun KeepPlayingButton(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AnimatedSaveAndShareButton(
|
||||
videoUri: String,
|
||||
mimeType: String?,
|
||||
nostrUriCallback: String?,
|
||||
fun AnimatedSaveButton(
|
||||
controllerVisible: State<Boolean>,
|
||||
modifier: Modifier,
|
||||
accountViewModel: AccountViewModel,
|
||||
onSaveClick: (localContext: Context) -> Unit,
|
||||
) {
|
||||
AnimatedSaveAndShareButton(controllerVisible, modifier) { popupExpanded, toggle ->
|
||||
ShareImageAction(popupExpanded, videoUri, nostrUriCallback, mimeType, toggle, accountViewModel)
|
||||
AnimatedVisibility(
|
||||
visible = controllerVisible.value,
|
||||
modifier = modifier,
|
||||
enter = remember { fadeIn() },
|
||||
exit = remember { fadeOut() },
|
||||
) {
|
||||
SaveButton(onSaveClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SaveAndShareButton(
|
||||
content: BaseMediaContent,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
SaveAndShareButton { popupExpanded, toggle ->
|
||||
ShareImageAction(popupExpanded, content, toggle, accountViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AnimatedSaveAndShareButton(
|
||||
fun AnimatedShareButton(
|
||||
controllerVisible: State<Boolean>,
|
||||
modifier: Modifier,
|
||||
innerAction: @Composable (MutableState<Boolean>, () -> Unit) -> Unit,
|
||||
@@ -1090,12 +1088,12 @@ fun AnimatedSaveAndShareButton(
|
||||
enter = remember { fadeIn() },
|
||||
exit = remember { fadeOut() },
|
||||
) {
|
||||
SaveAndShareButton(innerAction)
|
||||
ShareButton(innerAction)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SaveAndShareButton(innerAction: @Composable (MutableState<Boolean>, () -> Unit) -> Unit) {
|
||||
fun ShareButton(innerAction: @Composable (MutableState<Boolean>, () -> Unit) -> Unit) {
|
||||
Box(modifier = PinBottomIconSize) {
|
||||
Box(
|
||||
Modifier.clip(CircleShape)
|
||||
@@ -1122,3 +1120,64 @@ fun SaveAndShareButton(innerAction: @Composable (MutableState<Boolean>, () -> Un
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@kotlin.OptIn(ExperimentalPermissionsApi::class)
|
||||
@Composable
|
||||
fun SaveButton(onSaveClick: (localContext: Context) -> Unit) {
|
||||
Box(modifier = PinBottomIconSize) {
|
||||
Box(
|
||||
Modifier.clip(CircleShape)
|
||||
.fillMaxSize(0.6f)
|
||||
.align(Alignment.Center)
|
||||
.background(MaterialTheme.colorScheme.background),
|
||||
)
|
||||
|
||||
val localContext = LocalContext.current
|
||||
|
||||
val writeStoragePermissionState =
|
||||
rememberPermissionState(Manifest.permission.WRITE_EXTERNAL_STORAGE) { isGranted ->
|
||||
if (isGranted) {
|
||||
onSaveClick(localContext)
|
||||
}
|
||||
}
|
||||
|
||||
IconButton(
|
||||
onClick = {
|
||||
if (
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ||
|
||||
writeStoragePermissionState.status.isGranted
|
||||
) {
|
||||
onSaveClick(localContext)
|
||||
} else {
|
||||
writeStoragePermissionState.launchPermissionRequest()
|
||||
}
|
||||
},
|
||||
modifier = Size50Modifier,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Download,
|
||||
modifier = Size20Modifier,
|
||||
contentDescription = stringResource(R.string.save_to_gallery),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveImage(
|
||||
videoUri: String?,
|
||||
mimeType: String?,
|
||||
localContext: Context,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
ImageSaver.saveImage(
|
||||
videoUri = videoUri,
|
||||
mimeType = mimeType,
|
||||
localContext = localContext,
|
||||
onSuccess = {
|
||||
accountViewModel.toast(R.string.image_saved_to_the_gallery, R.string.image_saved_to_the_gallery)
|
||||
},
|
||||
onError = {
|
||||
accountViewModel.toast(R.string.failed_to_save_the_image, null, it)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@@ -20,6 +20,8 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.components
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.view.View
|
||||
import android.view.WindowInsets
|
||||
@@ -29,11 +31,13 @@ import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Arrangement.spacedBy
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -43,6 +47,7 @@ import androidx.compose.foundation.pager.PagerState
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Download
|
||||
import androidx.compose.material.icons.filled.Share
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
@@ -60,6 +65,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.window.Dialog
|
||||
@@ -67,14 +73,21 @@ import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.view.ViewCompat
|
||||
import coil.compose.AsyncImage
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.isGranted
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.richtext.BaseMediaContent
|
||||
import com.vitorpamplona.amethyst.commons.richtext.MediaLocalImage
|
||||
import com.vitorpamplona.amethyst.commons.richtext.MediaLocalVideo
|
||||
import com.vitorpamplona.amethyst.commons.richtext.MediaPreloadedContent
|
||||
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlContent
|
||||
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlImage
|
||||
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlVideo
|
||||
import com.vitorpamplona.amethyst.ui.actions.ImageSaver
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size10dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size15dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size5dp
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
@@ -156,7 +169,7 @@ fun ZoomableImageDialog(
|
||||
}
|
||||
|
||||
@Composable
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@OptIn(ExperimentalFoundationApi::class, ExperimentalPermissionsApi::class)
|
||||
private fun DialogContent(
|
||||
allImages: ImmutableList<BaseMediaContent>,
|
||||
imageUrl: BaseMediaContent,
|
||||
@@ -221,7 +234,7 @@ private fun DialogContent(
|
||||
exit = remember { fadeOut() },
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(Size10dp).statusBarsPadding().systemBarsPadding().fillMaxWidth(),
|
||||
modifier = Modifier.padding(horizontal = Size15dp, vertical = Size10dp).statusBarsPadding().systemBarsPadding().fillMaxWidth(),
|
||||
horizontalArrangement = spacedBy(Size10dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
@@ -236,6 +249,9 @@ private fun DialogContent(
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
allImages.getOrNull(pagerState.currentPage)?.let { myContent ->
|
||||
val popupExpanded = remember { mutableStateOf(false) }
|
||||
|
||||
OutlinedButton(
|
||||
@@ -246,13 +262,75 @@ private fun DialogContent(
|
||||
Icon(
|
||||
imageVector = Icons.Default.Share,
|
||||
modifier = Size20Modifier,
|
||||
contentDescription = stringResource(R.string.share_or_save),
|
||||
contentDescription = stringResource(R.string.quick_action_share),
|
||||
)
|
||||
|
||||
allImages.getOrNull(pagerState.currentPage)?.let { myContent ->
|
||||
ShareImageAction(popupExpanded = popupExpanded, myContent, onDismiss = { popupExpanded.value = false }, accountViewModel = accountViewModel)
|
||||
ShareImageAction(popupExpanded = popupExpanded, myContent, onDismiss = { popupExpanded.value = false })
|
||||
}
|
||||
|
||||
val localContext = LocalContext.current
|
||||
|
||||
val writeStoragePermissionState =
|
||||
rememberPermissionState(Manifest.permission.WRITE_EXTERNAL_STORAGE) { isGranted ->
|
||||
if (isGranted) {
|
||||
saveImage(myContent, localContext, accountViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
OutlinedButton(
|
||||
onClick = {
|
||||
if (
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ||
|
||||
writeStoragePermissionState.status.isGranted
|
||||
) {
|
||||
saveImage(myContent, localContext, accountViewModel)
|
||||
} else {
|
||||
writeStoragePermissionState.launchPermissionRequest()
|
||||
}
|
||||
},
|
||||
contentPadding = PaddingValues(horizontal = Size5dp),
|
||||
colors = ButtonDefaults.outlinedButtonColors().copy(containerColor = MaterialTheme.colorScheme.background),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Download,
|
||||
modifier = Size20Modifier,
|
||||
contentDescription = stringResource(R.string.save_to_gallery),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveImage(
|
||||
content: BaseMediaContent,
|
||||
localContext: Context,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
if (content is MediaUrlContent) {
|
||||
ImageSaver.saveImage(
|
||||
content.url,
|
||||
localContext,
|
||||
onSuccess = {
|
||||
accountViewModel.toast(R.string.image_saved_to_the_gallery, R.string.image_saved_to_the_gallery)
|
||||
},
|
||||
onError = {
|
||||
accountViewModel.toast(R.string.failed_to_save_the_image, null, it)
|
||||
},
|
||||
)
|
||||
} else if (content is MediaPreloadedContent) {
|
||||
content.localFile?.let {
|
||||
ImageSaver.saveImage(
|
||||
it,
|
||||
content.mimeType,
|
||||
localContext,
|
||||
onSuccess = {
|
||||
accountViewModel.toast(R.string.image_saved_to_the_gallery, R.string.image_saved_to_the_gallery)
|
||||
},
|
||||
onError = {
|
||||
accountViewModel.toast(R.string.failed_to_save_the_image, null, it)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -20,16 +20,15 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.components
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.view.Window
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
@@ -73,8 +72,6 @@ import coil.compose.AsyncImagePainter
|
||||
import coil.compose.SubcomposeAsyncImage
|
||||
import coil.compose.SubcomposeAsyncImageContent
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.isGranted
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.richtext.BaseMediaContent
|
||||
@@ -85,7 +82,6 @@ import com.vitorpamplona.amethyst.commons.richtext.MediaUrlContent
|
||||
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlImage
|
||||
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlVideo
|
||||
import com.vitorpamplona.amethyst.service.BlurHashRequester
|
||||
import com.vitorpamplona.amethyst.ui.actions.ImageSaver
|
||||
import com.vitorpamplona.amethyst.ui.actions.InformationDialog
|
||||
import com.vitorpamplona.amethyst.ui.actions.LoadingAnimation
|
||||
import com.vitorpamplona.amethyst.ui.note.BlankNote
|
||||
@@ -142,13 +138,13 @@ fun ZoomableContentView(
|
||||
mainImageModifier = mainImageModifier.clickable { dialogOpen = true }
|
||||
}
|
||||
|
||||
val controllerVisible = remember { mutableStateOf(true) }
|
||||
|
||||
when (content) {
|
||||
is MediaUrlImage ->
|
||||
SensitivityWarning(content.contentWarning != null, accountViewModel) {
|
||||
TwoSecondController(content) { controllerVisible ->
|
||||
UrlImageView(content, mainImageModifier, isFiniteHeight, controllerVisible, accountViewModel = accountViewModel)
|
||||
}
|
||||
}
|
||||
is MediaUrlVideo ->
|
||||
SensitivityWarning(content.contentWarning != null, accountViewModel) {
|
||||
Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
|
||||
@@ -169,7 +165,9 @@ fun ZoomableContentView(
|
||||
}
|
||||
}
|
||||
is MediaLocalImage ->
|
||||
TwoSecondController(content) { controllerVisible ->
|
||||
LocalImageView(content, mainImageModifier, isFiniteHeight, controllerVisible, accountViewModel = accountViewModel)
|
||||
}
|
||||
is MediaLocalVideo ->
|
||||
content.localFile?.let {
|
||||
Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
|
||||
@@ -194,6 +192,25 @@ fun ZoomableContentView(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TwoSecondController(
|
||||
content: BaseMediaContent,
|
||||
inner: @Composable (controllerVisible: MutableState<Boolean>) -> Unit,
|
||||
) {
|
||||
val controllerVisible = remember { mutableStateOf(true) }
|
||||
|
||||
LaunchedEffect(content) {
|
||||
launch(Dispatchers.Default) {
|
||||
delay(2000)
|
||||
withContext(Dispatchers.Main) {
|
||||
controllerVisible.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inner(controllerVisible)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LocalImageView(
|
||||
content: MediaLocalImage,
|
||||
@@ -600,25 +617,20 @@ fun ShareImageAction(
|
||||
popupExpanded: MutableState<Boolean>,
|
||||
content: BaseMediaContent,
|
||||
onDismiss: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
if (content is MediaUrlContent) {
|
||||
ShareImageAction(
|
||||
popupExpanded = popupExpanded,
|
||||
videoUri = content.url,
|
||||
postNostrUri = content.uri,
|
||||
mimeType = content.mimeType,
|
||||
onDismiss = onDismiss,
|
||||
accountViewModel = accountViewModel,
|
||||
)
|
||||
} else if (content is MediaPreloadedContent) {
|
||||
ShareImageAction(
|
||||
popupExpanded = popupExpanded,
|
||||
videoUri = content.localFile?.toUri().toString(),
|
||||
postNostrUri = content.uri,
|
||||
mimeType = content.mimeType,
|
||||
onDismiss = onDismiss,
|
||||
accountViewModel = accountViewModel,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -629,9 +641,7 @@ fun ShareImageAction(
|
||||
popupExpanded: MutableState<Boolean>,
|
||||
videoUri: String?,
|
||||
postNostrUri: String?,
|
||||
mimeType: String?,
|
||||
onDismiss: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
DropdownMenu(
|
||||
expanded = popupExpanded.value,
|
||||
@@ -658,85 +668,6 @@ fun ShareImageAction(
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if (videoUri != null) {
|
||||
if (!videoUri.startsWith("file")) {
|
||||
val localContext = LocalContext.current
|
||||
|
||||
fun saveImage() {
|
||||
ImageSaver.saveImage(
|
||||
context = localContext,
|
||||
url = videoUri,
|
||||
onSuccess = {
|
||||
accountViewModel.toast(R.string.image_saved_to_the_gallery, R.string.image_saved_to_the_gallery)
|
||||
},
|
||||
onError = {
|
||||
accountViewModel.toast(R.string.failed_to_save_the_image, null, it)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
val writeStoragePermissionState =
|
||||
rememberPermissionState(Manifest.permission.WRITE_EXTERNAL_STORAGE) { isGranted ->
|
||||
if (isGranted) {
|
||||
saveImage()
|
||||
}
|
||||
}
|
||||
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.save_to_gallery)) },
|
||||
onClick = {
|
||||
if (
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ||
|
||||
writeStoragePermissionState.status.isGranted
|
||||
) {
|
||||
saveImage()
|
||||
} else {
|
||||
writeStoragePermissionState.launchPermissionRequest()
|
||||
}
|
||||
onDismiss()
|
||||
},
|
||||
)
|
||||
} else {
|
||||
val localContext = LocalContext.current
|
||||
|
||||
fun saveImage() {
|
||||
ImageSaver.saveImage(
|
||||
context = localContext,
|
||||
localFile = videoUri.toUri().toFile(),
|
||||
mimeType = mimeType,
|
||||
onSuccess = {
|
||||
accountViewModel.toast(R.string.image_saved_to_the_gallery, R.string.image_saved_to_the_gallery)
|
||||
},
|
||||
onError = {
|
||||
accountViewModel.toast(R.string.failed_to_save_the_image, null, it)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
val writeStoragePermissionState =
|
||||
rememberPermissionState(Manifest.permission.WRITE_EXTERNAL_STORAGE) { isGranted ->
|
||||
if (isGranted) {
|
||||
saveImage()
|
||||
}
|
||||
}
|
||||
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.save_to_gallery)) },
|
||||
onClick = {
|
||||
if (
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ||
|
||||
writeStoragePermissionState.status.isGranted
|
||||
) {
|
||||
saveImage()
|
||||
} else {
|
||||
writeStoragePermissionState.launchPermissionRequest()
|
||||
}
|
||||
onDismiss()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -97,6 +97,7 @@ val Size40dp = 40.dp
|
||||
val Size55dp = 55.dp
|
||||
val Size75dp = 75.dp
|
||||
val Size110dp = 110.dp
|
||||
val Size165dp = 165.dp
|
||||
|
||||
val HalfEndPadding = Modifier.padding(end = 5.dp)
|
||||
val HalfStartPadding = Modifier.padding(start = 5.dp)
|
||||
|
Reference in New Issue
Block a user