react-native-kookit 0.3.4 → 0.3.6
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/CHANGELOG_CONTENT_URI.md +130 -0
- package/IMPLEMENTATION_SUMMARY.md +206 -0
- package/README.md +21 -1
- package/android/src/main/java/expo/modules/kookit/JcifsShareEnumerator.kt +2 -3
- package/android/src/main/java/expo/modules/kookit/ReactNativeKookitModule.kt +139 -0
- package/build/ReactNativeKookitModule.d.ts +28 -0
- package/build/ReactNativeKookitModule.d.ts.map +1 -1
- package/build/ReactNativeKookitModule.js.map +1 -1
- package/package.json +1 -1
- package/ANDROID_BUILD_FIX.md +0 -117
- package/ANDROID_FTP_UPDATE.md +0 -161
- package/ANDROID_PARAMETER_FIX.md +0 -0
- package/ANDROID_SETUP.md +0 -188
- package/ANDROID_SMB_LIBRARY_COMPARISON.md +0 -170
- package/ANDROID_SMB_LISTSHARES_FIX.md +0 -100
- package/API_UNIFICATION.md +0 -180
- package/EXPO_PLUGIN_README.md +0 -136
- package/FTP_CLIENT_API.md +0 -301
- package/FTP_EXAMPLE_IMPLEMENTATION.md +0 -0
- package/FTP_FILE_LIST_ENHANCEMENT.md +0 -186
- package/FTP_FILE_OPERATIONS.md +0 -0
- package/FTP_README.md +0 -322
- package/README_NEW.md +0 -143
- package/RELEASE_CHECKLIST.md +0 -115
- package/SMB_CLIENT_STATIC_METHODS.md +0 -175
- package/SMB_COMPLETE_GUIDE.md +0 -308
- package/SMB_HYBRID_IMPLEMENTATION.md +0 -160
- package/SMB_IMPLEMENTATION_SUMMARY.md +0 -229
- package/SMB_USAGE.md +0 -275
- package/TROUBLESHOOTING.md +0 -132
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Changelog - Content URI Handler
|
|
2
|
+
|
|
3
|
+
## [Unreleased] - 2025-10-09
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
#### Content URI Handler (Android)
|
|
8
|
+
通过 Android ContentResolver 处理 `content://` URI,解决权限限制问题。
|
|
9
|
+
|
|
10
|
+
**新增 API:**
|
|
11
|
+
- `copyContentUriToCache(contentUri, fileName?)` - 复制 content URI 到缓存目录
|
|
12
|
+
- `getContentUriMetadata(contentUri)` - 获取文件元数据(名称、大小、MIME 类型)
|
|
13
|
+
|
|
14
|
+
**核心功能:**
|
|
15
|
+
- ✅ 使用 ContentResolver 绕过权限限制
|
|
16
|
+
- ✅ 无需 `READ_EXTERNAL_STORAGE` 权限
|
|
17
|
+
- ✅ 支持所有 ContentProvider(文件管理器、下载管理器、第三方应用)
|
|
18
|
+
- ✅ 8KB 缓冲区流式复制
|
|
19
|
+
- ✅ 后台线程执行,避免阻塞 UI
|
|
20
|
+
- ✅ 完整的错误处理和资源清理
|
|
21
|
+
- ✅ TypeScript 类型定义
|
|
22
|
+
|
|
23
|
+
**解决的问题:**
|
|
24
|
+
- `SecurityException: Permission Denial` when accessing content:// URIs
|
|
25
|
+
- 第三方应用未正确授予 URI 读取权限
|
|
26
|
+
- expo-file-system 无法访问某些 content:// URIs
|
|
27
|
+
|
|
28
|
+
**文档:**
|
|
29
|
+
- 📖 [完整文档](./docs/CONTENT_URI_HANDLER.md)
|
|
30
|
+
- 💡 [快速示例](./example/ContentUriQuickExamples.ts)
|
|
31
|
+
- 🎨 [UI 示例组件](./example/ContentUriExample.tsx)
|
|
32
|
+
- 📝 [实现总结](./IMPLEMENTATION_SUMMARY.md)
|
|
33
|
+
|
|
34
|
+
**使用场景:**
|
|
35
|
+
- 处理"打开方式" Intent
|
|
36
|
+
- 处理"分享到" Intent
|
|
37
|
+
- 从文档选择器获取文件
|
|
38
|
+
- 从第三方文件管理器接收文件
|
|
39
|
+
- 与 FTP/SMB 客户端集成上传
|
|
40
|
+
|
|
41
|
+
**技术细节:**
|
|
42
|
+
- Platform: Android (API 19+)
|
|
43
|
+
- 实现语言: Kotlin
|
|
44
|
+
- 异步执行: Coroutines (Dispatchers.IO)
|
|
45
|
+
- 缓存位置: Context.getCacheDir()
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
- 更新 README.md,添加功能列表和 Content URI Handler 介绍
|
|
49
|
+
|
|
50
|
+
### Technical Notes
|
|
51
|
+
|
|
52
|
+
**原生模块更改:**
|
|
53
|
+
```kotlin
|
|
54
|
+
// 新增导入
|
|
55
|
+
import android.net.Uri
|
|
56
|
+
import android.provider.OpenableColumns
|
|
57
|
+
import android.database.Cursor
|
|
58
|
+
import java.io.File
|
|
59
|
+
import java.io.FileOutputStream
|
|
60
|
+
import java.io.InputStream
|
|
61
|
+
|
|
62
|
+
// 新增方法
|
|
63
|
+
AsyncFunction("copyContentUriToCache")
|
|
64
|
+
AsyncFunction("getContentUriMetadata")
|
|
65
|
+
|
|
66
|
+
// 新增辅助方法
|
|
67
|
+
private fun getContentUriMetadataInternal(context: Context, uri: Uri)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**TypeScript 接口更改:**
|
|
71
|
+
```typescript
|
|
72
|
+
// src/ReactNativeKookitModule.ts
|
|
73
|
+
copyContentUriToCache(contentUri: string, fileName?: string): Promise<{...}>
|
|
74
|
+
getContentUriMetadata(contentUri: string): Promise<{...}>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Migration Guide
|
|
78
|
+
|
|
79
|
+
此为新增功能,无需迁移。现有代码不受影响。
|
|
80
|
+
|
|
81
|
+
### Example Usage
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
import ReactNativeKookitModule from 'react-native-kookit';
|
|
85
|
+
|
|
86
|
+
// 复制文件
|
|
87
|
+
const result = await ReactNativeKookitModule.copyContentUriToCache(
|
|
88
|
+
'content://...',
|
|
89
|
+
'filename.pdf'
|
|
90
|
+
);
|
|
91
|
+
console.log('Local path:', result.localPath);
|
|
92
|
+
|
|
93
|
+
// 获取元数据
|
|
94
|
+
const metadata = await ReactNativeKookitModule.getContentUriMetadata(
|
|
95
|
+
'content://...'
|
|
96
|
+
);
|
|
97
|
+
console.log('File info:', metadata);
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Breaking Changes
|
|
101
|
+
|
|
102
|
+
无
|
|
103
|
+
|
|
104
|
+
### Deprecations
|
|
105
|
+
|
|
106
|
+
无
|
|
107
|
+
|
|
108
|
+
### Security
|
|
109
|
+
|
|
110
|
+
- 此功能仅访问应用明确接收的 content:// URIs
|
|
111
|
+
- 文件复制到应用私有缓存目录,其他应用无法访问
|
|
112
|
+
- 无需请求存储权限
|
|
113
|
+
- 符合 Android 安全最佳实践
|
|
114
|
+
|
|
115
|
+
### Performance
|
|
116
|
+
|
|
117
|
+
- 使用 8KB 缓冲区优化复制速度
|
|
118
|
+
- 在后台线程执行,不阻塞 UI
|
|
119
|
+
- 适合处理几 MB 到几百 MB 的文件
|
|
120
|
+
- 对于超大文件,建议添加进度监听(未来版本)
|
|
121
|
+
|
|
122
|
+
### Known Issues
|
|
123
|
+
|
|
124
|
+
- 仅支持 Android 平台(iOS 不需要此功能)
|
|
125
|
+
- 非常大的文件(> 500MB)可能需要较长时间
|
|
126
|
+
- 缓存文件需要手动清理(或等待系统清理)
|
|
127
|
+
|
|
128
|
+
### Credits
|
|
129
|
+
|
|
130
|
+
实现基于 Android ContentResolver API 和 Expo Modules 框架。
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# Content URI Handler - 实现总结
|
|
2
|
+
|
|
3
|
+
## 功能概述
|
|
4
|
+
|
|
5
|
+
为 `react-native-kookit` 添加了 Android ContentResolver 功能,用于处理来自其他应用的 `content://` URI,解决权限拒绝(Permission Denial)问题。
|
|
6
|
+
|
|
7
|
+
## 实现的文件
|
|
8
|
+
|
|
9
|
+
### 1. TypeScript 接口 (`src/ReactNativeKookitModule.ts`)
|
|
10
|
+
|
|
11
|
+
添加了两个新方法:
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
// 复制 content:// URI 到缓存目录
|
|
15
|
+
copyContentUriToCache(contentUri: string, fileName?: string): Promise<{
|
|
16
|
+
localPath: string;
|
|
17
|
+
fileName: string;
|
|
18
|
+
mimeType?: string;
|
|
19
|
+
size?: number;
|
|
20
|
+
}>;
|
|
21
|
+
|
|
22
|
+
// 获取 content:// URI 的元数据
|
|
23
|
+
getContentUriMetadata(contentUri: string): Promise<{
|
|
24
|
+
displayName?: string;
|
|
25
|
+
mimeType?: string;
|
|
26
|
+
size?: number;
|
|
27
|
+
}>;
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 2. Android 原生实现 (`android/src/main/java/expo/modules/kookit/ReactNativeKookitModule.kt`)
|
|
31
|
+
|
|
32
|
+
#### 添加的导入
|
|
33
|
+
```kotlin
|
|
34
|
+
import android.net.Uri
|
|
35
|
+
import android.provider.OpenableColumns
|
|
36
|
+
import android.database.Cursor
|
|
37
|
+
import java.io.File
|
|
38
|
+
import java.io.FileOutputStream
|
|
39
|
+
import java.io.InputStream
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
#### 实现的功能
|
|
43
|
+
|
|
44
|
+
**`copyContentUriToCache`**
|
|
45
|
+
- 验证 URI scheme 是否为 `content://`
|
|
46
|
+
- 使用 `ContentResolver.openInputStream()` 读取文件流
|
|
47
|
+
- 将数据复制到应用缓存目录
|
|
48
|
+
- 使用 8KB 缓冲区提高效率
|
|
49
|
+
- 在 IO 线程执行,避免阻塞主线程
|
|
50
|
+
- 返回本地文件路径和元数据
|
|
51
|
+
|
|
52
|
+
**`getContentUriMetadata`**
|
|
53
|
+
- 使用 `ContentResolver.query()` 查询文件信息
|
|
54
|
+
- 获取 `DISPLAY_NAME` 和 `SIZE`
|
|
55
|
+
- 获取 MIME 类型
|
|
56
|
+
|
|
57
|
+
**辅助方法 `getContentUriMetadataInternal`**
|
|
58
|
+
- 共享的元数据查询逻辑
|
|
59
|
+
- 使用 Cursor 安全访问数据库
|
|
60
|
+
- 正确处理资源释放
|
|
61
|
+
|
|
62
|
+
### 3. 文档
|
|
63
|
+
|
|
64
|
+
- **`docs/CONTENT_URI_HANDLER.md`** - 完整的 API 文档和使用指南
|
|
65
|
+
- **`README.md`** - 更新了功能列表,添加了 Content URI Handler 介绍
|
|
66
|
+
|
|
67
|
+
### 4. 示例代码
|
|
68
|
+
|
|
69
|
+
- **`example/ContentUriExample.tsx`** - 完整的 React Native 示例组件
|
|
70
|
+
- 文件选择和复制
|
|
71
|
+
- 元数据查询
|
|
72
|
+
- UI 展示
|
|
73
|
+
- 错误处理
|
|
74
|
+
|
|
75
|
+
- **`example/ContentUriQuickExamples.ts`** - 快速参考示例
|
|
76
|
+
- 7 个常见使用场景
|
|
77
|
+
- 与 FTP 客户端集成
|
|
78
|
+
- 完整的工作流示例
|
|
79
|
+
|
|
80
|
+
## 技术亮点
|
|
81
|
+
|
|
82
|
+
### 1. 解决的核心问题
|
|
83
|
+
|
|
84
|
+
**问题:** 当通过"打开方式"或"分享"接收文件时,第三方应用可能没有正确设置 `FLAG_GRANT_READ_URI_PERMISSION`,导致:
|
|
85
|
+
```
|
|
86
|
+
java.lang.SecurityException: Permission Denial
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**解决方案:** 使用 `ContentResolver` 直接读取输入流,无需文件系统权限。
|
|
90
|
+
|
|
91
|
+
### 2. 优势
|
|
92
|
+
|
|
93
|
+
✅ **无需额外权限** - 不需要 `READ_EXTERNAL_STORAGE`
|
|
94
|
+
✅ **兼容性强** - 适用于所有 ContentProvider
|
|
95
|
+
✅ **性能优化** - 使用缓冲区,在后台线程执行
|
|
96
|
+
✅ **错误处理** - 完善的异常处理和资源清理
|
|
97
|
+
✅ **类型安全** - 完整的 TypeScript 类型定义
|
|
98
|
+
|
|
99
|
+
### 3. 实现细节
|
|
100
|
+
|
|
101
|
+
**线程管理:**
|
|
102
|
+
```kotlin
|
|
103
|
+
moduleScope.launch(Dispatchers.IO) {
|
|
104
|
+
// 文件操作在 IO 线程
|
|
105
|
+
withContext(Dispatchers.Main) {
|
|
106
|
+
// 回调在主线程
|
|
107
|
+
promise.resolve(result)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**资源管理:**
|
|
113
|
+
```kotlin
|
|
114
|
+
try {
|
|
115
|
+
inputStream = context.contentResolver.openInputStream(uri)
|
|
116
|
+
// ... 复制操作
|
|
117
|
+
} finally {
|
|
118
|
+
inputStream?.close()
|
|
119
|
+
outputStream?.close()
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**错误处理:**
|
|
124
|
+
```kotlin
|
|
125
|
+
if (uri.scheme != "content") {
|
|
126
|
+
throw Exception("Only content:// URIs are supported")
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## 使用场景
|
|
131
|
+
|
|
132
|
+
### 1. 文件选择器集成
|
|
133
|
+
```typescript
|
|
134
|
+
const result = await DocumentPicker.getDocumentAsync(...);
|
|
135
|
+
const file = await copyContentUriToCache(result.assets[0].uri);
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 2. 处理 Intent
|
|
139
|
+
```typescript
|
|
140
|
+
const url = await Linking.getInitialURL();
|
|
141
|
+
if (url?.startsWith('content://')) {
|
|
142
|
+
const file = await copyContentUriToCache(url);
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 3. 与 FTP/SMB 集成
|
|
147
|
+
```typescript
|
|
148
|
+
const file = await copyContentUriToCache(contentUri);
|
|
149
|
+
await ftpClient.upload(file.localPath, '/uploads/file.pdf');
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## 测试建议
|
|
153
|
+
|
|
154
|
+
### 单元测试
|
|
155
|
+
1. 验证 content:// URI 的处理
|
|
156
|
+
2. 测试元数据提取
|
|
157
|
+
3. 测试错误情况(无效 URI、权限问题等)
|
|
158
|
+
|
|
159
|
+
### 集成测试
|
|
160
|
+
1. 使用文件选择器选择文件
|
|
161
|
+
2. 通过"打开方式"分享文件到应用
|
|
162
|
+
3. 从不同的 ContentProvider 获取文件
|
|
163
|
+
4. 测试大文件(> 100MB)
|
|
164
|
+
|
|
165
|
+
### 性能测试
|
|
166
|
+
1. 不同文件大小的复制速度
|
|
167
|
+
2. 内存使用情况
|
|
168
|
+
3. 并发操作
|
|
169
|
+
|
|
170
|
+
## 兼容性
|
|
171
|
+
|
|
172
|
+
- **最低 Android 版本:** API 19 (Android 4.4)
|
|
173
|
+
- **React Native:** 0.60+
|
|
174
|
+
- **Expo:** SDK 44+
|
|
175
|
+
- **依赖:** 仅标准 Android API,无额外依赖
|
|
176
|
+
|
|
177
|
+
## 未来改进方向
|
|
178
|
+
|
|
179
|
+
1. **进度回调** - 为大文件添加复制进度
|
|
180
|
+
2. **流式处理** - 对于超大文件,提供流式访问而非完整复制
|
|
181
|
+
3. **缓存管理** - 自动清理旧的缓存文件
|
|
182
|
+
4. **批量操作** - 同时处理多个 content:// URI
|
|
183
|
+
5. **iOS 支持** - 为 iOS 添加类似功能(如果需要)
|
|
184
|
+
|
|
185
|
+
## API 变更
|
|
186
|
+
|
|
187
|
+
### 新增方法
|
|
188
|
+
|
|
189
|
+
- `copyContentUriToCache(contentUri: string, fileName?: string)`
|
|
190
|
+
- `getContentUriMetadata(contentUri: string)`
|
|
191
|
+
|
|
192
|
+
### 无破坏性变更
|
|
193
|
+
|
|
194
|
+
所有现有 API 保持不变,这是一个纯新增功能。
|
|
195
|
+
|
|
196
|
+
## 文档
|
|
197
|
+
|
|
198
|
+
- 📖 [完整 API 文档](./docs/CONTENT_URI_HANDLER.md)
|
|
199
|
+
- 💡 [快速示例](./example/ContentUriQuickExamples.ts)
|
|
200
|
+
- 🎨 [UI 示例](./example/ContentUriExample.tsx)
|
|
201
|
+
|
|
202
|
+
## 相关资源
|
|
203
|
+
|
|
204
|
+
- [Android ContentResolver 文档](https://developer.android.com/reference/android/content/ContentResolver)
|
|
205
|
+
- [Android 内容 URI 最佳实践](https://developer.android.com/training/secure-file-sharing)
|
|
206
|
+
- [Expo 文件系统](https://docs.expo.dev/versions/latest/sdk/filesystem/)
|
package/README.md
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
# react-native-kookit
|
|
2
2
|
|
|
3
|
-
A React Native module
|
|
3
|
+
A comprehensive React Native/Expo module with multiple utilities for Android and iOS.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🎵 **Volume Key Interception** - Capture volume button presses on both iOS and Android
|
|
8
|
+
- 📁 **FTP Client** - Full-featured FTP client with upload/download capabilities
|
|
9
|
+
- 🗂️ **SMB Client** - Access SMB/CIFS network shares
|
|
10
|
+
- 📱 **Content URI Handler** (Android) - Handle `content://` URIs from other apps, bypassing permission restrictions
|
|
11
|
+
|
|
12
|
+
### Content URI Handler
|
|
13
|
+
|
|
14
|
+
The Content URI Handler solves a common Android problem: when receiving files via "Open with" or "Share" intents, apps receive `content://` URIs that often trigger `SecurityException: Permission Denial` errors.
|
|
15
|
+
|
|
16
|
+
**Key Benefits:**
|
|
17
|
+
- ✅ Bypass permission restrictions using Android's ContentResolver
|
|
18
|
+
- ✅ No `READ_EXTERNAL_STORAGE` permission needed
|
|
19
|
+
- ✅ Works with all third-party apps and file managers
|
|
20
|
+
- ✅ Get file metadata (name, size, MIME type)
|
|
21
|
+
- ✅ Copy to app cache directory for further processing
|
|
22
|
+
|
|
23
|
+
📖 [**View Content URI Handler Documentation**](./docs/CONTENT_URI_HANDLER.md)
|
|
4
24
|
|
|
5
25
|
# API documentation
|
|
6
26
|
|
|
@@ -42,9 +42,8 @@ class JcifsShareEnumerator {
|
|
|
42
42
|
} ?: emptyList()
|
|
43
43
|
|
|
44
44
|
} catch (e: Exception) {
|
|
45
|
-
// If JCIFS enumeration fails,
|
|
46
|
-
|
|
47
|
-
emptyList()
|
|
45
|
+
// If JCIFS enumeration fails, throw exception
|
|
46
|
+
throw Exception("Failed to enumerate shares using JCIFS: " + e.message)
|
|
48
47
|
}
|
|
49
48
|
}
|
|
50
49
|
|
|
@@ -11,6 +11,12 @@ import expo.modules.kotlin.exception.Exceptions
|
|
|
11
11
|
import expo.modules.kotlin.Promise
|
|
12
12
|
import kotlinx.coroutines.*
|
|
13
13
|
import java.net.URL
|
|
14
|
+
import android.net.Uri
|
|
15
|
+
import android.provider.OpenableColumns
|
|
16
|
+
import android.database.Cursor
|
|
17
|
+
import java.io.File
|
|
18
|
+
import java.io.FileOutputStream
|
|
19
|
+
import java.io.InputStream
|
|
14
20
|
|
|
15
21
|
class ReactNativeKookitModule : Module() {
|
|
16
22
|
private var isVolumeListenerEnabled = false
|
|
@@ -723,6 +729,89 @@ class ReactNativeKookitModule : Module() {
|
|
|
723
729
|
}
|
|
724
730
|
}
|
|
725
731
|
|
|
732
|
+
// ContentResolver functions for handling content:// URIs
|
|
733
|
+
AsyncFunction("copyContentUriToCache") { contentUri: String, fileName: String?, promise: Promise ->
|
|
734
|
+
moduleScope.launch(Dispatchers.IO) {
|
|
735
|
+
try {
|
|
736
|
+
val context = appContext.reactContext ?: throw Exception("React context is not available")
|
|
737
|
+
val uri = Uri.parse(contentUri)
|
|
738
|
+
|
|
739
|
+
if (uri.scheme != "content") {
|
|
740
|
+
throw Exception("Only content:// URIs are supported. Received: ${uri.scheme}://")
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// Get metadata from ContentResolver
|
|
744
|
+
val metadata = getContentUriMetadataInternal(context, uri)
|
|
745
|
+
|
|
746
|
+
// Determine file name
|
|
747
|
+
val finalFileName = fileName ?: metadata["displayName"] as? String
|
|
748
|
+
?: "file_${System.currentTimeMillis()}"
|
|
749
|
+
|
|
750
|
+
// Create destination file in cache directory
|
|
751
|
+
val cacheDir = context.cacheDir
|
|
752
|
+
val destFile = File(cacheDir, finalFileName)
|
|
753
|
+
|
|
754
|
+
// Copy file using ContentResolver InputStream
|
|
755
|
+
var inputStream: InputStream? = null
|
|
756
|
+
var outputStream: FileOutputStream? = null
|
|
757
|
+
var totalBytes = 0L
|
|
758
|
+
|
|
759
|
+
try {
|
|
760
|
+
inputStream = context.contentResolver.openInputStream(uri)
|
|
761
|
+
?: throw Exception("Failed to open input stream for URI: $contentUri")
|
|
762
|
+
|
|
763
|
+
outputStream = FileOutputStream(destFile)
|
|
764
|
+
val buffer = ByteArray(8192)
|
|
765
|
+
var bytesRead: Int
|
|
766
|
+
|
|
767
|
+
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
|
|
768
|
+
outputStream.write(buffer, 0, bytesRead)
|
|
769
|
+
totalBytes += bytesRead
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
outputStream.flush()
|
|
773
|
+
|
|
774
|
+
// Build result
|
|
775
|
+
val result = mapOf(
|
|
776
|
+
"localPath" to destFile.absolutePath,
|
|
777
|
+
"fileName" to finalFileName,
|
|
778
|
+
"mimeType" to metadata["mimeType"],
|
|
779
|
+
"size" to totalBytes
|
|
780
|
+
)
|
|
781
|
+
|
|
782
|
+
withContext(Dispatchers.Main) { promise.resolve(result) }
|
|
783
|
+
} finally {
|
|
784
|
+
inputStream?.close()
|
|
785
|
+
outputStream?.close()
|
|
786
|
+
}
|
|
787
|
+
} catch (e: Exception) {
|
|
788
|
+
withContext(Dispatchers.Main) {
|
|
789
|
+
promise.reject("COPY_CONTENT_URI_ERROR", "Failed to copy content URI: ${e.message}", e)
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
AsyncFunction("getContentUriMetadata") { contentUri: String, promise: Promise ->
|
|
796
|
+
moduleScope.launch(Dispatchers.IO) {
|
|
797
|
+
try {
|
|
798
|
+
val context = appContext.reactContext ?: throw Exception("React context is not available")
|
|
799
|
+
val uri = Uri.parse(contentUri)
|
|
800
|
+
|
|
801
|
+
if (uri.scheme != "content") {
|
|
802
|
+
throw Exception("Only content:// URIs are supported. Received: ${uri.scheme}://")
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
val metadata = getContentUriMetadataInternal(context, uri)
|
|
806
|
+
withContext(Dispatchers.Main) { promise.resolve(metadata) }
|
|
807
|
+
} catch (e: Exception) {
|
|
808
|
+
withContext(Dispatchers.Main) {
|
|
809
|
+
promise.reject("GET_CONTENT_URI_METADATA_ERROR", "Failed to get metadata: ${e.message}", e)
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
726
815
|
// Enables the module to be used as a native view. Definition components that are accepted as part of
|
|
727
816
|
// the view definition: Prop, Events.
|
|
728
817
|
View(ReactNativeKookitView::class) {
|
|
@@ -789,4 +878,54 @@ class ReactNativeKookitModule : Module() {
|
|
|
789
878
|
// Context might be lost, ignore
|
|
790
879
|
}
|
|
791
880
|
}
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* Internal helper to query content URI metadata using ContentResolver
|
|
884
|
+
*/
|
|
885
|
+
private fun getContentUriMetadataInternal(context: Context, uri: Uri): Map<String, Any?> {
|
|
886
|
+
var cursor: Cursor? = null
|
|
887
|
+
try {
|
|
888
|
+
cursor = context.contentResolver.query(
|
|
889
|
+
uri,
|
|
890
|
+
arrayOf(
|
|
891
|
+
OpenableColumns.DISPLAY_NAME,
|
|
892
|
+
OpenableColumns.SIZE
|
|
893
|
+
),
|
|
894
|
+
null,
|
|
895
|
+
null,
|
|
896
|
+
null
|
|
897
|
+
)
|
|
898
|
+
|
|
899
|
+
var displayName: String? = null
|
|
900
|
+
var size: Long? = null
|
|
901
|
+
|
|
902
|
+
cursor?.use {
|
|
903
|
+
if (it.moveToFirst()) {
|
|
904
|
+
val nameIndex = it.getColumnIndex(OpenableColumns.DISPLAY_NAME)
|
|
905
|
+
val sizeIndex = it.getColumnIndex(OpenableColumns.SIZE)
|
|
906
|
+
|
|
907
|
+
if (nameIndex != -1) {
|
|
908
|
+
displayName = it.getString(nameIndex)
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
if (sizeIndex != -1 && !it.isNull(sizeIndex)) {
|
|
912
|
+
size = it.getLong(sizeIndex)
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
// Get MIME type
|
|
918
|
+
val mimeType = context.contentResolver.getType(uri)
|
|
919
|
+
|
|
920
|
+
return mapOf(
|
|
921
|
+
"displayName" to displayName,
|
|
922
|
+
"mimeType" to mimeType,
|
|
923
|
+
"size" to size
|
|
924
|
+
)
|
|
925
|
+
} catch (e: Exception) {
|
|
926
|
+
throw Exception("Failed to query content URI metadata: ${e.message}", e)
|
|
927
|
+
} finally {
|
|
928
|
+
cursor?.close()
|
|
929
|
+
}
|
|
930
|
+
}
|
|
792
931
|
}
|
|
@@ -141,6 +141,34 @@ declare class ReactNativeKookitModule extends NativeModule<ReactNativeKookitModu
|
|
|
141
141
|
}>;
|
|
142
142
|
count: number;
|
|
143
143
|
}>;
|
|
144
|
+
/**
|
|
145
|
+
* Copy a file from content:// URI to local cache directory.
|
|
146
|
+
* This method uses Android's ContentResolver to read the file stream,
|
|
147
|
+
* bypassing permission restrictions when receiving content URIs from other apps.
|
|
148
|
+
*
|
|
149
|
+
* @param contentUri The content:// URI received from another app (e.g., via Intent)
|
|
150
|
+
* @param fileName Optional filename for the destination file. If not provided, tries to extract from URI
|
|
151
|
+
* @returns Promise that resolves with the local file path (file://)
|
|
152
|
+
* @platform android
|
|
153
|
+
*/
|
|
154
|
+
copyContentUriToCache(contentUri: string, fileName?: string): Promise<{
|
|
155
|
+
localPath: string;
|
|
156
|
+
fileName: string;
|
|
157
|
+
mimeType?: string;
|
|
158
|
+
size?: number;
|
|
159
|
+
}>;
|
|
160
|
+
/**
|
|
161
|
+
* Get metadata about a content:// URI without copying the file.
|
|
162
|
+
*
|
|
163
|
+
* @param contentUri The content:// URI to query
|
|
164
|
+
* @returns Promise that resolves with file metadata
|
|
165
|
+
* @platform android
|
|
166
|
+
*/
|
|
167
|
+
getContentUriMetadata(contentUri: string): Promise<{
|
|
168
|
+
displayName?: string;
|
|
169
|
+
mimeType?: string;
|
|
170
|
+
size?: number;
|
|
171
|
+
}>;
|
|
144
172
|
}
|
|
145
173
|
declare const _default: ReactNativeKookitModule;
|
|
146
174
|
export default _default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReactNativeKookitModule.d.ts","sourceRoot":"","sources":["../src/ReactNativeKookitModule.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ReactNativeKookitModule.d.ts","sourceRoot":"","sources":["../src/ReactNativeKookitModule.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReactNativeKookitModule.js","sourceRoot":"","sources":["../src/ReactNativeKookitModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"ReactNativeKookitModule.js","sourceRoot":"","sources":["../src/ReactNativeKookitModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAiOzD,yDAAyD;AACzD,eAAe,mBAAmB,CAChC,mBAAmB,CACpB,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from \"expo\";\n\nimport {\n ReactNativeKookitModuleEvents,\n FtpConnectionConfig,\n FtpFileInfo,\n SmbConnectionConfig,\n SmbFileInfo,\n} from \"./ReactNativeKookit.types\";\n\ndeclare class ReactNativeKookitModule extends NativeModule<ReactNativeKookitModuleEvents> {\n PI: number;\n\n /**\n * Returns a hello world string\n */\n hello(): string;\n\n /**\n * Test async function that sends a change event\n */\n setValueAsync(value: string): Promise<void>;\n\n /**\n * Enables volume key interception.\n * On Android, your MainActivity must implement VolumeKeyInterceptActivity interface.\n * On iOS, this works automatically.\n *\n * @throws Error if MainActivity doesn't implement VolumeKeyInterceptActivity on Android\n */\n enableVolumeKeyInterception(): void;\n\n /**\n * Disables volume key interception\n */\n disableVolumeKeyInterception(): void;\n\n // New FTP Client API Methods\n\n /**\n * Create a new FTP client instance\n * @param clientId Unique identifier for the FTP client\n * @returns Promise that resolves with client creation result\n */\n createFtpClient(clientId: string): Promise<{ clientId: string }>;\n\n /**\n * Connect FTP client to server\n * @param clientId FTP client identifier\n * @param config FTP connection configuration\n * @returns Promise that resolves when connected\n */\n ftpClientConnect(\n clientId: string,\n config: FtpConnectionConfig\n ): Promise<void>;\n\n /**\n * Disconnect FTP client from server\n * @param clientId FTP client identifier\n * @returns Promise that resolves when disconnected\n */\n ftpClientDisconnect(clientId: string): Promise<void>;\n\n /**\n * Dispose FTP client and clean up resources\n * @param clientId FTP client identifier\n * @returns Promise that resolves when disposed\n */\n disposeFtpClient(clientId: string): Promise<void>;\n\n /**\n * List files and directories\n * @param clientId FTP client identifier\n * @param path Optional path to list (defaults to current directory)\n * @returns Promise that resolves with array of file information\n */\n ftpClientList(clientId: string, path?: string): Promise<FtpFileInfo[]>;\n\n /**\n * Download a file from FTP server\n * @param clientId FTP client identifier\n * @param remotePath Remote file path on FTP server\n * @param localPath Local file path to save the downloaded file\n * @returns Promise that resolves when download is complete\n */\n ftpClientDownload(\n clientId: string,\n remotePath: string,\n localPath: string\n ): Promise<void>;\n\n /**\n * Upload a file to FTP server\n * @param clientId FTP client identifier\n * @param localPath Local file path to upload\n * @param remotePath Remote file path on FTP server\n * @returns Promise that resolves when upload is complete\n */\n ftpClientUpload(\n clientId: string,\n localPath: string,\n remotePath: string\n ): Promise<void>;\n\n /**\n * Delete a file or directory on FTP server\n * @param clientId FTP client identifier\n * @param remotePath Remote file or directory path to delete\n * @param isDirectory Whether the path is a directory (default: false)\n * @returns Promise that resolves when deletion is complete\n */\n ftpClientDelete(\n clientId: string,\n remotePath: string,\n isDirectory?: boolean\n ): Promise<void>;\n\n /**\n * Create a directory on FTP server\n * @param clientId FTP client identifier\n * @param remotePath Remote directory path to create\n * @returns Promise that resolves when directory is created\n */\n ftpClientCreateDirectory(clientId: string, remotePath: string): Promise<void>;\n\n /**\n * Change current working directory on FTP server\n * @param clientId FTP client identifier\n * @param remotePath Remote directory path to change to\n * @returns Promise that resolves when directory is changed\n */\n ftpClientChangeDirectory(clientId: string, remotePath: string): Promise<void>;\n\n /**\n * Get current working directory on FTP server\n * @param clientId FTP client identifier\n * @returns Promise that resolves with current directory path\n */\n ftpClientGetCurrentDirectory(clientId: string): Promise<string>;\n\n /**\n * Get FTP client status\n * @param clientId FTP client identifier\n * @returns Client status information\n */\n getFtpClientStatus(\n clientId: string\n ): Promise<{ exists: boolean; connected: boolean }>;\n\n /**\n * List all FTP clients\n * @returns Object containing all clients and their status\n */\n listFtpClients(): Promise<{\n clients: Record<string, { connected: boolean }>;\n count: number;\n }>;\n\n // SMB Client API\n\n createSmbClient(clientId: string): Promise<{ clientId: string }>;\n smbClientConnect(\n clientId: string,\n config: SmbConnectionConfig\n ): Promise<void>;\n smbClientConnectShare(clientId: string, shareName: string): Promise<void>;\n smbClientDisconnect(clientId: string): Promise<void>;\n disposeSmbClient(clientId: string): Promise<void>;\n smbClientList(clientId: string, path?: string): Promise<SmbFileInfo[]>;\n smbClientDownload(\n clientId: string,\n remotePath: string,\n localPath: string\n ): Promise<void>;\n smbClientUpload(\n clientId: string,\n localPath: string,\n remotePath: string\n ): Promise<void>;\n smbClientDelete(\n clientId: string,\n remotePath: string,\n isDirectory?: boolean\n ): Promise<void>;\n smbClientCreateDirectory(clientId: string, remotePath: string): Promise<void>;\n getSmbClientStatus(\n clientId: string\n ): Promise<{ exists: boolean; connected: boolean }>;\n listSmbClients(): Promise<{\n clients: Record<string, { connected: boolean }>;\n count: number;\n }>;\n\n /**\n * Copy a file from content:// URI to local cache directory.\n * This method uses Android's ContentResolver to read the file stream,\n * bypassing permission restrictions when receiving content URIs from other apps.\n * \n * @param contentUri The content:// URI received from another app (e.g., via Intent)\n * @param fileName Optional filename for the destination file. If not provided, tries to extract from URI\n * @returns Promise that resolves with the local file path (file://)\n * @platform android\n */\n copyContentUriToCache(contentUri: string, fileName?: string): Promise<{ \n localPath: string;\n fileName: string;\n mimeType?: string;\n size?: number;\n }>;\n\n /**\n * Get metadata about a content:// URI without copying the file.\n * \n * @param contentUri The content:// URI to query\n * @returns Promise that resolves with file metadata\n * @platform android\n */\n getContentUriMetadata(contentUri: string): Promise<{\n displayName?: string;\n mimeType?: string;\n size?: number;\n }>;\n}\n\n// This call loads the native module object from the JSI.\nexport default requireNativeModule<ReactNativeKookitModule>(\n \"ReactNativeKookit\"\n);\n"]}
|
package/package.json
CHANGED