mirror of
https://github.com/localsend/localsend.git
synced 2026-08-07 07:14:52 +00:00
feat(android): expose content URI file descriptor
This commit is contained in:
@@ -2,6 +2,7 @@ package org.localsend.localsend_app
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.database.Cursor
|
||||
@@ -58,6 +59,8 @@ class MainActivity : FlutterActivity() {
|
||||
|
||||
"createDirectory" -> handleCreateDirectory(call, result)
|
||||
|
||||
"getFileDescriptor" -> handleGetFileDescriptor(call, result)
|
||||
|
||||
"openContentUri" -> {
|
||||
openUri(context, call.argument<String>("uri")!!)
|
||||
result.success(null)
|
||||
@@ -82,6 +85,38 @@ class MainActivity : FlutterActivity() {
|
||||
Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f) != 0.0f;
|
||||
}
|
||||
|
||||
private fun handleGetFileDescriptor(call: MethodCall, result: MethodChannel.Result) {
|
||||
val uriString = call.argument<String>("uri")
|
||||
if (uriString == null) {
|
||||
result.error("INVALID_ARGUMENT", "Missing content URI", null)
|
||||
return
|
||||
}
|
||||
|
||||
val uri = Uri.parse(uriString)
|
||||
if (uri.scheme != ContentResolver.SCHEME_CONTENT) {
|
||||
result.error("INVALID_ARGUMENT", "Expected a content:// URI", null)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
val parcelFileDescriptor = contentResolver.openFileDescriptor(uri, "r")
|
||||
if (parcelFileDescriptor == null) {
|
||||
result.error("OPEN_FAILED", "The content provider did not return a file descriptor", null)
|
||||
return
|
||||
}
|
||||
|
||||
// Ownership of the detached descriptor is transferred to the caller. It must be
|
||||
// closed by Rust (or whichever native consumer receives it) after use.
|
||||
parcelFileDescriptor.use {
|
||||
result.success(it.detachFd())
|
||||
}
|
||||
} catch (e: SecurityException) {
|
||||
result.error("PERMISSION_DENIED", e.message ?: "Permission denied for content URI", null)
|
||||
} catch (e: Exception) {
|
||||
result.error("OPEN_FAILED", e.message ?: "Failed to open content URI", null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun openDirectoryPicker(onlyPath: Boolean) {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
||||
|
||||
@@ -44,6 +44,20 @@ Future<bool> getSystemAnimationsStatusAndroid() async {
|
||||
return await _methodChannel.invokeMethod('isAnimationsEnabled') ?? true;
|
||||
}
|
||||
|
||||
/// Opens [uri] for reading and returns an owned Linux file descriptor.
|
||||
///
|
||||
/// The descriptor stays open after this call and must be closed by the native
|
||||
/// consumer it is passed to.
|
||||
Future<int> getFileDescriptorAndroid({required String uri}) async {
|
||||
final fileDescriptor = await _methodChannel.invokeMethod<int>('getFileDescriptor', {
|
||||
'uri': uri,
|
||||
});
|
||||
if (fileDescriptor == null) {
|
||||
throw StateError('Android returned no file descriptor for $uri');
|
||||
}
|
||||
return fileDescriptor;
|
||||
}
|
||||
|
||||
Future<void> createDirectory({
|
||||
required String documentUri,
|
||||
required String directoryName,
|
||||
|
||||
Reference in New Issue
Block a user