Skip to content

Commit 8aed31d

Browse files
committed
Fixed file picker and updated sample code
1 parent 25c3aa5 commit 8aed31d

File tree

6 files changed

+143
-73
lines changed

6 files changed

+143
-73
lines changed

JetPDFVue/src/main/java/com/pratikk/jetpdfvue/state/VueFilePicker.kt

Lines changed: 73 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import android.net.Uri
77
import android.os.Build
88
import android.os.Parcelable
99
import android.provider.MediaStore
10-
import android.webkit.MimeTypeMap
1110
import androidx.activity.compose.ManagedActivityResultLauncher
1211
import androidx.activity.compose.rememberLauncherForActivityResult
1312
import androidx.activity.result.ActivityResult
@@ -21,11 +20,9 @@ import androidx.compose.runtime.saveable.listSaver
2120
import androidx.compose.runtime.setValue
2221
import androidx.compose.ui.platform.LocalContext
2322
import androidx.core.content.FileProvider
24-
import androidx.core.net.toFile
2523
import androidx.core.net.toUri
26-
import com.pratikk.jetpdfvue.util.generateFileName
2724
import com.pratikk.jetpdfvue.util.getDateddMMyyyyHHmm
28-
import com.pratikk.jetpdfvue.util.getFile
25+
import com.pratikk.jetpdfvue.util.getFileType
2926
import com.pratikk.jetpdfvue.util.toFile
3027
import kotlinx.coroutines.CoroutineStart
3128
import kotlinx.coroutines.Job
@@ -37,22 +34,11 @@ import java.util.Calendar
3734

3835
sealed 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

