Skip to content

Commit f898885

Browse files
authored
Migrate to android native photo picker approach. Deprecate usage of READ_MEDIA_IMAGES permission. (#353)
1 parent 1113109 commit f898885

File tree

12 files changed

+104
-30
lines changed

12 files changed

+104
-30
lines changed

app/src/foss/AndroidManifest.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
android:required="false" />
88

99
<uses-permission android:name="android.permission.CAMERA" />
10-
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
1110
<uses-permission android:name="android.permission.INTERNET" />
1211
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
1312
<uses-permission android:name="android.permission.VIBRATE" />

app/src/full/AndroidManifest.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
android:required="false" />
88

99
<uses-permission android:name="android.permission.CAMERA" />
10-
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
1110
<uses-permission android:name="android.permission.INTERNET" />
1211
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
1312
<uses-permission android:name="android.permission.VIBRATE" />

app/src/main/AndroidManifest.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,15 @@
5959
android:name="autoStoreLocales"
6060
android:value="true" />
6161
</service>
62+
63+
<service android:name="com.google.android.gms.metadata.ModuleDependencies"
64+
android:enabled="false"
65+
android:exported="false"
66+
tools:ignore="MissingClass">
67+
<intent-filter>
68+
<action android:name="com.google.android.gms.metadata.MODULE_DEPENDENCIES" />
69+
</intent-filter>
70+
<meta-data android:name="photopicker_activity:0:required" android:value="" />
71+
</service>
6272
</application>
6373
</manifest>

app/src/main/res/xml/file_provider_paths.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
name="folder_work"
1313
path="work/" />
1414

15+
<cache-path name="cache" path="." />
16+
1517
<external-path name="media" path="." />
1618
<external-path name="external_files" path="."/>
1719
<external-path name="external" path="."/>

app/src/playstore/AndroidManifest.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
android:required="false" />
88

99
<uses-permission android:name="android.permission.CAMERA" />
10-
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
1110
<uses-permission android:name="android.permission.INTERNET" />
1211
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
1312
<uses-permission android:name="android.permission.VIBRATE" />

gradle/libs.versions.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ crop = "0.1.1"
4242
mvi = "1.0.2"
4343
preferences = "1.0.1"
4444
dayNightSwitch = "1.0.0"
45-
imagepicker = "v2.0.3"
4645
catppuccin = "0.1.2"
4746
turbine = "1.2.0"
4847
roboelectric = "4.13"
@@ -108,7 +107,6 @@ compose-gestures = { group = "com.github.SmartToolFactory", name = "Compose-Exte
108107
compose-crop = { group = "io.github.mr0xf00", name = "easycrop", version.ref = "crop" }
109108
shifthackz-mvi = { group = "com.github.ShiftHackZ", name = "AndroidCoreMVI", version.ref = "mvi" }
110109
shifthackz-preferences = { group = "com.github.ShiftHackZ", name = "AndroidPreferences", version.ref = "preferences" }
111-
shifthackz-imagepicker = { group = "com.github.ShiftHackZ", name = "ImagePicker", version.ref = "imagepicker" }
112110
shifthackz-daynightswitch = { group = "com.github.ShiftHackZ", name = "DayNightSwitch", version.ref = "dayNightSwitch" }
113111
shifthackz-catppuccin-legacy = { group = "com.github.ShiftHackZ.Catppuccin-Android-Library", name = "palette-legacy", version.ref = "catppuccin" }
114112
shifthackz-catppuccin-compose = { group = "com.github.ShiftHackZ.Catppuccin-Android-Library", name = "compose", version.ref = "catppuccin" }

presentation/build.gradle.kts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ dependencies {
4242
implementation(libs.rx.kotlin)
4343
implementation(libs.rx.android)
4444

45-
implementation(libs.shifthackz.imagepicker)
4645
implementation(libs.shifthackz.daynightswitch)
4746
implementation(libs.shifthackz.catppuccin.compose)
4847
implementation(libs.shifthackz.catppuccin.splash)

presentation/src/main/java/com/shifthackz/aisdv1/presentation/core/GenerationMviIntent.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import com.shifthackz.aisdv1.domain.entity.StabilityAiStylePreset
1111
import com.shifthackz.aisdv1.presentation.model.Modal
1212
import com.shifthackz.aisdv1.presentation.screen.drawer.DrawerIntent
1313
import com.shifthackz.android.core.mvi.MviIntent
14-
import com.shz.imagepicker.imagepicker.model.PickedResult
1514

1615
sealed interface GenerationMviIntent : MviIntent {
1716

@@ -109,7 +108,7 @@ sealed interface ImageToImageIntent : GenerationMviIntent {
109108

110109
data class UpdateImage(val bitmap: Bitmap) : ImageToImageIntent
111110

112-
data class CropImage(val result: PickedResult) : ImageToImageIntent
111+
data class CropImage(val bitmap: Bitmap) : ImageToImageIntent
113112

114113
enum class Pick : ImageToImageIntent {
115114
Camera, Gallery

presentation/src/main/java/com/shifthackz/aisdv1/presentation/screen/img2img/ImageToImageScreen.kt

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
package com.shifthackz.aisdv1.presentation.screen.img2img
44

5+
import androidx.activity.compose.rememberLauncherForActivityResult
6+
import androidx.activity.result.PickVisualMediaRequest
7+
import androidx.activity.result.contract.ActivityResultContracts
58
import androidx.compose.foundation.background
69
import androidx.compose.foundation.clickable
710
import androidx.compose.foundation.layout.Arrangement
@@ -53,6 +56,7 @@ import androidx.compose.ui.res.stringResource
5356
import androidx.compose.ui.text.input.TextFieldValue
5457
import androidx.compose.ui.unit.dp
5558
import androidx.compose.ui.unit.sp
59+
import androidx.core.content.FileProvider
5660
import com.shifthackz.aisdv1.core.common.file.FileProviderDescriptor
5761
import com.shifthackz.aisdv1.core.common.math.roundTo
5862
import com.shifthackz.aisdv1.core.model.UiText
@@ -70,33 +74,73 @@ import com.shifthackz.aisdv1.presentation.screen.inpaint.components.InPaintCompo
7074
import com.shifthackz.aisdv1.presentation.theme.sliderColors
7175
import com.shifthackz.aisdv1.presentation.utils.Constants.DENOISING_STRENGTH_MAX
7276
import com.shifthackz.aisdv1.presentation.utils.Constants.DENOISING_STRENGTH_MIN
77+
import com.shifthackz.aisdv1.presentation.utils.PermissionUtil
78+
import com.shifthackz.aisdv1.presentation.utils.uriToBitmap
7379
import com.shifthackz.aisdv1.presentation.widget.input.GenerationInputForm
7480
import com.shifthackz.aisdv1.presentation.widget.toolbar.GenerationBottomToolbar
7581
import com.shifthackz.aisdv1.presentation.widget.work.BackgroundWorkWidget
76-
import com.shz.imagepicker.imagepicker.ImagePicker
77-
import com.shz.imagepicker.imagepicker.model.GalleryPicker
7882
import org.koin.androidx.compose.koinViewModel
7983
import org.koin.compose.koinInject
84+
import java.io.File
8085
import com.shifthackz.aisdv1.core.localization.R as LocalizationR
8186

8287
@Composable
8388
fun ImageToImageScreen() {
8489
val context = LocalContext.current
8590
val viewModel = koinViewModel<ImageToImageViewModel>()
8691
val fileProviderDescriptor: FileProviderDescriptor = koinInject()
92+
93+
val cameraFile = File(context.cacheDir, "camera.jpg").apply {
94+
createNewFile()
95+
deleteOnExit()
96+
}
97+
98+
val cameraUri = FileProvider.getUriForFile(
99+
context,
100+
fileProviderDescriptor.providerPath,
101+
cameraFile,
102+
)
103+
104+
val cameraPicker = rememberLauncherForActivityResult(
105+
ActivityResultContracts.TakePicture(),
106+
) { success ->
107+
if (!success) return@rememberLauncherForActivityResult
108+
val bitmap = uriToBitmap(context, cameraUri) ?: return@rememberLauncherForActivityResult
109+
viewModel.processIntent(ImageToImageIntent.CropImage(bitmap))
110+
}
111+
112+
val cameraPermission = rememberLauncherForActivityResult(
113+
ActivityResultContracts.RequestPermission()
114+
) { isGranted ->
115+
if (!isGranted) return@rememberLauncherForActivityResult
116+
cameraPicker.launch(cameraUri)
117+
}
118+
119+
val mediaPicker = rememberLauncherForActivityResult(
120+
ActivityResultContracts.PickVisualMedia(),
121+
) { uri ->
122+
val bitmap =
123+
uri?.let { uriToBitmap(context, it) } ?: return@rememberLauncherForActivityResult
124+
viewModel.processIntent(ImageToImageIntent.CropImage(bitmap))
125+
}
126+
87127
MviComponent(
88128
viewModel = viewModel,
89129
processEffect = { effect ->
90-
ImagePicker.Builder(fileProviderDescriptor.providerPath) { result ->
91-
viewModel.processIntent(ImageToImageIntent.CropImage(result))
130+
when (effect) {
131+
ImageToImageEffect.GalleryPicker -> {
132+
val request = PickVisualMediaRequest(
133+
ActivityResultContracts.PickVisualMedia.ImageOnly,
134+
)
135+
mediaPicker.launch(request)
136+
}
137+
138+
ImageToImageEffect.CameraPicker -> {
139+
if (PermissionUtil.checkCameraPermission(context, cameraPermission::launch)) {
140+
cameraPicker.launch(cameraUri)
141+
}
142+
}
92143
}
93-
.useGallery(effect == ImageToImageEffect.GalleryPicker)
94-
.useCamera(effect == ImageToImageEffect.CameraPicker)
95-
.autoRotate(effect == ImageToImageEffect.GalleryPicker)
96-
.multipleSelection(false)
97-
.galleryPicker(GalleryPicker.NATIVE)
98-
.build()
99-
.launch(context)
100144
},
101145
) { state, intentHandler ->
102146
ScreenContent(
@@ -111,7 +155,7 @@ fun ImageToImageScreen() {
111155
private fun ScreenContent(
112156
modifier: Modifier = Modifier,
113157
state: ImageToImageState,
114-
processIntent: (GenerationMviIntent) -> Unit = {}
158+
processIntent: (GenerationMviIntent) -> Unit = {},
115159
) {
116160
val promptChipTextFieldState = remember { mutableStateOf(TextFieldValue()) }
117161
val negativePromptChipTextFieldState = remember { mutableStateOf(TextFieldValue()) }
@@ -340,7 +384,7 @@ private fun ScreenContent(
340384
ServerSource.OPEN_AI -> LocalizationR.string.action_change_configuration
341385

342386
else -> LocalizationR.string.action_generate
343-
}
387+
},
344388
),
345389
color = LocalContentColor.current,
346390
)
@@ -499,7 +543,7 @@ private fun ImagePickButtonBox(
499543
id = when (buttonType) {
500544
ImagePickButton.PHOTO -> LocalizationR.string.action_image_picker_gallery
501545
ImagePickButton.CAMERA -> LocalizationR.string.action_image_picker_camera
502-
}
546+
},
503547
),
504548
fontSize = 17.sp,
505549
)

presentation/src/main/java/com/shifthackz/aisdv1/presentation/screen/img2img/ImageToImageViewModel.kt

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ import com.shifthackz.aisdv1.presentation.model.Modal
3232
import com.shifthackz.aisdv1.presentation.navigation.router.drawer.DrawerRouter
3333
import com.shifthackz.aisdv1.presentation.navigation.router.main.MainRouter
3434
import com.shifthackz.aisdv1.presentation.screen.inpaint.InPaintStateProducer
35-
import com.shz.imagepicker.imagepicker.model.PickedResult
3635
import io.reactivex.rxjava3.core.Single
3736
import io.reactivex.rxjava3.disposables.Disposable
3837
import io.reactivex.rxjava3.kotlin.subscribeBy
@@ -141,12 +140,8 @@ class ImageToImageViewModel(
141140

142141
ImageToImageIntent.Pick.Gallery -> emitEffect(ImageToImageEffect.GalleryPicker)
143142

144-
is ImageToImageIntent.CropImage -> when (intent.result) {
145-
is PickedResult.Single -> updateState {
146-
it.copy(screenModal = Modal.Image.Crop(intent.result.image.bitmap))
147-
}
148-
149-
else -> Unit
143+
is ImageToImageIntent.CropImage -> updateState {
144+
it.copy(screenModal = Modal.Image.Crop(intent.bitmap))
150145
}
151146

152147
is ImageToImageIntent.UpdateImage -> updateState {

0 commit comments

Comments
 (0)