improve handling of file intents
This commit is contained in:
@@ -20,6 +20,8 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:mime/mime.dart' as mime;
|
import 'package:mime/mime.dart' as mime;
|
||||||
|
import 'package:nullability/nullability.dart';
|
||||||
|
import 'package:path/path.dart' as p;
|
||||||
import 'package:riverpod/riverpod.dart';
|
import 'package:riverpod/riverpod.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:simple_intent_receiver/simple_intent_receiver.dart';
|
import 'package:simple_intent_receiver/simple_intent_receiver.dart';
|
||||||
@@ -44,13 +46,37 @@ final _sharingIntentTransformer =
|
|||||||
|
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
if (uri_to_file.isUriSupported(data)) {
|
if (uri_to_file.isUriSupported(data)) {
|
||||||
final file = await uri_to_file.toFile(data);
|
var path = data;
|
||||||
final mimeType = mime.lookupMimeType(file.path);
|
if (p.extension(data).whenNotEmpty == null) {
|
||||||
switch (mimeType) {
|
if (intent.mimeType.whenNotEmpty != null) {
|
||||||
case 'application/pdf':
|
final ext = mime.extensionFromMime(intent.mimeType!);
|
||||||
sink.add(ReceivedIntentParameter(data, null));
|
if (ext != null) {
|
||||||
default:
|
path = p.setExtension(path, '.$ext');
|
||||||
logger.w('Unhandled mime type: $mimeType');
|
} else {
|
||||||
|
logger.w(
|
||||||
|
'Could not determine file extension for: ${intent.mimeType}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.w(
|
||||||
|
'Received intent without extension and mime type $path',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final file = await uri_to_file.toFile(path);
|
||||||
|
final mimeType = mime.lookupMimeType(file.path);
|
||||||
|
switch (mimeType) {
|
||||||
|
case 'application/pdf':
|
||||||
|
sink.add(ReceivedIntentParameter(path, null));
|
||||||
|
default:
|
||||||
|
logger.w('Unhandled mime type: $mimeType');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.e('Failed to convert URI to file: $e');
|
||||||
|
// Fallback: pass the original URI
|
||||||
|
sink.add(ReceivedIntentParameter(data, null));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sink.add(ReceivedIntentParameter(data, null));
|
sink.add(ReceivedIntentParameter(data, null));
|
||||||
|
|||||||
+34
-13
@@ -3,6 +3,7 @@ package eu.weblibre.simple_intent_receiver
|
|||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import io.flutter.Log
|
import io.flutter.Log
|
||||||
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||||
@@ -30,10 +31,8 @@ class SimpleIntentReceiverPlugin: FlutterPlugin, ActivityAware, PluginRegistry.N
|
|||||||
activity = binding.activity
|
activity = binding.activity
|
||||||
binding.addOnNewIntentListener(this)
|
binding.addOnNewIntentListener(this)
|
||||||
|
|
||||||
// Process the initial intent if available
|
|
||||||
binding.activity.intent?.let { intent ->
|
binding.activity.intent?.let { intent ->
|
||||||
val uri = intent.toUri(0);
|
val uri = intent.toUri(0)
|
||||||
|
|
||||||
if (lastHandledIntent != uri) {
|
if (lastHandledIntent != uri) {
|
||||||
handleIntent(intent)
|
handleIntent(intent)
|
||||||
lastHandledIntent = uri
|
lastHandledIntent = uri
|
||||||
@@ -55,15 +54,43 @@ class SimpleIntentReceiverPlugin: FlutterPlugin, ActivityAware, PluginRegistry.N
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent): Boolean {
|
override fun onNewIntent(intent: Intent): Boolean {
|
||||||
// Update the activity's intent to ensure proper state
|
|
||||||
activity?.setIntent(intent)
|
activity?.setIntent(intent)
|
||||||
return handleIntent(intent)
|
return handleIntent(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleIntent(intent: Intent): Boolean {
|
private fun handleIntent(intent: Intent): Boolean {
|
||||||
// Check if this intent is coming from a different task
|
// Grant URI permissions for content URIs
|
||||||
|
intent.data?.let { uri ->
|
||||||
|
if (uri.scheme == "content") {
|
||||||
|
try {
|
||||||
|
activity?.grantUriPermission(
|
||||||
|
context.packageName,
|
||||||
|
uri,
|
||||||
|
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w("SimpleIntentReceiver", "Could not grant URI permission for: $uri", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle SEND action with STREAM extra
|
||||||
|
intent.getStringExtra(Intent.EXTRA_STREAM)?.let { streamUri ->
|
||||||
|
try {
|
||||||
|
val uri = Uri.parse(streamUri)
|
||||||
|
if (uri.scheme == "content") {
|
||||||
|
activity?.grantUriPermission(
|
||||||
|
context.packageName,
|
||||||
|
uri,
|
||||||
|
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w("SimpleIntentReceiver", "Could not grant URI permission for stream: $streamUri", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (intent.flags and Intent.FLAG_ACTIVITY_NEW_TASK != 0) {
|
if (intent.flags and Intent.FLAG_ACTIVITY_NEW_TASK != 0) {
|
||||||
// Clear any existing tasks with this activity
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,25 +104,20 @@ class SimpleIntentReceiverPlugin: FlutterPlugin, ActivityAware, PluginRegistry.N
|
|||||||
val data = intent.dataString
|
val data = intent.dataString
|
||||||
val fromPackageName = intent.getPackage()
|
val fromPackageName = intent.getPackage()
|
||||||
|
|
||||||
// Extract categories
|
|
||||||
val categories = ArrayList<String>()
|
val categories = ArrayList<String>()
|
||||||
intent.categories?.let {
|
intent.categories?.let {
|
||||||
categories.addAll(it)
|
categories.addAll(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract extras
|
|
||||||
val extras = HashMap<String, Any?>()
|
val extras = HashMap<String, Any?>()
|
||||||
intent.extras?.let { bundle ->
|
intent.extras?.let { bundle ->
|
||||||
for (key in bundle.keySet()) {
|
for (key in bundle.keySet()) {
|
||||||
try {
|
try {
|
||||||
when (val value = bundle.get(key)) {
|
when (val value = bundle.get(key)) {
|
||||||
// Handle Bundle objects specially
|
|
||||||
is Bundle -> {
|
is Bundle -> {
|
||||||
// Convert nested Bundle to Map
|
|
||||||
val bundleMap = HashMap<String, Any?>()
|
val bundleMap = HashMap<String, Any?>()
|
||||||
for (bundleKey in value.keySet()) {
|
for (bundleKey in value.keySet()) {
|
||||||
val bundleValue = value.get(bundleKey)
|
val bundleValue = value.get(bundleKey)
|
||||||
// Only add primitive types and strings that can be safely serialized
|
|
||||||
if (bundleValue == null || bundleValue is String ||
|
if (bundleValue == null || bundleValue is String ||
|
||||||
bundleValue is Boolean || bundleValue is Int ||
|
bundleValue is Boolean || bundleValue is Int ||
|
||||||
bundleValue is Long || bundleValue is Double ||
|
bundleValue is Long || bundleValue is Double ||
|
||||||
@@ -107,12 +129,10 @@ class SimpleIntentReceiverPlugin: FlutterPlugin, ActivityAware, PluginRegistry.N
|
|||||||
}
|
}
|
||||||
extras[key] = bundleMap
|
extras[key] = bundleMap
|
||||||
}
|
}
|
||||||
// Handle other types that Flutter can serialize
|
|
||||||
null, is String, is Boolean, is Int, is Long, is Double, is Float,
|
null, is String, is Boolean, is Int, is Long, is Double, is Float,
|
||||||
is ByteArray, is IntArray, is LongArray, is DoubleArray, is FloatArray -> {
|
is ByteArray, is IntArray, is LongArray, is DoubleArray, is FloatArray -> {
|
||||||
extras[key] = value
|
extras[key] = value
|
||||||
}
|
}
|
||||||
// For any other types, convert to string
|
|
||||||
else -> {
|
else -> {
|
||||||
extras[key] = value.toString()
|
extras[key] = value.toString()
|
||||||
}
|
}
|
||||||
@@ -129,6 +149,7 @@ class SimpleIntentReceiverPlugin: FlutterPlugin, ActivityAware, PluginRegistry.N
|
|||||||
action = action,
|
action = action,
|
||||||
data = data,
|
data = data,
|
||||||
categories = categories,
|
categories = categories,
|
||||||
|
mimeType = intent.type,
|
||||||
extra = extras
|
extra = extras
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-3
@@ -1,4 +1,4 @@
|
|||||||
// Autogenerated from Pigeon (v25.5.0), do not edit directly.
|
// Autogenerated from Pigeon (v26.0.0), do not edit directly.
|
||||||
// See also: https://pub.dev/packages/pigeon
|
// See also: https://pub.dev/packages/pigeon
|
||||||
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
|
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
|
||||||
|
|
||||||
@@ -67,6 +67,7 @@ data class Intent (
|
|||||||
val action: String? = null,
|
val action: String? = null,
|
||||||
val data: String? = null,
|
val data: String? = null,
|
||||||
val categories: List<String>,
|
val categories: List<String>,
|
||||||
|
val mimeType: String? = null,
|
||||||
val extra: Map<String, Any?>
|
val extra: Map<String, Any?>
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@@ -76,8 +77,9 @@ data class Intent (
|
|||||||
val action = pigeonVar_list[1] as String?
|
val action = pigeonVar_list[1] as String?
|
||||||
val data = pigeonVar_list[2] as String?
|
val data = pigeonVar_list[2] as String?
|
||||||
val categories = pigeonVar_list[3] as List<String>
|
val categories = pigeonVar_list[3] as List<String>
|
||||||
val extra = pigeonVar_list[4] as Map<String, Any?>
|
val mimeType = pigeonVar_list[4] as String?
|
||||||
return Intent(fromPackageName, action, data, categories, extra)
|
val extra = pigeonVar_list[5] as Map<String, Any?>
|
||||||
|
return Intent(fromPackageName, action, data, categories, mimeType, extra)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fun toList(): List<Any?> {
|
fun toList(): List<Any?> {
|
||||||
@@ -86,6 +88,7 @@ data class Intent (
|
|||||||
action,
|
action,
|
||||||
data,
|
data,
|
||||||
categories,
|
categories,
|
||||||
|
mimeType,
|
||||||
extra,
|
extra,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Autogenerated from Pigeon (v25.5.0), do not edit directly.
|
// Autogenerated from Pigeon (v26.0.0), do not edit directly.
|
||||||
// See also: https://pub.dev/packages/pigeon
|
// See also: https://pub.dev/packages/pigeon
|
||||||
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers
|
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers
|
||||||
|
|
||||||
@@ -38,6 +38,7 @@ class Intent {
|
|||||||
this.action,
|
this.action,
|
||||||
this.data,
|
this.data,
|
||||||
required this.categories,
|
required this.categories,
|
||||||
|
this.mimeType,
|
||||||
required this.extra,
|
required this.extra,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -49,6 +50,8 @@ class Intent {
|
|||||||
|
|
||||||
List<String> categories;
|
List<String> categories;
|
||||||
|
|
||||||
|
String? mimeType;
|
||||||
|
|
||||||
Map<String, Object?> extra;
|
Map<String, Object?> extra;
|
||||||
|
|
||||||
List<Object?> _toList() {
|
List<Object?> _toList() {
|
||||||
@@ -57,6 +60,7 @@ class Intent {
|
|||||||
action,
|
action,
|
||||||
data,
|
data,
|
||||||
categories,
|
categories,
|
||||||
|
mimeType,
|
||||||
extra,
|
extra,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -71,7 +75,8 @@ class Intent {
|
|||||||
action: result[1] as String?,
|
action: result[1] as String?,
|
||||||
data: result[2] as String?,
|
data: result[2] as String?,
|
||||||
categories: (result[3] as List<Object?>?)!.cast<String>(),
|
categories: (result[3] as List<Object?>?)!.cast<String>(),
|
||||||
extra: (result[4] as Map<Object?, Object?>?)!.cast<String, Object?>(),
|
mimeType: result[4] as String?,
|
||||||
|
extra: (result[5] as Map<Object?, Object?>?)!.cast<String, Object?>(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ class Intent {
|
|||||||
final String? action;
|
final String? action;
|
||||||
final String? data;
|
final String? data;
|
||||||
final List<String> categories;
|
final List<String> categories;
|
||||||
|
final String? mimeType;
|
||||||
final Map<String, Object?> extra;
|
final Map<String, Object?> extra;
|
||||||
|
|
||||||
Intent({
|
Intent({
|
||||||
@@ -12,6 +13,7 @@ class Intent {
|
|||||||
required this.action,
|
required this.action,
|
||||||
required this.data,
|
required this.data,
|
||||||
required this.categories,
|
required this.categories,
|
||||||
|
required this.mimeType,
|
||||||
required this.extra,
|
required this.extra,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user