From 5dfeada48a82a140b09d233d90203367fae2ec80 Mon Sep 17 00:00:00 2001 From: medAndro Date: Thu, 29 Jan 2026 16:25:24 +0900 Subject: [PATCH 01/10] feat: Integrate Compose Resources for multiplatform support --- library/build.gradle.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/build.gradle.kts b/library/build.gradle.kts index becc78b..38b4501 100644 --- a/library/build.gradle.kts +++ b/library/build.gradle.kts @@ -13,6 +13,7 @@ plugins { id("jacoco") id("io.gitlab.arturbosch.detekt") id("org.jetbrains.dokka") version "1.9.20" + id("org.jetbrains.compose") version "1.10.0" kotlin("plugin.serialization") version "1.9.22" } @@ -382,6 +383,7 @@ kotlin { implementation("io.coil-kt.coil3:coil-compose:3.2.0") implementation("org.jetbrains.compose.material:material-icons-core:1.7.3") implementation("org.jetbrains.compose.material:material-icons-extended:1.7.3") + implementation("org.jetbrains.compose.components:components-resources:1.10.0") // OCR dependencies implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2") // Ktor dependencies for HTTP client From 1cd42bdd46b9218d39523103f54a580f5a95bfc2 Mon Sep 17 00:00:00 2001 From: medAndro Date: Thu, 29 Jan 2026 16:25:43 +0900 Subject: [PATCH 02/10] feat: Add Korean localization for image crop view - Introduces Korean translations for the image crop view's title, cancel, and apply buttons. - Replaces hardcoded English strings in `CropHeaderControls` with string resources to support localization. - Adds default English string resources in `values/strings.xml`. --- .../commonMain/composeResources/values-ko/strings.xml | 5 +++++ .../commonMain/composeResources/values/strings.xml | 5 +++++ .../presentation/ui/components/CropHeaderControls.kt | 11 ++++++++--- 3 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 library/src/commonMain/composeResources/values-ko/strings.xml create mode 100644 library/src/commonMain/composeResources/values/strings.xml diff --git a/library/src/commonMain/composeResources/values-ko/strings.xml b/library/src/commonMain/composeResources/values-ko/strings.xml new file mode 100644 index 0000000..971d65c --- /dev/null +++ b/library/src/commonMain/composeResources/values-ko/strings.xml @@ -0,0 +1,5 @@ + + 이미지 자르기 + 닫기 + 적용 + \ No newline at end of file diff --git a/library/src/commonMain/composeResources/values/strings.xml b/library/src/commonMain/composeResources/values/strings.xml new file mode 100644 index 0000000..7346189 --- /dev/null +++ b/library/src/commonMain/composeResources/values/strings.xml @@ -0,0 +1,5 @@ + + Crop Image + Cancel + Apply + \ No newline at end of file diff --git a/library/src/commonMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/CropHeaderControls.kt b/library/src/commonMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/CropHeaderControls.kt index 5b9da9d..dc389c5 100644 --- a/library/src/commonMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/CropHeaderControls.kt +++ b/library/src/commonMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/CropHeaderControls.kt @@ -17,6 +17,11 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import imagepickerkmp.library.generated.resources.Res +import imagepickerkmp.library.generated.resources.image_crop_view_apply_description +import imagepickerkmp.library.generated.resources.image_crop_view_cancel_description +import imagepickerkmp.library.generated.resources.image_crop_view_title +import org.jetbrains.compose.resources.stringResource @Composable fun CropHeaderControls( @@ -35,13 +40,13 @@ fun CropHeaderControls( ) { Icon( imageVector = Icons.Default.Close, - contentDescription = "Cancel", + contentDescription = stringResource(Res.string.image_crop_view_cancel_description), tint = Color.White ) } Text( - text = "Crop Image", + text = stringResource(Res.string.image_crop_view_title), color = Color.White, fontWeight = FontWeight.Bold, fontSize = 18.sp, @@ -54,7 +59,7 @@ fun CropHeaderControls( ) { Icon( imageVector = Icons.Default.Check, - contentDescription = "Apply", + contentDescription = stringResource(Res.string.image_crop_view_apply_description), tint = Color.White ) } From d7b6f896ebb4a9acc0262c3ab3b4d53e6098fcef Mon Sep 17 00:00:00 2001 From: medAndro Date: Thu, 29 Jan 2026 16:44:47 +0900 Subject: [PATCH 03/10] feat: Add localization to crop controls - Added new string resources for crop control UI elements, including zoom, rotation, and crop shape descriptions. - Implemented Korean translations for the new crop control strings. - Updated the `CropControlsPanel` to use the new string resources for labels and content descriptions, enabling localization. --- .../composeResources/values-ko/strings.xml | 5 +++ .../composeResources/values/strings.xml | 5 +++ .../ui/components/CropControlsPanel.kt | 31 +++++++++++++++---- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/library/src/commonMain/composeResources/values-ko/strings.xml b/library/src/commonMain/composeResources/values-ko/strings.xml index 971d65c..d207a6b 100644 --- a/library/src/commonMain/composeResources/values-ko/strings.xml +++ b/library/src/commonMain/composeResources/values-ko/strings.xml @@ -2,4 +2,9 @@ 이미지 자르기 닫기 적용 + + 사각형으로 자르기 + 원형으로 자르기 + 확대/축소 + 회전 \ No newline at end of file diff --git a/library/src/commonMain/composeResources/values/strings.xml b/library/src/commonMain/composeResources/values/strings.xml index 7346189..220dc52 100644 --- a/library/src/commonMain/composeResources/values/strings.xml +++ b/library/src/commonMain/composeResources/values/strings.xml @@ -2,4 +2,9 @@ Crop Image Cancel Apply + + Rectangular crop + Circular crop + Zoom + Rotation \ No newline at end of file diff --git a/library/src/commonMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/CropControlsPanel.kt b/library/src/commonMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/CropControlsPanel.kt index 07728db..8e4c029 100644 --- a/library/src/commonMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/CropControlsPanel.kt +++ b/library/src/commonMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/CropControlsPanel.kt @@ -26,7 +26,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import imagepickerkmp.library.generated.resources.Res +import imagepickerkmp.library.generated.resources.image_crop_view_circular_description +import imagepickerkmp.library.generated.resources.image_crop_view_rectangular_description +import imagepickerkmp.library.generated.resources.image_crop_view_rotation_label +import imagepickerkmp.library.generated.resources.image_crop_view_zoom_label import io.github.ismoy.imagepickerkmp.domain.config.CropConfig +import org.jetbrains.compose.resources.stringResource @Composable fun CropControlsPanel( @@ -41,7 +47,7 @@ fun CropControlsPanel( onRotationChange: (Float) -> Unit ) { val defaultPadding = 16.dp - + Column( modifier = Modifier .fillMaxWidth() @@ -72,7 +78,7 @@ fun CropControlsPanel( ) { Icon( imageVector = Icons.Filled.Crop, - contentDescription = "Rectangular crop", + contentDescription = stringResource(Res.string.image_crop_view_rectangular_description), tint = if (!isCircularCrop) Color.Black else Color.White, modifier = Modifier.size(16.dp) ) @@ -95,7 +101,7 @@ fun CropControlsPanel( ) { Icon( imageVector = Icons.Outlined.Circle, - contentDescription = "Circular crop", + contentDescription = stringResource(Res.string.image_crop_view_circular_description), tint = if (isCircularCrop) Color.Black else Color.White, modifier = Modifier.size(16.dp) ) @@ -116,7 +122,10 @@ fun CropControlsPanel( backgroundColor = if (aspectRatio == ratio && !isCircularCrop) Color.White else Color.Transparent, disabledBackgroundColor = Color.Gray.copy(alpha = 0.3f) ), - border = BorderStroke(1.dp, if (isCircularCrop) Color.Gray else Color.White), + border = BorderStroke( + 1.dp, + if (isCircularCrop) Color.Gray else Color.White + ), contentPadding = PaddingValues(horizontal = 6.dp, vertical = 4.dp) ) { Text( @@ -132,7 +141,12 @@ fun CropControlsPanel( } Column { - Text("Zoom", color = Color.White, fontSize = 14.sp, modifier = Modifier.padding(bottom = 4.dp)) + Text( + text = stringResource(Res.string.image_crop_view_zoom_label), + color = Color.White, + fontSize = 14.sp, + modifier = Modifier.padding(bottom = 4.dp) + ) Slider( value = zoomLevel, onValueChange = onZoomChange, @@ -147,7 +161,12 @@ fun CropControlsPanel( Spacer(modifier = Modifier.height(16.dp)) Column { - Text("Rotation", color = Color.White, fontSize = 14.sp, modifier = Modifier.padding(bottom = 4.dp)) + Text( + text = stringResource(Res.string.image_crop_view_rotation_label), + color = Color.White, + fontSize = 14.sp, + modifier = Modifier.padding(bottom = 4.dp) + ) Slider( value = rotationAngle, onValueChange = onRotationChange, From 29f1ae05ad5df8252b6081e84243f45244141377 Mon Sep 17 00:00:00 2001 From: medAndro Date: Thu, 29 Jan 2026 18:17:14 +0900 Subject: [PATCH 04/10] refactor: Migrate to Compose multiplatform resources This commit refactors the string resource handling to use the modern Compose multiplatform resources library (`org.jetbrains.compose.resources`). This change centralizes string management, simplifies localization, and removes the legacy `expect/actual` implementation for string resources across all platforms (Android, iOS, JVM, and JS). - **Centralized String Resources:** - Moved all string definitions from platform-specific files (`values/strings.xml` on Android, `.kt` files on other platforms) into a single `commonMain/composeResources/values/strings.xml` file. - Added Spanish (`-es`) and French (`-fr`) translations, which are now managed within the `composeResources` directory. - **Removed Legacy `expect/actual` Pattern:** - Deleted the `expect fun stringResource` declaration and its platform-specific `actual` implementations. - Removed the `StringResource` enum, as resource access is now handled by the generated `Res` class. - **Updated Resource Accessors:** - Replaced calls like `stringResource(StringResource.MY_STRING)` with the new type-safe accessor `stringResource(Res.string.my_string)`. - Updated UI components across all modules to use the new resource-loading mechanism. --- .../ImagePickerLauncherOCR.android.kt | 9 +- .../presentation/resources/StringResource.kt | 81 ------------ .../GalleryPickerLauncher.android.kt | 8 +- .../ImageConfirmationViewWithCustomButtons.kt | 26 ++-- .../components/ImagePickerLauncher.android.kt | 12 +- .../components/LandscapeConfirmationLayout.kt | 27 ++-- .../gallery/GalleryOnlyPickerLaunchers.kt | 120 ++++++++++-------- .../gallery/GalleryPickerLaunchers.kt | 51 ++++---- .../ui/screens/CameraCaptureView.kt | 25 +++- .../src/androidMain/res/values/strings.xml | 39 ------ .../composeResources}/values-es/strings.xml | 0 .../composeResources}/values-fr/strings.xml | 0 .../composeResources/values/strings.xml | 39 +++++- .../domain/config/PermissionConfig.kt | 22 ++-- .../presentation/resources/StringResource.kt | 4 - .../presentation/resources/Strings.kt | 39 ------ .../resources/StringResource.js.kt | 36 ------ .../resources/StringResource.jvm.kt | 40 ------ .../resources/StringResource.wasmJs.kt | 40 ------ 19 files changed, 211 insertions(+), 407 deletions(-) delete mode 100644 library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/resources/StringResource.kt delete mode 100644 library/src/androidMain/res/values/strings.xml rename library/src/{androidMain/res => commonMain/composeResources}/values-es/strings.xml (100%) rename library/src/{androidMain/res => commonMain/composeResources}/values-fr/strings.xml (100%) delete mode 100644 library/src/iosMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/resources/Strings.kt delete mode 100644 library/src/jsMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/resources/StringResource.js.kt delete mode 100644 library/src/jvmMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/resources/StringResource.jvm.kt delete mode 100644 library/src/wasmJsMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/resources/StringResource.wasmJs.kt diff --git a/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/features/ocr/presentation/ImagePickerLauncherOCR.android.kt b/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/features/ocr/presentation/ImagePickerLauncherOCR.android.kt index 9a25c6c..fd1cfec 100644 --- a/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/features/ocr/presentation/ImagePickerLauncherOCR.android.kt +++ b/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/features/ocr/presentation/ImagePickerLauncherOCR.android.kt @@ -19,8 +19,6 @@ import io.github.ismoy.imagepickerkmp.domain.config.PermissionAndConfirmationCon import io.github.ismoy.imagepickerkmp.domain.extensions.loadBytes import io.github.ismoy.imagepickerkmp.features.ocr.model.OCRProcessState import io.github.ismoy.imagepickerkmp.features.ocr.model.ScanMode -import io.github.ismoy.imagepickerkmp.presentation.resources.StringResource -import io.github.ismoy.imagepickerkmp.presentation.resources.getStringResource import io.github.ismoy.imagepickerkmp.presentation.ui.components.ImagePickerLauncher import io.github.ismoy.imagepickerkmp.features.ocr.presentation.components.OCRProgressDialog import io.github.ismoy.imagepickerkmp.presentation.ui.extensions.activity @@ -32,7 +30,9 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext - +import org.jetbrains.compose.resources.stringResource +import imagepickerkmp.library.generated.resources.Res +import imagepickerkmp.library.generated.resources.invalid_context_error @Suppress("FunctionNaming") @Composable @@ -42,9 +42,10 @@ actual fun ImagePickerLauncherOCR( ) { val context = LocalContext.current val activity = context.activity + val invalidContextErrorMsg = stringResource(Res.string.invalid_context_error) if (activity !is ComponentActivity) { LaunchedEffect(Unit) { - config.onError(Exception(getStringResource(StringResource.INVALID_CONTEXT_ERROR))) + config.onError(Exception(invalidContextErrorMsg)) } return } diff --git a/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/resources/StringResource.kt b/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/resources/StringResource.kt deleted file mode 100644 index c5d16d6..0000000 --- a/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/resources/StringResource.kt +++ /dev/null @@ -1,81 +0,0 @@ -package io.github.ismoy.imagepickerkmp.presentation.resources - -import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalContext -import io.github.ismoy.imagepickerkmp.R - -@Composable -internal actual fun stringResource(id: StringResource): String { - val context = LocalContext.current - return context.getString(getAndroidStringResId(id)) -} - -private val androidStringResMap = mapOf( - StringResource.CAMERA_PERMISSION_REQUIRED to R.string.camera_permission_required, - StringResource.CAMERA_PERMISSION_DESCRIPTION to R.string.camera_permission_description, - StringResource.OPEN_SETTINGS to R.string.open_settings, - StringResource.CAMERA_PERMISSION_DENIED to R.string.camera_permission_denied, - StringResource.CAMERA_PERMISSION_DENIED_DESCRIPTION to R.string.camera_permission_denied_description, - StringResource.GRANT_PERMISSION to R.string.grant_permission, - StringResource.CAMERA_PERMISSION_PERMANENTLY_DENIED to R.string.camera_permission_permanently_denied, - StringResource.IMAGE_CONFIRMATION_TITLE to R.string.image_confirmation_title, - StringResource.ACCEPT_BUTTON to R.string.accept_button, - StringResource.RETRY_BUTTON to R.string.retry_button, - StringResource.SELECT_OPTION_DIALOG_TITLE to R.string.select_option_dialog_title, - StringResource.TAKE_PHOTO_OPTION to R.string.take_photo_option, - StringResource.SELECT_FROM_GALLERY_OPTION to R.string.select_from_gallery_option, - StringResource.CANCEL_OPTION to R.string.cancel_option, - StringResource.PREVIEW_IMAGE_DESCRIPTION to R.string.preview_image_description, - StringResource.HD_QUALITY_DESCRIPTION to R.string.hd_quality_description, - StringResource.SD_QUALITY_DESCRIPTION to R.string.sd_quality_description, - StringResource.INVALID_CONTEXT_ERROR to R.string.invalid_context_error, - StringResource.PHOTO_CAPTURE_ERROR to R.string.photo_capture_error, - StringResource.GALLERY_SELECTION_ERROR to R.string.gallery_selection_error, - StringResource.PERMISSION_ERROR to R.string.permission_error, - StringResource.GALLERY_PERMISSION_REQUIRED to R.string.gallery_permission_required, - StringResource.GALLERY_PERMISSION_DESCRIPTION to R.string.gallery_permission_description, - StringResource.GALLERY_PERMISSION_DENIED to R.string.gallery_permission_denied, - StringResource.GALLERY_PERMISSION_DENIED_DESCRIPTION to R.string.gallery_permission_denied_description, - StringResource.GALLERY_GRANT_PERMISSION to R.string.gallery_grant_permission, - StringResource.GALLERY_BTN_SETTINGS to R.string.gallery_btn_settings -) -private fun getAndroidStringResId(id: StringResource): Int = - androidStringResMap[id] ?: error("Missing Android string resource mapping for $id") - -@Suppress("CyclomaticComplexMethod") -internal fun getStringResource(id: StringResource): String { - return when (id) { - StringResource.CAMERA_PERMISSION_REQUIRED -> "Camera permission required" - StringResource.CAMERA_PERMISSION_DESCRIPTION -> "Camera permission is required to capture photos." + - " Please grant it in settings" - StringResource.OPEN_SETTINGS -> "Open settings" - StringResource.CAMERA_PERMISSION_DENIED -> "Camera permission denied" - StringResource.CAMERA_PERMISSION_DENIED_DESCRIPTION -> "Camera permission is required to" + - " capture photos. Please grant the permissions" - StringResource.GRANT_PERMISSION -> "Grant permission" - StringResource.CAMERA_PERMISSION_PERMANENTLY_DENIED -> "Camera permission permanently denied" - StringResource.IMAGE_CONFIRMATION_TITLE -> "Are you satisfied with the photo?" - StringResource.ACCEPT_BUTTON -> "Accept" - StringResource.RETRY_BUTTON -> "Retry" - StringResource.SELECT_OPTION_DIALOG_TITLE -> "Select option" - StringResource.TAKE_PHOTO_OPTION -> "Take photo" - StringResource.SELECT_FROM_GALLERY_OPTION -> "Select from gallery" - StringResource.CANCEL_OPTION -> "Cancel" - StringResource.PREVIEW_IMAGE_DESCRIPTION -> "Preview" - StringResource.HD_QUALITY_DESCRIPTION -> "HD" - StringResource.SD_QUALITY_DESCRIPTION -> "SD" - StringResource.INVALID_CONTEXT_ERROR -> "Invalid context. Must be ComponentActivity" - StringResource.PHOTO_CAPTURE_ERROR -> "Photo capture failed" - StringResource.GALLERY_SELECTION_ERROR -> "Gallery selection failed" - StringResource.PERMISSION_ERROR -> "Permission error occurred" - StringResource.GALLERY_PERMISSION_REQUIRED -> "Storage permission required" - StringResource.GALLERY_PERMISSION_DESCRIPTION -> "Access to storage is required to select " + - "images from your gallery. Please grant the permission." - StringResource.GALLERY_PERMISSION_DENIED -> "Storage permission denied" - StringResource.GALLERY_PERMISSION_DENIED_DESCRIPTION -> "Storage permission is required " + - "to select images. Please enable it in app settings." - StringResource.GALLERY_GRANT_PERMISSION -> "Grant permission" - StringResource.GALLERY_BTN_SETTINGS -> "Open settings" - - } -} diff --git a/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/GalleryPickerLauncher.android.kt b/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/GalleryPickerLauncher.android.kt index 386f6e9..4e530d2 100644 --- a/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/GalleryPickerLauncher.android.kt +++ b/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/GalleryPickerLauncher.android.kt @@ -7,9 +7,10 @@ import io.github.ismoy.imagepickerkmp.domain.config.CameraCaptureConfig import io.github.ismoy.imagepickerkmp.domain.models.GalleryPhotoResult import io.github.ismoy.imagepickerkmp.domain.models.GalleryPickerConfig import io.github.ismoy.imagepickerkmp.domain.models.MimeType -import io.github.ismoy.imagepickerkmp.presentation.resources.StringResource -import io.github.ismoy.imagepickerkmp.presentation.resources.getStringResource import io.github.ismoy.imagepickerkmp.presentation.ui.components.gallery.GalleryPickerLauncherContent +import imagepickerkmp.library.generated.resources.Res +import imagepickerkmp.library.generated.resources.invalid_context_error +import org.jetbrains.compose.resources.stringResource @Suppress("ReturnCount","LongParameterList") @Composable @@ -27,8 +28,9 @@ actual fun GalleryPickerLauncher( ) { val context = LocalContext.current val activity = context + val invalidContextMsg = stringResource(Res.string.invalid_context_error) if (activity !is ComponentActivity) { - onError(Exception(getStringResource(StringResource.INVALID_CONTEXT_ERROR))) + onError(Exception(invalidContextMsg)) return } diff --git a/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/ImageConfirmationViewWithCustomButtons.kt b/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/ImageConfirmationViewWithCustomButtons.kt index 46559cd..dcfbaf6 100644 --- a/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/ImageConfirmationViewWithCustomButtons.kt +++ b/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/ImageConfirmationViewWithCustomButtons.kt @@ -40,11 +40,17 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.platform.LocalConfiguration import coil3.compose.AsyncImage +import imagepickerkmp.library.generated.resources.Res +import imagepickerkmp.library.generated.resources.accept_button +import imagepickerkmp.library.generated.resources.hd_quality_description +import imagepickerkmp.library.generated.resources.image_confirmation_title +import imagepickerkmp.library.generated.resources.preview_image_description +import imagepickerkmp.library.generated.resources.retry_button +import imagepickerkmp.library.generated.resources.sd_quality_description import io.github.ismoy.imagepickerkmp.domain.config.ImagePickerUiConstants import io.github.ismoy.imagepickerkmp.domain.config.UiConfig import io.github.ismoy.imagepickerkmp.domain.models.PhotoResult -import io.github.ismoy.imagepickerkmp.presentation.resources.StringResource -import io.github.ismoy.imagepickerkmp.presentation.resources.stringResource +import org.jetbrains.compose.resources.stringResource @SuppressLint("ConfigurationScreenWidthHeight") @Suppress("EndOfSentenceFormat","LongMethod","FunctionNaming") @@ -102,7 +108,7 @@ fun ImageConfirmationViewWithCustomButtons( .aspectRatio(ImagePickerUiConstants.ConfirmationCardImageAspectRatio)){ AsyncImage( model = result.uri, - contentDescription = stringResource(StringResource.PREVIEW_IMAGE_DESCRIPTION), + contentDescription = stringResource(Res.string.preview_image_description), modifier = Modifier .fillMaxSize() .aspectRatio(ImagePickerUiConstants.ConfirmationCardImageAspectRatio) @@ -124,8 +130,8 @@ fun ImageConfirmationViewWithCustomButtons( ) { Icon( imageVector = if (isHD) Icons.Default.Hd else Icons.Default.Sd, - contentDescription = if (isHD) stringResource(StringResource.HD_QUALITY_DESCRIPTION) - else stringResource(StringResource.SD_QUALITY_DESCRIPTION), + contentDescription = if (isHD) stringResource(Res.string.hd_quality_description) + else stringResource(Res.string.sd_quality_description), tint = resolvedIconColor ) } @@ -141,7 +147,7 @@ fun ImageConfirmationViewWithCustomButtons( horizontalAlignment = Alignment.CenterHorizontally ) { Text( - text = stringResource(StringResource.IMAGE_CONFIRMATION_TITLE), + text = stringResource(Res.string.image_confirmation_title), color = ImagePickerUiConstants.ConfirmationCardTitleColor, fontSize = ImagePickerUiConstants.ConfirmationCardTitleFontSize, fontWeight = FontWeight.Bold, @@ -168,13 +174,13 @@ fun ImageConfirmationViewWithCustomButtons( ) { Icon( imageVector = uiConfig.galleryIcon ?: Icons.Default.Refresh, - contentDescription = stringResource(StringResource.RETRY_BUTTON), + contentDescription = stringResource(Res.string.retry_button), tint = resolvedIconColor, modifier = Modifier .padding(end = ImagePickerUiConstants.ConfirmationCardButtonIconPadding) ) Text( - stringResource(StringResource.RETRY_BUTTON), color = resolvedIconColor, + stringResource(Res.string.retry_button), color = resolvedIconColor, fontWeight = ImagePickerUiConstants.ConfirmationCardButtonTextFontWeight) } Button( @@ -189,13 +195,13 @@ fun ImageConfirmationViewWithCustomButtons( ) { Icon( imageVector = Icons.Default.Check, - contentDescription = stringResource(StringResource.ACCEPT_BUTTON), + contentDescription = stringResource(Res.string.accept_button), tint = resolvedIconColor, modifier = Modifier .padding(end = ImagePickerUiConstants.ConfirmationCardButtonIconPadding) ) Text( - stringResource(StringResource.ACCEPT_BUTTON), + stringResource(Res.string.accept_button), color = resolvedIconColor, fontWeight = ImagePickerUiConstants.ConfirmationCardButtonTextFontWeight) } diff --git a/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/ImagePickerLauncher.android.kt b/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/ImagePickerLauncher.android.kt index dc8b472..a0d78fd 100644 --- a/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/ImagePickerLauncher.android.kt +++ b/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/ImagePickerLauncher.android.kt @@ -1,4 +1,3 @@ - package io.github.ismoy.imagepickerkmp.presentation.ui.components import androidx.activity.ComponentActivity @@ -7,19 +6,20 @@ import androidx.compose.ui.platform.LocalContext import io.github.ismoy.imagepickerkmp.presentation.ui.screens.CameraCaptureView import io.github.ismoy.imagepickerkmp.domain.config.ImagePickerConfig import io.github.ismoy.imagepickerkmp.domain.config.CropConfig -import io.github.ismoy.imagepickerkmp.presentation.resources.getStringResource -import io.github.ismoy.imagepickerkmp.presentation.resources.StringResource import io.github.ismoy.imagepickerkmp.presentation.ui.extensions.activity +import org.jetbrains.compose.resources.stringResource +import imagepickerkmp.library.generated.resources.Res +import imagepickerkmp.library.generated.resources.invalid_context_error @Suppress("FunctionNaming") @Composable actual fun ImagePickerLauncher( config: ImagePickerConfig -){ +) { val context = LocalContext.current val activity = context.activity if (activity !is ComponentActivity) { - config.onError(Exception(getStringResource(StringResource.INVALID_CONTEXT_ERROR))) + config.onError(Exception(stringResource(Res.string.invalid_context_error))) return } CameraCaptureView( @@ -40,7 +40,7 @@ actual fun ImagePickerLauncher( } else if (config.enableCrop) { CropConfig( enabled = true, - circularCrop = true, + circularCrop = true, squareCrop = true ) } else { diff --git a/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/LandscapeConfirmationLayout.kt b/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/LandscapeConfirmationLayout.kt index 3fb6ebc..60bc7cc 100644 --- a/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/LandscapeConfirmationLayout.kt +++ b/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/LandscapeConfirmationLayout.kt @@ -39,9 +39,14 @@ import coil3.compose.AsyncImage import io.github.ismoy.imagepickerkmp.domain.config.ImagePickerUiConstants import io.github.ismoy.imagepickerkmp.domain.config.UiConfig import io.github.ismoy.imagepickerkmp.domain.models.PhotoResult -import io.github.ismoy.imagepickerkmp.presentation.resources.StringResource -import io.github.ismoy.imagepickerkmp.presentation.resources.stringResource - +import imagepickerkmp.library.generated.resources.Res +import imagepickerkmp.library.generated.resources.accept_button +import imagepickerkmp.library.generated.resources.hd_quality_description +import imagepickerkmp.library.generated.resources.image_confirmation_title +import imagepickerkmp.library.generated.resources.preview_image_description +import imagepickerkmp.library.generated.resources.retry_button +import imagepickerkmp.library.generated.resources.sd_quality_description +import org.jetbrains.compose.resources.stringResource @Composable fun LandscapeConfirmationLayout( result: PhotoResult, @@ -71,7 +76,7 @@ import io.github.ismoy.imagepickerkmp.presentation.resources.stringResource ) { AsyncImage( model = result.uri, - contentDescription = stringResource(StringResource.PREVIEW_IMAGE_DESCRIPTION), + contentDescription = stringResource(Res.string.preview_image_description), modifier = Modifier .fillMaxSize() .clip(RoundedCornerShape( @@ -96,8 +101,8 @@ import io.github.ismoy.imagepickerkmp.presentation.resources.stringResource ) { Icon( imageVector = if (isHD) Icons.Default.Hd else Icons.Default.Sd, - contentDescription = if (isHD) stringResource(StringResource.HD_QUALITY_DESCRIPTION) - else stringResource(StringResource.SD_QUALITY_DESCRIPTION), + contentDescription = if (isHD) stringResource(Res.string.hd_quality_description) + else stringResource(Res.string.sd_quality_description), tint = resolvedIconColor ) } @@ -114,7 +119,7 @@ import io.github.ismoy.imagepickerkmp.presentation.resources.stringResource verticalArrangement = Arrangement.Center ) { Text( - text = stringResource(StringResource.IMAGE_CONFIRMATION_TITLE), + text = stringResource(Res.string.image_confirmation_title), color = ImagePickerUiConstants.ConfirmationCardTitleColor, fontSize = ImagePickerUiConstants.ConfirmationCardTitleFontSize, fontWeight = FontWeight.Bold, @@ -141,12 +146,12 @@ import io.github.ismoy.imagepickerkmp.presentation.resources.stringResource ) { Icon( imageVector = Icons.Default.Check, - contentDescription = stringResource(StringResource.ACCEPT_BUTTON), + contentDescription = stringResource(Res.string.accept_button), tint = resolvedIconColor, modifier = Modifier.padding(end = 8.dp) ) Text( - stringResource(StringResource.ACCEPT_BUTTON), + stringResource(Res.string.accept_button), color = resolvedIconColor, fontWeight = ImagePickerUiConstants.ConfirmationCardButtonTextFontWeight ) @@ -164,12 +169,12 @@ import io.github.ismoy.imagepickerkmp.presentation.resources.stringResource ) { Icon( imageVector = uiConfig.galleryIcon ?: Icons.Default.Refresh, - contentDescription = stringResource(StringResource.RETRY_BUTTON), + contentDescription = stringResource(Res.string.retry_button), tint = resolvedIconColor, modifier = Modifier.padding(end = 8.dp) ) Text( - stringResource(StringResource.RETRY_BUTTON), + stringResource(Res.string.retry_button), color = resolvedIconColor, fontWeight = ImagePickerUiConstants.ConfirmationCardButtonTextFontWeight ) diff --git a/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/gallery/GalleryOnlyPickerLaunchers.kt b/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/gallery/GalleryOnlyPickerLaunchers.kt index d9b017f..76f088f 100644 --- a/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/gallery/GalleryOnlyPickerLaunchers.kt +++ b/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/gallery/GalleryOnlyPickerLaunchers.kt @@ -3,15 +3,14 @@ package io.github.ismoy.imagepickerkmp.presentation.ui.components.gallery import android.content.Context import android.content.Intent import android.net.Uri -import android.provider.MediaStore +import androidx.activity.compose.ManagedActivityResultLauncher import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContract -import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.runtime.Composable +import imagepickerkmp.library.generated.resources.Res +import imagepickerkmp.library.generated.resources.gallery_selection_error import io.github.ismoy.imagepickerkmp.domain.models.CompressionLevel import io.github.ismoy.imagepickerkmp.domain.models.GalleryPhotoResult -import io.github.ismoy.imagepickerkmp.presentation.resources.StringResource -import io.github.ismoy.imagepickerkmp.presentation.resources.getStringResource import io.github.ismoy.imagepickerkmp.presentation.ui.components.gallery.GalleryFileProcessor.processSelectedFile import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -20,6 +19,7 @@ import kotlinx.coroutines.awaitAll import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.withPermit +import org.jetbrains.compose.resources.stringResource class PickImageFromGallery : ActivityResultContract() { @@ -49,11 +49,11 @@ class PickMultipleImagesFromGallery : ActivityResultContract>( override fun parseResult(resultCode: Int, intent: Intent?): List { val uris = mutableListOf() - + if (resultCode != android.app.Activity.RESULT_OK) { - return uris + return uris } - + intent?.let { it.clipData?.let { clipData -> for (i in 0 until clipData.itemCount) { @@ -68,7 +68,7 @@ class PickMultipleImagesFromGallery : ActivityResultContract>( } } } - + return uris } } @@ -81,24 +81,28 @@ internal fun rememberGalleryOnlyPickerLauncher( onDismiss: () -> Unit, compressionLevel: CompressionLevel? = null, includeExif: Boolean = false -) = rememberLauncherForActivityResult( - contract = PickImageFromGallery() -) { uri: Uri? -> - if (uri != null) { - try { - CoroutineScope(Dispatchers.Main).launch { - val result = processSelectedFile(context, uri, compressionLevel, includeExif) - if (result != null) { - onPhotoSelected(result) - } else { - onError(Exception(getStringResource(StringResource.GALLERY_SELECTION_ERROR))) +): ManagedActivityResultLauncher { + val gallerySelectionErrorMsg = stringResource(Res.string.gallery_selection_error) + + return rememberLauncherForActivityResult( + contract = PickImageFromGallery() + ) { uri: Uri? -> + if (uri != null) { + try { + CoroutineScope(Dispatchers.Main).launch { + val result = processSelectedFile(context, uri, compressionLevel, includeExif) + if (result != null) { + onPhotoSelected(result) + } else { + onError(Exception(gallerySelectionErrorMsg)) + } } + } catch (e: Exception) { + onError(e) } - } catch (e: Exception) { - onError(e) + } else { + onDismiss() } - } else { - onDismiss() } } @@ -110,44 +114,48 @@ internal fun rememberGalleryOnlyMultiplePickerLauncher( onDismiss: () -> Unit, compressionLevel: CompressionLevel? = null, includeExif: Boolean = false -) = rememberLauncherForActivityResult( - contract = PickMultipleImagesFromGallery() -) { uris: List -> - if (uris.isNotEmpty()) { - try { - CoroutineScope(Dispatchers.Main).launch { - val semaphore = Semaphore(3) - val results = mutableListOf() - val errors = mutableListOf() - - val deferredResults = uris.map { uri -> - async(Dispatchers.IO) { - semaphore.withPermit { - try { - processSelectedFile(context, uri, compressionLevel, includeExif) - } catch (e: Exception) { - errors.add(e) - null +): ManagedActivityResultLauncher> { + val gallerySelectionErrorMsg = stringResource(Res.string.gallery_selection_error) + + return rememberLauncherForActivityResult( + contract = PickMultipleImagesFromGallery() + ) { uris: List -> + if (uris.isNotEmpty()) { + try { + CoroutineScope(Dispatchers.Main).launch { + val semaphore = Semaphore(3) + val results = mutableListOf() + val errors = mutableListOf() + + val deferredResults = uris.map { uri -> + async(Dispatchers.IO) { + semaphore.withPermit { + try { + processSelectedFile(context, uri, compressionLevel, includeExif) + } catch (e: Exception) { + errors.add(e) + null + } } } } + + val finalResults = deferredResults.awaitAll().filterNotNull() + results.addAll(finalResults) + + if (results.isNotEmpty()) { + onPhotosSelected(results) + } else if (errors.isNotEmpty()) { + onError(errors.first()) + } else { + onError(Exception(gallerySelectionErrorMsg)) + } } - - val finalResults = deferredResults.awaitAll().filterNotNull() - results.addAll(finalResults) - - if (results.isNotEmpty()) { - onPhotosSelected(results) - } else if (errors.isNotEmpty()) { - onError(errors.first()) - } else { - onError(Exception(getStringResource(StringResource.GALLERY_SELECTION_ERROR))) - } + } catch (e: Exception) { + onError(e) } - } catch (e: Exception) { - onError(e) + } else { + onDismiss() } - } else { - onDismiss() } } diff --git a/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/gallery/GalleryPickerLaunchers.kt b/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/gallery/GalleryPickerLaunchers.kt index 2af1e1d..b0f99c3 100644 --- a/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/gallery/GalleryPickerLaunchers.kt +++ b/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/components/gallery/GalleryPickerLaunchers.kt @@ -2,13 +2,14 @@ package io.github.ismoy.imagepickerkmp.presentation.ui.components.gallery import android.content.Context import android.net.Uri +import androidx.activity.compose.ManagedActivityResultLauncher import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.runtime.Composable +import imagepickerkmp.library.generated.resources.Res +import imagepickerkmp.library.generated.resources.gallery_selection_error import io.github.ismoy.imagepickerkmp.domain.models.CompressionLevel import io.github.ismoy.imagepickerkmp.domain.models.GalleryPhotoResult -import io.github.ismoy.imagepickerkmp.presentation.resources.StringResource -import io.github.ismoy.imagepickerkmp.presentation.resources.getStringResource import io.github.ismoy.imagepickerkmp.presentation.ui.components.gallery.GalleryFileProcessor.processSelectedFile import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -17,6 +18,8 @@ import kotlinx.coroutines.awaitAll import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.withPermit +import org.jetbrains.compose.resources.getString +import org.jetbrains.compose.resources.stringResource @Composable @@ -27,24 +30,28 @@ internal fun rememberSinglePickerLauncher( onDismiss: () -> Unit, compressionLevel: CompressionLevel? = null, includeExif: Boolean = false -) = rememberLauncherForActivityResult( - contract = ActivityResultContracts.GetContent() -) { uri: Uri? -> - if (uri != null) { - try { - CoroutineScope(Dispatchers.Main).launch { - val result = processSelectedFile(context, uri, compressionLevel, includeExif) - if (result != null) { - onPhotoSelected(result) - } else { - onError(Exception(getStringResource(StringResource.GALLERY_SELECTION_ERROR))) +): ManagedActivityResultLauncher { + val gallerySelectionErrorMsg = stringResource(Res.string.gallery_selection_error) + + return rememberLauncherForActivityResult( + contract = ActivityResultContracts.GetContent() + ) { uri: Uri? -> + if (uri != null) { + try { + CoroutineScope(Dispatchers.Main).launch { + val result = processSelectedFile(context, uri, compressionLevel, includeExif) + if (result != null) { + onPhotoSelected(result) + } else { + onError(Exception(gallerySelectionErrorMsg)) + } } + } catch (e: Exception) { + onError(e) } - } catch (e: Exception) { - onError(e) + } else { + onDismiss() } - } else { - onDismiss() } } @@ -65,7 +72,7 @@ internal fun rememberMultiplePickerLauncher( val semaphore = Semaphore(3) val results = mutableListOf() val errors = mutableListOf() - + val deferredResults = uris.map { uri -> async(Dispatchers.IO) { semaphore.withPermit { @@ -78,17 +85,17 @@ internal fun rememberMultiplePickerLauncher( } } } - + results.addAll(deferredResults.awaitAll().filterNotNull()) - + if (errors.isNotEmpty()) { errors.forEach { onError(it) } } - + if (results.isNotEmpty()) { onPhotosSelected(results) } else { - onError(Exception(getStringResource(StringResource.GALLERY_SELECTION_ERROR))) + onError(Exception(getString(Res.string.gallery_selection_error))) } } } catch (e: Exception) { diff --git a/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/screens/CameraCaptureView.kt b/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/screens/CameraCaptureView.kt index 29221e4..4fd7ad4 100644 --- a/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/screens/CameraCaptureView.kt +++ b/library/src/androidMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/ui/screens/CameraCaptureView.kt @@ -21,8 +21,6 @@ import io.github.ismoy.imagepickerkmp.domain.models.GalleryPhotoResult import io.github.ismoy.imagepickerkmp.domain.models.PhotoResult import io.github.ismoy.imagepickerkmp.domain.utils.defaultCameraPermissionDialogConfig import io.github.ismoy.imagepickerkmp.domain.utils.playShutterSound -import io.github.ismoy.imagepickerkmp.presentation.resources.StringResource -import io.github.ismoy.imagepickerkmp.presentation.resources.getStringResource import io.github.ismoy.imagepickerkmp.presentation.ui.components.CameraCapturePreview import io.github.ismoy.imagepickerkmp.presentation.ui.components.GalleryPickerLauncher import io.github.ismoy.imagepickerkmp.presentation.ui.components.ImageConfirmationViewWithCustomButtons @@ -30,8 +28,11 @@ import io.github.ismoy.imagepickerkmp.presentation.ui.components.ImageCropView import io.github.ismoy.imagepickerkmp.presentation.ui.components.RequestCameraPermission import io.github.ismoy.imagepickerkmp.presentation.ui.utils.rememberCameraManager import io.github.ismoy.imagepickerkmp.presentation.ui.utils.rememberImagePickerViewModel +import imagepickerkmp.library.generated.resources.Res +import imagepickerkmp.library.generated.resources.camera_permission_permanently_denied +import org.jetbrains.compose.resources.stringResource -@Suppress("LongMethod","LongParameterList") +@Suppress("LongMethod", "LongParameterList") @Composable fun CameraCaptureView( activity: ComponentActivity, @@ -46,10 +47,10 @@ fun CameraCaptureView( var photoResult by remember { mutableStateOf(null) } var showCropView by remember { mutableStateOf(false) } var hasPermission by remember { mutableStateOf(false) } - + val imagePickerViewModel = rememberImagePickerViewModel() val cameraManager = rememberCameraManager(context, activity) - + if (cameraManager == null) { onError(Exception("Camera dependencies not available. Please check camera permissions and device capabilities.")) return @@ -89,9 +90,14 @@ fun CameraCaptureView( } ) } + cameraCaptureConfig.galleryConfig.allowMultiple && onPhotosSelected != null -> { GalleryPickerLauncher( - onPhotosSelected = { results: List -> onPhotosSelected(results) }, + onPhotosSelected = { results: List -> + onPhotosSelected( + results + ) + }, onError = { exception: Exception -> imagePickerViewModel.onError(exception) onError(exception) @@ -106,6 +112,7 @@ fun CameraCaptureView( includeExif = cameraCaptureConfig.galleryConfig.includeExif ) } + photoResult == null -> { CameraAndPreview( cameraCaptureConfig = cameraCaptureConfig, @@ -127,6 +134,7 @@ fun CameraCaptureView( } ) } + photoResult != null && !cameraCaptureConfig.permissionAndConfirmationConfig.skipConfirmation -> { ConfirmationView( photoResult = photoResult!!, @@ -153,13 +161,16 @@ private fun PermissionHandler( customDeniedDialog = customDeniedDialog, customSettingsDialog = customSettingsDialog ) + val cameraPermissionPermanentlyDeniedMsg = + stringResource(Res.string.camera_permission_permanently_denied) + if (customPermissionHandler != null) { customPermissionHandler(defaultConfig) } else { RequestCameraPermission( dialogConfig = dialogConfig, onPermissionPermanentlyDenied = { - onError(PhotoCaptureException(getStringResource(StringResource.CAMERA_PERMISSION_PERMANENTLY_DENIED))) + onError(PhotoCaptureException(cameraPermissionPermanentlyDeniedMsg)) }, onResult = { _: Boolean -> onPermissionGranted() }, customPermissionHandler = null diff --git a/library/src/androidMain/res/values/strings.xml b/library/src/androidMain/res/values/strings.xml deleted file mode 100644 index 267bbcb..0000000 --- a/library/src/androidMain/res/values/strings.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - Camera permission required - Camera permission is required to capture photos. Please grant it in settings - Open settings - Camera permission denied - Camera permission is required to capture photos. Please grant the permissions - Grant permission - Camera permission permanently denied - Storage permission required - Access to storage is required to select images from your gallery. Please grant the permission. - Storage permission denied - Storage permission is required to select images. Please enable it in app settings. - Grant permission - Open settings - - - Are you satisfied with the photo? - Accept - Retry - - - Select option - Take photo - Select from gallery - Cancel - - - Preview - HD - SD - - - Invalid context. Must be ComponentActivity - Photo capture failed - Gallery selection failed - Permission error occurred - \ No newline at end of file diff --git a/library/src/androidMain/res/values-es/strings.xml b/library/src/commonMain/composeResources/values-es/strings.xml similarity index 100% rename from library/src/androidMain/res/values-es/strings.xml rename to library/src/commonMain/composeResources/values-es/strings.xml diff --git a/library/src/androidMain/res/values-fr/strings.xml b/library/src/commonMain/composeResources/values-fr/strings.xml similarity index 100% rename from library/src/androidMain/res/values-fr/strings.xml rename to library/src/commonMain/composeResources/values-fr/strings.xml diff --git a/library/src/commonMain/composeResources/values/strings.xml b/library/src/commonMain/composeResources/values/strings.xml index 220dc52..528c0e6 100644 --- a/library/src/commonMain/composeResources/values/strings.xml +++ b/library/src/commonMain/composeResources/values/strings.xml @@ -1,8 +1,45 @@ + + Camera permission required + Camera permission is required to capture photos. Please grant it in settings + Open settings + Camera permission denied + Camera permission is required to capture photos. Please grant the permissions + Grant permission + Camera permission permanently denied + Storage permission required + Access to storage is required to select images from your gallery. Please grant the permission. + Storage permission denied + Storage permission is required to select images. Please enable it in app settings. + Grant permission + Open settings + + + Are you satisfied with the photo? + Accept + Retry + + + Select option + Take photo + Select from gallery + Cancel + + + Preview + HD + SD + + + Invalid context. Must be ComponentActivity + Photo capture failed + Gallery selection failed + Permission error occurred + + Crop Image Cancel Apply - Rectangular crop Circular crop Zoom diff --git a/library/src/commonMain/kotlin/io/github/ismoy/imagepickerkmp/domain/config/PermissionConfig.kt b/library/src/commonMain/kotlin/io/github/ismoy/imagepickerkmp/domain/config/PermissionConfig.kt index 46bc04f..d067e5e 100644 --- a/library/src/commonMain/kotlin/io/github/ismoy/imagepickerkmp/domain/config/PermissionConfig.kt +++ b/library/src/commonMain/kotlin/io/github/ismoy/imagepickerkmp/domain/config/PermissionConfig.kt @@ -1,8 +1,14 @@ package io.github.ismoy.imagepickerkmp.domain.config import androidx.compose.runtime.Composable -import io.github.ismoy.imagepickerkmp.presentation.resources.StringResource -import io.github.ismoy.imagepickerkmp.presentation.resources.stringResource +import org.jetbrains.compose.resources.stringResource +import imagepickerkmp.library.generated.resources.Res +import imagepickerkmp.library.generated.resources.camera_permission_denied +import imagepickerkmp.library.generated.resources.camera_permission_denied_description +import imagepickerkmp.library.generated.resources.camera_permission_description +import imagepickerkmp.library.generated.resources.camera_permission_required +import imagepickerkmp.library.generated.resources.grant_permission +import imagepickerkmp.library.generated.resources.open_settings /** * Configuration for camera permission dialogs and messages. @@ -26,12 +32,12 @@ data class PermissionConfig( @Composable fun createLocalizedComposable(): PermissionConfig { return PermissionConfig( - titleDialogConfig = stringResource(StringResource.CAMERA_PERMISSION_REQUIRED), - descriptionDialogConfig = stringResource(StringResource.CAMERA_PERMISSION_DESCRIPTION), - btnDialogConfig = stringResource(StringResource.OPEN_SETTINGS), - titleDialogDenied = stringResource(StringResource.CAMERA_PERMISSION_DENIED), - descriptionDialogDenied = stringResource(StringResource.CAMERA_PERMISSION_DENIED_DESCRIPTION), - btnDialogDenied = stringResource(StringResource.GRANT_PERMISSION) + titleDialogConfig = stringResource(Res.string.camera_permission_required), + descriptionDialogConfig = stringResource(Res.string.camera_permission_description), + btnDialogConfig = stringResource(Res.string.open_settings), + titleDialogDenied = stringResource(Res.string.camera_permission_denied), + descriptionDialogDenied = stringResource(Res.string.camera_permission_denied_description), + btnDialogDenied = stringResource(Res.string.grant_permission) ) } } diff --git a/library/src/commonMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/resources/StringResource.kt b/library/src/commonMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/resources/StringResource.kt index 0413ff1..5306266 100644 --- a/library/src/commonMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/resources/StringResource.kt +++ b/library/src/commonMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/resources/StringResource.kt @@ -1,6 +1,5 @@ package io.github.ismoy.imagepickerkmp.presentation.resources -import androidx.compose.runtime.Composable /** * Enum that defines all string resources used in the ImagePicker library. @@ -36,6 +35,3 @@ enum class StringResource { GALLERY_GRANT_PERMISSION, GALLERY_BTN_SETTINGS } - -@Composable -internal expect fun stringResource(id: StringResource): String diff --git a/library/src/iosMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/resources/Strings.kt b/library/src/iosMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/resources/Strings.kt deleted file mode 100644 index 255097c..0000000 --- a/library/src/iosMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/resources/Strings.kt +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.ismoy.imagepickerkmp.presentation.resources - -import androidx.compose.runtime.Composable -import kotlinx.cinterop.ExperimentalForeignApi -import platform.Foundation.NSBundle -import platform.Foundation.NSString -import platform.Foundation.NSUTF8StringEncoding -import platform.Foundation.stringWithContentsOfFile - -@OptIn(ExperimentalForeignApi::class) -private val defaultStringsMap: Map by lazy { - val bundle = NSBundle.mainBundle - val path = bundle.pathForResource("strings.default", "properties") ?: return@lazy emptyMap() - val content = NSString.stringWithContentsOfFile(path, NSUTF8StringEncoding, null) ?: return@lazy emptyMap() - - content - .lineSequence() - .filter { it.isNotBlank() && !it.trim().startsWith("#") } - .mapNotNull { line -> - val (key, value) = line.split("=", limit = 2).map { it.trim() } - if (key.isNotEmpty() && value.isNotEmpty()) key to value else null - }.toMap() -} - -@Composable -internal actual fun stringResource(id: StringResource): String = getDefaultString(id) - -private fun getDefaultString(id: StringResource): String { - val key = when (id) { - StringResource.GALLERY_PERMISSION_REQUIRED -> "gallery_permission_required" - StringResource.GALLERY_PERMISSION_DESCRIPTION -> "gallery_permission_description" - StringResource.GALLERY_PERMISSION_DENIED -> "gallery_permission_denied" - StringResource.GALLERY_PERMISSION_DENIED_DESCRIPTION -> "gallery_permission_denied_description" - StringResource.GALLERY_GRANT_PERMISSION -> "grant_gallery_permission" - StringResource.GALLERY_BTN_SETTINGS -> "gallery_btn_settings" - else -> id.name.lowercase() - } - return defaultStringsMap[key] ?: id.name.replace("_", " ").replaceFirstChar { it.uppercase() } -} diff --git a/library/src/jsMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/resources/StringResource.js.kt b/library/src/jsMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/resources/StringResource.js.kt deleted file mode 100644 index e7474f9..0000000 --- a/library/src/jsMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/resources/StringResource.js.kt +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.ismoy.imagepickerkmp.presentation.resources - -import androidx.compose.runtime.Composable - -@Composable -internal actual fun stringResource(id: StringResource): String { - return when (id) { - StringResource.CAMERA_PERMISSION_REQUIRED -> "Camera Permission Required" - StringResource.CAMERA_PERMISSION_DESCRIPTION -> "This app needs camera access to take photos. Please grant camera permission in your browser." - StringResource.OPEN_SETTINGS -> "Open Settings" - StringResource.CAMERA_PERMISSION_DENIED -> "Permission Denied" - StringResource.CAMERA_PERMISSION_DENIED_DESCRIPTION -> "Camera permission was denied" - StringResource.GRANT_PERMISSION -> "Grant Permission" - StringResource.CAMERA_PERMISSION_PERMANENTLY_DENIED -> "Camera Permission Permanently Denied" - StringResource.IMAGE_CONFIRMATION_TITLE -> "Confirm Image" - StringResource.ACCEPT_BUTTON -> "Accept" - StringResource.RETRY_BUTTON -> "Retry" - StringResource.SELECT_OPTION_DIALOG_TITLE -> "Select Option" - StringResource.TAKE_PHOTO_OPTION -> "Take Photo" - StringResource.SELECT_FROM_GALLERY_OPTION -> "Select from Gallery" - StringResource.CANCEL_OPTION -> "Cancel" - StringResource.PREVIEW_IMAGE_DESCRIPTION -> "Preview Image" - StringResource.HD_QUALITY_DESCRIPTION -> "HD Quality" - StringResource.SD_QUALITY_DESCRIPTION -> "SD Quality" - StringResource.INVALID_CONTEXT_ERROR -> "Invalid Context Error" - StringResource.PHOTO_CAPTURE_ERROR -> "Photo Capture Error" - StringResource.GALLERY_SELECTION_ERROR -> "Gallery Selection Error" - StringResource.PERMISSION_ERROR -> "Permission Error" - StringResource.GALLERY_PERMISSION_REQUIRED -> "Gallery Permission Required" - StringResource.GALLERY_PERMISSION_DESCRIPTION -> "This app needs gallery access to select photos" - StringResource.GALLERY_PERMISSION_DENIED -> "Gallery Permission Denied" - StringResource.GALLERY_PERMISSION_DENIED_DESCRIPTION -> "Gallery permission was denied" - StringResource.GALLERY_GRANT_PERMISSION -> "Grant Gallery Permission" - StringResource.GALLERY_BTN_SETTINGS -> "Gallery Settings" - } -} diff --git a/library/src/jvmMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/resources/StringResource.jvm.kt b/library/src/jvmMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/resources/StringResource.jvm.kt deleted file mode 100644 index 2a9e35e..0000000 --- a/library/src/jvmMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/resources/StringResource.jvm.kt +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.ismoy.imagepickerkmp.presentation.resources - -import androidx.compose.runtime.Composable - -/** - * Desktop implementation of stringResource. - * Returns a simple English translation for each string resource. - */ -@Composable -internal actual fun stringResource(id: StringResource): String { - return when (id) { - StringResource.CAMERA_PERMISSION_REQUIRED -> "Camera Permission Required" - StringResource.CAMERA_PERMISSION_DESCRIPTION -> "This app needs camera permission to take photos" - StringResource.OPEN_SETTINGS -> "Open Settings" - StringResource.CAMERA_PERMISSION_DENIED -> "Camera Permission Denied" - StringResource.CAMERA_PERMISSION_DENIED_DESCRIPTION -> "Camera permission was denied. This app needs camera permission to take photos." - StringResource.GRANT_PERMISSION -> "Grant Permission" - StringResource.CAMERA_PERMISSION_PERMANENTLY_DENIED -> "Camera permission was permanently denied. Please enable it in settings." - StringResource.IMAGE_CONFIRMATION_TITLE -> "Use this image?" - StringResource.ACCEPT_BUTTON -> "Use Image" - StringResource.RETRY_BUTTON -> "Try Again" - StringResource.SELECT_OPTION_DIALOG_TITLE -> "Select Image Source" - StringResource.TAKE_PHOTO_OPTION -> "Take Photo" - StringResource.SELECT_FROM_GALLERY_OPTION -> "Choose from Gallery" - StringResource.CANCEL_OPTION -> "Cancel" - StringResource.PREVIEW_IMAGE_DESCRIPTION -> "Preview of captured image" - StringResource.HD_QUALITY_DESCRIPTION -> "HD Quality" - StringResource.SD_QUALITY_DESCRIPTION -> "Standard Quality" - StringResource.INVALID_CONTEXT_ERROR -> "Invalid context provided" - StringResource.PHOTO_CAPTURE_ERROR -> "Failed to capture image" - StringResource.GALLERY_SELECTION_ERROR -> "Failed to select image from gallery" - StringResource.PERMISSION_ERROR -> "Permission error" - StringResource.GALLERY_PERMISSION_REQUIRED -> "Gallery Permission Required" - StringResource.GALLERY_PERMISSION_DESCRIPTION -> "This app needs gallery permission to access photos" - StringResource.GALLERY_PERMISSION_DENIED -> "Gallery Permission Denied" - StringResource.GALLERY_PERMISSION_DENIED_DESCRIPTION -> "Gallery permission was denied. This app needs gallery permission to access photos." - StringResource.GALLERY_GRANT_PERMISSION -> "Grant Gallery Permission" - StringResource.GALLERY_BTN_SETTINGS -> "Settings" - } -} diff --git a/library/src/wasmJsMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/resources/StringResource.wasmJs.kt b/library/src/wasmJsMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/resources/StringResource.wasmJs.kt deleted file mode 100644 index 8eb06bf..0000000 --- a/library/src/wasmJsMain/kotlin/io/github/ismoy/imagepickerkmp/presentation/resources/StringResource.wasmJs.kt +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.ismoy.imagepickerkmp.presentation.resources - -import androidx.compose.runtime.Composable - -/** - * WASM platform implementation of stringResource. - * Provides English fallback strings for WASM platform. - */ -@Composable -internal actual fun stringResource(id: StringResource): String { - return when (id) { - StringResource.CAMERA_PERMISSION_REQUIRED -> "Camera Permission Required" - StringResource.CAMERA_PERMISSION_DESCRIPTION -> "This app needs access to your camera to take photos" - StringResource.OPEN_SETTINGS -> "Open Settings" - StringResource.CAMERA_PERMISSION_DENIED -> "Camera Permission Denied" - StringResource.CAMERA_PERMISSION_DENIED_DESCRIPTION -> "Camera access was denied. You can enable it in settings." - StringResource.GRANT_PERMISSION -> "Grant Permission" - StringResource.CAMERA_PERMISSION_PERMANENTLY_DENIED -> "Camera Permission Permanently Denied" - StringResource.IMAGE_CONFIRMATION_TITLE -> "Are you satisfied with the photo?" - StringResource.ACCEPT_BUTTON -> "Accept" - StringResource.RETRY_BUTTON -> "Retry" - StringResource.SELECT_OPTION_DIALOG_TITLE -> "Select option" - StringResource.TAKE_PHOTO_OPTION -> "Take photo" - StringResource.SELECT_FROM_GALLERY_OPTION -> "Select from gallery" - StringResource.CANCEL_OPTION -> "Cancel" - StringResource.PREVIEW_IMAGE_DESCRIPTION -> "Preview" - StringResource.HD_QUALITY_DESCRIPTION -> "HD" - StringResource.SD_QUALITY_DESCRIPTION -> "SD" - StringResource.INVALID_CONTEXT_ERROR -> "Invalid context error" - StringResource.PHOTO_CAPTURE_ERROR -> "Photo capture failed" - StringResource.GALLERY_SELECTION_ERROR -> "Gallery selection failed" - StringResource.PERMISSION_ERROR -> "Permission error" - StringResource.GALLERY_PERMISSION_REQUIRED -> "Gallery Permission Required" - StringResource.GALLERY_PERMISSION_DESCRIPTION -> "This app needs access to your gallery to select photos" - StringResource.GALLERY_PERMISSION_DENIED -> "Gallery Permission Denied" - StringResource.GALLERY_PERMISSION_DENIED_DESCRIPTION -> "Gallery access was denied. You can enable it in settings." - StringResource.GALLERY_GRANT_PERMISSION -> "Grant Permission" - StringResource.GALLERY_BTN_SETTINGS -> "Settings" - } -} From a110fcfa859dbcfeb29b06ddc4f7daff58912e5d Mon Sep 17 00:00:00 2001 From: medAndro Date: Thu, 29 Jan 2026 19:49:53 +0900 Subject: [PATCH 05/10] refactor: Migrate to version catalog for Compose Multiplatform plugin This commit updates the project's build configuration to use the Gradle version catalog for managing the Compose Multiplatform plugin and its dependencies. This change improves dependency management by centralizing version and alias definitions. - Replaced the hardcoded Compose Multiplatform plugin declaration with a type-safe accessor from the version catalog (`libs.plugins.composeMultiplatform`). - Moved the `components-resources` dependency to the `libs.versions.toml` file for consistent version management. --- gradle/libs.versions.toml | 3 +++ library/build.gradle.kts | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 06a226f..e5b80f8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,6 +3,7 @@ accompanistPermissions = "0.37.3" agp = "8.13.0" cameraCore = "1.5.1" cameraView = "1.5.1" +componentsResources = "1.10.0" core = "3.5.3" exifinterface = "1.4.2" kotlin = "2.1.21" @@ -26,6 +27,7 @@ androidx-exifinterface = { module = "androidx.exifinterface:exifinterface", vers androidx-material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version.ref = "materialIconsExtendedVersion" } androidx-ui = { module = "androidx.compose.ui:ui", version.ref = "ui" } androidx-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "ui" } +components-resources = { module = "org.jetbrains.compose.components:components-resources", version.ref = "compose" } core = { module = "com.google.zxing:core", version.ref = "core" } compose-ui = { module = "org.jetbrains.compose.ui:ui", version.ref = "compose" } compose-animation = { module = "org.jetbrains.compose.animation:animation", version.ref = "compose" } @@ -45,3 +47,4 @@ ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "kto androidLibrary = { id = "com.android.library", version.ref = "agp" } kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version = "2.0.21" } +composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose" } \ No newline at end of file diff --git a/library/build.gradle.kts b/library/build.gradle.kts index 38b4501..e2baf54 100644 --- a/library/build.gradle.kts +++ b/library/build.gradle.kts @@ -8,12 +8,12 @@ plugins { alias(libs.plugins.kotlinMultiplatform) alias(libs.plugins.androidLibrary) alias(libs.plugins.composeCompiler) + alias(libs.plugins.composeMultiplatform) id("com.vanniktech.maven.publish") version "0.33.0" id("maven-publish") id("jacoco") id("io.gitlab.arturbosch.detekt") id("org.jetbrains.dokka") version "1.9.20" - id("org.jetbrains.compose") version "1.10.0" kotlin("plugin.serialization") version "1.9.22" } @@ -383,7 +383,7 @@ kotlin { implementation("io.coil-kt.coil3:coil-compose:3.2.0") implementation("org.jetbrains.compose.material:material-icons-core:1.7.3") implementation("org.jetbrains.compose.material:material-icons-extended:1.7.3") - implementation("org.jetbrains.compose.components:components-resources:1.10.0") + implementation(libs.components.resources) // OCR dependencies implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2") // Ktor dependencies for HTTP client From 5fc9d0fc6bbb42a7f998c20213b89f18ad4112b7 Mon Sep 17 00:00:00 2001 From: medAndro Date: Thu, 29 Jan 2026 20:03:58 +0900 Subject: [PATCH 06/10] refactor: Migrate to version catalog for Compose Multiplatform plugin This commit updates the project's build configuration to use the Gradle version catalog for managing the Compose Multiplatform plugin and its dependencies. This change improves dependency management by centralizing version and alias definitions. - Replaced the hardcoded Compose Multiplatform plugin declaration with a type-safe accessor from the version catalog (`libs.plugins.composeMultiplatform`). - Moved the `components-resources` dependency to the `libs.versions.toml` file for consistent version management. --- .../resources/en.lproj/Localizable.strings | 30 ------------------- .../resources/es.lproj/Localizable.strings | 30 ------------------- .../resources/fr.lproj/Localizable.strings | 30 ------------------- 3 files changed, 90 deletions(-) delete mode 100644 library/src/iosMain/resources/en.lproj/Localizable.strings delete mode 100644 library/src/iosMain/resources/es.lproj/Localizable.strings delete mode 100644 library/src/iosMain/resources/fr.lproj/Localizable.strings diff --git a/library/src/iosMain/resources/en.lproj/Localizable.strings b/library/src/iosMain/resources/en.lproj/Localizable.strings deleted file mode 100644 index 0bf497c..0000000 --- a/library/src/iosMain/resources/en.lproj/Localizable.strings +++ /dev/null @@ -1,30 +0,0 @@ -/* Camera Permissions */ -"camera_permission_required" = "Camera permission required"; -"camera_permission_description" = "Camera permission is required to capture photos. Please grant it in settings"; -"open_settings" = "Open settings"; -"camera_permission_denied" = "Camera permission denied"; -"camera_permission_denied_description" = "Camera permission is required to capture photos. Please grant the permissions"; -"grant_permission" = "Grant permission"; -"camera_permission_permanently_denied" = "Camera permission permanently denied"; - -/* Image Confirmation */ -"image_confirmation_title" = "Are you satisfied with the photo?"; -"accept_button" = "Accept"; -"retry_button" = "Retry"; - -/* Selection Dialogs */ -"select_option_dialog_title" = "Select option"; -"take_photo_option" = "Take photo"; -"select_from_gallery_option" = "Select from gallery"; -"cancel_option" = "Cancel"; - -/* Accessibility */ -"preview_image_description" = "Preview"; -"hd_quality_description" = "HD"; -"sd_quality_description" = "SD"; - -/* Errors */ -"invalid_context_error" = "Invalid context. Must be ComponentActivity"; -"photo_capture_error" = "Photo capture failed"; -"gallery_selection_error" = "Gallery selection failed"; -"permission_error" = "Permission error occurred"; \ No newline at end of file diff --git a/library/src/iosMain/resources/es.lproj/Localizable.strings b/library/src/iosMain/resources/es.lproj/Localizable.strings deleted file mode 100644 index 0548ff7..0000000 --- a/library/src/iosMain/resources/es.lproj/Localizable.strings +++ /dev/null @@ -1,30 +0,0 @@ -/* Camera Permissions */ -"camera_permission_required" = "Permiso de cámara requerido"; -"camera_permission_description" = "El permiso de cámara es necesario para capturar fotos. Por favor, concédelo en la configuración"; -"open_settings" = "Abrir configuración"; -"camera_permission_denied" = "Permiso de cámara denegado"; -"camera_permission_denied_description" = "El permiso de cámara es necesario para capturar fotos. Por favor, concede los permisos"; -"grant_permission" = "Conceder permiso"; -"camera_permission_permanently_denied" = "Permiso de cámara denegado permanentemente"; - -/* Image Confirmation */ -"image_confirmation_title" = "¿Estás satisfecho con la foto?"; -"accept_button" = "Aceptar"; -"retry_button" = "Reintentar"; - -/* Selection Dialogs */ -"select_option_dialog_title" = "Seleccionar opción"; -"take_photo_option" = "Tomar foto"; -"select_from_gallery_option" = "Seleccionar de galería"; -"cancel_option" = "Cancelar"; - -/* Accessibility */ -"preview_image_description" = "Vista previa"; -"hd_quality_description" = "HD"; -"sd_quality_description" = "SD"; - -/* Errors */ -"invalid_context_error" = "Contexto inválido. Debe ser ComponentActivity"; -"photo_capture_error" = "Error al capturar la foto"; -"gallery_selection_error" = "Error al seleccionar de la galería"; -"permission_error" = "Error de permisos"; \ No newline at end of file diff --git a/library/src/iosMain/resources/fr.lproj/Localizable.strings b/library/src/iosMain/resources/fr.lproj/Localizable.strings deleted file mode 100644 index f7add27..0000000 --- a/library/src/iosMain/resources/fr.lproj/Localizable.strings +++ /dev/null @@ -1,30 +0,0 @@ -/* Permissions d'appareil photo */ -"camera_permission_required" = "Permission d'appareil photo requise"; -"camera_permission_description" = "Cette application a besoin d'accéder à votre appareil photo pour prendre des photos"; -"open_settings" = "Ouvrir les paramètres"; -"camera_permission_denied" = "Permission d'appareil photo refusée"; -"camera_permission_denied_description" = "Pour utiliser cette fonctionnalité, vous devez autoriser l'accès à l'appareil photo dans les paramètres"; -"grant_permission" = "Accorder la permission"; -"camera_permission_permanently_denied" = "Permission d'appareil photo définitivement refusée"; - -/* Confirmation d'image */ -"image_confirmation_title" = "Confirmer l'image"; -"accept_button" = "Accepter"; -"retry_button" = "Réessayer"; - -/* Dialogues de sélection */ -"select_option_dialog_title" = "Sélectionner une option"; -"take_photo_option" = "Prendre une photo"; -"select_from_gallery_option" = "Sélectionner depuis la galerie"; -"cancel_option" = "Annuler"; - -/* Accessibilité */ -"preview_image_description" = "Aperçu de l'image capturée"; -"hd_quality_description" = "Qualité haute définition"; -"sd_quality_description" = "Qualité standard"; - -/* Erreurs */ -"invalid_context_error" = "Contexte invalide"; -"photo_capture_error" = "Erreur lors de la capture de la photo"; -"gallery_selection_error" = "Erreur lors de la sélection depuis la galerie"; -"permission_error" = "Erreur de permission"; \ No newline at end of file From 3c8359493eba63bd94edc6ff120b909703a4459f Mon Sep 17 00:00:00 2001 From: medAndro Date: Thu, 29 Jan 2026 21:03:28 +0900 Subject: [PATCH 07/10] feat: Add Korean localization for core features This commit introduces comprehensive Korean translations for core functionalities of the image picker library. It covers permission handling, image confirmation, selection dialogs, accessibility labels, and error messages. - **Permission Handling:** Added Korean strings for camera and gallery permission requests, denials, and guidance to open settings. - **Image Confirmation & Selection:** Translated the confirmation dialog ("accept", "retry") and the source selection dialog ("take photo", "select from gallery"). - **Accessibility & Errors:** Provided Korean translations for accessibility descriptions (e.g., image previews) and various error messages (e.g., capture failed, permission error). - **Existing Strings:** Integrated new strings alongside existing Korean translations for the image crop view. --- .../composeResources/values-ko/strings.xml | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/library/src/commonMain/composeResources/values-ko/strings.xml b/library/src/commonMain/composeResources/values-ko/strings.xml index d207a6b..b2468de 100644 --- a/library/src/commonMain/composeResources/values-ko/strings.xml +++ b/library/src/commonMain/composeResources/values-ko/strings.xml @@ -1,8 +1,45 @@ + + 카메라 권한 필요 + 사진을 촬영하려면 카메라 권한이 필요합니다. 설정에서 권한을 허용해주세요 + 설정 열기 + 카메라 권한 거부됨 + 사진을 촬영하려면 카메라 권한이 필요합니다. 권한을 허용해주세요 + 권한 허용 + 카메라 권한이 영구적으로 거부됨 + 저장소 권한 필요 + 갤러리에서 이미지를 선택하려면 저장소 접근 권한이 필요합니다. 권한을 허용해주세요. + 저장소 권한 거부됨 + 이미지를 선택하려면 저장소 권한이 필요합니다. 앱 설정에서 권한을 활성화해주세요. + 권한 허용 + 설정 열기 + + + 이 사진이 마음에 드시나요? + 확인 + 다시 찍기 + + + 옵션 선택 + 사진 촬영 + 갤러리에서 선택 + 취소 + + + 미리보기 + HD + SD + + + 잘못된 컨텍스트입니다. ComponentActivity여야 합니다 + 사진 촬영 실패 + 갤러리 선택 실패 + 권한 오류 발생 + + 이미지 자르기 닫기 적용 - 사각형으로 자르기 원형으로 자르기 확대/축소 From 5bad535c46659d9f21a7325dd904c05ee2101477 Mon Sep 17 00:00:00 2001 From: medAndro Date: Thu, 29 Jan 2026 23:43:43 +0900 Subject: [PATCH 08/10] refactor: Remove redundant resources source directory This commit removes an explicit declaration for `resources.srcDir("src/commonMain/resources")` from the iOS source set in the `build.gradle.kts` file. This path is already a default resource location for Kotlin Multiplatform projects, making the explicit declaration redundant. --- library/build.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/library/build.gradle.kts b/library/build.gradle.kts index e2baf54..2a1097d 100644 --- a/library/build.gradle.kts +++ b/library/build.gradle.kts @@ -419,7 +419,6 @@ kotlin { tasks.withType { duplicatesStrategy = DuplicatesStrategy.INCLUDE } - resources.srcDir("src/commonMain/resources") dependencies { // Ktor iOS engine implementation(libs.ktor.client.darwin) From 1776c78a63170a0da1a87b545ab52d86a0f9668b Mon Sep 17 00:00:00 2001 From: medAndro Date: Thu, 29 Jan 2026 23:54:24 +0900 Subject: [PATCH 09/10] fix: Correct string resource name for image confirmation This commit fixes a missing resource name for the image confirmation title and adds the standard XML declaration to the string resource files. - Renamed the Korean translation string for "Do you like this image?" to `image_confirmation_title` to match its intended use. - Added the `` declaration to both the default English and Korean `strings.xml` files for correctness. --- library/src/commonMain/composeResources/values-ko/strings.xml | 3 ++- library/src/commonMain/composeResources/values/strings.xml | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/library/src/commonMain/composeResources/values-ko/strings.xml b/library/src/commonMain/composeResources/values-ko/strings.xml index b2468de..32fd358 100644 --- a/library/src/commonMain/composeResources/values-ko/strings.xml +++ b/library/src/commonMain/composeResources/values-ko/strings.xml @@ -1,3 +1,4 @@ + 카메라 권한 필요 @@ -15,7 +16,7 @@ 설정 열기 - 이 사진이 마음에 드시나요? + 이 사진이 마음에 드시나요? 확인 다시 찍기 diff --git a/library/src/commonMain/composeResources/values/strings.xml b/library/src/commonMain/composeResources/values/strings.xml index 528c0e6..054e711 100644 --- a/library/src/commonMain/composeResources/values/strings.xml +++ b/library/src/commonMain/composeResources/values/strings.xml @@ -1,3 +1,4 @@ + Camera permission required From 92cb94205da649bcb77f5470117816c75cde7723 Mon Sep 17 00:00:00 2001 From: medAndro Date: Sat, 31 Jan 2026 06:51:41 +0900 Subject: [PATCH 10/10] refactor: Remove unused `componentsResources` from version catalog This commit removes the `componentsResources` version alias from the `gradle/libs.versions.toml` file as it is no longer in use. --- gradle/libs.versions.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e5b80f8..a25ff96 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,6 @@ accompanistPermissions = "0.37.3" agp = "8.13.0" cameraCore = "1.5.1" cameraView = "1.5.1" -componentsResources = "1.10.0" core = "3.5.3" exifinterface = "1.4.2" kotlin = "2.1.21"