@@ -7,7 +7,6 @@ import android.net.Uri
77import android.os.Build
88import android.os.Parcelable
99import android.provider.MediaStore
10- import android.webkit.MimeTypeMap
1110import androidx.activity.compose.ManagedActivityResultLauncher
1211import androidx.activity.compose.rememberLauncherForActivityResult
1312import androidx.activity.result.ActivityResult
@@ -21,11 +20,9 @@ import androidx.compose.runtime.saveable.listSaver
2120import androidx.compose.runtime.setValue
2221import androidx.compose.ui.platform.LocalContext
2322import androidx.core.content.FileProvider
24- import androidx.core.net.toFile
2523import androidx.core.net.toUri
26- import com.pratikk.jetpdfvue.util.generateFileName
2724import com.pratikk.jetpdfvue.util.getDateddMMyyyyHHmm
28- import com.pratikk.jetpdfvue.util.getFile
25+ import com.pratikk.jetpdfvue.util.getFileType
2926import com.pratikk.jetpdfvue.util.toFile
3027import kotlinx.coroutines.CoroutineStart
3128import kotlinx.coroutines.Job
@@ -37,22 +34,11 @@ import java.util.Calendar
3734
3835sealed class VueFilePickerState {
3936 @Parcelize
40- data class VueFilePickerImported (val uri : Uri ) : VueFilePickerState(), Parcelable {
41- fun getFileType (context : Context ): VueFileType {
42- val type = context.contentResolver.getType(uri)
43- ? : uri.scheme ? : throw Throwable (" File type cannot be decoded, please check uri $uri " )
44- return if (type.contains(" pdf" ))
45- VueFileType .PDF
46- else if (type.contains(" text" ) || type.contains(" txt" ))
47- VueFileType .BASE64
48- else
49- VueFileType .IMAGE
50- }
51- }
37+ data class VueFilePickerImported (val uri : Uri ) : VueFilePickerState(), Parcelable
5238
53- @Parcelize
54- data object VueFilePickerIdeal : VueFilePickerState (), Parcelable
55- }
39+ @Parcelize
40+ data object VueFilePickerIdeal : VueFilePickerState (), Parcelable
41+ }
5642
5743enum class VueImportSources {
5844 CAMERA , GALLERY , BASE64 , PDF
@@ -61,8 +47,9 @@ enum class VueImportSources {
6147class VueFilePicker {
6248 private var importFile: File ? = null
6349 private var importJob: Job ? = null
64- var vueFilePickerState by mutableStateOf< VueFilePickerState >( VueFilePickerState . VueFilePickerIdeal )
50+ var isImporting by mutableStateOf( false )
6551 private set
52+ private var vueFilePickerState by mutableStateOf<VueFilePickerState >(VueFilePickerState .VueFilePickerIdeal )
6653
6754 companion object {
6855 val Saver : Saver <VueFilePicker , * > = listSaver(
@@ -82,6 +69,12 @@ class VueFilePicker {
8269 }
8370 }
8471 )
72+ val UriSaver : Saver <Uri ,* > = listSaver(
73+ save = { listOf (VueFilePickerState .VueFilePickerImported (it)) },
74+ restore = {
75+ it[0 ].uri
76+ }
77+ )
8578 }
8679
8780 /* *
@@ -98,7 +91,7 @@ class VueFilePicker {
9891 lazyMessage = { " File Sources cannot be empty" })
9992 val intents = ArrayList <Intent >()
10093 val filterImportState = vueImportSources.toMutableList().let {
101- if (it.contains(VueImportSources .BASE64 ) && it.contains(VueImportSources .PDF )) {
94+ if (it.contains(VueImportSources .BASE64 ) && it.contains(VueImportSources .PDF )) {
10295 it.remove(VueImportSources .PDF )
10396 it.remove(VueImportSources .BASE64 )
10497 intents.add(base64AndPdfIntent())
@@ -118,22 +111,68 @@ class VueFilePicker {
118111 launcher.launch(chooserIntent)
119112 }
120113
114+ /* *
115+ * @param interceptResult Any operations to be done on file should happen inside this lambda. Note : This lambda will be invoked on every configuration change until onResult is called
116+ * @param onResult Final result can be obtained inside this block. The result should be copied to a known location for further use
117+ */
121118 @Composable
122- fun getLauncher (onResult : (Uri ) -> Unit = {}): ManagedActivityResultLauncher <Intent , ActivityResult > {
119+ fun getLauncher (
120+ interceptResult : suspend (File ) -> Unit = {},
121+ onResult : (File ) -> Unit = {}
122+ ): ManagedActivityResultLauncher <Intent , ActivityResult > {
123+ val context = LocalContext .current
124+ LaunchedEffect (key1 = vueFilePickerState, block = {
125+ if (vueFilePickerState is VueFilePickerState .VueFilePickerImported && importJob == null ) {
126+ importJob = launch(context = coroutineContext, start = CoroutineStart .LAZY ) {
127+ with ((vueFilePickerState as VueFilePickerState .VueFilePickerImported )) {
128+ // Create a temp file using result uri
129+ val file = context.contentResolver.openInputStream(uri)?.use {
130+ val ext = when (uri.getFileType(context)) {
131+ VueFileType .PDF -> {
132+ " pdf"
133+ }
134+
135+ VueFileType .IMAGE -> {
136+ " jpg"
137+ }
138+
139+ VueFileType .BASE64 -> {
140+ " txt"
141+ }
142+ }
143+ it.toFile(ext)
144+ }!!
145+
146+ isImporting = true
147+ interceptResult(file)
148+
149+ if (isActive) {
150+ onResult(file)
151+ }
152+ }
153+ }.apply {
154+ invokeOnCompletion {
155+ importJob = null
156+ vueFilePickerState = VueFilePickerState .VueFilePickerIdeal
157+ isImporting = false
158+ }
159+ }
160+ importJob?.start()
161+ importJob?.join()
162+ }
163+ })
123164 return rememberLauncherForActivityResult(ActivityResultContracts .StartActivityForResult ()) {
124- if (it.resultCode == Activity .RESULT_OK ) {
165+ vueFilePickerState = if (it.resultCode == Activity .RESULT_OK ) {
125166 val uri = it.data?.data
126167 if (uri != null ) {
127168 // Other sources
128- vueFilePickerState = VueFilePickerState .VueFilePickerImported (uri)
129- onResult(uri)
169+ VueFilePickerState .VueFilePickerImported (uri)
130170 } else {
131171 // From Camera
132- vueFilePickerState = VueFilePickerState .VueFilePickerImported (importFile!! .toUri())
133- onResult(importFile!! .toUri())
172+ VueFilePickerState .VueFilePickerImported (importFile!! .toUri())
134173 }
135174 } else {
136- vueFilePickerState = VueFilePickerState .VueFilePickerIdeal
175+ VueFilePickerState .VueFilePickerIdeal
137176 }
138177 }
139178 }
@@ -192,10 +231,14 @@ class VueFilePicker {
192231 intent.addCategory(Intent .CATEGORY_OPENABLE )
193232 return intent
194233 }
195- private fun base64AndPdfIntent ():Intent {
234+
235+ private fun base64AndPdfIntent (): Intent {
196236 val intent = Intent (Intent .ACTION_GET_CONTENT )
197237 intent.type = " */*"
198- intent.putExtra(Intent .EXTRA_MIME_TYPES , listOf (" application/pdf" ," text/plain" ).toTypedArray())
238+ intent.putExtra(
239+ Intent .EXTRA_MIME_TYPES ,
240+ listOf (" application/pdf" , " text/plain" ).toTypedArray()
241+ )
199242 intent.addCategory(Intent .CATEGORY_OPENABLE )
200243 return intent
201244 }
0 commit comments