implemented intent receiver
This commit is contained in:
@@ -1,4 +1,35 @@
|
|||||||
include: package:flutter_lints/flutter.yaml
|
# This file configures the static analysis results for your project (errors,
|
||||||
|
# warnings, and lints).
|
||||||
|
#
|
||||||
|
# This enables the 'recommended' set of lints from `package:lints`.
|
||||||
|
# This set helps identify many issues that may lead to problems when running
|
||||||
|
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
|
||||||
|
# style and format.
|
||||||
|
#
|
||||||
|
# If you want a smaller set of lints you can change this to specify
|
||||||
|
# 'package:lints/core.yaml'. These are just the most critical lints
|
||||||
|
# (the recommended set includes the core lints).
|
||||||
|
# The core lints are also what is used by pub.dev for scoring packages.
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
include: package:lint/package.yaml
|
||||||
|
# Uncomment the following section to specify additional rules.
|
||||||
|
|
||||||
|
linter:
|
||||||
|
rules:
|
||||||
|
unawaited_futures: true
|
||||||
|
discarded_futures: true
|
||||||
|
collection_methods_unrelated_type: true
|
||||||
|
|
||||||
|
analyzer:
|
||||||
|
plugins:
|
||||||
|
- custom_lint
|
||||||
|
exclude:
|
||||||
|
- "**.g.dart"
|
||||||
|
- "**.swagger.dart"
|
||||||
|
- "**.freezed.dart"
|
||||||
|
- "**.chopper.dart"
|
||||||
|
# For more information about the core and recommended set of lints, see
|
||||||
|
# https://dart.dev/go/core-lints
|
||||||
|
|
||||||
|
# For additional information about configuring this file, see
|
||||||
# https://dart.dev/guides/language/analysis-options
|
# https://dart.dev/guides/language/analysis-options
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ group = "me.movenext.simple_intent_receiver"
|
|||||||
version = "1.0-SNAPSHOT"
|
version = "1.0-SNAPSHOT"
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = "1.8.22"
|
ext.kotlin_version = "2.1.20"
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath("com.android.tools.build:gradle:8.7.0")
|
classpath("com.android.tools.build:gradle:8.7.3")
|
||||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")
|
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -30,12 +30,12 @@ android {
|
|||||||
compileSdk = 35
|
compileSdk = 35
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility = JavaVersion.VERSION_11
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
targetCompatibility = JavaVersion.VERSION_11
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = JavaVersion.VERSION_11
|
jvmTarget = JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
|
|||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package me.movenext.simple_intent_receiver
|
||||||
|
|
||||||
|
import io.flutter.plugin.common.BinaryMessenger
|
||||||
|
import me.movenext.simple_intent_receiver.pigeons.IntentEvents
|
||||||
|
import me.movenext.simple_intent_receiver.pigeons.Intent as PigeonIntent
|
||||||
|
|
||||||
|
class IntentReceiver(messenger: BinaryMessenger) {
|
||||||
|
private val intentEvents: IntentEvents = IntentEvents(messenger)
|
||||||
|
|
||||||
|
fun sendIntent(timestamp: Long, intent: PigeonIntent) {
|
||||||
|
intentEvents.onIntentReceived(timestamp, intent) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
+73
-21
@@ -1,33 +1,85 @@
|
|||||||
package me.movenext.simple_intent_receiver
|
package me.movenext.simple_intent_receiver
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||||
import io.flutter.plugin.common.MethodCall
|
import io.flutter.embedding.engine.plugins.activity.ActivityAware
|
||||||
import io.flutter.plugin.common.MethodChannel
|
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
|
||||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
import io.flutter.plugin.common.PluginRegistry
|
||||||
import io.flutter.plugin.common.MethodChannel.Result
|
import me.movenext.simple_intent_receiver.pigeons.Intent as PigeonIntent
|
||||||
|
|
||||||
/** SimpleIntentReceiverPlugin */
|
class SimpleIntentReceiverPlugin: FlutterPlugin, ActivityAware, PluginRegistry.NewIntentListener {
|
||||||
class SimpleIntentReceiverPlugin: FlutterPlugin, MethodCallHandler {
|
private lateinit var context: Context
|
||||||
/// The MethodChannel that will the communication between Flutter and native Android
|
private var intentReceiver: IntentReceiver? = null
|
||||||
///
|
private var handledInitialIntent = false
|
||||||
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
|
|
||||||
/// when the Flutter Engine is detached from the Activity
|
|
||||||
private lateinit var channel : MethodChannel
|
|
||||||
|
|
||||||
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
||||||
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "simple_intent_receiver")
|
context = flutterPluginBinding.applicationContext
|
||||||
channel.setMethodCallHandler(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onMethodCall(call: MethodCall, result: Result) {
|
intentReceiver = IntentReceiver(flutterPluginBinding.binaryMessenger)
|
||||||
if (call.method == "getPlatformVersion") {
|
|
||||||
result.success("Android ${android.os.Build.VERSION.RELEASE}")
|
|
||||||
} else {
|
|
||||||
result.notImplemented()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
|
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
|
||||||
channel.setMethodCallHandler(null)
|
intentReceiver = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
|
||||||
|
binding.addOnNewIntentListener(this)
|
||||||
|
|
||||||
|
// Process the initial intent if available
|
||||||
|
binding.activity.intent?.let { intent ->
|
||||||
|
if (!handledInitialIntent) {
|
||||||
|
handleIntent(intent)
|
||||||
|
handledInitialIntent = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDetachedFromActivityForConfigChanges() {
|
||||||
|
// No implementation needed
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
|
||||||
|
binding.addOnNewIntentListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDetachedFromActivity() {
|
||||||
|
// No implementation needed
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNewIntent(intent: Intent): Boolean {
|
||||||
|
return handleIntent(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleIntent(intent: Intent): Boolean {
|
||||||
|
val pigeonIntent = convertToPigeonIntent(intent)
|
||||||
|
intentReceiver?.sendIntent(System.currentTimeMillis(), pigeonIntent)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun convertToPigeonIntent(intent: Intent): PigeonIntent {
|
||||||
|
val action = intent.action
|
||||||
|
val data = intent.dataString
|
||||||
|
val fromPackageName = intent.getPackage()
|
||||||
|
|
||||||
|
// Extract categories
|
||||||
|
val categories = ArrayList<String>()
|
||||||
|
intent.categories?.let {
|
||||||
|
categories.addAll(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract extras
|
||||||
|
val extras = HashMap<String, Any?>()
|
||||||
|
intent.extras?.keySet()?.forEach { key ->
|
||||||
|
extras[key] = intent.extras?.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return PigeonIntent(
|
||||||
|
fromPackageName = fromPackageName,
|
||||||
|
action = action,
|
||||||
|
data = data,
|
||||||
|
categories = categories,
|
||||||
|
extra = extras
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+150
@@ -0,0 +1,150 @@
|
|||||||
|
// Autogenerated from Pigeon (v25.3.1), do not edit directly.
|
||||||
|
// See also: https://pub.dev/packages/pigeon
|
||||||
|
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
|
||||||
|
|
||||||
|
package me.movenext.simple_intent_receiver.pigeons
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import io.flutter.plugin.common.BasicMessageChannel
|
||||||
|
import io.flutter.plugin.common.BinaryMessenger
|
||||||
|
import io.flutter.plugin.common.EventChannel
|
||||||
|
import io.flutter.plugin.common.MessageCodec
|
||||||
|
import io.flutter.plugin.common.StandardMethodCodec
|
||||||
|
import io.flutter.plugin.common.StandardMessageCodec
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
private object IntentPigeonUtils {
|
||||||
|
|
||||||
|
fun createConnectionError(channelName: String): FlutterError {
|
||||||
|
return FlutterError("channel-error", "Unable to establish connection on channel: '$channelName'.", "") }
|
||||||
|
fun deepEquals(a: Any?, b: Any?): Boolean {
|
||||||
|
if (a is ByteArray && b is ByteArray) {
|
||||||
|
return a.contentEquals(b)
|
||||||
|
}
|
||||||
|
if (a is IntArray && b is IntArray) {
|
||||||
|
return a.contentEquals(b)
|
||||||
|
}
|
||||||
|
if (a is LongArray && b is LongArray) {
|
||||||
|
return a.contentEquals(b)
|
||||||
|
}
|
||||||
|
if (a is DoubleArray && b is DoubleArray) {
|
||||||
|
return a.contentEquals(b)
|
||||||
|
}
|
||||||
|
if (a is Array<*> && b is Array<*>) {
|
||||||
|
return a.size == b.size &&
|
||||||
|
a.indices.all{ deepEquals(a[it], b[it]) }
|
||||||
|
}
|
||||||
|
if (a is List<*> && b is List<*>) {
|
||||||
|
return a.size == b.size &&
|
||||||
|
a.indices.all{ deepEquals(a[it], b[it]) }
|
||||||
|
}
|
||||||
|
if (a is Map<*, *> && b is Map<*, *>) {
|
||||||
|
return a.size == b.size && a.all {
|
||||||
|
(b as Map<Any?, Any?>).containsKey(it.key) &&
|
||||||
|
deepEquals(it.value, b[it.key])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return a == b
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error class for passing custom error details to Flutter via a thrown PlatformException.
|
||||||
|
* @property code The error code.
|
||||||
|
* @property message The error message.
|
||||||
|
* @property details The error details. Must be a datatype supported by the api codec.
|
||||||
|
*/
|
||||||
|
class FlutterError (
|
||||||
|
val code: String,
|
||||||
|
override val message: String? = null,
|
||||||
|
val details: Any? = null
|
||||||
|
) : Throwable()
|
||||||
|
|
||||||
|
/** Generated class from Pigeon that represents data sent in messages. */
|
||||||
|
data class Intent (
|
||||||
|
val fromPackageName: String? = null,
|
||||||
|
val action: String? = null,
|
||||||
|
val data: String? = null,
|
||||||
|
val categories: List<String>,
|
||||||
|
val extra: Map<String, Any?>
|
||||||
|
)
|
||||||
|
{
|
||||||
|
companion object {
|
||||||
|
fun fromList(pigeonVar_list: List<Any?>): Intent {
|
||||||
|
val fromPackageName = pigeonVar_list[0] as String?
|
||||||
|
val action = pigeonVar_list[1] as String?
|
||||||
|
val data = pigeonVar_list[2] as String?
|
||||||
|
val categories = pigeonVar_list[3] as List<String>
|
||||||
|
val extra = pigeonVar_list[4] as Map<String, Any?>
|
||||||
|
return Intent(fromPackageName, action, data, categories, extra)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun toList(): List<Any?> {
|
||||||
|
return listOf(
|
||||||
|
fromPackageName,
|
||||||
|
action,
|
||||||
|
data,
|
||||||
|
categories,
|
||||||
|
extra,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (other !is Intent) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (this === other) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return IntentPigeonUtils.deepEquals(toList(), other.toList()) }
|
||||||
|
|
||||||
|
override fun hashCode(): Int = toList().hashCode()
|
||||||
|
}
|
||||||
|
private open class IntentPigeonCodec : StandardMessageCodec() {
|
||||||
|
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
|
||||||
|
return when (type) {
|
||||||
|
129.toByte() -> {
|
||||||
|
return (readValue(buffer) as? List<Any?>)?.let {
|
||||||
|
Intent.fromList(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> super.readValueOfType(type, buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
|
||||||
|
when (value) {
|
||||||
|
is Intent -> {
|
||||||
|
stream.write(129)
|
||||||
|
writeValue(stream, value.toList())
|
||||||
|
}
|
||||||
|
else -> super.writeValue(stream, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 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 {
|
||||||
|
/** The codec used by IntentEvents. */
|
||||||
|
val codec: MessageCodec<Any?> by lazy {
|
||||||
|
IntentPigeonCodec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun onIntentReceived(timestampArg: Long, intentArg: Intent, callback: (Result<Unit>) -> Unit)
|
||||||
|
{
|
||||||
|
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
|
||||||
|
val channelName = "dev.flutter.pigeon.simple_intent_receiver.IntentEvents.onIntentReceived$separatedMessageChannelSuffix"
|
||||||
|
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
|
||||||
|
channel.send(listOf(timestampArg, intentArg)) {
|
||||||
|
if (it is List<*>) {
|
||||||
|
if (it.size > 1) {
|
||||||
|
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
|
||||||
|
} else {
|
||||||
|
callback(Result.success(Unit))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
callback(Result.failure(IntentPigeonUtils.createConnectionError(channelName)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,6 +23,55 @@
|
|||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
<category android:name="android.intent.category.APP_BROWSER"/>
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:mimeType="text/*" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<!-- Required exactly like this to appear in both default app choosers and when explicitly browsing -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="http" />
|
||||||
|
<data android:scheme="https" />
|
||||||
|
</intent-filter>
|
||||||
|
<!-- like above but specifying additional schemes. Not qualified for app chooser. -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="http" />
|
||||||
|
<data android:scheme="https" />
|
||||||
|
<data android:mimeType="text/html" />
|
||||||
|
<data android:mimeType="text/plain" />
|
||||||
|
<data android:mimeType="application/xhtml+xml" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:mimeType="application/pdf" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.WEB_SEARCH" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<!-- Don't delete the meta-data below.
|
<!-- Don't delete the meta-data below.
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
// This is a basic Flutter integration test.
|
|
||||||
//
|
|
||||||
// Since integration tests run in a full Flutter application, they can interact
|
|
||||||
// with the host side of a plugin implementation, unlike Dart unit tests.
|
|
||||||
//
|
|
||||||
// For more information about Flutter integration tests, please see
|
|
||||||
// https://flutter.dev/to/integration-testing
|
|
||||||
|
|
||||||
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
import 'package:integration_test/integration_test.dart';
|
|
||||||
|
|
||||||
import 'package:simple_intent_receiver/simple_intent_receiver.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
|
||||||
|
|
||||||
testWidgets('getPlatformVersion test', (WidgetTester tester) async {
|
|
||||||
final SimpleIntentReceiver plugin = SimpleIntentReceiver();
|
|
||||||
final String? version = await plugin.getPlatformVersion();
|
|
||||||
// The version string depends on the host platform running the test, so
|
|
||||||
// just assert that some non-empty string is returned.
|
|
||||||
expect(version?.isNotEmpty, true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,63 +1,28 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'dart:async';
|
import 'package:logger/logger.dart';
|
||||||
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:simple_intent_receiver/simple_intent_receiver.dart';
|
import 'package:simple_intent_receiver/simple_intent_receiver.dart';
|
||||||
|
|
||||||
|
late final IntentReceiver receiver;
|
||||||
|
|
||||||
|
final logger = Logger();
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
receiver = IntentReceiver.setUp();
|
||||||
|
|
||||||
|
receiver.events.listen((data) {
|
||||||
|
logger.d(data.action);
|
||||||
|
});
|
||||||
|
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatefulWidget {
|
class MyApp extends StatelessWidget {
|
||||||
const MyApp({super.key});
|
const MyApp({super.key});
|
||||||
|
|
||||||
@override
|
|
||||||
State<MyApp> createState() => _MyAppState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MyAppState extends State<MyApp> {
|
|
||||||
String _platformVersion = 'Unknown';
|
|
||||||
final _simpleIntentReceiverPlugin = SimpleIntentReceiver();
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
initPlatformState();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Platform messages are asynchronous, so we initialize in an async method.
|
|
||||||
Future<void> initPlatformState() async {
|
|
||||||
String platformVersion;
|
|
||||||
// Platform messages may fail, so we use a try/catch PlatformException.
|
|
||||||
// We also handle the message potentially returning null.
|
|
||||||
try {
|
|
||||||
platformVersion =
|
|
||||||
await _simpleIntentReceiverPlugin.getPlatformVersion() ?? 'Unknown platform version';
|
|
||||||
} on PlatformException {
|
|
||||||
platformVersion = 'Failed to get platform version.';
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the widget was removed from the tree while the asynchronous platform
|
|
||||||
// message was in flight, we want to discard the reply rather than calling
|
|
||||||
// setState to update our non-existent appearance.
|
|
||||||
if (!mounted) return;
|
|
||||||
|
|
||||||
setState(() {
|
|
||||||
_platformVersion = platformVersion;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp();
|
||||||
home: Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: const Text('Plugin example app'),
|
|
||||||
),
|
|
||||||
body: Center(
|
|
||||||
child: Text('Running on: $_platformVersion\n'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ dependencies:
|
|||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: ^1.0.8
|
cupertino_icons: ^1.0.8
|
||||||
|
logger: ^2.5.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
integration_test:
|
integration_test:
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
// This is a basic Flutter widget test.
|
|
||||||
//
|
|
||||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
|
||||||
// utility in the flutter_test package. For example, you can send tap and scroll
|
|
||||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
|
||||||
// tree, read text, and verify that the values of widget properties are correct.
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
|
|
||||||
import 'package:simple_intent_receiver_example/main.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
testWidgets('Verify Platform version', (WidgetTester tester) async {
|
|
||||||
// Build our app and trigger a frame.
|
|
||||||
await tester.pumpWidget(const MyApp());
|
|
||||||
|
|
||||||
// Verify that platform version is retrieved.
|
|
||||||
expect(
|
|
||||||
find.byWidgetPredicate(
|
|
||||||
(Widget widget) => widget is Text &&
|
|
||||||
widget.data!.startsWith('Running on:'),
|
|
||||||
),
|
|
||||||
findsOneWidget,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,2 @@
|
|||||||
|
export 'src/intent_receiver.dart';
|
||||||
import 'simple_intent_receiver_platform_interface.dart';
|
export 'src/pigeons/intent.g.dart' show Intent;
|
||||||
|
|
||||||
class SimpleIntentReceiver {
|
|
||||||
Future<String?> getPlatformVersion() {
|
|
||||||
return SimpleIntentReceiverPlatform.instance.getPlatformVersion();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
|
|
||||||
import 'simple_intent_receiver_platform_interface.dart';
|
|
||||||
|
|
||||||
/// An implementation of [SimpleIntentReceiverPlatform] that uses method channels.
|
|
||||||
class MethodChannelSimpleIntentReceiver extends SimpleIntentReceiverPlatform {
|
|
||||||
/// The method channel used to interact with the native platform.
|
|
||||||
@visibleForTesting
|
|
||||||
final methodChannel = const MethodChannel('simple_intent_receiver');
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<String?> getPlatformVersion() async {
|
|
||||||
final version = await methodChannel.invokeMethod<String>('getPlatformVersion');
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
|
||||||
|
|
||||||
import 'simple_intent_receiver_method_channel.dart';
|
|
||||||
|
|
||||||
abstract class SimpleIntentReceiverPlatform extends PlatformInterface {
|
|
||||||
/// Constructs a SimpleIntentReceiverPlatform.
|
|
||||||
SimpleIntentReceiverPlatform() : super(token: _token);
|
|
||||||
|
|
||||||
static final Object _token = Object();
|
|
||||||
|
|
||||||
static SimpleIntentReceiverPlatform _instance = MethodChannelSimpleIntentReceiver();
|
|
||||||
|
|
||||||
/// The default instance of [SimpleIntentReceiverPlatform] to use.
|
|
||||||
///
|
|
||||||
/// Defaults to [MethodChannelSimpleIntentReceiver].
|
|
||||||
static SimpleIntentReceiverPlatform get instance => _instance;
|
|
||||||
|
|
||||||
/// Platform-specific implementations should set this with their own
|
|
||||||
/// platform-specific class that extends [SimpleIntentReceiverPlatform] when
|
|
||||||
/// they register themselves.
|
|
||||||
static set instance(SimpleIntentReceiverPlatform instance) {
|
|
||||||
PlatformInterface.verifyToken(instance, _token);
|
|
||||||
_instance = instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String?> getPlatformVersion() {
|
|
||||||
throw UnimplementedError('platformVersion() has not been implemented.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:simple_intent_receiver/src/pigeons/intent.g.dart';
|
||||||
|
|
||||||
|
class IntentReceiver extends IntentEvents {
|
||||||
|
final _controller = StreamController<Intent>();
|
||||||
|
int? _lastAdded;
|
||||||
|
|
||||||
|
Stream<Intent> get events => _controller.stream;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onIntentReceived(int timestamp, Intent intent) {
|
||||||
|
if (_lastAdded == null || timestamp > _lastAdded!) {
|
||||||
|
_controller.add(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IntentReceiver.setUp({
|
||||||
|
BinaryMessenger? binaryMessenger,
|
||||||
|
String messageChannelSuffix = '',
|
||||||
|
}) {
|
||||||
|
IntentEvents.setUp(
|
||||||
|
this,
|
||||||
|
binaryMessenger: binaryMessenger,
|
||||||
|
messageChannelSuffix: messageChannelSuffix,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> dispose() async {
|
||||||
|
await _controller.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
// Autogenerated from Pigeon (v25.3.1), do not edit directly.
|
||||||
|
// 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
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
List<Object?> wrapResponse({Object? result, PlatformException? error, bool empty = false}) {
|
||||||
|
if (empty) {
|
||||||
|
return <Object?>[];
|
||||||
|
}
|
||||||
|
if (error == null) {
|
||||||
|
return <Object?>[result];
|
||||||
|
}
|
||||||
|
return <Object?>[error.code, error.message, error.details];
|
||||||
|
}
|
||||||
|
bool _deepEquals(Object? a, Object? b) {
|
||||||
|
if (a is List && b is List) {
|
||||||
|
return a.length == b.length &&
|
||||||
|
a.indexed
|
||||||
|
.every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1]));
|
||||||
|
}
|
||||||
|
if (a is Map && b is Map) {
|
||||||
|
return a.length == b.length && a.entries.every((MapEntry<Object?, Object?> entry) =>
|
||||||
|
(b as Map<Object?, Object?>).containsKey(entry.key) &&
|
||||||
|
_deepEquals(entry.value, b[entry.key]));
|
||||||
|
}
|
||||||
|
return a == b;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Intent {
|
||||||
|
Intent({
|
||||||
|
this.fromPackageName,
|
||||||
|
this.action,
|
||||||
|
this.data,
|
||||||
|
required this.categories,
|
||||||
|
required this.extra,
|
||||||
|
});
|
||||||
|
|
||||||
|
String? fromPackageName;
|
||||||
|
|
||||||
|
String? action;
|
||||||
|
|
||||||
|
String? data;
|
||||||
|
|
||||||
|
List<String> categories;
|
||||||
|
|
||||||
|
Map<String, Object?> extra;
|
||||||
|
|
||||||
|
List<Object?> _toList() {
|
||||||
|
return <Object?>[
|
||||||
|
fromPackageName,
|
||||||
|
action,
|
||||||
|
data,
|
||||||
|
categories,
|
||||||
|
extra,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
Object encode() {
|
||||||
|
return _toList(); }
|
||||||
|
|
||||||
|
static Intent decode(Object result) {
|
||||||
|
result as List<Object?>;
|
||||||
|
return Intent(
|
||||||
|
fromPackageName: result[0] as String?,
|
||||||
|
action: result[1] as String?,
|
||||||
|
data: result[2] as String?,
|
||||||
|
categories: (result[3] as List<Object?>?)!.cast<String>(),
|
||||||
|
extra: (result[4] as Map<Object?, Object?>?)!.cast<String, Object?>(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
// ignore: avoid_equals_and_hash_code_on_mutable_classes
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (other is! Intent || other.runtimeType != runtimeType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return _deepEquals(encode(), other.encode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
// ignore: avoid_equals_and_hash_code_on_mutable_classes
|
||||||
|
int get hashCode => Object.hashAll(_toList())
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class _PigeonCodec extends StandardMessageCodec {
|
||||||
|
const _PigeonCodec();
|
||||||
|
@override
|
||||||
|
void writeValue(WriteBuffer buffer, Object? value) {
|
||||||
|
if (value is int) {
|
||||||
|
buffer.putUint8(4);
|
||||||
|
buffer.putInt64(value);
|
||||||
|
} else if (value is Intent) {
|
||||||
|
buffer.putUint8(129);
|
||||||
|
writeValue(buffer, value.encode());
|
||||||
|
} else {
|
||||||
|
super.writeValue(buffer, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object? readValueOfType(int type, ReadBuffer buffer) {
|
||||||
|
switch (type) {
|
||||||
|
case 129:
|
||||||
|
return Intent.decode(readValue(buffer)!);
|
||||||
|
default:
|
||||||
|
return super.readValueOfType(type, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class IntentEvents {
|
||||||
|
static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();
|
||||||
|
|
||||||
|
void onIntentReceived(int timestamp, Intent intent);
|
||||||
|
|
||||||
|
static void setUp(IntentEvents? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) {
|
||||||
|
messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
|
||||||
|
{
|
||||||
|
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.simple_intent_receiver.IntentEvents.onIntentReceived$messageChannelSuffix', pigeonChannelCodec,
|
||||||
|
binaryMessenger: binaryMessenger);
|
||||||
|
if (api == null) {
|
||||||
|
pigeonVar_channel.setMessageHandler(null);
|
||||||
|
} else {
|
||||||
|
pigeonVar_channel.setMessageHandler((Object? message) async {
|
||||||
|
assert(message != null,
|
||||||
|
'Argument for dev.flutter.pigeon.simple_intent_receiver.IntentEvents.onIntentReceived was null.');
|
||||||
|
final List<Object?> args = (message as List<Object?>?)!;
|
||||||
|
final int? arg_timestamp = (args[0] as int?);
|
||||||
|
assert(arg_timestamp != null,
|
||||||
|
'Argument for dev.flutter.pigeon.simple_intent_receiver.IntentEvents.onIntentReceived was null, expected non-null int.');
|
||||||
|
final Intent? arg_intent = (args[1] as Intent?);
|
||||||
|
assert(arg_intent != null,
|
||||||
|
'Argument for dev.flutter.pigeon.simple_intent_receiver.IntentEvents.onIntentReceived was null, expected non-null Intent.');
|
||||||
|
try {
|
||||||
|
api.onIntentReceived(arg_timestamp!, arg_intent!);
|
||||||
|
return wrapResponse(empty: true);
|
||||||
|
} on PlatformException catch (e) {
|
||||||
|
return wrapResponse(error: e);
|
||||||
|
} catch (e) {
|
||||||
|
return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import 'package:pigeon/pigeon.dart';
|
||||||
|
|
||||||
|
class Intent {
|
||||||
|
final String? fromPackageName;
|
||||||
|
final String? action;
|
||||||
|
final String? data;
|
||||||
|
final List<String> categories;
|
||||||
|
final Map<String, Object?> extra;
|
||||||
|
|
||||||
|
Intent({
|
||||||
|
required this.fromPackageName,
|
||||||
|
required this.action,
|
||||||
|
required this.data,
|
||||||
|
required this.categories,
|
||||||
|
required this.extra,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigurePigeon(
|
||||||
|
PigeonOptions(
|
||||||
|
dartOut: 'lib/src/pigeons/intent.g.dart',
|
||||||
|
dartOptions: DartOptions(),
|
||||||
|
kotlinOut:
|
||||||
|
'android/src/main/kotlin/me/movenext/simple_intent_receiver/pigeons/Intent.g.kt',
|
||||||
|
kotlinOptions: KotlinOptions(
|
||||||
|
package: 'me.movenext.simple_intent_receiver.pigeons',
|
||||||
|
),
|
||||||
|
dartPackageName: 'simple_intent_receiver',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
@FlutterApi()
|
||||||
|
abstract class IntentEvents {
|
||||||
|
void onIntentReceived(int timestamp, Intent intent);
|
||||||
|
}
|
||||||
@@ -10,12 +10,12 @@ environment:
|
|||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
plugin_platform_interface: ^2.0.2
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_lints: ^5.0.0
|
lint: ^2.8.0
|
||||||
|
pigeon: ^25.3.1
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
import 'package:simple_intent_receiver/simple_intent_receiver_method_channel.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
TestWidgetsFlutterBinding.ensureInitialized();
|
|
||||||
|
|
||||||
MethodChannelSimpleIntentReceiver platform = MethodChannelSimpleIntentReceiver();
|
|
||||||
const MethodChannel channel = MethodChannel('simple_intent_receiver');
|
|
||||||
|
|
||||||
setUp(() {
|
|
||||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
|
|
||||||
channel,
|
|
||||||
(MethodCall methodCall) async {
|
|
||||||
return '42';
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
tearDown(() {
|
|
||||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('getPlatformVersion', () async {
|
|
||||||
expect(await platform.getPlatformVersion(), '42');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
import 'package:simple_intent_receiver/simple_intent_receiver.dart';
|
|
||||||
import 'package:simple_intent_receiver/simple_intent_receiver_platform_interface.dart';
|
|
||||||
import 'package:simple_intent_receiver/simple_intent_receiver_method_channel.dart';
|
|
||||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
|
||||||
|
|
||||||
class MockSimpleIntentReceiverPlatform
|
|
||||||
with MockPlatformInterfaceMixin
|
|
||||||
implements SimpleIntentReceiverPlatform {
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<String?> getPlatformVersion() => Future.value('42');
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
final SimpleIntentReceiverPlatform initialPlatform = SimpleIntentReceiverPlatform.instance;
|
|
||||||
|
|
||||||
test('$MethodChannelSimpleIntentReceiver is the default instance', () {
|
|
||||||
expect(initialPlatform, isInstanceOf<MethodChannelSimpleIntentReceiver>());
|
|
||||||
});
|
|
||||||
|
|
||||||
test('getPlatformVersion', () async {
|
|
||||||
SimpleIntentReceiver simpleIntentReceiverPlugin = SimpleIntentReceiver();
|
|
||||||
MockSimpleIntentReceiverPlatform fakePlatform = MockSimpleIntentReceiverPlatform();
|
|
||||||
SimpleIntentReceiverPlatform.instance = fakePlatform;
|
|
||||||
|
|
||||||
expect(await simpleIntentReceiverPlugin.getPlatformVersion(), '42');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user