diff --git a/app/src/foss/AndroidManifest.xml b/app/src/foss/AndroidManifest.xml
index 5e2a44ae..6e78f8d0 100755
--- a/app/src/foss/AndroidManifest.xml
+++ b/app/src/foss/AndroidManifest.xml
@@ -7,7 +7,6 @@
android:required="false" />
-
diff --git a/app/src/full/AndroidManifest.xml b/app/src/full/AndroidManifest.xml
index 5e2a44ae..6e78f8d0 100755
--- a/app/src/full/AndroidManifest.xml
+++ b/app/src/full/AndroidManifest.xml
@@ -7,7 +7,6 @@
android:required="false" />
-
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4aac9eb0..857bdb8d 100755
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -59,5 +59,15 @@
android:name="autoStoreLocales"
android:value="true" />
+
+
+
+
+
+
+
diff --git a/app/src/main/res/xml/file_provider_paths.xml b/app/src/main/res/xml/file_provider_paths.xml
index 2f3afb0d..532c7d27 100644
--- a/app/src/main/res/xml/file_provider_paths.xml
+++ b/app/src/main/res/xml/file_provider_paths.xml
@@ -12,6 +12,8 @@
name="folder_work"
path="work/" />
+
+
diff --git a/app/src/playstore/AndroidManifest.xml b/app/src/playstore/AndroidManifest.xml
index bb6cecbe..481f8223 100755
--- a/app/src/playstore/AndroidManifest.xml
+++ b/app/src/playstore/AndroidManifest.xml
@@ -7,7 +7,6 @@
android:required="false" />
-
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 6186b7c3..f79c2d2f 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -42,7 +42,6 @@ crop = "0.1.1"
mvi = "1.0.2"
preferences = "1.0.1"
dayNightSwitch = "1.0.0"
-imagepicker = "v2.0.3"
catppuccin = "0.1.2"
turbine = "1.2.0"
roboelectric = "4.13"
@@ -108,7 +107,6 @@ compose-gestures = { group = "com.github.SmartToolFactory", name = "Compose-Exte
compose-crop = { group = "io.github.mr0xf00", name = "easycrop", version.ref = "crop" }
shifthackz-mvi = { group = "com.github.ShiftHackZ", name = "AndroidCoreMVI", version.ref = "mvi" }
shifthackz-preferences = { group = "com.github.ShiftHackZ", name = "AndroidPreferences", version.ref = "preferences" }
-shifthackz-imagepicker = { group = "com.github.ShiftHackZ", name = "ImagePicker", version.ref = "imagepicker" }
shifthackz-daynightswitch = { group = "com.github.ShiftHackZ", name = "DayNightSwitch", version.ref = "dayNightSwitch" }
shifthackz-catppuccin-legacy = { group = "com.github.ShiftHackZ.Catppuccin-Android-Library", name = "palette-legacy", version.ref = "catppuccin" }
shifthackz-catppuccin-compose = { group = "com.github.ShiftHackZ.Catppuccin-Android-Library", name = "compose", version.ref = "catppuccin" }
diff --git a/presentation/build.gradle.kts b/presentation/build.gradle.kts
index 254bb6ec..4fec26cb 100755
--- a/presentation/build.gradle.kts
+++ b/presentation/build.gradle.kts
@@ -42,7 +42,6 @@ dependencies {
implementation(libs.rx.kotlin)
implementation(libs.rx.android)
- implementation(libs.shifthackz.imagepicker)
implementation(libs.shifthackz.daynightswitch)
implementation(libs.shifthackz.catppuccin.compose)
implementation(libs.shifthackz.catppuccin.splash)
diff --git a/presentation/src/main/java/com/shifthackz/aisdv1/presentation/core/GenerationMviIntent.kt b/presentation/src/main/java/com/shifthackz/aisdv1/presentation/core/GenerationMviIntent.kt
index e496b190..8adef7d4 100644
--- a/presentation/src/main/java/com/shifthackz/aisdv1/presentation/core/GenerationMviIntent.kt
+++ b/presentation/src/main/java/com/shifthackz/aisdv1/presentation/core/GenerationMviIntent.kt
@@ -11,7 +11,6 @@ import com.shifthackz.aisdv1.domain.entity.StabilityAiStylePreset
import com.shifthackz.aisdv1.presentation.model.Modal
import com.shifthackz.aisdv1.presentation.screen.drawer.DrawerIntent
import com.shifthackz.android.core.mvi.MviIntent
-import com.shz.imagepicker.imagepicker.model.PickedResult
sealed interface GenerationMviIntent : MviIntent {
@@ -109,7 +108,7 @@ sealed interface ImageToImageIntent : GenerationMviIntent {
data class UpdateImage(val bitmap: Bitmap) : ImageToImageIntent
- data class CropImage(val result: PickedResult) : ImageToImageIntent
+ data class CropImage(val bitmap: Bitmap) : ImageToImageIntent
enum class Pick : ImageToImageIntent {
Camera, Gallery
diff --git a/presentation/src/main/java/com/shifthackz/aisdv1/presentation/screen/img2img/ImageToImageScreen.kt b/presentation/src/main/java/com/shifthackz/aisdv1/presentation/screen/img2img/ImageToImageScreen.kt
index 421a98fd..a069c396 100644
--- a/presentation/src/main/java/com/shifthackz/aisdv1/presentation/screen/img2img/ImageToImageScreen.kt
+++ b/presentation/src/main/java/com/shifthackz/aisdv1/presentation/screen/img2img/ImageToImageScreen.kt
@@ -2,6 +2,9 @@
package com.shifthackz.aisdv1.presentation.screen.img2img
+import androidx.activity.compose.rememberLauncherForActivityResult
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
@@ -53,6 +56,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
+import androidx.core.content.FileProvider
import com.shifthackz.aisdv1.core.common.file.FileProviderDescriptor
import com.shifthackz.aisdv1.core.common.math.roundTo
import com.shifthackz.aisdv1.core.model.UiText
@@ -70,13 +74,14 @@ import com.shifthackz.aisdv1.presentation.screen.inpaint.components.InPaintCompo
import com.shifthackz.aisdv1.presentation.theme.sliderColors
import com.shifthackz.aisdv1.presentation.utils.Constants.DENOISING_STRENGTH_MAX
import com.shifthackz.aisdv1.presentation.utils.Constants.DENOISING_STRENGTH_MIN
+import com.shifthackz.aisdv1.presentation.utils.PermissionUtil
+import com.shifthackz.aisdv1.presentation.utils.uriToBitmap
import com.shifthackz.aisdv1.presentation.widget.input.GenerationInputForm
import com.shifthackz.aisdv1.presentation.widget.toolbar.GenerationBottomToolbar
import com.shifthackz.aisdv1.presentation.widget.work.BackgroundWorkWidget
-import com.shz.imagepicker.imagepicker.ImagePicker
-import com.shz.imagepicker.imagepicker.model.GalleryPicker
import org.koin.androidx.compose.koinViewModel
import org.koin.compose.koinInject
+import java.io.File
import com.shifthackz.aisdv1.core.localization.R as LocalizationR
@Composable
@@ -84,19 +89,58 @@ fun ImageToImageScreen() {
val context = LocalContext.current
val viewModel = koinViewModel()
val fileProviderDescriptor: FileProviderDescriptor = koinInject()
+
+ val cameraFile = File(context.cacheDir, "camera.jpg").apply {
+ createNewFile()
+ deleteOnExit()
+ }
+
+ val cameraUri = FileProvider.getUriForFile(
+ context,
+ fileProviderDescriptor.providerPath,
+ cameraFile,
+ )
+
+ val cameraPicker = rememberLauncherForActivityResult(
+ ActivityResultContracts.TakePicture(),
+ ) { success ->
+ if (!success) return@rememberLauncherForActivityResult
+ val bitmap = uriToBitmap(context, cameraUri) ?: return@rememberLauncherForActivityResult
+ viewModel.processIntent(ImageToImageIntent.CropImage(bitmap))
+ }
+
+ val cameraPermission = rememberLauncherForActivityResult(
+ ActivityResultContracts.RequestPermission()
+ ) { isGranted ->
+ if (!isGranted) return@rememberLauncherForActivityResult
+ cameraPicker.launch(cameraUri)
+ }
+
+ val mediaPicker = rememberLauncherForActivityResult(
+ ActivityResultContracts.PickVisualMedia(),
+ ) { uri ->
+ val bitmap =
+ uri?.let { uriToBitmap(context, it) } ?: return@rememberLauncherForActivityResult
+ viewModel.processIntent(ImageToImageIntent.CropImage(bitmap))
+ }
+
MviComponent(
viewModel = viewModel,
processEffect = { effect ->
- ImagePicker.Builder(fileProviderDescriptor.providerPath) { result ->
- viewModel.processIntent(ImageToImageIntent.CropImage(result))
+ when (effect) {
+ ImageToImageEffect.GalleryPicker -> {
+ val request = PickVisualMediaRequest(
+ ActivityResultContracts.PickVisualMedia.ImageOnly,
+ )
+ mediaPicker.launch(request)
+ }
+
+ ImageToImageEffect.CameraPicker -> {
+ if (PermissionUtil.checkCameraPermission(context, cameraPermission::launch)) {
+ cameraPicker.launch(cameraUri)
+ }
+ }
}
- .useGallery(effect == ImageToImageEffect.GalleryPicker)
- .useCamera(effect == ImageToImageEffect.CameraPicker)
- .autoRotate(effect == ImageToImageEffect.GalleryPicker)
- .multipleSelection(false)
- .galleryPicker(GalleryPicker.NATIVE)
- .build()
- .launch(context)
},
) { state, intentHandler ->
ScreenContent(
@@ -111,7 +155,7 @@ fun ImageToImageScreen() {
private fun ScreenContent(
modifier: Modifier = Modifier,
state: ImageToImageState,
- processIntent: (GenerationMviIntent) -> Unit = {}
+ processIntent: (GenerationMviIntent) -> Unit = {},
) {
val promptChipTextFieldState = remember { mutableStateOf(TextFieldValue()) }
val negativePromptChipTextFieldState = remember { mutableStateOf(TextFieldValue()) }
@@ -340,7 +384,7 @@ private fun ScreenContent(
ServerSource.OPEN_AI -> LocalizationR.string.action_change_configuration
else -> LocalizationR.string.action_generate
- }
+ },
),
color = LocalContentColor.current,
)
@@ -499,7 +543,7 @@ private fun ImagePickButtonBox(
id = when (buttonType) {
ImagePickButton.PHOTO -> LocalizationR.string.action_image_picker_gallery
ImagePickButton.CAMERA -> LocalizationR.string.action_image_picker_camera
- }
+ },
),
fontSize = 17.sp,
)
diff --git a/presentation/src/main/java/com/shifthackz/aisdv1/presentation/screen/img2img/ImageToImageViewModel.kt b/presentation/src/main/java/com/shifthackz/aisdv1/presentation/screen/img2img/ImageToImageViewModel.kt
index 5ffb6212..dde779a5 100644
--- a/presentation/src/main/java/com/shifthackz/aisdv1/presentation/screen/img2img/ImageToImageViewModel.kt
+++ b/presentation/src/main/java/com/shifthackz/aisdv1/presentation/screen/img2img/ImageToImageViewModel.kt
@@ -32,7 +32,6 @@ import com.shifthackz.aisdv1.presentation.model.Modal
import com.shifthackz.aisdv1.presentation.navigation.router.drawer.DrawerRouter
import com.shifthackz.aisdv1.presentation.navigation.router.main.MainRouter
import com.shifthackz.aisdv1.presentation.screen.inpaint.InPaintStateProducer
-import com.shz.imagepicker.imagepicker.model.PickedResult
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.kotlin.subscribeBy
@@ -141,12 +140,8 @@ class ImageToImageViewModel(
ImageToImageIntent.Pick.Gallery -> emitEffect(ImageToImageEffect.GalleryPicker)
- is ImageToImageIntent.CropImage -> when (intent.result) {
- is PickedResult.Single -> updateState {
- it.copy(screenModal = Modal.Image.Crop(intent.result.image.bitmap))
- }
-
- else -> Unit
+ is ImageToImageIntent.CropImage -> updateState {
+ it.copy(screenModal = Modal.Image.Crop(intent.bitmap))
}
is ImageToImageIntent.UpdateImage -> updateState {
diff --git a/presentation/src/main/java/com/shifthackz/aisdv1/presentation/utils/PermissionUtil.kt b/presentation/src/main/java/com/shifthackz/aisdv1/presentation/utils/PermissionUtil.kt
index c001bcb6..9e0b83ed 100644
--- a/presentation/src/main/java/com/shifthackz/aisdv1/presentation/utils/PermissionUtil.kt
+++ b/presentation/src/main/java/com/shifthackz/aisdv1/presentation/utils/PermissionUtil.kt
@@ -11,7 +11,7 @@ object PermissionUtil {
fun checkStoragePermission(
context: Context,
onLaunch: (missingPermissions: Array) -> Unit = {},
- ): Boolean {
+ ): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
return false
}
@@ -43,4 +43,19 @@ object PermissionUtil {
}
return true
}
+
+ fun checkCameraPermission(
+ context: Context,
+ onLaunch: (missingPermission: String) -> Unit,
+ ): Boolean {
+ if (ActivityCompat.checkSelfPermission(
+ context,
+ Manifest.permission.CAMERA
+ ) != PackageManager.PERMISSION_GRANTED
+ ) {
+ onLaunch(Manifest.permission.CAMERA)
+ return false
+ }
+ return true
+ }
}
diff --git a/presentation/src/main/java/com/shifthackz/aisdv1/presentation/utils/UriToBitmap.kt b/presentation/src/main/java/com/shifthackz/aisdv1/presentation/utils/UriToBitmap.kt
new file mode 100644
index 00000000..56930b01
--- /dev/null
+++ b/presentation/src/main/java/com/shifthackz/aisdv1/presentation/utils/UriToBitmap.kt
@@ -0,0 +1,15 @@
+package com.shifthackz.aisdv1.presentation.utils
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.net.Uri
+import com.shifthackz.aisdv1.core.common.log.errorLog
+
+fun uriToBitmap(context: Context, uri: Uri): Bitmap? = try {
+ val inputStream = context.contentResolver.openInputStream(uri)
+ BitmapFactory.decodeStream(inputStream).also { inputStream?.close() }
+} catch (e: Exception) {
+ errorLog("UrlToBitmap", e)
+ null
+}