diff --git a/android/app/build.gradle b/android/app/build.gradle index 348262e..bbe3f74 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -23,6 +23,10 @@ if (flutterVersionName == null) { flutterVersionName = "1.0" } +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' + android { namespace = "org.rocstreaming.rocdroid" compileSdk = flutter.compileSdkVersion @@ -33,6 +37,10 @@ android { targetCompatibility = JavaVersion.VERSION_1_8 } + kotlinOptions { + jvmTarget = '1.8' + } + defaultConfig { applicationId = "org.rocstreaming.rocdroid" // You can update the following values to match your application needs. @@ -41,6 +49,7 @@ android { targetSdk = flutter.targetSdkVersion versionCode = flutterVersionCode.toInteger() versionName = flutterVersionName + minSdkVersion = 26 } applicationVariants.all { variant -> @@ -61,3 +70,16 @@ android { flutter { source = "../.." } + +dependencies { + implementation 'org.roc-streaming.roctoolkit:roc-android:0.2.1' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.core:core-ktx:1.3.0' + implementation 'androidx.activity:activity-ktx:1.2.4' + implementation 'androidx.fragment:fragment-ktx:1.3.6' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation 'com.google.android.material:material:1.3.0' + implementation 'androidx.viewpager2:viewpager2:1.0.0' + implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.25' +} diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index cad64e5..c792549 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,13 @@ - + + + + + + + { + return listOf(result) +} + +private fun wrapError(exception: Throwable): List { + return if (exception is FlutterError) { + listOf( + exception.code, + exception.message, + exception.details + ) + } else { + listOf( + exception.javaClass.simpleName, + exception.toString(), + "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception) + ) + } +} + +/** + * 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() +private object ConnectorPigeonCodec : StandardMessageCodec() { + override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { + return super.readValueOfType(type, buffer) + } + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + super.writeValue(stream, value) + } +} + +/** + * ???. + * + * Generated interface from Pigeon that represents a handler of messages from Flutter. + */ +interface Backend { + fun startReceiver() + fun stopReceiver() + fun isReceiverAlive(): Boolean + fun startSender(ip: String) + fun stopSender() + fun isSenderAlive(): Boolean + + companion object { + /** The codec used by Backend. */ + val codec: MessageCodec by lazy { + ConnectorPigeonCodec + } + /** Sets up an instance of `Backend` to handle messages through the `binaryMessenger`. */ + @JvmOverloads + fun setUp(binaryMessenger: BinaryMessenger, api: Backend?, messageChannelSuffix: String = "") { + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.roc_droid_package.Backend.startReceiver$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.startReceiver() + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.roc_droid_package.Backend.stopReceiver$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.stopReceiver() + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.roc_droid_package.Backend.isReceiverAlive$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + listOf(api.isReceiverAlive()) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.roc_droid_package.Backend.startSender$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val ipArg = args[0] as String + val wrapped: List = try { + api.startSender(ipArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.roc_droid_package.Backend.stopSender$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.stopSender() + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.roc_droid_package.Backend.isSenderAlive$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + listOf(api.isSenderAlive()) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + } + } + + fun getHostLanguage(): String +} diff --git a/android/app/src/main/kotlin/org/rocstreaming/connector/Connector.kt b/android/app/src/main/kotlin/org/rocstreaming/connector/Connector.kt new file mode 100644 index 0000000..4db8727 --- /dev/null +++ b/android/app/src/main/kotlin/org/rocstreaming/connector/Connector.kt @@ -0,0 +1,36 @@ +package org.rocstreaming.connector + +import Backend +import org.rocstreaming.service.SenderReceiverService + +private class Connector : Backend { + override fun getHostLanguage(): String { + return "Kotlin" + } + + private var senderReceiverService: SenderReceiverService = SenderReceiverService() + + override fun startReceiver() { + senderReceiverService.startReceiver() + } + + override fun stopReceiver() { + senderReceiverService.stopReceiver() + } + + override fun isReceiverAlive(): Boolean { + return senderReceiverService.isReceiverAlive() + } + + override fun startSender(ip: String) { + senderReceiverService.startSender(ip, null) + } + + override fun stopSender() { + senderReceiverService.stopSender() + } + + override fun isSenderAlive(): Boolean { + return senderReceiverService.isSenderAlive() + } +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/org/rocstreaming/rocdroid/MainActivity.kt b/android/app/src/main/kotlin/org/rocstreaming/rocdroid/MainActivity.kt index 5be9115..5ed1fe4 100644 --- a/android/app/src/main/kotlin/org/rocstreaming/rocdroid/MainActivity.kt +++ b/android/app/src/main/kotlin/org/rocstreaming/rocdroid/MainActivity.kt @@ -1,5 +1,10 @@ package org.rocstreaming.rocdroid import io.flutter.embedding.android.FlutterActivity +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.embedding.engine.plugins.FlutterPlugin -class MainActivity: FlutterActivity() +class MainActivity: FlutterActivity(){ + override fun configureFlutterEngine(flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) +}} diff --git a/_temp_storage/app/src/main/java/org/rocstreaming/rocdroid/SenderReceiverService.kt b/android/app/src/main/kotlin/org/rocstreaming/service/SenderReceiverService.kt similarity index 99% rename from _temp_storage/app/src/main/java/org/rocstreaming/rocdroid/SenderReceiverService.kt rename to android/app/src/main/kotlin/org/rocstreaming/service/SenderReceiverService.kt index 2f5be92..b8ffec8 100644 --- a/_temp_storage/app/src/main/java/org/rocstreaming/rocdroid/SenderReceiverService.kt +++ b/android/app/src/main/kotlin/org/rocstreaming/service/SenderReceiverService.kt @@ -1,4 +1,4 @@ -package org.rocstreaming.rocdroid +package org.rocstreaming.service import android.app.Notification import android.app.NotificationChannel @@ -23,6 +23,8 @@ import android.os.IBinder import android.util.Log import androidx.annotation.RequiresApi import androidx.appcompat.app.AlertDialog +import org.rocstreaming.rocdroid.MainActivity +import org.rocstreaming.rocdroid.R import org.rocstreaming.roctoolkit.ChannelSet import org.rocstreaming.roctoolkit.ClockSource import org.rocstreaming.roctoolkit.Endpoint diff --git a/android/app/src/main/res/drawable-hdpi/ic_notification.png b/android/app/src/main/res/drawable-hdpi/ic_notification.png new file mode 100644 index 0000000..a0c06dc Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/ic_notification.png differ diff --git a/android/app/src/main/res/drawable-mdpi/ic_notification.png b/android/app/src/main/res/drawable-mdpi/ic_notification.png new file mode 100644 index 0000000..804fc8b Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/ic_notification.png differ diff --git a/android/app/src/main/res/drawable-night-xhdpi/ic_notification.png b/android/app/src/main/res/drawable-night-xhdpi/ic_notification.png new file mode 100644 index 0000000..b92da3e Binary files /dev/null and b/android/app/src/main/res/drawable-night-xhdpi/ic_notification.png differ diff --git a/android/app/src/main/res/drawable-night-xxhdpi/ic_notification.png b/android/app/src/main/res/drawable-night-xxhdpi/ic_notification.png new file mode 100644 index 0000000..b48aa1b Binary files /dev/null and b/android/app/src/main/res/drawable-night-xxhdpi/ic_notification.png differ diff --git a/android/app/src/main/res/drawable-night-xxxhdpi/ic_notification.png b/android/app/src/main/res/drawable-night-xxxhdpi/ic_notification.png new file mode 100644 index 0000000..867045b Binary files /dev/null and b/android/app/src/main/res/drawable-night-xxxhdpi/ic_notification.png differ diff --git a/android/app/src/main/res/drawable/ic_stop.xml b/android/app/src/main/res/drawable/ic_stop.xml new file mode 100644 index 0000000..d099f1a --- /dev/null +++ b/android/app/src/main/res/drawable/ic_stop.xml @@ -0,0 +1,10 @@ + + + diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..63d4c2f --- /dev/null +++ b/android/app/src/main/res/values/strings.xml @@ -0,0 +1,58 @@ + + Roc Droid + + Receiver + Start receiver + Stop receiver + + Sender + Receiver IP + 192.168.0.1 + Start sender + Stop sender + Invalid IP address + The receiver IP address looks invalid. + Mic permission + The app needs permission to record audio from your phone mic. + Thanks! The sender will now start. + + OK + + Sender and Receiver foreground service + Receiver running + Sender running + Sender and Receiver running + Neither Sender or Receiver running + Roc State + Stop Sender + Stop Receiver + + 1. Start sender on the remote device + 2. Use one of IP addresses of this device as the remote on the sender + 5. Start receiver on this device + + 1. Start receiver on the remote device + 4. Put IP address of the remote receiver device below + 5. Choose source to capture audio from + Currently playing apps + Microphone + 6. Start sender on this device + Source to capture audio from + + %s. Use this port for source stream + %s. Use this port for repair stream + About + Source Code + Bug Tracker + Contributors + Mozilla Public License 2.0 + + https://github.com/roc-streaming/roc-droid + https://github.com/roc-streaming/roc-droid/issues + https://github.com/roc-streaming/roc-droid/graphs/contributors + https://github.com/roc-streaming/roc-droid/blob/main/LICENSE + App Logo + Copy + Open dropdown + Roc Droid receiver + diff --git a/android/build.gradle b/android/build.gradle index d2ffbff..9c6a893 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,6 +1,20 @@ +buildscript { + ext.kotlin_version = '1.7.21' + + repositories { + google() + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:7.4.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + allprojects { repositories { google() + jcenter() mavenCentral() } } @@ -13,6 +27,14 @@ subprojects { project.evaluationDependsOn(":app") } +subprojects { + beforeEvaluate { project -> + if (project.name == "flutter_localization") { + project.buildscript.dependencies.classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlin_version}" + } + } +} + tasks.register("clean", Delete) { delete rootProject.buildDir } diff --git a/android/gradle.properties b/android/gradle.properties index 3b5b324..958b7f2 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,3 +1,4 @@ org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true android.enableJetifier=true +minSdkVersion=26 diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index e1ca574..5af0f53 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-all.zip diff --git a/dodo.py b/dodo.py index e20fe16..a6bf591 100644 --- a/dodo.py +++ b/dodo.py @@ -56,20 +56,37 @@ def task_wipe(): # doit gen def task_gen(): - """run flutter build_runner code generation""" + """run all flutter code generation""" + return { + 'actions': [get_action(task_gen_model()), + get_action(task_gen_agent())], + 'title': title_with_actions, + } + +# doit gen_model +def task_gen_model(): + """run flutter build_runner Model code generation""" return { 'actions': ['dart run build_runner build --delete-conflicting-outputs'], 'title': title_with_actions, } -# doit gen_watch -def task_gen_watch(): - """run flutter build_runner code watch generation""" +# doit gen_model_watch +def task_gen_model_watch(): + """run flutter build_runner Moddel code watch generation""" return { 'actions': [LongRunning('dart run build_runner watch --delete-conflicting-outputs')], 'title': title_with_actions, } +# doit gen_agent +def task_gen_agent(): + """run flutter pigeon Agent code generation""" + return { + 'actions': ['dart run pigeon --input lib/src/agent/ibackend.dart'], + 'title': title_with_actions, + } + # doit icons def task_icons(): """run flutter icons generation (flutter_launcher_icons)""" @@ -85,3 +102,12 @@ def task_splash(): 'actions': ['dart run flutter_native_splash:create '], 'title': title_with_actions, } + +# Get first doit method 'actions' value +def get_action(dic): + + for key, value in dic.items(): + if key == 'actions': + return value[0] + + return "action doesn't exist" diff --git a/lib/main.dart b/lib/main.dart index 0bd0150..5e535a4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,5 +5,6 @@ import 'src/model.dart'; import 'src/ui.dart'; void main() { + WidgetsFlutterBinding.ensureInitialized(); runApp(AppRoot(ModelRoot())); } diff --git a/lib/src/agent.dart b/lib/src/agent.dart index d122d0e..c136d93 100644 --- a/lib/src/agent.dart +++ b/lib/src/agent.dart @@ -1,3 +1,2 @@ /// Agent export definition. -export 'agent/android_service.dart'; -export 'agent/desktop_service.dart'; +export 'agent/backend.dart'; diff --git a/lib/src/agent/android_backend.dart b/lib/src/agent/android_backend.dart new file mode 100644 index 0000000..2c05764 --- /dev/null +++ b/lib/src/agent/android_backend.dart @@ -0,0 +1,36 @@ +import 'backend.g.dart'; + +/// ???. +class AndroidBackend implements Backend { + final Backend _backend = Backend(); + + @override + Future isReceiverAlive() async { + return await _backend.isReceiverAlive(); + } + + @override + Future isSenderAlive() async { + return await _backend.isSenderAlive(); + } + + @override + Future startReceiver() async { + await _backend.startReceiver(); + } + + @override + Future startSender(String ip) async { + await _backend.startSender(ip); + } + + @override + Future stopReceiver() async { + await _backend.stopReceiver(); + } + + @override + Future stopSender() async { + await _backend.stopSender(); + } +} diff --git a/lib/src/agent/android_service.dart b/lib/src/agent/android_service.dart deleted file mode 100644 index c44f192..0000000 --- a/lib/src/agent/android_service.dart +++ /dev/null @@ -1,4 +0,0 @@ -/// Implementation of the Agent Android service class. -class AndroidService { - // TODO - implement this class -} diff --git a/lib/src/agent/backend.dart b/lib/src/agent/backend.dart new file mode 100644 index 0000000..3d60882 --- /dev/null +++ b/lib/src/agent/backend.dart @@ -0,0 +1,32 @@ +import 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon(PigeonOptions( + dartOut: 'lib/src/agent/backend.g.dart', + dartOptions: DartOptions(), + kotlinOut: + 'android/app/src/main/kotlin/org/rocstreaming/connector/Connector.g.kt', + kotlinOptions: KotlinOptions(), + dartPackageName: 'roc_droid_package', +)) + +/// ???. +@HostApi() +abstract class Backend { + void startReceiver(); + + void stopReceiver(); + + bool isReceiverAlive(); + + void startSender(String ip); + + void stopSender(); + + bool isSenderAlive(); +} + +// /// ???. +// @FlutterApi() +// abstract class FlutterHandler { +// void textChanged(String text); +// } diff --git a/lib/src/agent/backend.g.dart b/lib/src/agent/backend.g.dart new file mode 100644 index 0000000..e6a8f14 --- /dev/null +++ b/lib/src/agent/backend.g.dart @@ -0,0 +1,190 @@ +// Autogenerated from Pigeon (v21.1.0), 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'; + +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); +} + +/// ???. +class Backend { + /// Constructor for [Backend]. 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. + Backend({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : __pigeon_binaryMessenger = binaryMessenger, + __pigeon_messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? __pigeon_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + final String __pigeon_messageChannelSuffix; + + Future startReceiver() async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.roc_droid_package.Backend.startReceiver$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future stopReceiver() async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.roc_droid_package.Backend.stopReceiver$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future isReceiverAlive() async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.roc_droid_package.Backend.isReceiverAlive$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as bool?)!; + } + } + + Future startSender(String ip) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.roc_droid_package.Backend.startSender$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([ip]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future stopSender() async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.roc_droid_package.Backend.stopSender$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future isSenderAlive() async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.roc_droid_package.Backend.isSenderAlive$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as bool?)!; + } + } +} diff --git a/lib/src/agent/desktop_service.dart b/lib/src/agent/desktop_service.dart deleted file mode 100644 index 5998656..0000000 --- a/lib/src/agent/desktop_service.dart +++ /dev/null @@ -1,4 +0,0 @@ -/// Implementation of the Agent Desktop service class. -class DesktopService { - // TODO - implement this class -} diff --git a/lib/src/model/model_root.dart b/lib/src/model/model_root.dart index 4a95ad6..40aadef 100644 --- a/lib/src/model/model_root.dart +++ b/lib/src/model/model_root.dart @@ -1,5 +1,7 @@ import 'package:logger/logger.dart'; +import '../agent/android_backend.dart'; +import '../agent/backend.g.dart'; import 'receiver.dart'; import 'sender.dart'; @@ -11,8 +13,12 @@ class ModelRoot { ModelRoot() { var mainLogger = Logger(); - receiver = Receiver(mainLogger); - sender = Sender(mainLogger); + // Temporary assignment of Android backend. + // In the future we will probably need some mechanism + // to decide which type of backend to assign. + Backend backend = AndroidBackend(); + receiver = Receiver(mainLogger, backend); + sender = Sender(mainLogger, backend); logger = mainLogger; } } diff --git a/lib/src/model/receiver.dart b/lib/src/model/receiver.dart index 71f6fbb..d6e6f5d 100644 --- a/lib/src/model/receiver.dart +++ b/lib/src/model/receiver.dart @@ -3,6 +3,8 @@ import 'dart:collection'; import 'package:logger/logger.dart'; import 'package:mobx/mobx.dart'; +import '../agent/backend.g.dart'; + part 'receiver.g.dart'; /// Implementation of the Model Receiver class. @@ -10,6 +12,7 @@ class Receiver = _Receiver with _$Receiver; abstract class _Receiver with Store { final Logger _logger; + final Backend _backend; // Determines whether the receiver is running or not @observable @@ -40,7 +43,9 @@ abstract class _Receiver with Store { @computed int get repairPort => _repairPort; - _Receiver(Logger logger) : _logger = logger; + _Receiver(Logger logger, Backend backend) + : _logger = logger, + _backend = backend; // Start current receiver. @action @@ -49,6 +54,9 @@ abstract class _Receiver with Store { return; } + // Main backend call + _backend.startReceiver(); + _isStarted = !_isStarted; _logger.i('Receiver started'); } @@ -60,6 +68,9 @@ abstract class _Receiver with Store { return; } + // Main backend call + _backend.stopReceiver(); + _isStarted = !_isStarted; _logger.i('Receiver stopped'); } diff --git a/lib/src/model/sender.dart b/lib/src/model/sender.dart index 97b3aee..8994a95 100644 --- a/lib/src/model/sender.dart +++ b/lib/src/model/sender.dart @@ -1,6 +1,7 @@ import 'package:logger/logger.dart'; import 'package:mobx/mobx.dart'; +import '../agent/backend.g.dart'; import 'capture_source_type.dart'; part 'sender.g.dart'; @@ -10,6 +11,7 @@ class Sender = _Sender with _$Sender; abstract class _Sender with Store { final Logger _logger; + final Backend _backend; // Determines whether the sender is running or not @observable @@ -47,7 +49,9 @@ abstract class _Sender with Store { @computed CaptureSourceType get captureSource => _captureSource; - _Sender(Logger logger) : _logger = logger; + _Sender(Logger logger, Backend backend) + : _logger = logger, + _backend = backend; // Start current sender. @action @@ -56,6 +60,9 @@ abstract class _Sender with Store { return; } + // Main backend call + _backend.startSender(receiverIP); + _isStarted = !_isStarted; _logger.i('Sender started'); } @@ -67,6 +74,9 @@ abstract class _Sender with Store { return; } + // Main backend call + _backend.stopSender(); + _isStarted = !_isStarted; _logger.i('Sender stopped'); } diff --git a/pubspec.lock b/pubspec.lock index 0192f60..a9a289c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,26 +5,31 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" + sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834 url: "https://pub.dev" source: hosted - version: "67.0.0" + version: "72.0.0" + _macros: + dependency: transitive + description: dart + source: sdk + version: "0.3.2" analyzer: dependency: transitive description: name: analyzer - sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" + sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139 url: "https://pub.dev" source: hosted - version: "6.4.1" + version: "6.7.0" ansicolor: dependency: transitive description: name: ansicolor - sha256: "8bf17a8ff6ea17499e40a2d2542c2f481cd7615760c6d34065cb22bfd22e6880" + sha256: "50e982d500bc863e1d703448afdbf9e5a72eb48840a4f766fa361ffd6877055f" url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "2.0.3" archive: dependency: transitive description: @@ -93,18 +98,18 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "644dc98a0f179b872f612d3eb627924b578897c629788e858157fa5e704ca0c7" + sha256: dd09dd4e2b078992f42aac7f1a622f01882a8492fef08486b27ddde929c19f04 url: "https://pub.dev" source: hosted - version: "2.4.11" + version: "2.4.12" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: e3c79f69a64bdfcd8a776a3c28db4eb6e3fb5356d013ae5eb2e52007706d5dbe + sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0 url: "https://pub.dev" source: hosted - version: "7.3.1" + version: "7.3.2" built_collection: dependency: transitive description: @@ -181,18 +186,18 @@ packages: dependency: transitive description: name: coverage - sha256: "3945034e86ea203af7a056d98e98e42a5518fff200d6e8e6647e1886b07e936e" + sha256: c1fb2dce3c0085f39dc72668e85f8e0210ec7de05345821ff58530567df345a5 url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.2" crypto: dependency: transitive description: name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27 url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" csslib: dependency: transitive description: @@ -213,10 +218,10 @@ packages: dependency: transitive description: name: dart_style - sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" + sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab" url: "https://pub.dev" source: hosted - version: "2.3.6" + version: "2.3.7" fake_async: dependency: transitive description: @@ -229,10 +234,10 @@ packages: dependency: transitive description: name: ffi - sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" file: dependency: transitive description: @@ -266,18 +271,18 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" + sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "4.0.0" flutter_localization: dependency: "direct main" description: name: flutter_localization - sha256: faaeb1eba307473032e2c2af737f36ced61fc98735608410d0a6d9c231b50912 + sha256: "9b3b8825146a3850297a0ec3686b326d618328dca4cd5178b764af9303a26379" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.2.2" flutter_localizations: dependency: transitive description: flutter @@ -401,18 +406,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -425,10 +430,10 @@ packages: dependency: transitive description: name: lints - sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "4.0.0" logger: dependency: "direct main" description: @@ -445,6 +450,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + macros: + dependency: transitive + description: + name: macros + sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536" + url: "https://pub.dev" + source: hosted + version: "0.1.2-main.4" matcher: dependency: transitive description: @@ -457,26 +470,26 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" mime: dependency: transitive description: name: mime - sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" + sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a" url: "https://pub.dev" source: hosted - version: "1.0.5" + version: "1.0.6" mobx: dependency: "direct main" description: @@ -557,6 +570,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.2" + pigeon: + dependency: "direct main" + description: + name: pigeon + sha256: f77c57c204c7174e3b95fa7b32a145b1b8ddb51e4619c642f3caf17d7e537275 + url: "https://pub.dev" + source: hosted + version: "22.3.0" platform: dependency: transitive description: @@ -609,58 +630,58 @@ packages: dependency: transitive description: name: shared_preferences - sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180 + sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.3.2" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "93d0ec9dd902d85f326068e6a899487d1f65ffcd5798721a95330b26c8131577" + sha256: "480ba4345773f56acda9abf5f50bd966f581dac5d514e5fc4a18c62976bbba7e" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.3.2" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7" + sha256: c4b35f6cb8f63c147312c054ce7c2254c8066745125264f0c88739c417fc9d9f url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.5.2" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux - sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.1" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface - sha256: "034650b71e73629ca08a0bd789fd1d83cc63c2d1e405946f7cef7bc37432f93a" + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.1" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a" + sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.4.2" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.1" shelf: dependency: transitive description: @@ -681,18 +702,18 @@ packages: dependency: transitive description: name: shelf_static - sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e + sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.3" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" + sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "2.0.0" sky_engine: dependency: transitive description: flutter @@ -710,10 +731,10 @@ packages: dependency: transitive description: name: source_map_stack_trace - sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" + sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" source_maps: dependency: transitive description: @@ -774,26 +795,26 @@ packages: dependency: "direct dev" description: name: test - sha256: "7ee446762c2c50b3bd4ea96fe13ffac69919352bd3b4b17bac3f3465edc58073" + sha256: "7ee44229615f8f642b68120165ae4c2a75fe77ae2065b1e55ae4711f6cf0899e" url: "https://pub.dev" source: hosted - version: "1.25.2" + version: "1.25.7" test_api: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.2" test_core: dependency: transitive description: name: test_core - sha256: "2bc4b4ecddd75309300d8096f781c0e3280ca1ef85beda558d33fcbedc2eead4" + sha256: "55ea5a652e38a1dfb32943a7973f3681a60f872f8c3a05a14664ad54ef9c6696" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.4" timing: dependency: transitive description: @@ -830,10 +851,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.2.5" watcher: dependency: transitive description: @@ -846,18 +867,26 @@ packages: dependency: transitive description: name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062 url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "1.0.0" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" + url: "https://pub.dev" + source: hosted + version: "0.1.6" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" + sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" url: "https://pub.dev" source: hosted - version: "2.4.5" + version: "3.0.1" webkit_inspection_protocol: dependency: transitive description: @@ -891,5 +920,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.4.3 <4.0.0" + dart: ">=3.5.0-259.0.dev <4.0.0" flutter: ">=3.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index 5677bde..84ecbfe 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,7 +9,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_localization: ^0.2.0 + flutter_localization: ^0.2.2 intl: ^0.19.0 logger: ^2.4.0 mobx: ^2.3.3+2 @@ -17,11 +17,12 @@ dependencies: darq: ^2.0.0 flutter_native_splash: ^2.4.1 flutter_launcher_icons: ^0.13.1 + pigeon: ^22.3.0 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^3.0.0 + flutter_lints: ^4.0.0 test: ^1.25.2 build_runner: ^2.4.11 mobx_codegen: ^2.6.1 diff --git a/test/model/receiver_test.dart b/test/model/receiver_test.dart index 984f2e1..1988dde 100644 --- a/test/model/receiver_test.dart +++ b/test/model/receiver_test.dart @@ -1,10 +1,11 @@ import 'package:logger/logger.dart'; +import 'package:roc_droid/src/agent/android_backend.dart'; import 'package:roc_droid/src/model/receiver.dart'; import 'package:test/test.dart'; // Receiver class unit tests. void main() { - Receiver makeReceiver() => Receiver(Logger()); + Receiver makeReceiver() => Receiver(Logger(), AndroidBackend()); test('Check the receivers initial values and getters.', () { final receiver = makeReceiver(); diff --git a/test/model/sender_test.dart b/test/model/sender_test.dart index 5a584aa..9d0fe9a 100644 --- a/test/model/sender_test.dart +++ b/test/model/sender_test.dart @@ -1,11 +1,12 @@ import 'package:logger/logger.dart'; +import 'package:roc_droid/src/agent/android_backend.dart'; import 'package:roc_droid/src/model/capture_source_type.dart'; import 'package:roc_droid/src/model/sender.dart'; import 'package:test/test.dart'; // Receiver class unit tests. void main() { - Sender makeSender() => Sender(Logger()); + Sender makeSender() => Sender(Logger(), AndroidBackend()); test('Check the senders initial values and getters.', () { final sender = makeSender();