-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDocumentPickerService.kt
More file actions
182 lines (161 loc) · 6.24 KB
/
Copy pathDocumentPickerService.kt
File metadata and controls
182 lines (161 loc) · 6.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
package com.github.familyvault.services
import androidx.compose.runtime.mutableStateListOf
import com.github.familyvault.utils.MimeTypeParser
import io.ktor.utils.io.core.toByteArray
import kotlinx.cinterop.BetaInteropApi
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.useContents
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
import kotlinx.coroutines.runBlocking
import platform.CoreGraphics.CGContextScaleCTM
import platform.CoreGraphics.CGContextTranslateCTM
import platform.Foundation.NSData
import platform.Foundation.NSURL
import platform.Foundation.dataWithContentsOfURL
import platform.PDFKit.PDFDocument
import platform.PDFKit.kPDFDisplayBoxMediaBox
import platform.UIKit.UIApplication
import platform.UIKit.UIColor
import platform.UIKit.UIDocumentPickerDelegateProtocol
import platform.UIKit.UIDocumentPickerViewController
import platform.UIKit.UIGraphicsImageRenderer
import platform.UIKit.UIImageJPEGRepresentation
import platform.UniformTypeIdentifiers.UTTypeArchive
import platform.UniformTypeIdentifiers.UTTypeImage
import platform.UniformTypeIdentifiers.UTTypeMovie
import platform.UniformTypeIdentifiers.UTTypePDF
import platform.UniformTypeIdentifiers.UTTypePresentation
import platform.UniformTypeIdentifiers.UTTypeSpreadsheet
import platform.UniformTypeIdentifiers.UTTypeText
import platform.darwin.NSObject
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
class DocumentPickerService : IDocumentPickerService {
private var continuation: Continuation<List<ByteArray>>? = null
private val selectedDocumentUrls = mutableStateListOf<String>()
private var isInitialized = false
private var documentPickerViewController: UIDocumentPickerViewController? = null
private val pickerController = DocumentPickerController(selectedDocumentUrls) {
continuation?.resume(getSelectedDocumentAsByteArrays())
continuation = null
documentPickerViewController = null
}
companion object {
private const val TAG = "DocumentPickerService"
}
fun initializeWithActivity() {
}
override fun openDocumentPicker() {
if (documentPickerViewController == null) documentPickerViewController =
UIDocumentPickerViewController(
forOpeningContentTypes = listOf(
UTTypeSpreadsheet,
UTTypePresentation,
UTTypePDF,
UTTypeText,
UTTypeMovie,
UTTypeArchive,
UTTypeImage
)
)
documentPickerViewController?.allowsMultipleSelection = true
documentPickerViewController?.setDelegate(pickerController)
UIApplication.sharedApplication.keyWindow?.rootViewController?.presentViewController(
documentPickerViewController!!,
true,
null
)
}
override suspend fun pickDocumentsAndReturnByteArrays(): List<ByteArray> =
suspendCoroutine { cont ->
continuation = cont
try {
openDocumentPicker()
} catch (e: Exception) {
cont.resume(emptyList())
}
}
override fun getBytesFromUri(uriString: String): ByteArray? {
return selectedDocumentUrls.firstOrNull { uri ->
uri == uriString
}.let { it ->
return@let runBlocking(Dispatchers.IO) {
suspendCoroutine { cont ->
cont.resume(it?.toByteArray())
}
}
}
}
override fun getSelectedDocumentAsByteArrays(): List<ByteArray> {
return selectedDocumentUrls.map { url ->
runBlocking {
suspendCoroutine { cont ->
val data: NSData = NSData.dataWithContentsOfURL(NSURL.fileURLWithPath(url))!!
cont.resume(data.toByteArray())
}
}
}
}
override fun getSelectedDocumentUrls(): List<String> {
return selectedDocumentUrls
}
override fun clearSelectedDocuments() {
selectedDocumentUrls.clear()
}
override fun removeSelectedDocument(uri: String) {
selectedDocumentUrls.remove(uri)
}
override fun getDocumentNameFromUri(uriString: String): String? {
return NSURL.fileURLWithPath(uriString).lastPathComponent()
}
override fun getDocumentMimeTypeFromUri(uriString: String): String? {
return MimeTypeParser.getMimeType(
NSURL.fileURLWithPath(uriString).pathExtension().toString()
)
}
@OptIn(ExperimentalForeignApi::class)
override fun getDocumentPreviewPageFromUri(uriString: String): ByteArray {
val url = NSURL.fileURLWithPath(uriString)
val pdf = PDFDocument(url)
val page = pdf.pageAtIndex(0u)
val bounds = page?.boundsForBox(kPDFDisplayBoxMediaBox)
val renderer = UIGraphicsImageRenderer(bounds!!)
val image = renderer.imageWithActions { it ->
UIColor.whiteColor.set()
it?.fillRect(page.boundsForBox(kPDFDisplayBoxMediaBox))
val context = it!!.CGContext
CGContextTranslateCTM(context, 0.0, (bounds.useContents { size.height }))
CGContextScaleCTM(context, 1.0, (-1.0))
page.drawWithBox(kPDFDisplayBoxMediaBox, context)
}
val data = UIImageJPEGRepresentation(image, 0.95)
return data?.toByteArray()!!
}
}
@OptIn(BetaInteropApi::class)
class DocumentPickerController(
val selectedUris: MutableList<String>,
val onFinish: () -> Unit
) : NSObject(),
UIDocumentPickerDelegateProtocol {
override fun documentPicker(
controller: UIDocumentPickerViewController,
didPickDocumentsAtURLs: List<*>
) {
val pickedDocumentsURLs =
didPickDocumentsAtURLs.filterIsInstance<NSURL>().map { it.path ?: "" }
selectedUris.clear()
selectedUris.addAll(pickedDocumentsURLs)
controller.dismissViewControllerAnimated(true) {
onFinish()
}
}
override fun documentPickerWasCancelled(controller: UIDocumentPickerViewController) {
selectedUris.clear()
controller.dismissViewControllerAnimated(true) {
onFinish()
}
}
}