From dec9b35ca02b58d2b9ea4bc44d787d05e3541803 Mon Sep 17 00:00:00 2001 From: IsmaiL <99780212+Ismail-AD@users.noreply.github.com> Date: Mon, 9 Oct 2023 23:32:19 +0500 Subject: [PATCH 1/3] Camera Feature --- app/build.gradle.kts | 12 ++ app/src/main/AndroidManifest.xml | 5 + .../actions/ClickedPhotosBottomSheet.kt | 48 ++++++ .../components/camPreview/CameraPreview.kt | 19 +++ .../notes/addEditScreen/AddEditScreen.kt | 140 +++++++++++++++++- .../notes/addEditScreen/AddEditViewModel.kt | 17 ++- app/src/main/res/values/strings.xml | 2 + gradle/libs.versions.toml | 10 ++ 8 files changed, 249 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/com/aritra/notify/components/actions/ClickedPhotosBottomSheet.kt create mode 100644 app/src/main/java/com/aritra/notify/components/camPreview/CameraPreview.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1d4be44f..abe8671d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -98,6 +98,7 @@ dependencies { // Lifecycle implementation(libs.androidx.lifecycle.viewmodel.compose) + implementation(libs.androidx.lifecycle.runtime.compose) implementation(libs.androidx.lifecycle.viewmodel.ktx) implementation(libs.androidx.lifecycle.livedata.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) @@ -128,6 +129,17 @@ dependencies { implementation(libs.zoomable) implementation(libs.zoomable.image.coil) + + // CameraX + implementation(libs.camera.core) + implementation(libs.camera.camera2) + implementation(libs.camera.lifecycle) + implementation(libs.camera.extensions) + implementation(libs.camera.video) + implementation(libs.camera.view) + + + } ktlint { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0d27432a..8e81ffa7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,11 @@ + + + , modifier: Modifier) { + if (bitmaps.isEmpty()) { + Box(modifier = modifier.padding(16.dp), contentAlignment = Alignment.Center) { + Text(text = stringResource(id = R.string.no_clicked_photos)) + } + } else { + LazyVerticalStaggeredGrid( + columns = StaggeredGridCells.Fixed(2), + horizontalArrangement = Arrangement.spacedBy(16.dp), + verticalItemSpacing = 16.dp, + contentPadding = PaddingValues(16.dp), + modifier = modifier + ) { + items(bitmaps) { bitmap -> + Image( + bitmap = bitmap.asImageBitmap(), contentDescription = null, modifier = Modifier.clip( + RoundedCornerShape(10.dp) + ) + ) + + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/aritra/notify/components/camPreview/CameraPreview.kt b/app/src/main/java/com/aritra/notify/components/camPreview/CameraPreview.kt new file mode 100644 index 00000000..152289d4 --- /dev/null +++ b/app/src/main/java/com/aritra/notify/components/camPreview/CameraPreview.kt @@ -0,0 +1,19 @@ +package com.aritra.notify.components.camPreview + +import androidx.camera.view.LifecycleCameraController +import androidx.camera.view.PreviewView +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.compose.ui.viewinterop.AndroidView + +@Composable +fun CameraPreview(controller: LifecycleCameraController, modifier: Modifier) { + val lifecycleOwner = LocalLifecycleOwner.current + AndroidView(factory = { + PreviewView(it).apply { + this.controller = controller + controller.bindToLifecycle(lifecycleOwner) + } + }, modifier = modifier) +} \ No newline at end of file diff --git a/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditScreen.kt b/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditScreen.kt index 39d4312a..f0561139 100644 --- a/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditScreen.kt +++ b/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditScreen.kt @@ -1,12 +1,23 @@ package com.aritra.notify.ui.screens.notes.addEditScreen import android.Manifest +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Matrix import android.net.Uri +import android.provider.MediaStore import android.widget.Toast import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.PickVisualMediaRequest import androidx.activity.result.contract.ActivityResultContracts +import androidx.camera.core.CameraSelector +import androidx.camera.core.ImageCapture.OnImageCapturedCallback +import androidx.camera.core.ImageCaptureException +import androidx.camera.core.ImageProxy +import androidx.camera.view.CameraController +import androidx.camera.view.LifecycleCameraController import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -15,6 +26,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyRow @@ -24,9 +36,16 @@ import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.Camera +import androidx.compose.material.icons.filled.CameraAlt +import androidx.compose.material.icons.filled.Cameraswitch +import androidx.compose.material.icons.filled.PhotoCamera +import androidx.compose.material.icons.filled.PhotoLibrary import androidx.compose.material.icons.outlined.Close import androidx.compose.material3.BottomAppBar import androidx.compose.material3.BottomSheetDefaults +import androidx.compose.material3.BottomSheetScaffold import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FilledTonalIconButton import androidx.compose.material3.Icon @@ -38,6 +57,7 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TextField import androidx.compose.material3.TextFieldDefaults +import androidx.compose.material3.rememberBottomSheetScaffoldState import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -47,6 +67,7 @@ import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -65,12 +86,17 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.tooling.data.SourceContext import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.core.content.ContextCompat import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.aritra.notify.R import com.aritra.notify.components.actions.BottomSheetOptions +import com.aritra.notify.components.actions.ClickedPhotosBottomSheet import com.aritra.notify.components.actions.SpeechRecognizerContract +import com.aritra.notify.components.camPreview.CameraPreview import com.aritra.notify.components.dialog.TextDialog import com.aritra.notify.components.topbar.AddEditTopBar import com.aritra.notify.domain.models.Note @@ -78,7 +104,9 @@ import com.aritra.notify.utils.Const import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.isGranted import com.google.accompanist.permissions.rememberPermissionState +import kotlinx.coroutines.launch import me.saket.telephoto.zoomable.coil.ZoomableAsyncImage +import java.io.ByteArrayOutputStream import java.text.SimpleDateFormat import java.util.Calendar import java.util.Date @@ -118,22 +146,41 @@ fun AddEditScreen( val bottomSheetState = rememberModalBottomSheetState( skipPartiallyExpanded = skipPartiallyExpanded ) + val coroutineScope = rememberCoroutineScope() + val scaffoldState = rememberBottomSheetScaffoldState() + var openCameraBottomSheet by remember { + mutableStateOf(false) + } val formattedDateTime = SimpleDateFormat(Const.DATE_TIME_FORMAT, Locale.getDefault()).format(dateTime ?: 0) val formattedCharacterCount = "${(title.length) + (description.length)} characters" val launcher = rememberLauncherForActivityResult(ActivityResultContracts.PickMultipleVisualMedia()) { uris -> photoUri = uris } - + val controller = remember { + LifecycleCameraController(context).apply { + setEnabledUseCases(CameraController.IMAGE_CAPTURE) + } + } + val bitmap_List by addEditViewModel.bitmapList.collectAsStateWithLifecycle() val permissionState = rememberPermissionState( permission = Manifest.permission.RECORD_AUDIO ) + val camPermissionState = rememberPermissionState( + permission = Manifest.permission.CAMERA + ) + // add note if (isNew) { SideEffect { permissionState.launchPermissionRequest() } + if ((permissionState.status.isGranted || !permissionState.status.isGranted) && !camPermissionState.status.isGranted) { + SideEffect { + camPermissionState.launchPermissionRequest() + } + } } val speechRecognizerLauncher = rememberLauncherForActivityResult(contract = SpeechRecognizerContract(), onResult = { it?.let { @@ -235,6 +282,18 @@ fun AddEditScreen( .navigationBarsPadding() .padding(16.dp) ) { + BottomSheetOptions( + text = stringResource(R.string.take_image), + icon = painterResource(id = R.drawable.camera_icon), + onClick = { + if (camPermissionState.status.isGranted) { + openCameraBottomSheet = true + } else { + camPermissionState.launchPermissionRequest() + } + showSheet = false + } + ) BottomSheetOptions( text = stringResource(R.string.add_image), icon = painterResource(id = R.drawable.gallery_icon), @@ -450,4 +509,83 @@ fun AddEditScreen( cancelDialogState.value = false } ) + + if (openCameraBottomSheet) { + BottomSheetScaffold(scaffoldState = scaffoldState, sheetPeekHeight = 0.dp, sheetContent = { + ClickedPhotosBottomSheet(bitmaps = bitmap_List, modifier = Modifier.fillMaxWidth()) + }) { + Box( + modifier = Modifier + .fillMaxSize() + .padding(it) + ) { + CameraPreview(controller = controller, modifier = Modifier.fillMaxSize()) + + IconButton(onClick = { + openCameraBottomSheet = false + }, modifier = Modifier.offset(16.dp, 16.dp)) { + Icon(imageVector = Icons.Filled.ArrowBack, contentDescription = "navigate back") + } + Row( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.BottomCenter) + .padding(16.dp), horizontalArrangement = Arrangement.SpaceAround + ) { + IconButton(onClick = { + controller.cameraSelector = + if (controller.cameraSelector == CameraSelector.DEFAULT_BACK_CAMERA) { + CameraSelector.DEFAULT_FRONT_CAMERA + } else { + CameraSelector.DEFAULT_BACK_CAMERA + } + }) { + Icon(imageVector = Icons.Filled.Cameraswitch, contentDescription = "camera Switch") + } + IconButton(onClick = { + takePhoto(controller, addEditViewModel::onPhotoTaken, context) + }) { + Icon(imageVector = Icons.Filled.PhotoCamera, contentDescription = "Click To Capture") + } + } + + } + } + } + } +fun takePhoto(controller: LifecycleCameraController, onPhotoTaken: (Bitmap) -> Unit, context: Context) { + controller.takePicture(ContextCompat.getMainExecutor(context), object : OnImageCapturedCallback() { + override fun onCaptureSuccess(image: ImageProxy) { + super.onCaptureSuccess(image) + val matrix = Matrix().apply { + postRotate(image.imageInfo.rotationDegrees.toFloat()) + if (controller.cameraSelector == CameraSelector.DEFAULT_FRONT_CAMERA) { + postScale(-1f, 1f) + } + } + val bitmap = Bitmap.createBitmap(image.toBitmap(), 0, 0, image.width, image.height, matrix, true) + bitmap.toUri(context = context) + onPhotoTaken(bitmap) + Toast.makeText(context, "Attach Captured Photo from 'Add Image' option.", Toast.LENGTH_SHORT).show() + } + + override fun onError(exception: ImageCaptureException) { + super.onError(exception) + Toast.makeText(context, "Something Went Wrong ! Try Again", Toast.LENGTH_SHORT).show() + } + }) +} + +fun Bitmap.toUri(context: Context, format: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG) { + val bytes = ByteArrayOutputStream() + compress(format, 100, bytes) + MediaStore.Images.Media.insertImage( + context.contentResolver, + this, + "${System.currentTimeMillis()}", + null + ) +} + + diff --git a/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditViewModel.kt b/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditViewModel.kt index 095461aa..49174f4a 100644 --- a/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditViewModel.kt +++ b/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditViewModel.kt @@ -1,6 +1,7 @@ package com.aritra.notify.ui.screens.notes.addEditScreen import android.app.Application +import android.graphics.Bitmap import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData @@ -11,6 +12,9 @@ import com.aritra.notify.domain.usecase.SaveSelectedImageUseCase import com.aritra.notify.utils.toFile import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -26,6 +30,13 @@ class AddEditViewModel @Inject constructor( private val _noteModel = MutableLiveData(Note(0, "", "", Date(), emptyList())) val noteModel: LiveData get() = _noteModel + private val _bitmap = MutableStateFlow>(emptyList()) + val bitmapList = _bitmap.asStateFlow() + + fun onPhotoTaken(bitmap: Bitmap) { + _bitmap.value += bitmap + } + fun insertNote(note: Note, onSuccess: () -> Unit) { viewModelScope.launch(Dispatchers.IO) { val id: Int = noteRepository.insertNoteToRoom(note).toInt() @@ -147,7 +158,7 @@ class AddEditViewModel @Inject constructor( _noteModel.postValue(noteModel.value?.copy(note = description)) } - /* fun updateImage(imageList: List) { - _noteModel.postValue(noteModel.value?.copy(image = imageList)) - }*/ + /* fun updateImage(imageList: List) { + _noteModel.postValue(noteModel.value?.copy(image = imageList)) + }*/ } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4eeab1ec..92ed4da4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -41,9 +41,11 @@ Rate us on Google Play https://play.google.com/store/apps/details?id=com.aritra.notify Image + No Clicked Photos Clear Image Speech to Text Add image + Capture a Photo Add Box notify Request permission diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index aeed2065..8410d9ca 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -33,6 +33,7 @@ devtools-ksp = "1.8.10-1.0.9" zoomable = "0.6.2" zoomableImageCoil = "0.6.2" ktlint = "11.6.0" +camerax_version = "1.4.0-alpha01" [libraries] accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanistPermissions" } @@ -58,6 +59,7 @@ androidx-lifecycle-extensions = { module = "androidx.lifecycle:lifecycle-extensi androidx-lifecycle-livedata-ktx = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "lifecycleVersion" } androidx-lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle-runtime-ktx" } androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleVersion" } +androidx-lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycleVersion" } androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycleVersion" } androidx-material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version.ref = "materialIconsExtended" } androidx-material3 = { module = "androidx.compose.material3:material3", version.ref = "material3" } @@ -77,6 +79,14 @@ hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "hil junit = { module = "junit:junit", version.ref = "junit" } zoomable = { module = "me.saket.telephoto:zoomable", version.ref = "zoomable" } zoomable-image-coil = { module = "me.saket.telephoto:zoomable-image-coil", version.ref = "zoomableImageCoil" } +camera-core = { module = "androidx.camera:camera-core", version.ref = "camerax_version" } +camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "camerax_version" } +camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "camerax_version" } +camera-video = { module = "androidx.camera:camera-video", version.ref = "camerax_version" } +camera-mlkit-vision = { module = "androidx.camera:camera-mlkit-vision", version.ref = "camerax_version" } +camera-view = { module = "androidx.camera:camera-view", version.ref = "camerax_version" } +camera-extensions = { module = "androidx.camera:camera-extensions", version.ref = "camerax_version" } + [plugins] android-application = { id = "com.android.application", version.ref = "android-application" } From 46fcf6d46bad325f0b2ea9d55905ed4e1fbabf6b Mon Sep 17 00:00:00 2001 From: IsmaiL <99780212+Ismail-AD@users.noreply.github.com> Date: Tue, 10 Oct 2023 06:08:47 +0500 Subject: [PATCH 2/3] Camera Feature --- .../actions/ClickedPhotosBottomSheet.kt | 48 ------------------- .../notes/addEditScreen/AddEditScreen.kt | 16 ++----- .../notes/addEditScreen/AddEditViewModel.kt | 7 --- 3 files changed, 5 insertions(+), 66 deletions(-) delete mode 100644 app/src/main/java/com/aritra/notify/components/actions/ClickedPhotosBottomSheet.kt diff --git a/app/src/main/java/com/aritra/notify/components/actions/ClickedPhotosBottomSheet.kt b/app/src/main/java/com/aritra/notify/components/actions/ClickedPhotosBottomSheet.kt deleted file mode 100644 index 876b4dff..00000000 --- a/app/src/main/java/com/aritra/notify/components/actions/ClickedPhotosBottomSheet.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.aritra.notify.components.actions - -import android.graphics.Bitmap -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid -import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells -import androidx.compose.foundation.lazy.staggeredgrid.items -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.asImageBitmap -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import com.aritra.notify.R - -@Composable -fun ClickedPhotosBottomSheet(bitmaps: List, modifier: Modifier) { - if (bitmaps.isEmpty()) { - Box(modifier = modifier.padding(16.dp), contentAlignment = Alignment.Center) { - Text(text = stringResource(id = R.string.no_clicked_photos)) - } - } else { - LazyVerticalStaggeredGrid( - columns = StaggeredGridCells.Fixed(2), - horizontalArrangement = Arrangement.spacedBy(16.dp), - verticalItemSpacing = 16.dp, - contentPadding = PaddingValues(16.dp), - modifier = modifier - ) { - items(bitmaps) { bitmap -> - Image( - bitmap = bitmap.asImageBitmap(), contentDescription = null, modifier = Modifier.clip( - RoundedCornerShape(10.dp) - ) - ) - - } - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditScreen.kt b/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditScreen.kt index f0561139..0d740650 100644 --- a/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditScreen.kt +++ b/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditScreen.kt @@ -91,10 +91,8 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.core.content.ContextCompat import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.aritra.notify.R import com.aritra.notify.components.actions.BottomSheetOptions -import com.aritra.notify.components.actions.ClickedPhotosBottomSheet import com.aritra.notify.components.actions.SpeechRecognizerContract import com.aritra.notify.components.camPreview.CameraPreview import com.aritra.notify.components.dialog.TextDialog @@ -104,7 +102,6 @@ import com.aritra.notify.utils.Const import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.isGranted import com.google.accompanist.permissions.rememberPermissionState -import kotlinx.coroutines.launch import me.saket.telephoto.zoomable.coil.ZoomableAsyncImage import java.io.ByteArrayOutputStream import java.text.SimpleDateFormat @@ -146,7 +143,7 @@ fun AddEditScreen( val bottomSheetState = rememberModalBottomSheetState( skipPartiallyExpanded = skipPartiallyExpanded ) - val coroutineScope = rememberCoroutineScope() + val scaffoldState = rememberBottomSheetScaffoldState() var openCameraBottomSheet by remember { mutableStateOf(false) @@ -162,7 +159,7 @@ fun AddEditScreen( setEnabledUseCases(CameraController.IMAGE_CAPTURE) } } - val bitmap_List by addEditViewModel.bitmapList.collectAsStateWithLifecycle() + val permissionState = rememberPermissionState( permission = Manifest.permission.RECORD_AUDIO ) @@ -511,9 +508,7 @@ fun AddEditScreen( ) if (openCameraBottomSheet) { - BottomSheetScaffold(scaffoldState = scaffoldState, sheetPeekHeight = 0.dp, sheetContent = { - ClickedPhotosBottomSheet(bitmaps = bitmap_List, modifier = Modifier.fillMaxWidth()) - }) { + BottomSheetScaffold(scaffoldState = scaffoldState, sheetPeekHeight = 0.dp, sheetContent = {}) { Box( modifier = Modifier .fillMaxSize() @@ -543,7 +538,7 @@ fun AddEditScreen( Icon(imageVector = Icons.Filled.Cameraswitch, contentDescription = "camera Switch") } IconButton(onClick = { - takePhoto(controller, addEditViewModel::onPhotoTaken, context) + takePhoto(controller, context) }) { Icon(imageVector = Icons.Filled.PhotoCamera, contentDescription = "Click To Capture") } @@ -554,7 +549,7 @@ fun AddEditScreen( } } -fun takePhoto(controller: LifecycleCameraController, onPhotoTaken: (Bitmap) -> Unit, context: Context) { +fun takePhoto(controller: LifecycleCameraController, context: Context) { controller.takePicture(ContextCompat.getMainExecutor(context), object : OnImageCapturedCallback() { override fun onCaptureSuccess(image: ImageProxy) { super.onCaptureSuccess(image) @@ -566,7 +561,6 @@ fun takePhoto(controller: LifecycleCameraController, onPhotoTaken: (Bitmap) -> U } val bitmap = Bitmap.createBitmap(image.toBitmap(), 0, 0, image.width, image.height, matrix, true) bitmap.toUri(context = context) - onPhotoTaken(bitmap) Toast.makeText(context, "Attach Captured Photo from 'Add Image' option.", Toast.LENGTH_SHORT).show() } diff --git a/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditViewModel.kt b/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditViewModel.kt index 49174f4a..0ebc2b40 100644 --- a/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditViewModel.kt +++ b/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditViewModel.kt @@ -30,13 +30,6 @@ class AddEditViewModel @Inject constructor( private val _noteModel = MutableLiveData(Note(0, "", "", Date(), emptyList())) val noteModel: LiveData get() = _noteModel - private val _bitmap = MutableStateFlow>(emptyList()) - val bitmapList = _bitmap.asStateFlow() - - fun onPhotoTaken(bitmap: Bitmap) { - _bitmap.value += bitmap - } - fun insertNote(note: Note, onSuccess: () -> Unit) { viewModelScope.launch(Dispatchers.IO) { val id: Int = noteRepository.insertNoteToRoom(note).toInt() From 70bc9374432a56a9ef90a93c45b55d62e12cbd47 Mon Sep 17 00:00:00 2001 From: IsmaiL <99780212+Ismail-AD@users.noreply.github.com> Date: Wed, 11 Oct 2023 19:06:43 +0500 Subject: [PATCH 3/3] Camera Feature --- .idea/codeStyles/Project.xml | 123 ++++++++++++++++++ .idea/codeStyles/codeStyleConfig.xml | 5 + app/build.gradle.kts | 3 - .../components/camPreview/CameraPreview.kt | 3 +- .../notes/addEditScreen/AddEditScreen.kt | 27 ++-- gradle/libs.versions.toml | 18 +-- gradle/wrapper/gradle-wrapper.properties | 2 +- 7 files changed, 151 insertions(+), 30 deletions(-) create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 00000000..7643783a --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,123 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 00000000..79ee123c --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index abe8671d..7f616a3d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -137,9 +137,6 @@ dependencies { implementation(libs.camera.extensions) implementation(libs.camera.video) implementation(libs.camera.view) - - - } ktlint { diff --git a/app/src/main/java/com/aritra/notify/components/camPreview/CameraPreview.kt b/app/src/main/java/com/aritra/notify/components/camPreview/CameraPreview.kt index 152289d4..dbdd7144 100644 --- a/app/src/main/java/com/aritra/notify/components/camPreview/CameraPreview.kt +++ b/app/src/main/java/com/aritra/notify/components/camPreview/CameraPreview.kt @@ -1,5 +1,4 @@ package com.aritra.notify.components.camPreview - import androidx.camera.view.LifecycleCameraController import androidx.camera.view.PreviewView import androidx.compose.runtime.Composable @@ -16,4 +15,4 @@ fun CameraPreview(controller: LifecycleCameraController, modifier: Modifier) { controller.bindToLifecycle(lifecycleOwner) } }, modifier = modifier) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditScreen.kt b/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditScreen.kt index 0d740650..aebee96f 100644 --- a/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditScreen.kt +++ b/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditScreen.kt @@ -37,11 +37,8 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack -import androidx.compose.material.icons.filled.Camera -import androidx.compose.material.icons.filled.CameraAlt import androidx.compose.material.icons.filled.Cameraswitch import androidx.compose.material.icons.filled.PhotoCamera -import androidx.compose.material.icons.filled.PhotoLibrary import androidx.compose.material.icons.outlined.Close import androidx.compose.material3.BottomAppBar import androidx.compose.material3.BottomSheetDefaults @@ -67,7 +64,6 @@ import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -86,7 +82,6 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.tooling.data.SourceContext import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.core.content.ContextCompat @@ -386,7 +381,6 @@ fun AddEditScreen( } } } - TextField( modifier = Modifier.fillMaxWidth(), value = title, onValueChange = { newTitle -> if (isNew) { @@ -495,7 +489,6 @@ fun AddEditScreen( } } } - TextDialog( title = stringResource(R.string.are_you_sure), description = stringResource(R.string.the_text_change_will_not_be_saved), @@ -538,18 +531,21 @@ fun AddEditScreen( Icon(imageVector = Icons.Filled.Cameraswitch, contentDescription = "camera Switch") } IconButton(onClick = { - takePhoto(controller, context) + takePhoto(controller, context, onPhotoCaptured = { receviedUri -> + receviedUri?.let { + photoUri += it + } + }) }) { Icon(imageVector = Icons.Filled.PhotoCamera, contentDescription = "Click To Capture") } } - } } } - } -fun takePhoto(controller: LifecycleCameraController, context: Context) { + +fun takePhoto(controller: LifecycleCameraController, context: Context, onPhotoCaptured: (Uri?) -> Unit) { controller.takePicture(ContextCompat.getMainExecutor(context), object : OnImageCapturedCallback() { override fun onCaptureSuccess(image: ImageProxy) { super.onCaptureSuccess(image) @@ -560,8 +556,8 @@ fun takePhoto(controller: LifecycleCameraController, context: Context) { } } val bitmap = Bitmap.createBitmap(image.toBitmap(), 0, 0, image.width, image.height, matrix, true) - bitmap.toUri(context = context) - Toast.makeText(context, "Attach Captured Photo from 'Add Image' option.", Toast.LENGTH_SHORT).show() + Toast.makeText(context, "Photo Attached Successfully", Toast.LENGTH_SHORT).show() + onPhotoCaptured(bitmap.toUri(context = context)) } override fun onError(exception: ImageCaptureException) { @@ -571,15 +567,16 @@ fun takePhoto(controller: LifecycleCameraController, context: Context) { }) } -fun Bitmap.toUri(context: Context, format: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG) { +fun Bitmap.toUri(context: Context, format: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG): Uri? { val bytes = ByteArrayOutputStream() compress(format, 100, bytes) - MediaStore.Images.Media.insertImage( + val path = MediaStore.Images.Media.insertImage( context.contentResolver, this, "${System.currentTimeMillis()}", null ) + return Uri.parse(path) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8410d9ca..70da0023 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,30 +1,30 @@ [versions] accompanistPermissions = "0.28.0" accompanistSystemuicontroller = "0.32.0" -activity-compose = "1.7.2" +activity-compose = "1.8.0" androidx-junit = "1.1.5" appUpdate = "2.1.0" biometric = "1.2.0-alpha05" coilCompose = "2.4.0" -core-ktx = "1.10.1" +core-ktx = "1.12.0" coreSplashscreen = "1.0.1" datastorePreferences = "1.0.0" espresso-core = "3.5.1" google-gson = "2.9.0" -gradle = "3.4.0" -hiltAndroid = "2.44" +gradle = "8.0.2" +hiltAndroid = "2.48" hiltNavigationCompose = "1.0.0" junit = "4.13.2" -lifecycle-runtime-ktx = "2.6.1" +lifecycle-runtime-ktx = "2.6.2" lifecycleExtensions = "2.2.0" lifecycleVersion = "2.6.2" material3 = "1.1.2" -materialIconsExtended = "1.5.2" -navVersion = "2.7.3" +materialIconsExtended = "1.5.3" +navVersion = "2.7.4" roomVersion = "2.5.2" -runtimeLivedata = "1.4.3" +runtimeLivedata = "1.5.3" bom = "2023.10.00" -kotlinBom = "1.8.0" +kotlinBom = "1.9.10" material3windowsize = "1.1.2" android-application = "8.0.2" kotlin-android = "1.7.20" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 57025c84..c719492c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ -#Tue Jun 27 10:53:54 IST 2023 +#Tue Oct 10 18:09:43 PKT 2023 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip