@simplysm/capacitor-plugin-usb-storage 14.0.10 → 14.0.12

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.
@@ -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.10",
3
+ "version": "14.0.12",
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.10",
22
- "@simplysm/core-common": "14.0.10"
21
+ "@simplysm/core-browser": "14.0.12",
22
+ "@simplysm/core-common": "14.0.12"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@capacitor/core": "^7.6.1"