5743
enum class VueImportSources {
5844
CAMERA, GALLERY, BASE64, PDF
@@ -61,8 +47,9 @@ enum class VueImportSources {
6147
class 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
}

JetPDFVue/src/main/java/com/pratikk/jetpdfvue/state/VueReaderState.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,12 +139,12 @@ abstract class VueReaderState(
139139
mFile = when(vueResource.fileType){
140140
VueFileType.PDF -> {
141141
val blankFile = File(context.filesDir, generateFileName())
142-
inputStream.toFile(".pdf").copyTo(blankFile,true)
142+
inputStream.toFile("pdf").copyTo(blankFile,true)
143143
blankFile
144144
}
145145

146146
VueFileType.IMAGE -> {
147-
val imgFile = inputStream.toFile(".jpg")
147+
val imgFile = inputStream.toFile("jpg")
148148
val _file = File(context.filesDir, generateFileName())
149149
addImageToPdf(
150150
imageFilePath = imgFile.absolutePath,
@@ -154,7 +154,7 @@ abstract class VueReaderState(
154154
}
155155
VueFileType.BASE64 -> {
156156
val blankFile = File(context.filesDir, generateFileName())
157-
inputStream.toFile(".txt").toBase64File().copyTo(blankFile,true)
157+
inputStream.toFile("txt").toBase64File().copyTo(blankFile,true)
158158
blankFile
159159
}
160160
}

JetPDFVue/src/main/java/com/pratikk/jetpdfvue/util/VueFileExtensions.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import android.media.ExifInterface
99
import android.net.Uri
1010
import android.util.Base64
1111
import androidx.core.content.FileProvider
12+
import com.pratikk.jetpdfvue.state.VueFileType
1213
import java.io.ByteArrayOutputStream
1314
import java.io.File
1415
import java.io.FileInputStream
@@ -20,6 +21,16 @@ import java.util.Calendar
2021
import java.util.Date
2122
import java.util.Locale
2223

24+
fun Uri.getFileType(context: Context): VueFileType {
25+
val type = context.contentResolver.getType(this)
26+
?: scheme ?: throw Throwable("File type cannot be decoded, please check uri $this")
27+
return if (type.contains("pdf"))
28+
VueFileType.PDF
29+
else if (type.contains("text") || type.contains("txt"))
30+
VueFileType.BASE64
31+
else
32+
VueFileType.IMAGE
33+
}
2334

2435
fun File.share(context: Context) {
2536
if (exists()) {
@@ -116,7 +127,7 @@ fun File.compressImageToThreshold(threshold: Int) {
116127
* Extension function to convert any input stream to a file
117128
* */
118129
fun InputStream.toFile(extension:String):File{
119-
val _file = File.createTempFile("temp",extension)
130+
val _file = File.createTempFile("temp",".${extension}")
120131
val byteArrayOutputStream = ByteArrayOutputStream()
121132
val buffer = ByteArray(1024)
122133
var bytesRead: Int

app/src/main/java/com/pratikk/jetpackpdf/MainActivity.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import com.pratikk.jetpackpdf.horizontalSamples.HorizontalPdfViewerLocal
4545
import com.pratikk.jetpackpdf.ui.theme.JetpackPDFTheme
4646
import com.pratikk.jetpackpdf.verticalSamples.VerticalPdfViewer
4747
import com.pratikk.jetpackpdf.verticalSamples.VerticalPdfViewerLocal
48+
import com.pratikk.jetpdfvue.state.VueFilePicker
4849
import com.pratikk.jetpdfvue.state.VueFileType
4950
import com.pratikk.jetpdfvue.state.VueResourceType
5051
import com.pratikk.jetpdfvue.state.rememberHorizontalVueReaderState
@@ -108,7 +109,7 @@ class MainActivity : ComponentActivity() {
108109
resource = VueResourceType.Local(
109110
uri = context.resources.openRawResource(
110111
R.raw.demo
111-
).toFile(".jpg").toUri(),
112+
).toFile("jpg").toUri(),
112113
fileType = VueFileType.IMAGE
113114
),
114115
)
@@ -212,7 +213,7 @@ class MainActivity : ComponentActivity() {
212213
resource = VueResourceType.Local(
213214
uri = context.resources.openRawResource(
214215
R.raw.demo
215-
).toFile(".jpg").toUri(),
216+
).toFile("jpg").toUri(),
216217
fileType = VueFileType.IMAGE
217218
),
218219
)

app/src/main/java/com/pratikk/jetpackpdf/horizontalSamples/HorizontalPdfViewer.kt

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.pratikk.jetpackpdf.horizontalSamples
22

33
import android.content.res.Configuration
4+
import android.net.Uri
45
import androidx.compose.foundation.layout.Arrangement
56
import androidx.compose.foundation.layout.BoxWithConstraints
67
import androidx.compose.foundation.layout.Column
@@ -10,9 +11,12 @@ import androidx.compose.material3.CircularProgressIndicator
1011
import androidx.compose.material3.Text
1112
import androidx.compose.runtime.Composable
1213
import androidx.compose.runtime.LaunchedEffect
14+
import androidx.compose.runtime.getValue
15+
import androidx.compose.runtime.mutableStateOf
1316
import androidx.compose.runtime.remember
1417
import androidx.compose.runtime.rememberCoroutineScope
1518
import androidx.compose.runtime.saveable.rememberSaveable
19+
import androidx.compose.runtime.setValue
1620
import androidx.compose.ui.Alignment
1721
import androidx.compose.ui.Modifier
1822
import androidx.compose.ui.platform.LocalConfiguration
@@ -28,6 +32,7 @@ import com.pratikk.jetpdfvue.state.VueLoadState
2832
import com.pratikk.jetpdfvue.state.VueResourceType
2933
import com.pratikk.jetpdfvue.state.rememberHorizontalVueReaderState
3034
import com.pratikk.jetpdfvue.util.compressImageToThreshold
35+
import com.pratikk.jetpdfvue.util.getFileType
3136
import kotlinx.coroutines.launch
3237

3338
@Composable
@@ -36,26 +41,29 @@ fun HorizontalPdfViewerLocal(){
3641
val vueFilePicker = rememberSaveable(saver = VueFilePicker.Saver) {
3742
VueFilePicker()
3843
}
39-
val launcher = vueFilePicker.getLauncher()
40-
when(vueFilePicker.vueFilePickerState){
41-
VueFilePickerState.VueFilePickerIdeal -> {
42-
Column(modifier = Modifier.fillMaxSize(),
43-
horizontalAlignment = Alignment.CenterHorizontally,
44-
verticalArrangement = Arrangement.Center) {
45-
Button(onClick = { vueFilePicker.launchIntent(context, listOf(VueImportSources.CAMERA,VueImportSources.GALLERY,VueImportSources.PDF,VueImportSources.BASE64),launcher)}) {
46-
Text(text = "Import Document")
47-
}
44+
var uri:Uri by rememberSaveable(stateSaver = VueFilePicker.UriSaver) {
45+
mutableStateOf(Uri.EMPTY)
46+
}
47+
val launcher = vueFilePicker.getLauncher(
48+
onResult = {
49+
uri = it.toUri()
50+
})
51+
if(uri == Uri.EMPTY){
52+
Column(modifier = Modifier.fillMaxSize(),
53+
horizontalAlignment = Alignment.CenterHorizontally,
54+
verticalArrangement = Arrangement.Center) {
55+
Button(onClick = { vueFilePicker.launchIntent(context, listOf(VueImportSources.CAMERA,VueImportSources.GALLERY,VueImportSources.PDF,VueImportSources.BASE64),launcher)}) {
56+
Text(text = "Import Document")
4857
}
4958
}
50-
is VueFilePickerState.VueFilePickerImported -> {
51-
val localImage = rememberHorizontalVueReaderState(
52-
resource = VueResourceType.Local(
53-
uri = (vueFilePicker.vueFilePickerState as VueFilePickerState.VueFilePickerImported).uri,
54-
fileType = (vueFilePicker.vueFilePickerState as VueFilePickerState.VueFilePickerImported).getFileType(context)
55-
)
59+
}else{
60+
val localImage = rememberHorizontalVueReaderState(
61+
resource = VueResourceType.Local(
62+
uri = uri,
63+
fileType = uri.getFileType(context)
5664
)
57-
HorizontalPdfViewer(horizontalVueReaderState = localImage)
58-
}
65+
)
66+
HorizontalPdfViewer(horizontalVueReaderState = localImage)
5967
}
6068
}
6169

0 commit comments

Comments
 (0)