CH-J SM Mobilequản lý máy chủ qua SSH
Menu
Mã nguồn đã công bố

CH-J SM Mobile

Duyệt thư mục và tệp của một bản phát hành ứng dụng cụ thể.

Tải mã nguồn dạng ZIP
CH-J / SM Mobile 175 tệp
sm-mobile-20261016T185235Z-8e93ecc1b59a Ngày thêm: · Ngày dựng:
documents.dart Ngày thêm: Chưa ghi nhận ngày 7,1 KB · 240 dòng
Mã Tải tệp xuống
1import 'dart:async';
2import 'package:flutter/services.dart';
3import '../core/failure.dart';
5/// A file or folder chosen by the user in the system file picker.
6///
7/// [id] is opaque: on iOS a security-scoped bookmark (optionally followed by
8/// a path inside a chosen folder), on Android a `content://` document URI.
9/// Access only exists within what the user granted; nothing here bypasses
10/// the system sandbox.
11class DeviceDocument {
12 final String id, name;
13 final int? size;
14 final bool writable, directory;
15 final String? mime;
16 final DateTime? modified;
18 /// Where it lives, for display only (provider or folder name).
19 final String? location;
20 const DeviceDocument({
21 required this.id,
22 required this.name,
23 this.size,
24 this.writable = false,
25 this.directory = false,
26 this.mime,
27 this.modified,
28 this.location,
29 });
31 factory DeviceDocument.fromMap(Map m) => DeviceDocument(
32 id: m['id'] as String,
33 name: m['name'] as String? ?? '?',
34 size: (m['size'] as num?)?.toInt(),
35 writable: m['writable'] == true,
36 directory: m['directory'] == true,
37 mime: m['mime'] as String?,
38 modified: m['modified'] == null
39 ? null
40 : DateTime.fromMillisecondsSinceEpoch((m['modified'] as num).toInt()),
41 location: m['location'] as String?,
42 );
44 Map<String, dynamic> toJson() => {
45 'id': id,
46 'name': name,
47 'size': ?size,
48 'writable': writable,
49 'directory': directory,
50 'mime': ?mime,
51 'modified': ?modified?.millisecondsSinceEpoch,
52 'location': ?location,
53 };
56/// Access to files on the device through the system document picker
57/// (iOS/iPadOS Files, Android Storage Access Framework).
58abstract interface class DocumentPlatform {
59 /// Lets the user choose one or more files. Empty when cancelled.
60 Future<List<DeviceDocument>> pick({bool multiple = false});
62 /// Lets the user choose a folder. Null when cancelled.
63 Future<DeviceDocument?> pickFolder();
65 /// The entries of a folder chosen with [pickFolder].
66 Future<List<DeviceDocument>> list(DeviceDocument folder);
68 /// Current name, size, date and write permission.
69 Future<DeviceDocument> stat(DeviceDocument doc);
71 Future<Uint8List> read(DeviceDocument doc);
73 /// Replaces the content of [doc] in place (coordinated with other apps on
74 /// iOS). Throws 'readOnly' when the provider does not allow writing.
75 Future<DeviceDocument> write(DeviceDocument doc, Uint8List bytes);
77 /// Lets the user choose where to save a new file. Null when cancelled.
78 Future<DeviceDocument?> saveAs(
79 String suggestedName,
80 Uint8List bytes, {
81 String? mime,
82 });
84 /// Creates a new file in a chosen folder.
85 Future<DeviceDocument> create(
86 DeviceDocument folder,
87 String name,
88 Uint8List bytes, {
89 String? mime,
90 });
92 /// Shows a local file (in the app's temporary directory) with the system
93 /// viewer: Quick Look on iOS (in the app), an app chosen by the user on
94 /// Android. Returns false when nothing on the device can show it.
95 Future<bool> preview(String path, {String? mime});
97 /// Android only: renders PDF pages with the system PdfRenderer.
98 Future<int> pdfPageCount(String path);
99 Future<Uint8List> pdfPage(String path, int index, int width);
101 /// Files handed to SM Mobile by other apps ("Open in", share).
102 Stream<List<DeviceDocument>> get incoming;
104 /// Releases a persisted permission when a recent entry is removed.
105 Future<void> forget(DeviceDocument doc);
108class MethodChannelDocuments implements DocumentPlatform {
109 static const channel = MethodChannel('eu.chj.sm_mobile/documents');
110 final _incoming = StreamController<List<DeviceDocument>>.broadcast();
111 bool _listening = false;
113 MethodChannelDocuments();
115 Future<T?> _call<T>(String method, [Object? args]) async {
116 try {
117 return await channel.invokeMethod<T>(method, args);
118 } on PlatformException catch (e) {
119 throw Failure(switch (e.code) {
120 'readOnly' => 'readOnlyError',
121 'denied' => 'permissionDenied',
122 'notFound' => 'fileNotFound',
123 'noSpace' => 'noSpace',
124 'stale' => 'documentStale',
125 'busy' => 'operationFailed',
126 _ => 'fileError',
127 });
128 } on MissingPluginException {
129 throw const Failure('unsupportedPlatform');
130 }
131 }
133 List<DeviceDocument> _docs(List? raw) => [
134 for (final m in raw ?? const []) DeviceDocument.fromMap(m as Map),
135 ];
137 @override
138 Future<List<DeviceDocument>> pick({bool multiple = false}) async =>
139 _docs(await _call<List>('pick', {'multiple': multiple}));
141 @override
142 Future<DeviceDocument?> pickFolder() async {
143 final m = await _call<Map>('pickFolder');
144 return m == null ? null : DeviceDocument.fromMap(m);
145 }
147 @override
148 Future<List<DeviceDocument>> list(DeviceDocument folder) async =>
149 _docs(await _call<List>('list', {'id': folder.id}));
151 @override
152 Future<DeviceDocument> stat(DeviceDocument doc) async =>
153 DeviceDocument.fromMap((await _call<Map>('stat', {'id': doc.id}))!);
155 @override
156 Future<Uint8List> read(DeviceDocument doc) async =>
157 (await _call<Uint8List>('read', {'id': doc.id}))!;
159 @override
160 Future<DeviceDocument> write(DeviceDocument doc, Uint8List bytes) async =>
161 DeviceDocument.fromMap(
162 (await _call<Map>('write', {'id': doc.id, 'bytes': bytes}))!,
163 );
165 @override
166 Future<DeviceDocument?> saveAs(
167 String suggestedName,
168 Uint8List bytes, {
169 String? mime,
170 }) async {
171 final m = await _call<Map>('saveAs', {
172 'name': suggestedName,
173 'bytes': bytes,
174 'mime': mime ?? 'application/octet-stream',
175 });
176 return m == null ? null : DeviceDocument.fromMap(m);
177 }
179 @override
180 Future<DeviceDocument> create(
181 DeviceDocument folder,
182 String name,
183 Uint8List bytes, {
184 String? mime,
185 }) async => DeviceDocument.fromMap(
186 (await _call<Map>('create', {
187 'folder': folder.id,
188 'name': name,
189 'bytes': bytes,
190 'mime': mime ?? 'application/octet-stream',
191 }))!,
192 );
194 @override
195 Future<bool> preview(String path, {String? mime}) async =>
196 await _call<bool>('preview', {'path': path, 'mime': ?mime}) ?? false;
198 @override
199 Future<int> pdfPageCount(String path) async =>
200 await _call<int>('pdfPageCount', {'path': path}) ?? 0;
202 @override
203 Future<Uint8List> pdfPage(String path, int index, int width) async =>
204 (await _call<Uint8List>('pdfPage', {
205 'path': path,
206 'index': index,
207 'width': width,
208 }))!;
210 @override
211 Stream<List<DeviceDocument>> get incoming {
212 if (!_listening) {
213 _listening = true;
214 channel.setMethodCallHandler((call) async {
215 if (call.method == 'incoming') {
216 _incoming.add(_docs(call.arguments as List?));
217 }
218 });
219 // Files that arrived before Dart was listening (cold start).
220 unawaited(
221 _call<List>('takeIncoming')
222 .then((raw) {
223 final docs = _docs(raw);
224 if (docs.isNotEmpty) _incoming.add(docs);
225 })
226 .catchError((Object _) {}),
227 );
228 }
229 return _incoming.stream;
230 }
232 @override
233 Future<void> forget(DeviceDocument doc) async {
234 try {
235 await _call<void>('forget', {'id': doc.id});
236 } catch (_) {
237 /* Nothing to release. */
238 }
239 }
SHA-2569633a5bf460cef3d55e8298e3575b55b589145676c2adf5aa37c9bb6539d2236

CH-J Proprietary Software License 1.14 · Mã nguồn được cung cấp theo CH-J Proprietary Software License 1.14. Việc công khai mã nguồn không thay đổi các điều khoản giấy phép và không cấp thêm quyền nào.

SHA-256 của tệp nén46e427a43e87af091004f36145590bda8fe90086dfb0f631afeb5029fb0b5655
Tệp