Add Supa account and search changes
This commit is contained in:
+46
-9
@@ -32,10 +32,11 @@ import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityAware
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
|
||||
import io.flutter.plugin.common.PluginRegistry
|
||||
import eu.weblibre.simple_intent_receiver.pigeons.IntentHost
|
||||
import eu.weblibre.simple_intent_receiver.pigeons.Intent as PigeonIntent
|
||||
import eu.weblibre.simple_intent_receiver.pigeons.IntentGatekeeperHostApi
|
||||
|
||||
class SimpleIntentReceiverPlugin: FlutterPlugin, ActivityAware, PluginRegistry.NewIntentListener {
|
||||
class SimpleIntentReceiverPlugin: FlutterPlugin, ActivityAware, PluginRegistry.NewIntentListener, IntentHost {
|
||||
companion object {
|
||||
// Stable names that must match the notification replay path and shared-prefs schema.
|
||||
private const val PREFS_NAME = "weblibre_intent_gatekeeper"
|
||||
@@ -53,10 +54,28 @@ class SimpleIntentReceiverPlugin: FlutterPlugin, ActivityAware, PluginRegistry.N
|
||||
private var activity: Activity? = null
|
||||
private var binaryMessenger: io.flutter.plugin.common.BinaryMessenger? = null
|
||||
|
||||
/**
|
||||
* Caches the launch intent so Dart can retrieve it after setUp().
|
||||
* On cold start, onAttachedToActivity fires before Dart registers its
|
||||
* Pigeon handler, so the initial sendIntent message is lost. This field
|
||||
* lets Dart call getInitialIntent() to recover it.
|
||||
*
|
||||
* On Android configuration change (rotation, theme switch) the activity
|
||||
* is recreated and onAttachedToActivity fires again with the same
|
||||
* launching intent. The `lastHandledIntent` guard below prevents
|
||||
* re-caching that identical intent. If a NEW deep link arrives via the
|
||||
* launcher between two Dart-side reads of getInitialIntent(),
|
||||
* pendingInitialIntent is overwritten — only the latest intent is
|
||||
* delivered. This is intentional: dropping the stale one keeps the
|
||||
* "initial" intent meaning "what should the app open into right now".
|
||||
*/
|
||||
private var pendingInitialIntent: PigeonIntent? = null
|
||||
|
||||
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
||||
context = flutterPluginBinding.applicationContext
|
||||
intentReceiver = IntentReceiver(flutterPluginBinding.binaryMessenger)
|
||||
binaryMessenger = flutterPluginBinding.binaryMessenger
|
||||
IntentHost.setUp(flutterPluginBinding.binaryMessenger, this)
|
||||
IntentGatekeeperHostApi.setUp(
|
||||
flutterPluginBinding.binaryMessenger,
|
||||
IntentGatekeeperHostApiImpl(flutterPluginBinding.applicationContext),
|
||||
@@ -65,10 +84,19 @@ class SimpleIntentReceiverPlugin: FlutterPlugin, ActivityAware, PluginRegistry.N
|
||||
|
||||
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
|
||||
intentReceiver = null
|
||||
binaryMessenger?.let { IntentGatekeeperHostApi.setUp(it, null) }
|
||||
binaryMessenger?.let {
|
||||
IntentHost.setUp(it, null)
|
||||
IntentGatekeeperHostApi.setUp(it, null)
|
||||
}
|
||||
binaryMessenger = null
|
||||
}
|
||||
|
||||
override fun getInitialIntent(): PigeonIntent? {
|
||||
val intent = pendingInitialIntent
|
||||
pendingInitialIntent = null
|
||||
return intent
|
||||
}
|
||||
|
||||
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
|
||||
activity = binding.activity
|
||||
binding.addOnNewIntentListener(this)
|
||||
@@ -76,7 +104,10 @@ class SimpleIntentReceiverPlugin: FlutterPlugin, ActivityAware, PluginRegistry.N
|
||||
binding.activity.intent?.let { intent ->
|
||||
val uri = intent.toUri(0)
|
||||
if (lastHandledIntent != uri) {
|
||||
handleIntent(intent)
|
||||
// Cache the launch intent for Dart to retrieve via getInitialIntent().
|
||||
// Don't send via Pigeon here — the Dart handler isn't registered yet
|
||||
// during cold start so the message would be lost.
|
||||
pendingInitialIntent = prepareIntentForDelivery(intent)
|
||||
lastHandledIntent = uri
|
||||
}
|
||||
}
|
||||
@@ -100,8 +131,7 @@ class SimpleIntentReceiverPlugin: FlutterPlugin, ActivityAware, PluginRegistry.N
|
||||
return handleIntent(intent)
|
||||
}
|
||||
|
||||
private fun handleIntent(intent: Intent): Boolean {
|
||||
// Grant URI permissions for content URIs
|
||||
private fun grantUriPermissions(intent: Intent) {
|
||||
intent.data?.let { uri ->
|
||||
if (uri.scheme == "content") {
|
||||
try {
|
||||
@@ -116,7 +146,6 @@ class SimpleIntentReceiverPlugin: FlutterPlugin, ActivityAware, PluginRegistry.N
|
||||
}
|
||||
}
|
||||
|
||||
// Handle SEND action with STREAM extra
|
||||
intent.getStringExtra(Intent.EXTRA_STREAM)?.let { streamUri ->
|
||||
try {
|
||||
val uri = Uri.parse(streamUri)
|
||||
@@ -131,15 +160,23 @@ class SimpleIntentReceiverPlugin: FlutterPlugin, ActivityAware, PluginRegistry.N
|
||||
Log.w("SimpleIntentReceiver", "Could not grant URI permission for stream: $streamUri", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleIntent(intent: Intent): Boolean {
|
||||
val pigeonIntent = prepareIntentForDelivery(intent)
|
||||
intentReceiver?.sendIntent(pigeonIntent)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun prepareIntentForDelivery(intent: Intent): PigeonIntent {
|
||||
grantUriPermissions(intent)
|
||||
|
||||
if (intent.flags and Intent.FLAG_ACTIVITY_NEW_TASK != 0) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
}
|
||||
|
||||
val notificationApproval = consumeNotificationApproval(intent)
|
||||
val pigeonIntent = convertToPigeonIntent(intent, notificationApproval)
|
||||
intentReceiver?.sendIntent(pigeonIntent)
|
||||
return true
|
||||
return convertToPigeonIntent(intent, notificationApproval)
|
||||
}
|
||||
|
||||
private fun resolveCallerPackage(intent: Intent, notificationApproval: NotificationApproval?): String? {
|
||||
|
||||
+37
@@ -270,6 +270,43 @@ private open class IntentPigeonCodec : StandardMessageCodec() {
|
||||
}
|
||||
}
|
||||
|
||||
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
||||
interface IntentHost {
|
||||
/**
|
||||
* Returns the launch intent that started the activity, if any.
|
||||
* This allows Dart to retrieve an intent that arrived before
|
||||
* IntentEvents.setUp() was called (cold-start deep links).
|
||||
* Returns null if no launch intent is pending.
|
||||
*/
|
||||
fun getInitialIntent(): Intent?
|
||||
|
||||
companion object {
|
||||
/** The codec used by IntentHost. */
|
||||
val codec: MessageCodec<Any?> by lazy {
|
||||
IntentPigeonCodec()
|
||||
}
|
||||
/** Sets up an instance of `IntentHost` to handle messages through the `binaryMessenger`. */
|
||||
@JvmOverloads
|
||||
fun setUp(binaryMessenger: BinaryMessenger, api: IntentHost?, messageChannelSuffix: String = "") {
|
||||
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.simple_intent_receiver.IntentHost.getInitialIntent$separatedMessageChannelSuffix", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { _, reply ->
|
||||
val wrapped: List<Any?> = try {
|
||||
listOf(api.getInitialIntent())
|
||||
} catch (exception: Throwable) {
|
||||
IntentPigeonUtils.wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. */
|
||||
class IntentEvents(private val binaryMessenger: BinaryMessenger, private val messageChannelSuffix: String = "") {
|
||||
companion object {
|
||||
|
||||
@@ -26,11 +26,61 @@ class IntentReceiver extends IntentEvents {
|
||||
final _controller = StreamController<Intent>.broadcast();
|
||||
int? _lastAdded;
|
||||
|
||||
Stream<Intent> get events => _controller.stream;
|
||||
/// Intent events, including the cold-start launch intent for each listener.
|
||||
///
|
||||
/// New-intent callbacks still arrive through the broadcast controller below.
|
||||
/// The initial intent is replayed per listener so existing callers that only
|
||||
/// listen to [events] continue to receive terminated-app launches.
|
||||
Stream<Intent> get events {
|
||||
return Stream.multi((controller) {
|
||||
final subscription = _controller.stream.listen(
|
||||
controller.add,
|
||||
onError: controller.addError,
|
||||
onDone: controller.close,
|
||||
);
|
||||
|
||||
unawaited(
|
||||
initialIntent.then(
|
||||
(intent) {
|
||||
if (intent != null && !controller.isClosed) {
|
||||
controller.add(intent);
|
||||
}
|
||||
},
|
||||
onError: (Object error, StackTrace stackTrace) {
|
||||
if (!controller.isClosed) {
|
||||
controller.addError(error, stackTrace);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
controller.onCancel = subscription.cancel;
|
||||
}, isBroadcast: true);
|
||||
}
|
||||
|
||||
/// The launch intent recovered from the host, if any. Resolves to
|
||||
/// `Future<null>` for instances not constructed via [IntentReceiver.setUp]
|
||||
/// (e.g. test fakes / subclasses), so callers can always `await` without
|
||||
/// guarding for `LateInitializationError`.
|
||||
///
|
||||
/// On cold start the Android plugin sees the launch intent from
|
||||
/// onAttachedToActivity before Dart has registered the Pigeon handler, so it
|
||||
/// caches the value for Dart to recover. The [events] stream already replays
|
||||
/// this value for compatibility; use this future directly only when the
|
||||
/// launch intent needs one-shot handling outside the event stream.
|
||||
///
|
||||
/// Note on rotation: the Android plugin's `pendingInitialIntent` cache
|
||||
/// is intentionally overwritten on configuration change. If the user
|
||||
/// triggers a new deep link via the activity launcher before Dart
|
||||
/// drains the previous initial intent (rare — the future is read on
|
||||
/// IntentReceiver construction, which happens during app bootstrap),
|
||||
/// only the newest intent is delivered.
|
||||
Future<Intent?> initialIntent = Future.value(null);
|
||||
|
||||
@override
|
||||
void onIntentReceived(int sequence, Intent intent) {
|
||||
if (_lastAdded == null || sequence > _lastAdded!) {
|
||||
_lastAdded = sequence;
|
||||
_controller.add(intent);
|
||||
}
|
||||
}
|
||||
@@ -44,6 +94,12 @@ class IntentReceiver extends IntentEvents {
|
||||
binaryMessenger: binaryMessenger,
|
||||
messageChannelSuffix: messageChannelSuffix,
|
||||
);
|
||||
|
||||
final host = IntentHost(
|
||||
binaryMessenger: binaryMessenger,
|
||||
messageChannelSuffix: messageChannelSuffix,
|
||||
);
|
||||
initialIntent = host.getInitialIntent();
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
|
||||
@@ -199,6 +199,43 @@ class _PigeonCodec extends StandardMessageCodec {
|
||||
}
|
||||
}
|
||||
|
||||
class IntentHost {
|
||||
/// Constructor for [IntentHost]. The [binaryMessenger] named argument is
|
||||
/// available for dependency injection. If it is left null, the default
|
||||
/// BinaryMessenger will be used which routes to the host platform.
|
||||
IntentHost({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''})
|
||||
: pigeonVar_binaryMessenger = binaryMessenger,
|
||||
pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
|
||||
final BinaryMessenger? pigeonVar_binaryMessenger;
|
||||
|
||||
static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();
|
||||
|
||||
final String pigeonVar_messageChannelSuffix;
|
||||
|
||||
/// Returns the launch intent that started the activity, if any.
|
||||
/// This allows Dart to retrieve an intent that arrived before
|
||||
/// IntentEvents.setUp() was called (cold-start deep links).
|
||||
/// Returns null if no launch intent is pending.
|
||||
Future<Intent?> getInitialIntent() async {
|
||||
final pigeonVar_channelName = 'dev.flutter.pigeon.simple_intent_receiver.IntentHost.getInitialIntent$pigeonVar_messageChannelSuffix';
|
||||
final pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||
pigeonVar_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: pigeonVar_binaryMessenger,
|
||||
);
|
||||
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);
|
||||
final pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
|
||||
|
||||
final Object? pigeonVar_replyValue = _extractReplyValueOrThrow(
|
||||
pigeonVar_replyList,
|
||||
pigeonVar_channelName,
|
||||
isNullValid: true,
|
||||
)
|
||||
;
|
||||
return pigeonVar_replyValue as Intent?;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class IntentEvents {
|
||||
static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();
|
||||
|
||||
|
||||
@@ -49,6 +49,15 @@ class Intent {
|
||||
dartPackageName: 'simple_intent_receiver',
|
||||
),
|
||||
)
|
||||
@HostApi()
|
||||
abstract class IntentHost {
|
||||
/// Returns the launch intent that started the activity, if any.
|
||||
/// This allows Dart to retrieve an intent that arrived before
|
||||
/// IntentEvents.setUp() was called (cold-start deep links).
|
||||
/// Returns null if no launch intent is pending.
|
||||
Intent? getInitialIntent();
|
||||
}
|
||||
|
||||
@FlutterApi()
|
||||
abstract class IntentEvents {
|
||||
void onIntentReceived(int sequence, Intent intent);
|
||||
|
||||
Reference in New Issue
Block a user