diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt index 7e0d6b7cc..7fd0e1e62 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt @@ -21,11 +21,9 @@ package com.vitorpamplona.amethyst.ui.actions import android.Manifest -import android.content.Context import android.graphics.Bitmap import android.net.Uri import android.os.Build -import android.os.Environment import android.util.Log import android.util.Size import android.widget.Toast @@ -123,7 +121,6 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties -import androidx.core.content.FileProvider import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel @@ -189,11 +186,7 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch -import java.io.File import java.lang.Math.round -import java.text.SimpleDateFormat -import java.util.Date -import java.util.Locale @OptIn(ExperimentalMaterial3Api::class, FlowPreview::class) @Composable @@ -645,23 +638,6 @@ private fun BottomRowActions(postViewModel: NewPostViewModel) { } } -fun getPhotoUri(context: Context): Uri { - val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date()) - val storageDir: File? = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) - return File - .createTempFile( - "JPEG_${timeStamp}_", - ".jpg", - storageDir, - ).let { - FileProvider.getUriForFile( - context, - "${context.packageName}.provider", - it, - ) - } -} - @OptIn(ExperimentalPermissionsApi::class) @Composable fun TakePictureButton(onPictureTaken: (Uri) -> Unit) { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/UploadFromGallery.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/UploadFromGallery.kt index 8c8d6b3a8..ff5560063 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/UploadFromGallery.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/UploadFromGallery.kt @@ -20,8 +20,10 @@ */ package com.vitorpamplona.amethyst.ui.actions +import android.content.Context import android.net.Uri import android.os.Build +import android.os.Environment import androidx.activity.compose.rememberLauncherForActivityResult import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.animateFloat @@ -52,6 +54,7 @@ import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import androidx.core.content.FileProvider import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.isGranted import com.google.accompanist.permissions.rememberPermissionState @@ -60,6 +63,10 @@ import com.vitorpamplona.amethyst.ui.GetMediaActivityResultContract import com.vitorpamplona.amethyst.ui.stringRes import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList +import java.io.File +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale import java.util.concurrent.atomic.AtomicBoolean @OptIn(ExperimentalPermissionsApi::class) @@ -125,6 +132,23 @@ private fun UploadBoxButton( } } +fun getPhotoUri(context: Context): Uri { + val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date()) + val storageDir: File? = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) + return File + .createTempFile( + "JPEG_${timeStamp}_", + ".jpg", + storageDir, + ).let { + FileProvider.getUriForFile( + context, + "${context.packageName}.provider", + it, + ) + } +} + val DefaultAnimationColors = listOf( Color(0xFF5851D8), diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/NewImageButton.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/NewImageButton.kt index d73a27518..60110f49a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/NewImageButton.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/video/NewImageButton.kt @@ -23,11 +23,19 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.video import android.Manifest import android.net.Uri import android.os.Build +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.AddPhotoAlternate +import androidx.compose.material.icons.filled.CameraAlt import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon @@ -45,6 +53,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp @@ -57,6 +66,7 @@ import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.ui.actions.GallerySelect import com.vitorpamplona.amethyst.ui.actions.NewMediaModel import com.vitorpamplona.amethyst.ui.actions.NewMediaView +import com.vitorpamplona.amethyst.ui.actions.getPhotoUri import com.vitorpamplona.amethyst.ui.navigation.INav import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.stringRes @@ -73,7 +83,15 @@ fun NewImageButton( nav: INav, navScrollToTop: () -> Unit, ) { - var wantsToPost by remember { mutableStateOf(false) } + val context = LocalContext.current + + var isOpen by remember { mutableStateOf(false) } + + var wantsToPostFromGallery by remember { mutableStateOf(false) } + + var wantsToPostFromCamera by remember { mutableStateOf(false) } + + var cameraUri by remember { mutableStateOf(null) } var pickedURI by remember { mutableStateOf(null) } @@ -87,7 +105,46 @@ fun NewImageButton( } } - if (wantsToPost) { + if (wantsToPostFromCamera) { + val launcher = + rememberLauncherForActivityResult( + contract = ActivityResultContracts.TakePicture(), + ) { success -> + if (success) { + cameraUri?.let { + pickedURI = it + } + } + cameraUri = null + wantsToPostFromCamera = false + } + + val cameraPermissionState = + rememberPermissionState( + Manifest.permission.CAMERA, + onPermissionResult = { + if (it) { + scope.launch(Dispatchers.IO) { + cameraUri = getPhotoUri(context) + cameraUri?.let { launcher.launch(it) } + } + } + }, + ) + + if (cameraPermissionState.status.isGranted) { + LaunchedEffect(key1 = accountViewModel) { + launch(Dispatchers.IO) { + cameraUri = getPhotoUri(context) + cameraUri?.let { launcher.launch(it) } + } + } + } else { + LaunchedEffect(key1 = accountViewModel) { cameraPermissionState.launchPermissionRequest() } + } + } + + if (wantsToPostFromGallery) { val cameraPermissionState = rememberPermissionState( if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { @@ -102,7 +159,7 @@ fun NewImageButton( if (showGallerySelect) { GallerySelect( onImageUri = { uri -> - wantsToPost = false + wantsToPostFromGallery = false showGallerySelect = false pickedURI = uri }, @@ -128,18 +185,62 @@ fun NewImageButton( if (postViewModel.isUploadingImage) { ShowProgress(postViewModel) } else { - FloatingActionButton( - onClick = { wantsToPost = true }, - modifier = Size55Modifier, - shape = CircleShape, - containerColor = MaterialTheme.colorScheme.primary, - ) { - Icon( - painter = painterResource(R.drawable.ic_compose), - contentDescription = stringRes(id = R.string.new_short), - modifier = Modifier.size(26.dp), - tint = Color.White, - ) + Column { + if (isOpen) { + FloatingActionButton( + onClick = { + wantsToPostFromCamera = true + isOpen = false + }, + modifier = Size55Modifier, + shape = CircleShape, + containerColor = MaterialTheme.colorScheme.primary, + ) { + Icon( + imageVector = Icons.Default.CameraAlt, + contentDescription = stringRes(id = R.string.upload_image), + modifier = Modifier.size(26.dp), + tint = Color.White, + ) + } + + Spacer(modifier = Modifier.height(20.dp)) + + FloatingActionButton( + onClick = { + wantsToPostFromGallery = true + isOpen = false + }, + modifier = Size55Modifier, + shape = CircleShape, + containerColor = MaterialTheme.colorScheme.primary, + ) { + Icon( + imageVector = Icons.Default.AddPhotoAlternate, + contentDescription = stringRes(id = R.string.upload_image), + modifier = Modifier.size(26.dp), + tint = Color.White, + ) + } + + Spacer(modifier = Modifier.height(20.dp)) + } + + FloatingActionButton( + onClick = { + isOpen = !isOpen + }, + modifier = Size55Modifier, + shape = CircleShape, + containerColor = MaterialTheme.colorScheme.primary, + ) { + Icon( + painter = painterResource(R.drawable.ic_compose), + contentDescription = stringRes(id = R.string.new_short), + modifier = Modifier.size(26.dp), + tint = Color.White, + ) + } } } }