@simplysm/capacitor-plugin-usb-storage 14.0.11 → 14.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/android/build.gradle
CHANGED
|
@@ -7,6 +7,14 @@ android {
|
|
|
7
7
|
defaultConfig {
|
|
8
8
|
minSdk project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 23
|
|
9
9
|
}
|
|
10
|
+
compileOptions {
|
|
11
|
+
sourceCompatibility JavaVersion.VERSION_21
|
|
12
|
+
targetCompatibility JavaVersion.VERSION_21
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
kotlin {
|
|
17
|
+
jvmToolchain(21)
|
|
10
18
|
}
|
|
11
19
|
|
|
12
20
|
dependencies {
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
package kr.co.simplysm.capacitor.usbstorage
|
|
2
|
+
|
|
3
|
+
import android.app.PendingIntent
|
|
4
|
+
import android.content.BroadcastReceiver
|
|
5
|
+
import android.content.Context
|
|
6
|
+
import android.content.Intent
|
|
7
|
+
import android.content.IntentFilter
|
|
8
|
+
import android.hardware.usb.UsbManager
|
|
9
|
+
import android.os.Build
|
|
10
|
+
import android.util.Base64
|
|
11
|
+
import android.util.Log
|
|
12
|
+
import com.getcapacitor.JSArray
|
|
13
|
+
import com.getcapacitor.JSObject
|
|
14
|
+
import com.getcapacitor.Plugin
|
|
15
|
+
import com.getcapacitor.PluginCall
|
|
16
|
+
import com.getcapacitor.PluginMethod
|
|
17
|
+
import com.getcapacitor.annotation.CapacitorPlugin
|
|
18
|
+
import me.jahnen.libaums.core.UsbMassStorageDevice
|
|
19
|
+
import me.jahnen.libaums.core.fs.UsbFileInputStream
|
|
20
|
+
import java.nio.ByteBuffer
|
|
21
|
+
|
|
22
|
+
@CapacitorPlugin(name = "UsbStorage")
|
|
23
|
+
class UsbStoragePlugin : Plugin() {
|
|
24
|
+
|
|
25
|
+
companion object {
|
|
26
|
+
private const val TAG = "UsbStoragePlugin"
|
|
27
|
+
private const val ACTION_USB_PERMISSION = "kr.co.simplysm.capacitor.usbstorage.USB_PERMISSION"
|
|
28
|
+
private const val MAX_FILE_SIZE = 100L * 1024 * 1024 // 100MB
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@PluginMethod
|
|
32
|
+
fun getDevices(call: PluginCall) {
|
|
33
|
+
try {
|
|
34
|
+
val devices = UsbMassStorageDevice.getMassStorageDevices(context)
|
|
35
|
+
|
|
36
|
+
val result = JSArray()
|
|
37
|
+
for (device in devices) {
|
|
38
|
+
val usbDevice = device.usbDevice
|
|
39
|
+
|
|
40
|
+
val deviceObj = JSObject()
|
|
41
|
+
deviceObj.put("deviceName", usbDevice.deviceName)
|
|
42
|
+
deviceObj.put("manufacturerName", usbDevice.manufacturerName)
|
|
43
|
+
deviceObj.put("productName", usbDevice.productName)
|
|
44
|
+
deviceObj.put("vendorId", usbDevice.vendorId)
|
|
45
|
+
deviceObj.put("productId", usbDevice.productId)
|
|
46
|
+
result.put(deviceObj)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
val ret = JSObject()
|
|
50
|
+
ret.put("devices", result)
|
|
51
|
+
call.resolve(ret)
|
|
52
|
+
} catch (e: Exception) {
|
|
53
|
+
Log.e(TAG, "getDevices failed", e)
|
|
54
|
+
call.reject("getDevices failed: ${e.message}")
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@PluginMethod
|
|
59
|
+
fun requestPermissions(call: PluginCall) {
|
|
60
|
+
val vendorId = call.getInt("vendorId")
|
|
61
|
+
val productId = call.getInt("productId")
|
|
62
|
+
|
|
63
|
+
if (vendorId == null || productId == null) {
|
|
64
|
+
call.reject("vendorId and productId are required")
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
val device = getDevice(vendorId, productId)
|
|
70
|
+
val usbDevice = device.usbDevice
|
|
71
|
+
|
|
72
|
+
val usbManager = context.getSystemService(Context.USB_SERVICE) as UsbManager
|
|
73
|
+
if (usbManager.hasPermission(usbDevice)) {
|
|
74
|
+
val ret = JSObject()
|
|
75
|
+
ret.put("granted", true)
|
|
76
|
+
call.resolve(ret)
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
val receiver = object : BroadcastReceiver() {
|
|
81
|
+
override fun onReceive(context: Context, intent: Intent) {
|
|
82
|
+
try {
|
|
83
|
+
context.unregisterReceiver(this)
|
|
84
|
+
val granted = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)
|
|
85
|
+
val ret = JSObject()
|
|
86
|
+
ret.put("granted", granted)
|
|
87
|
+
call.resolve(ret)
|
|
88
|
+
} catch (e: Exception) {
|
|
89
|
+
Log.e(TAG, "requestPermission callback failed", e)
|
|
90
|
+
call.reject("requestPermission failed: ${e.message}")
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
val filter = IntentFilter(ACTION_USB_PERMISSION)
|
|
96
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
97
|
+
context.registerReceiver(receiver, filter, Context.RECEIVER_NOT_EXPORTED)
|
|
98
|
+
} else {
|
|
99
|
+
context.registerReceiver(receiver, filter)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
103
|
+
PendingIntent.FLAG_MUTABLE
|
|
104
|
+
} else {
|
|
105
|
+
0
|
|
106
|
+
}
|
|
107
|
+
val permissionIntent = PendingIntent.getBroadcast(
|
|
108
|
+
context, 0, Intent(ACTION_USB_PERMISSION), flags
|
|
109
|
+
)
|
|
110
|
+
usbManager.requestPermission(usbDevice, permissionIntent)
|
|
111
|
+
} catch (e: Exception) {
|
|
112
|
+
Log.e(TAG, "requestPermission failed", e)
|
|
113
|
+
call.reject("requestPermission failed: ${e.message}")
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@PluginMethod
|
|
118
|
+
fun checkPermissions(call: PluginCall) {
|
|
119
|
+
val vendorId = call.getInt("vendorId")
|
|
120
|
+
val productId = call.getInt("productId")
|
|
121
|
+
|
|
122
|
+
if (vendorId == null || productId == null) {
|
|
123
|
+
call.reject("vendorId and productId are required")
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
val device = getDevice(vendorId, productId)
|
|
129
|
+
val usbManager = context.getSystemService(Context.USB_SERVICE) as UsbManager
|
|
130
|
+
val hasPermission = usbManager.hasPermission(device.usbDevice)
|
|
131
|
+
|
|
132
|
+
val ret = JSObject()
|
|
133
|
+
ret.put("granted", hasPermission)
|
|
134
|
+
call.resolve(ret)
|
|
135
|
+
} catch (e: Exception) {
|
|
136
|
+
Log.e(TAG, "hasPermission failed", e)
|
|
137
|
+
call.reject("hasPermission failed: ${e.message}")
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
@PluginMethod
|
|
142
|
+
fun readdir(call: PluginCall) {
|
|
143
|
+
val vendorId = call.getInt("vendorId")
|
|
144
|
+
val productId = call.getInt("productId")
|
|
145
|
+
val path = call.getString("path")
|
|
146
|
+
|
|
147
|
+
if (vendorId == null || productId == null || path == null) {
|
|
148
|
+
call.reject("vendorId, productId, and path are required")
|
|
149
|
+
return
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
val device = getDevice(vendorId, productId)
|
|
154
|
+
|
|
155
|
+
val usbManager = context.getSystemService(Context.USB_SERVICE) as UsbManager
|
|
156
|
+
if (!usbManager.hasPermission(device.usbDevice)) {
|
|
157
|
+
call.reject("No permission for this USB device")
|
|
158
|
+
return
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
device.init()
|
|
162
|
+
try {
|
|
163
|
+
val fs = device.partitions[0].fileSystem
|
|
164
|
+
val root = fs.rootDirectory
|
|
165
|
+
val dir = root.search(path)
|
|
166
|
+
|
|
167
|
+
if (dir == null || !dir.isDirectory) {
|
|
168
|
+
call.reject("Directory not found: $path")
|
|
169
|
+
return
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
val files = dir.listFiles()
|
|
173
|
+
|
|
174
|
+
val result = JSArray()
|
|
175
|
+
for (file in files) {
|
|
176
|
+
val info = JSObject()
|
|
177
|
+
info.put("name", file.name)
|
|
178
|
+
info.put("isDirectory", file.isDirectory)
|
|
179
|
+
result.put(info)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
val ret = JSObject()
|
|
183
|
+
ret.put("files", result)
|
|
184
|
+
call.resolve(ret)
|
|
185
|
+
} finally {
|
|
186
|
+
device.close()
|
|
187
|
+
}
|
|
188
|
+
} catch (e: Exception) {
|
|
189
|
+
Log.e(TAG, "readdir failed", e)
|
|
190
|
+
call.reject("readdir failed: ${e.message}")
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
@PluginMethod
|
|
195
|
+
fun readFile(call: PluginCall) {
|
|
196
|
+
val vendorId = call.getInt("vendorId")
|
|
197
|
+
val productId = call.getInt("productId")
|
|
198
|
+
val path = call.getString("path")
|
|
199
|
+
|
|
200
|
+
if (vendorId == null || productId == null || path == null) {
|
|
201
|
+
call.reject("vendorId, productId, and path are required")
|
|
202
|
+
return
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
val device = getDevice(vendorId, productId)
|
|
207
|
+
|
|
208
|
+
val usbManager = context.getSystemService(Context.USB_SERVICE) as UsbManager
|
|
209
|
+
if (!usbManager.hasPermission(device.usbDevice)) {
|
|
210
|
+
call.reject("No permission for this USB device")
|
|
211
|
+
return
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
device.init()
|
|
215
|
+
try {
|
|
216
|
+
val fs = device.partitions[0].fileSystem
|
|
217
|
+
val root = fs.rootDirectory
|
|
218
|
+
val usbFile = root.search(path)
|
|
219
|
+
|
|
220
|
+
if (usbFile == null) {
|
|
221
|
+
val ret = JSObject()
|
|
222
|
+
ret.put("data", null as String?)
|
|
223
|
+
call.resolve(ret)
|
|
224
|
+
return
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (usbFile.isDirectory) {
|
|
228
|
+
call.reject("Path is a directory: $path")
|
|
229
|
+
return
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
val fileLength = usbFile.length
|
|
233
|
+
if (fileLength > MAX_FILE_SIZE) {
|
|
234
|
+
call.reject("File too large: $fileLength bytes (max $MAX_FILE_SIZE)")
|
|
235
|
+
return
|
|
236
|
+
}
|
|
237
|
+
val buffer = ByteBuffer.allocate(fileLength.toInt())
|
|
238
|
+
|
|
239
|
+
val inputStream = UsbFileInputStream(usbFile)
|
|
240
|
+
val tmpBuf = ByteArray(fs.chunkSize)
|
|
241
|
+
var count: Int
|
|
242
|
+
while (inputStream.read(tmpBuf).also { count = it } != -1) {
|
|
243
|
+
buffer.put(tmpBuf, 0, count)
|
|
244
|
+
}
|
|
245
|
+
inputStream.close()
|
|
246
|
+
|
|
247
|
+
val base64Data = Base64.encodeToString(buffer.array(), Base64.NO_WRAP)
|
|
248
|
+
|
|
249
|
+
val ret = JSObject()
|
|
250
|
+
ret.put("data", base64Data)
|
|
251
|
+
call.resolve(ret)
|
|
252
|
+
} finally {
|
|
253
|
+
device.close()
|
|
254
|
+
}
|
|
255
|
+
} catch (e: Exception) {
|
|
256
|
+
Log.e(TAG, "readFile failed", e)
|
|
257
|
+
call.reject("readFile failed: ${e.message}")
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
@Throws(Exception::class)
|
|
262
|
+
private fun getDevice(vendorId: Int, productId: Int): UsbMassStorageDevice {
|
|
263
|
+
val devices = UsbMassStorageDevice.getMassStorageDevices(context)
|
|
264
|
+
return devices.firstOrNull { device ->
|
|
265
|
+
val usbDevice = device.usbDevice
|
|
266
|
+
usbDevice.vendorId == vendorId && usbDevice.productId == productId
|
|
267
|
+
} ?: throw Exception("USB device not found: vendorId=$vendorId, productId=$productId")
|
|
268
|
+
}
|
|
269
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simplysm/capacitor-plugin-usb-storage",
|
|
3
|
-
"version": "14.0.
|
|
3
|
+
"version": "14.0.13",
|
|
4
4
|
"description": "심플리즘 패키지 - Capacitor USB 저장소 플러그인",
|
|
5
5
|
"author": "심플리즘",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
"android"
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@simplysm/core-browser": "14.0.
|
|
22
|
-
"@simplysm/core-common": "14.0.
|
|
21
|
+
"@simplysm/core-browser": "14.0.13",
|
|
22
|
+
"@simplysm/core-common": "14.0.13"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@capacitor/core": "^7.6.1"
|