@simplysm/capacitor-plugin-usb-storage 14.0.51 → 14.0.52
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.
|
@@ -56,7 +56,7 @@ class UsbStoragePlugin : Plugin() {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
@PluginMethod
|
|
59
|
-
fun requestPermissions(call: PluginCall) {
|
|
59
|
+
override fun requestPermissions(call: PluginCall) {
|
|
60
60
|
val vendorId = call.getInt("vendorId")
|
|
61
61
|
val productId = call.getInt("productId")
|
|
62
62
|
|
|
@@ -115,7 +115,7 @@ class UsbStoragePlugin : Plugin() {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
@PluginMethod
|
|
118
|
-
fun checkPermissions(call: PluginCall) {
|
|
118
|
+
override fun checkPermissions(call: PluginCall) {
|
|
119
119
|
val vendorId = call.getInt("vendorId")
|
|
120
120
|
val productId = call.getInt("productId")
|
|
121
121
|
|
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.52",
|
|
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.52",
|
|
22
|
+
"@simplysm/core-common": "14.0.52"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@capacitor/core": "^7.6.2"
|
package/README.md
DELETED
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
# @simplysm/capacitor-plugin-usb-storage
|
|
2
|
-
|
|
3
|
-
Capacitor USB 저장소 플러그인. Android에서 libaums 라이브러리를 통해 USB Mass Storage 장치를 열거하고 파일을 읽는다. 브라우저에서는 IndexedDB 기반 가상 USB 저장소로 에뮬레이션한다.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install @simplysm/capacitor-plugin-usb-storage
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## API Overview
|
|
12
|
-
|
|
13
|
-
### USB 저장소 - 공개 API
|
|
14
|
-
|
|
15
|
-
| API | Type | Description |
|
|
16
|
-
|-----|------|-------------|
|
|
17
|
-
| `UsbDeviceInfo` | interface | USB 장치 정보 (이름, 제조사, vendorId 등) |
|
|
18
|
-
| `UsbDeviceFilter` | interface | USB 장치 필터 (vendorId + productId) |
|
|
19
|
-
| `UsbFileInfo` | interface | USB 저장소 내 파일/디렉토리 정보 |
|
|
20
|
-
| `UsbStoragePlugin` | interface | Capacitor 네이티브 플러그인 인터페이스 |
|
|
21
|
-
| `UsbStorage` | abstract class | USB 저장 장치 접근 정적 파사드 |
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## `UsbDeviceInfo`
|
|
26
|
-
|
|
27
|
-
연결된 USB 장치 정보를 나타내는 인터페이스.
|
|
28
|
-
|
|
29
|
-
```typescript
|
|
30
|
-
export interface UsbDeviceInfo {
|
|
31
|
-
deviceName: string;
|
|
32
|
-
manufacturerName: string;
|
|
33
|
-
productName: string;
|
|
34
|
-
vendorId: number;
|
|
35
|
-
productId: number;
|
|
36
|
-
}
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
| Field | Type | Description |
|
|
40
|
-
|-------|------|-------------|
|
|
41
|
-
| `deviceName` | `string` | 장치 이름 |
|
|
42
|
-
| `manufacturerName` | `string` | 제조사 이름 |
|
|
43
|
-
| `productName` | `string` | 제품 이름 |
|
|
44
|
-
| `vendorId` | `number` | USB Vendor ID |
|
|
45
|
-
| `productId` | `number` | USB Product ID |
|
|
46
|
-
|
|
47
|
-
## `UsbDeviceFilter`
|
|
48
|
-
|
|
49
|
-
USB 장치를 식별하기 위한 필터 인터페이스. 모든 장치 접근 메서드의 첫 번째 파라미터로 사용된다.
|
|
50
|
-
|
|
51
|
-
```typescript
|
|
52
|
-
export interface UsbDeviceFilter {
|
|
53
|
-
vendorId: number;
|
|
54
|
-
productId: number;
|
|
55
|
-
}
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
| Field | Type | Description |
|
|
59
|
-
|-------|------|-------------|
|
|
60
|
-
| `vendorId` | `number` | USB Vendor ID |
|
|
61
|
-
| `productId` | `number` | USB Product ID |
|
|
62
|
-
|
|
63
|
-
## `UsbFileInfo`
|
|
64
|
-
|
|
65
|
-
USB 저장소 내 파일 또는 디렉토리 정보를 나타내는 인터페이스.
|
|
66
|
-
|
|
67
|
-
```typescript
|
|
68
|
-
export interface UsbFileInfo {
|
|
69
|
-
name: string;
|
|
70
|
-
isDirectory: boolean;
|
|
71
|
-
}
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
| Field | Type | Description |
|
|
75
|
-
|-------|------|-------------|
|
|
76
|
-
| `name` | `string` | 파일 또는 디렉토리 이름 |
|
|
77
|
-
| `isDirectory` | `boolean` | 디렉토리 여부 |
|
|
78
|
-
|
|
79
|
-
## `UsbStoragePlugin`
|
|
80
|
-
|
|
81
|
-
Capacitor 네이티브 플러그인 인터페이스. 직접 사용하지 않고 `UsbStorage` 파사드를 통해 접근한다.
|
|
82
|
-
|
|
83
|
-
```typescript
|
|
84
|
-
export interface UsbStoragePlugin {
|
|
85
|
-
getDevices(): Promise<{ devices: UsbDeviceInfo[] }>;
|
|
86
|
-
requestPermissions(options: UsbDeviceFilter): Promise<{ granted: boolean }>;
|
|
87
|
-
checkPermissions(options: UsbDeviceFilter): Promise<{ granted: boolean }>;
|
|
88
|
-
readdir(options: UsbDeviceFilter & { path: string }): Promise<{ files: UsbFileInfo[] }>;
|
|
89
|
-
readFile(options: UsbDeviceFilter & { path: string }): Promise<{ data: string | null }>;
|
|
90
|
-
}
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
| Method | Parameters | Return | Description |
|
|
94
|
-
|--------|-----------|--------|-------------|
|
|
95
|
-
| `getDevices` | 없음 | `Promise<{ devices: UsbDeviceInfo[] }>` | 연결된 USB 장치 목록 조회 |
|
|
96
|
-
| `requestPermissions` | `UsbDeviceFilter` | `Promise<{ granted: boolean }>` | USB 장치 접근 권한 요청 |
|
|
97
|
-
| `checkPermissions` | `UsbDeviceFilter` | `Promise<{ granted: boolean }>` | USB 장치 접근 권한 확인 |
|
|
98
|
-
| `readdir` | `UsbDeviceFilter & { path }` | `Promise<{ files: UsbFileInfo[] }>` | 디렉토리 내용 읽기 |
|
|
99
|
-
| `readFile` | `UsbDeviceFilter & { path }` | `Promise<{ data: string \| null }>` | 파일 읽기 (Base64). 파일이 없으면 `null` |
|
|
100
|
-
|
|
101
|
-
## `UsbStorage`
|
|
102
|
-
|
|
103
|
-
USB 저장 장치 접근 정적 파사드 클래스. Android에서는 libaums를 통해 USB Mass Storage에 접근하고, 브라우저에서는 IndexedDB 기반으로 에뮬레이션된다.
|
|
104
|
-
|
|
105
|
-
`readFile()`의 반환 타입 `Bytes`는 `@simplysm/core-common`의 타입이다 (`Uint8Array` 별칭).
|
|
106
|
-
|
|
107
|
-
```typescript
|
|
108
|
-
export abstract class UsbStorage {
|
|
109
|
-
static async getDevices(): Promise<UsbDeviceInfo[]>;
|
|
110
|
-
static async requestPermissions(filter: UsbDeviceFilter): Promise<boolean>;
|
|
111
|
-
static async checkPermissions(filter: UsbDeviceFilter): Promise<boolean>;
|
|
112
|
-
static async readdir(filter: UsbDeviceFilter, dirPath: string): Promise<UsbFileInfo[]>;
|
|
113
|
-
static async readFile(filter: UsbDeviceFilter, filePath: string): Promise<Bytes | undefined>;
|
|
114
|
-
}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
| Method | Parameters | Return | Description |
|
|
118
|
-
|--------|-----------|--------|-------------|
|
|
119
|
-
| `getDevices` | 없음 | `Promise<UsbDeviceInfo[]>` | 연결된 USB Mass Storage 장치 목록 조회 |
|
|
120
|
-
| `requestPermissions` | `filter: UsbDeviceFilter` | `Promise<boolean>` | USB 장치 접근 권한 요청. 승인 여부 반환 |
|
|
121
|
-
| `checkPermissions` | `filter: UsbDeviceFilter` | `Promise<boolean>` | USB 장치 접근 권한 보유 여부 확인 |
|
|
122
|
-
| `readdir` | `filter: UsbDeviceFilter, dirPath: string` | `Promise<UsbFileInfo[]>` | USB 저장 장치의 디렉토리 내용 읽기 |
|
|
123
|
-
| `readFile` | `filter: UsbDeviceFilter, filePath: string` | `Promise<Bytes \| undefined>` | USB 저장 장치에서 파일 읽기. 파일이 없으면 `undefined` 반환. 최대 100MB |
|
|
124
|
-
|
|
125
|
-
## Browser-Only Testing API (`UsbStorageWeb`)
|
|
126
|
-
|
|
127
|
-
브라우저 환경에서 테스트 및 개발 목적으로 사용하는 API. `UsbStorage` 정적 파사드로는 접근할 수 없으며, `UsbStorageWeb` 인스턴스를 직접 사용할 때만 호출 가능하다.
|
|
128
|
-
|
|
129
|
-
### `addVirtualDevice(device)`
|
|
130
|
-
|
|
131
|
-
가상 USB 장치를 IndexedDB에 등록한다. (테스트/개발용)
|
|
132
|
-
|
|
133
|
-
```typescript
|
|
134
|
-
async addVirtualDevice(device: {
|
|
135
|
-
vendorId: number;
|
|
136
|
-
productId: number;
|
|
137
|
-
deviceName: string;
|
|
138
|
-
manufacturerName: string;
|
|
139
|
-
productName: string;
|
|
140
|
-
}): Promise<void>
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
| Parameter | Type | Description |
|
|
144
|
-
|-----------|------|-------------|
|
|
145
|
-
| `device.vendorId` | `number` | USB Vendor ID |
|
|
146
|
-
| `device.productId` | `number` | USB Product ID |
|
|
147
|
-
| `device.deviceName` | `string` | 장치 이름 |
|
|
148
|
-
| `device.manufacturerName` | `string` | 제조사 이름 |
|
|
149
|
-
| `device.productName` | `string` | 제품 이름 |
|
|
150
|
-
|
|
151
|
-
### `addVirtualFile(filter, filePath, data)`
|
|
152
|
-
|
|
153
|
-
가상 USB 장치에 파일을 추가한다. (테스트/개발용)
|
|
154
|
-
|
|
155
|
-
```typescript
|
|
156
|
-
async addVirtualFile(
|
|
157
|
-
filter: UsbDeviceFilter,
|
|
158
|
-
filePath: string,
|
|
159
|
-
data: Uint8Array,
|
|
160
|
-
): Promise<void>
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
| Parameter | Type | Description |
|
|
164
|
-
|-----------|------|-------------|
|
|
165
|
-
| `filter.vendorId` | `number` | 대상 장치의 USB Vendor ID |
|
|
166
|
-
| `filter.productId` | `number` | 대상 장치의 USB Product ID |
|
|
167
|
-
| `filePath` | `string` | 파일 경로 (예: `/updates/config.json`) |
|
|
168
|
-
| `data` | `Uint8Array` | 파일 바이너리 데이터 |
|
|
169
|
-
|
|
170
|
-
**주의**: 파일이 위치할 부모 디렉토리가 존재하지 않으면 자동으로 생성된다.
|
|
171
|
-
|
|
172
|
-
### `addVirtualDirectory(filter, dirPath)`
|
|
173
|
-
|
|
174
|
-
가상 USB 장치에 디렉토리를 추가한다. (테스트/개발용)
|
|
175
|
-
|
|
176
|
-
```typescript
|
|
177
|
-
async addVirtualDirectory(
|
|
178
|
-
filter: UsbDeviceFilter,
|
|
179
|
-
dirPath: string,
|
|
180
|
-
): Promise<void>
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
| Parameter | Type | Description |
|
|
184
|
-
|-----------|------|-------------|
|
|
185
|
-
| `filter.vendorId` | `number` | 대상 장치의 USB Vendor ID |
|
|
186
|
-
| `filter.productId` | `number` | 대상 장치의 USB Product ID |
|
|
187
|
-
| `dirPath` | `string` | 생성할 디렉토리 경로 (예: `/updates`) |
|
|
188
|
-
|
|
189
|
-
---
|
|
190
|
-
|
|
191
|
-
## Usage Examples
|
|
192
|
-
|
|
193
|
-
### USB 장치 열거 및 권한 요청
|
|
194
|
-
|
|
195
|
-
```typescript
|
|
196
|
-
import { UsbStorage } from "@simplysm/capacitor-plugin-usb-storage";
|
|
197
|
-
|
|
198
|
-
const devices = await UsbStorage.getDevices();
|
|
199
|
-
if (devices.length > 0) {
|
|
200
|
-
const device = devices[0];
|
|
201
|
-
const filter = { vendorId: device.vendorId, productId: device.productId };
|
|
202
|
-
|
|
203
|
-
const granted = await UsbStorage.checkPermissions(filter);
|
|
204
|
-
if (!granted) {
|
|
205
|
-
await UsbStorage.requestPermissions(filter);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
### USB 저장소 파일 읽기
|
|
211
|
-
|
|
212
|
-
```typescript
|
|
213
|
-
import { UsbStorage } from "@simplysm/capacitor-plugin-usb-storage";
|
|
214
|
-
import type { UsbDeviceFilter } from "@simplysm/capacitor-plugin-usb-storage";
|
|
215
|
-
|
|
216
|
-
const filter: UsbDeviceFilter = { vendorId: 1234, productId: 5678 };
|
|
217
|
-
|
|
218
|
-
// 디렉토리 목록 조회
|
|
219
|
-
const files = await UsbStorage.readdir(filter, "/updates");
|
|
220
|
-
|
|
221
|
-
// 파일 읽기 (Bytes | undefined 반환)
|
|
222
|
-
const data = await UsbStorage.readFile(filter, "/updates/config.json");
|
|
223
|
-
if (data != null) {
|
|
224
|
-
const text = new TextDecoder().decode(data);
|
|
225
|
-
}
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
### 브라우저 테스트 - 가상 USB 장치 설정
|
|
229
|
-
|
|
230
|
-
브라우저 환경에서 테스트할 때, `UsbStorageWeb`을 직접 import하여 가상 USB 저장소를 설정한다. `UsbStorageWeb`은 패키지 공개 API(`index.ts`)에서 export되지 않으므로, 소스 경로를 직접 참조한다.
|
|
231
|
-
|
|
232
|
-
```typescript
|
|
233
|
-
import { UsbStorageWeb } from "@simplysm/capacitor-plugin-usb-storage/src/web/UsbStorageWeb";
|
|
234
|
-
import { UsbStorage } from "@simplysm/capacitor-plugin-usb-storage";
|
|
235
|
-
|
|
236
|
-
const usbStorageWeb = new UsbStorageWeb();
|
|
237
|
-
|
|
238
|
-
// 가상 장치 등록
|
|
239
|
-
await usbStorageWeb.addVirtualDevice({
|
|
240
|
-
vendorId: 1234,
|
|
241
|
-
productId: 5678,
|
|
242
|
-
deviceName: "Test Device",
|
|
243
|
-
manufacturerName: "Test Manufacturer",
|
|
244
|
-
productName: "Test Product",
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
// 가상 파일 추가
|
|
248
|
-
const fileData = new TextEncoder().encode("Hello, USB!");
|
|
249
|
-
await usbStorageWeb.addVirtualFile(
|
|
250
|
-
{ vendorId: 1234, productId: 5678 },
|
|
251
|
-
"/updates/config.json",
|
|
252
|
-
fileData,
|
|
253
|
-
);
|
|
254
|
-
|
|
255
|
-
// UsbStorage 정적 API로 접근 (Capacitor가 브라우저에서 UsbStorageWeb 인스턴스를 사용)
|
|
256
|
-
const files = await UsbStorage.readdir({ vendorId: 1234, productId: 5678 }, "/updates");
|
|
257
|
-
const data = await UsbStorage.readFile({ vendorId: 1234, productId: 5678 }, "/updates/config.json");
|
|
258
|
-
```
|
|
@@ -1,269 +0,0 @@
|
|
|
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
|
-
}
|