From e64948bef5c3d0024ad3756a812ba980b6aa62f2 Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Sat, 10 Aug 2024 14:24:28 -0400 Subject: [PATCH 01/19] ws --- lib/main.dart | 3 +++ lib/views/radio.dart | 4 ++-- pubspec.lock | 16 ++++++++++++++++ pubspec.yaml | 1 + 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 46e3626..3f6c3ff 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,7 +1,10 @@ import 'package:flutter/material.dart'; import 'views/radio.dart'; +import 'controller/ws.dart'; void main() { + LiveFeeder f = LiveFeeder(); + f.init(); runApp(const MyApp()); } diff --git a/lib/views/radio.dart b/lib/views/radio.dart index 97922db..76dce53 100644 --- a/lib/views/radio.dart +++ b/lib/views/radio.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'lcd.dart'; -import 'keypad.dart'; +import '../../views/lcd.dart'; +import '../../views/keypad.dart'; class MainRadio extends StatefulWidget { const MainRadio({super.key, required this.title}); diff --git a/pubspec.lock b/pubspec.lock index 4c6d22a..aa3e13f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -344,6 +344,22 @@ packages: url: "https://pub.dev" source: hosted 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: "direct main" + description: + name: web_socket_channel + sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" + url: "https://pub.dev" + source: hosted + version: "3.0.1" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 75aa45d..df22576 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -38,6 +38,7 @@ dependencies: google_fonts: ^6.2.1 fixnum: ^1.1.0 protobuf: ^3.1.0 + web_socket_channel: ^3.0.1 dev_dependencies: flutter_test: From 8e99afeb31f7bba36264347ad11aed0ce48100f0 Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Sat, 10 Aug 2024 14:36:08 -0400 Subject: [PATCH 02/19] Controller --- lib/controller/ws.dart | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 lib/controller/ws.dart diff --git a/lib/controller/ws.dart b/lib/controller/ws.dart new file mode 100644 index 0000000..1f708f0 --- /dev/null +++ b/lib/controller/ws.dart @@ -0,0 +1,24 @@ +import 'package:web_socket_channel/web_socket_channel.dart'; + +class LiveFeeder { + late WebSocketChannel channel; + + LiveFeeder(); + + void init() { + String socketUrl = 'ws://xenon:3050/ws'; + Uri baseUri = Uri.base; + if (baseUri.scheme == 'http' || baseUri.scheme == 'https') { + String port = (baseUri.hasPort ? ':' + baseUri.port.toString() : ''); + socketUrl = 'ws://${baseUri.host}$port/ws'; + } + final wsUri = Uri.parse(socketUrl); + + channel = WebSocketChannel.connect(wsUri); + channel.stream.listen((event) => _handleData(event)); + } + + void _handleData(dynamic event) { + print(event); + } +} From 9bc788035b1ba3c576a3ee1c7120259e727e24a2 Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Sat, 10 Aug 2024 16:38:46 -0400 Subject: [PATCH 03/19] wip --- lib/controller/ws.dart | 16 +++++++++++++--- lib/views/radio.dart | 34 ++-------------------------------- 2 files changed, 15 insertions(+), 35 deletions(-) diff --git a/lib/controller/ws.dart b/lib/controller/ws.dart index 1f708f0..7d2ba49 100644 --- a/lib/controller/ws.dart +++ b/lib/controller/ws.dart @@ -1,6 +1,8 @@ import 'package:web_socket_channel/web_socket_channel.dart'; +import '../pb/stillbox.pb.dart'; class LiveFeeder { + late Uri _wsUri; late WebSocketChannel channel; LiveFeeder(); @@ -12,13 +14,21 @@ class LiveFeeder { String port = (baseUri.hasPort ? ':' + baseUri.port.toString() : ''); socketUrl = 'ws://${baseUri.host}$port/ws'; } - final wsUri = Uri.parse(socketUrl); + _wsUri = Uri.parse(socketUrl); + } - channel = WebSocketChannel.connect(wsUri); + void connect() { + channel = WebSocketChannel.connect(_wsUri); channel.stream.listen((event) => _handleData(event)); } void _handleData(dynamic event) { - print(event); + final msg = Message.fromBuffer(event); + switch (msg.whichToClientMessage()) { + case Message_ToClientMessage.call: + case Message_ToClientMessage.notification: + case Message_ToClientMessage.popup: + case Message_ToClientMessage.error: + } } } diff --git a/lib/views/radio.dart b/lib/views/radio.dart index 76dce53..40416ef 100644 --- a/lib/views/radio.dart +++ b/lib/views/radio.dart @@ -5,15 +5,6 @@ import '../../views/keypad.dart'; class MainRadio extends StatefulWidget { const MainRadio({super.key, required this.title}); - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - final String title; @override @@ -23,32 +14,11 @@ class MainRadio extends StatefulWidget { class _MainRadioState extends State { @override Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. - return Scaffold( - body: const Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. + return const Scaffold( + body: Center( child: SizedBox( width: 500.0, child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - // - // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint" - // action in the IDE, or press "p" in the console), to see the - // wireframe for each widget. mainAxisAlignment: MainAxisAlignment.start, children: [ ScannerLabel(), From fe8bdc13a5ea12b10eb4110158aa7248ff851889 Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Sat, 10 Aug 2024 17:07:04 -0400 Subject: [PATCH 04/19] add default case --- lib/controller/ws.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/controller/ws.dart b/lib/controller/ws.dart index 7d2ba49..72f9464 100644 --- a/lib/controller/ws.dart +++ b/lib/controller/ws.dart @@ -29,6 +29,7 @@ class LiveFeeder { case Message_ToClientMessage.notification: case Message_ToClientMessage.popup: case Message_ToClientMessage.error: + default: } } } From f4ea5916b8ca4361aaaddc4e4e1842e8328747a6 Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Sat, 10 Aug 2024 17:07:52 -0400 Subject: [PATCH 05/19] macos stuff --- ios/Flutter/Debug.xcconfig | 1 + ios/Flutter/Release.xcconfig | 1 + ios/Podfile | 44 ++++++++++++++++++++++++++ macos/Flutter/Flutter-Debug.xcconfig | 1 + macos/Flutter/Flutter-Release.xcconfig | 1 + macos/Podfile | 43 +++++++++++++++++++++++++ 6 files changed, 91 insertions(+) create mode 100644 ios/Podfile create mode 100644 macos/Podfile diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig index 592ceee..ec97fc6 100644 --- a/ios/Flutter/Debug.xcconfig +++ b/ios/Flutter/Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig index 592ceee..c4855bf 100644 --- a/ios/Flutter/Release.xcconfig +++ b/ios/Flutter/Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 0000000..d97f17e --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,44 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '12.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig index c2efd0b..4b81f9b 100644 --- a/macos/Flutter/Flutter-Debug.xcconfig +++ b/macos/Flutter/Flutter-Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig index c2efd0b..5caa9d1 100644 --- a/macos/Flutter/Flutter-Release.xcconfig +++ b/macos/Flutter/Flutter-Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Podfile b/macos/Podfile new file mode 100644 index 0000000..c795730 --- /dev/null +++ b/macos/Podfile @@ -0,0 +1,43 @@ +platform :osx, '10.14' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end From d61ce79c295d965f457138e928b03aae4e3e6a6d Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Sun, 11 Aug 2024 19:23:16 -0400 Subject: [PATCH 06/19] wip --- lib/controller/ws.dart | 4 +--- protobuf/stillbox.proto | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/controller/ws.dart b/lib/controller/ws.dart index 72f9464..b55cd07 100644 --- a/lib/controller/ws.dart +++ b/lib/controller/ws.dart @@ -5,9 +5,7 @@ class LiveFeeder { late Uri _wsUri; late WebSocketChannel channel; - LiveFeeder(); - - void init() { + LiveFeeder() { String socketUrl = 'ws://xenon:3050/ws'; Uri baseUri = Uri.base; if (baseUri.scheme == 'http' || baseUri.scheme == 'https') { diff --git a/protobuf/stillbox.proto b/protobuf/stillbox.proto index ccaf9ca..a3c3391 100644 --- a/protobuf/stillbox.proto +++ b/protobuf/stillbox.proto @@ -24,7 +24,8 @@ message Call { repeated int64 frequencies = 8; repeated int32 patches = 9; repeated int32 sources = 10; - bytes audio = 11; + optional int32 duration = 11; + bytes audio = 12; } message UserPopup { From 9d58f4fa7f75fccd88e8720af5e6aa1668bbb403 Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Sun, 11 Aug 2024 23:59:01 -0400 Subject: [PATCH 07/19] wip --- lib/main.dart | 9 ++---- lib/views/login.dart | 71 +++++++++++++++++++++++++++++++++++++++++++ lib/views/radio.dart | 2 ++ test/widget_test.dart | 2 +- 4 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 lib/views/login.dart diff --git a/lib/main.dart b/lib/main.dart index 3f6c3ff..94a24e8 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,15 +1,12 @@ import 'package:flutter/material.dart'; import 'views/radio.dart'; -import 'controller/ws.dart'; void main() { - LiveFeeder f = LiveFeeder(); - f.init(); - runApp(const MyApp()); + runApp(const CallsApp()); } -class MyApp extends StatelessWidget { - const MyApp({super.key}); +class CallsApp extends StatelessWidget { + const CallsApp({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/views/login.dart b/lib/views/login.dart new file mode 100644 index 0000000..88d0665 --- /dev/null +++ b/lib/views/login.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; + +class Login extends StatefulWidget { + const Login({super.key, required this.title}); + + final String title; + + @override + State createState() => _LoginState(); +} + +class _LoginState extends State { + final _formKey = GlobalKey(); + TextEditingController userController = TextEditingController(); + TextEditingController passwordController = TextEditingController(); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Form( + key: _formKey, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16), + child: Column(children: [ + TextFormField( + controller: userController, + decoration: const InputDecoration( + border: OutlineInputBorder(), labelText: "Username"), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your username.'; + } + return null; + }, + ), + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 8, vertical: 16), + child: TextFormField( + controller: passwordController, + obscureText: true, + decoration: const InputDecoration( + border: OutlineInputBorder(), labelText: "Password"), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your password'; + } + return null; + }, + )), + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 8, vertical: 16.0), + child: Center( + child: ElevatedButton( + onPressed: () { + if (_formKey.currentState!.validate()) { + // TODO do login here + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Please login.')), + ); + } + }, + child: const Text('Login'), + ), + )), + ])), + )); + } +} diff --git a/lib/views/radio.dart b/lib/views/radio.dart index 40416ef..d69dbcb 100644 --- a/lib/views/radio.dart +++ b/lib/views/radio.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import '../../views/lcd.dart'; import '../../views/keypad.dart'; +import '../controller/ws.dart'; class MainRadio extends StatefulWidget { const MainRadio({super.key, required this.title}); @@ -12,6 +13,7 @@ class MainRadio extends StatefulWidget { } class _MainRadioState extends State { + LiveFeeder f = LiveFeeder(); @override Widget build(BuildContext context) { return const Scaffold( diff --git a/test/widget_test.dart b/test/widget_test.dart index 44d58b6..16287a1 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -13,7 +13,7 @@ import 'package:calls/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); + await tester.pumpWidget(const CallsApp()); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget); From d6d590684d091d4746743fe8a77ee548e88a3f17 Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Mon, 12 Aug 2024 07:47:08 -0400 Subject: [PATCH 08/19] fix --- lib/controller/ws.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/controller/ws.dart b/lib/controller/ws.dart index b55cd07..400d94b 100644 --- a/lib/controller/ws.dart +++ b/lib/controller/ws.dart @@ -9,7 +9,7 @@ class LiveFeeder { String socketUrl = 'ws://xenon:3050/ws'; Uri baseUri = Uri.base; if (baseUri.scheme == 'http' || baseUri.scheme == 'https') { - String port = (baseUri.hasPort ? ':' + baseUri.port.toString() : ''); + String port = (baseUri.hasPort ? ':${baseUri.port}' : ''); socketUrl = 'ws://${baseUri.host}$port/ws'; } _wsUri = Uri.parse(socketUrl); From 5f92cf977ba657b94c623b891421169ce6d887fd Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Mon, 12 Aug 2024 09:13:43 -0400 Subject: [PATCH 09/19] wip --- lib/controller/ws.dart | 13 +++-- lib/main.dart | 47 ++++++++++++++++- lib/views/login.dart | 113 ++++++++++++++++++++++------------------- lib/views/radio.dart | 1 - test/widget_test.dart | 2 +- 5 files changed, 117 insertions(+), 59 deletions(-) diff --git a/lib/controller/ws.dart b/lib/controller/ws.dart index 400d94b..a4e3d98 100644 --- a/lib/controller/ws.dart +++ b/lib/controller/ws.dart @@ -1,20 +1,25 @@ import 'package:web_socket_channel/web_socket_channel.dart'; import '../pb/stillbox.pb.dart'; -class LiveFeeder { +class Client { late Uri _wsUri; late WebSocketChannel channel; - LiveFeeder() { + Client() { String socketUrl = 'ws://xenon:3050/ws'; Uri baseUri = Uri.base; if (baseUri.scheme == 'http' || baseUri.scheme == 'https') { - String port = (baseUri.hasPort ? ':${baseUri.port}' : ''); - socketUrl = 'ws://${baseUri.host}$port/ws'; + final port = (baseUri.hasPort ? ':${baseUri.port}' : ''); + socketUrl = + '${baseUri.scheme == 'http' ? 'ws' : 'wss'}://${baseUri.host}$port/ws'; } _wsUri = Uri.parse(socketUrl); } + bool isConnected() { + return false; + } + void connect() { channel = WebSocketChannel.connect(_wsUri); channel.stream.listen((event) => _handleData(event)); diff --git a/lib/main.dart b/lib/main.dart index 94a24e8..33a0e99 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; import 'views/radio.dart'; +import 'views/login.dart'; +import 'controller/ws.dart'; void main() { runApp(const CallsApp()); @@ -20,7 +22,50 @@ class CallsApp extends StatelessWidget { brightness: Brightness.dark, ), themeMode: ThemeMode.dark, - home: const MainRadio(title: 'Stillbox'), + home: const CallsHome(), ); } } + +class CallsHome extends StatefulWidget { + const CallsHome({super.key}); + + @override + State createState() => CallsHomeState(); +} + +class CallsHomeState extends State { + final c = Client(); + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + loadData(); + }); + } + + Future loadData() async { + await Future.delayed(const Duration(seconds: 1)); // Simulate some delay + + // Ensure the navigation happens in the context of this widget's subtree + if (mounted) { + Navigator.pushReplacement( + context, + PageRouteBuilder( + pageBuilder: (context, animation, secondaryAnimation) => + const Login(), + transitionsBuilder: (context, animation, secondaryAnimation, child) { + return child; + }, + transitionDuration: const Duration(milliseconds: 0), + ), + ); + } + } + + @override + Widget build(BuildContext context) { + return const MainRadio(title: 'Stillbox'); + } +} diff --git a/lib/views/login.dart b/lib/views/login.dart index 88d0665..bf8eb79 100644 --- a/lib/views/login.dart +++ b/lib/views/login.dart @@ -1,9 +1,7 @@ import 'package:flutter/material.dart'; class Login extends StatefulWidget { - const Login({super.key, required this.title}); - - final String title; + const Login({super.key}); @override State createState() => _LoginState(); @@ -17,55 +15,66 @@ class _LoginState extends State { @override Widget build(BuildContext context) { return Scaffold( + appBar: AppBar( + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + title: const Text('stillbox login:'), + ), body: Form( - key: _formKey, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16), - child: Column(children: [ - TextFormField( - controller: userController, - decoration: const InputDecoration( - border: OutlineInputBorder(), labelText: "Username"), - validator: (value) { - if (value == null || value.isEmpty) { - return 'Please enter your username.'; - } - return null; - }, - ), - Padding( - padding: - const EdgeInsets.symmetric(horizontal: 8, vertical: 16), - child: TextFormField( - controller: passwordController, - obscureText: true, - decoration: const InputDecoration( - border: OutlineInputBorder(), labelText: "Password"), - validator: (value) { - if (value == null || value.isEmpty) { - return 'Please enter your password'; - } - return null; - }, - )), - Padding( - padding: - const EdgeInsets.symmetric(horizontal: 8, vertical: 16.0), - child: Center( - child: ElevatedButton( - onPressed: () { - if (_formKey.currentState!.validate()) { - // TODO do login here - } else { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Please login.')), - ); - } - }, - child: const Text('Login'), - ), - )), - ])), - )); + key: _formKey, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16), + child: Column(children: [ + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 8, vertical: 10), + child: TextFormField( + controller: userController, + decoration: const InputDecoration( + border: OutlineInputBorder(), labelText: "Username"), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your username.'; + } + return null; + }, + )), + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 8, vertical: 16), + child: TextFormField( + controller: passwordController, + obscureText: true, + decoration: const InputDecoration( + border: OutlineInputBorder(), labelText: "Password"), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your password.'; + } + return null; + }, + )), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8, vertical: 16.0), + child: Center( + child: ElevatedButton( + onPressed: () { + if (_formKey.currentState!.validate()) { + // TODO do login here + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Please login.')), + ); + } + }, + style: ElevatedButton.styleFrom( + foregroundColor: Colors.white, + backgroundColor: Colors.green, + ), + child: const Text('Login'), + ), + )), + ])), + )); } } diff --git a/lib/views/radio.dart b/lib/views/radio.dart index d69dbcb..dc5b18f 100644 --- a/lib/views/radio.dart +++ b/lib/views/radio.dart @@ -13,7 +13,6 @@ class MainRadio extends StatefulWidget { } class _MainRadioState extends State { - LiveFeeder f = LiveFeeder(); @override Widget build(BuildContext context) { return const Scaffold( diff --git a/test/widget_test.dart b/test/widget_test.dart index 16287a1..a954be8 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -13,7 +13,7 @@ import 'package:calls/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(const CallsApp()); + await tester.pumpWidget(const CallsHome()); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget); From 08183de44f347e2962b2a5830b920ce3bad08a7a Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Tue, 13 Aug 2024 10:41:06 -0400 Subject: [PATCH 10/19] notifier --- lib/controller/ws.dart | 9 +- lib/main.dart | 8 +- macos/Podfile.lock | 23 +++++ macos/Runner.xcodeproj/project.pbxproj | 98 ++++++++++++++++++- .../contents.xcworkspacedata | 3 + pubspec.lock | 24 ++++- pubspec.yaml | 1 + 7 files changed, 155 insertions(+), 11 deletions(-) create mode 100644 macos/Podfile.lock diff --git a/lib/controller/ws.dart b/lib/controller/ws.dart index a4e3d98..8f7212f 100644 --- a/lib/controller/ws.dart +++ b/lib/controller/ws.dart @@ -1,9 +1,10 @@ -import 'package:web_socket_channel/web_socket_channel.dart'; +import 'package:flutter/material.dart'; +import 'package:web_socket_channel/io.dart'; import '../pb/stillbox.pb.dart'; -class Client { +class Client extends ChangeNotifier { late Uri _wsUri; - late WebSocketChannel channel; + late IOWebSocketChannel channel; Client() { String socketUrl = 'ws://xenon:3050/ws'; @@ -21,7 +22,7 @@ class Client { } void connect() { - channel = WebSocketChannel.connect(_wsUri); + channel = IOWebSocketChannel.connect(_wsUri); channel.stream.listen((event) => _handleData(event)); } diff --git a/lib/main.dart b/lib/main.dart index 33a0e99..3555d07 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,10 +1,14 @@ import 'package:flutter/material.dart'; import 'views/radio.dart'; +import 'package:provider/provider.dart'; import 'views/login.dart'; import 'controller/ws.dart'; void main() { - runApp(const CallsApp()); + runApp(ChangeNotifierProvider( + create: (context) => Client(), + child: const CallsApp(), + )); } class CallsApp extends StatelessWidget { @@ -68,4 +72,4 @@ class CallsHomeState extends State { Widget build(BuildContext context) { return const MainRadio(title: 'Stillbox'); } -} +} \ No newline at end of file diff --git a/macos/Podfile.lock b/macos/Podfile.lock new file mode 100644 index 0000000..a83eba5 --- /dev/null +++ b/macos/Podfile.lock @@ -0,0 +1,23 @@ +PODS: + - FlutterMacOS (1.0.0) + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + +DEPENDENCIES: + - FlutterMacOS (from `Flutter/ephemeral`) + - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) + +EXTERNAL SOURCES: + FlutterMacOS: + :path: Flutter/ephemeral + path_provider_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin + +SPEC CHECKSUMS: + FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + +PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 + +COCOAPODS: 1.15.2 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index cf15055..6ed6bd6 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -27,6 +27,8 @@ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 496F5DB8060BD261E7F08A61 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 589297E16DDB86D73B9F1899 /* Pods_Runner.framework */; }; + 5936FE4C52AFCC9C748ED48F /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08DCFF62E26924DC0830A593 /* Pods_RunnerTests.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -60,11 +62,14 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 08DCFF62E26924DC0830A593 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 2C35E72338A581BA4B223314 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 308BAD42558871EC233F8F66 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* calls.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "calls.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10ED2044A3C60003C045 /* calls.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = calls.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; @@ -76,8 +81,13 @@ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 4392AEEBD311A7BC0ECA86F6 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 589297E16DDB86D73B9F1899 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 7FCC556DDF94000424BFB234 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 92AB83FE6D1A31C09BF27594 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + B9508550333170474A537DFE /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -85,6 +95,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 5936FE4C52AFCC9C748ED48F /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -92,6 +103,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 496F5DB8060BD261E7F08A61 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -125,6 +137,7 @@ 331C80D6294CF71000263BE5 /* RunnerTests */, 33CC10EE2044A3C60003C045 /* Products */, D73912EC22F37F3D000D13A0 /* Frameworks */, + 54640E6EACE1A859E10FFE72 /* Pods */, ); sourceTree = ""; }; @@ -172,9 +185,25 @@ path = Runner; sourceTree = ""; }; + 54640E6EACE1A859E10FFE72 /* Pods */ = { + isa = PBXGroup; + children = ( + 92AB83FE6D1A31C09BF27594 /* Pods-Runner.debug.xcconfig */, + B9508550333170474A537DFE /* Pods-Runner.release.xcconfig */, + 4392AEEBD311A7BC0ECA86F6 /* Pods-Runner.profile.xcconfig */, + 7FCC556DDF94000424BFB234 /* Pods-RunnerTests.debug.xcconfig */, + 2C35E72338A581BA4B223314 /* Pods-RunnerTests.release.xcconfig */, + 308BAD42558871EC233F8F66 /* Pods-RunnerTests.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; D73912EC22F37F3D000D13A0 /* Frameworks */ = { isa = PBXGroup; children = ( + 589297E16DDB86D73B9F1899 /* Pods_Runner.framework */, + 08DCFF62E26924DC0830A593 /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -186,6 +215,7 @@ isa = PBXNativeTarget; buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( + 6EEF1D362AEFC1359DD7CECE /* [CP] Check Pods Manifest.lock */, 331C80D1294CF70F00263BE5 /* Sources */, 331C80D2294CF70F00263BE5 /* Frameworks */, 331C80D3294CF70F00263BE5 /* Resources */, @@ -204,11 +234,13 @@ isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 04AC2F7D25D4934C471C4CC7 /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, + 1040CC1F5F69FF2080559F98 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -291,6 +323,45 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 04AC2F7D25D4934C471C4CC7 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 1040CC1F5F69FF2080559F98 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 3399D490228B24CF009A79C7 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -329,6 +400,28 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; + 6EEF1D362AEFC1359DD7CECE /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -380,6 +473,7 @@ /* Begin XCBuildConfiguration section */ 331C80DB294CF71000263BE5 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 7FCC556DDF94000424BFB234 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; @@ -394,6 +488,7 @@ }; 331C80DC294CF71000263BE5 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 2C35E72338A581BA4B223314 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; @@ -408,6 +503,7 @@ }; 331C80DD294CF71000263BE5 /* Profile */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 308BAD42558871EC233F8F66 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; diff --git a/macos/Runner.xcworkspace/contents.xcworkspacedata b/macos/Runner.xcworkspace/contents.xcworkspacedata index 1d526a1..21a3cc1 100644 --- a/macos/Runner.xcworkspace/contents.xcworkspacedata +++ b/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/pubspec.lock b/pubspec.lock index aa3e13f..70fc4c9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -69,10 +69,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" fixnum: dependency: "direct main" description: @@ -179,6 +179,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.15.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" path: dependency: transitive description: @@ -199,10 +207,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: "490539678396d4c3c0b06efdaab75ae60675c3e0c66f72bc04c2e2c1e0e2abeb" + sha256: "6f01f8e37ec30b07bc424b4deabac37cacb1bc7e2e515ad74486039918a37eb7" url: "https://pub.dev" source: hosted - version: "2.2.9" + version: "2.2.10" path_provider_foundation: dependency: transitive description: @@ -259,6 +267,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.0" + provider: + dependency: "direct main" + description: + name: provider + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c + url: "https://pub.dev" + source: hosted + version: "6.1.2" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index df22576..218163a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,6 +39,7 @@ dependencies: fixnum: ^1.1.0 protobuf: ^3.1.0 web_socket_channel: ^3.0.1 + provider: ^6.1.2 dev_dependencies: flutter_test: From 5af1b90ccca175566c3bfad397dbd7b14e08cee7 Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Tue, 13 Aug 2024 20:04:37 -0400 Subject: [PATCH 11/19] WIP --- lib/controller/play.dart | 6 ++ lib/controller/{ws.dart => stillbox.dart} | 39 +++++-- lib/main.dart | 8 +- linux/flutter/generated_plugin_registrant.cc | 4 + linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 101 ++++++++++++++++++ pubspec.yaml | 1 + .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 10 files changed, 153 insertions(+), 13 deletions(-) create mode 100644 lib/controller/play.dart rename lib/controller/{ws.dart => stillbox.dart} (55%) diff --git a/lib/controller/play.dart b/lib/controller/play.dart new file mode 100644 index 0000000..bf2ac3e --- /dev/null +++ b/lib/controller/play.dart @@ -0,0 +1,6 @@ +import 'package:audioplayers/audioplayers.dart'; + +class Player { + final player = AudioPlayer(); + Player(); +} diff --git a/lib/controller/ws.dart b/lib/controller/stillbox.dart similarity index 55% rename from lib/controller/ws.dart rename to lib/controller/stillbox.dart index 8f7212f..d34deda 100644 --- a/lib/controller/ws.dart +++ b/lib/controller/stillbox.dart @@ -1,12 +1,33 @@ import 'package:flutter/material.dart'; import 'package:web_socket_channel/io.dart'; import '../pb/stillbox.pb.dart'; +import 'play.dart'; -class Client extends ChangeNotifier { - late Uri _wsUri; +class Stillbox extends ChangeNotifier { + Player player = Player(); late IOWebSocketChannel channel; + bool connected = false; + late Uri _wsUri; + LiveState _state = LiveState.LS_LIVE; + Filter? currentFilter; + Call? _currentCall; + set state(LiveState newState) { + channel.sink.add(Live(state: newState, filter: currentFilter)); + _state = newState; + notifyListeners(); + } - Client() { + LiveState get state { + return _state; + } + + Call? get currentCall => _currentCall; + set currentCall(Call? call) { + _currentCall = call; + notifyListeners(); + } + + Stillbox() { String socketUrl = 'ws://xenon:3050/ws'; Uri baseUri = Uri.base; if (baseUri.scheme == 'http' || baseUri.scheme == 'https') { @@ -17,13 +38,15 @@ class Client extends ChangeNotifier { _wsUri = Uri.parse(socketUrl); } - bool isConnected() { - return false; - } - void connect() { channel = IOWebSocketChannel.connect(_wsUri); - channel.stream.listen((event) => _handleData(event)); + channel.stream.listen((event) => _handleData(event), onDone: () { + connected = false; + }, onError: (error) { + print(error); + }); + connected = true; + notifyListeners(); } void _handleData(dynamic event) { diff --git a/lib/main.dart b/lib/main.dart index 3555d07..8402b08 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,11 +2,11 @@ import 'package:flutter/material.dart'; import 'views/radio.dart'; import 'package:provider/provider.dart'; import 'views/login.dart'; -import 'controller/ws.dart'; +import 'controller/stillbox.dart'; void main() { runApp(ChangeNotifierProvider( - create: (context) => Client(), + create: (context) => Stillbox(), child: const CallsApp(), )); } @@ -39,8 +39,6 @@ class CallsHome extends StatefulWidget { } class CallsHomeState extends State { - final c = Client(); - @override void initState() { super.initState(); @@ -72,4 +70,4 @@ class CallsHomeState extends State { Widget build(BuildContext context) { return const MainRadio(title: 'Stillbox'); } -} \ No newline at end of file +} diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index e71a16d..1830e5c 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,6 +6,10 @@ #include "generated_plugin_registrant.h" +#include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin"); + audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 2e1de87..e9abb91 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + audioplayers_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index e777c67..a9f2f23 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,8 +5,10 @@ import FlutterMacOS import Foundation +import audioplayers_darwin import path_provider_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 70fc4c9..4932418 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -9,6 +9,62 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" + audioplayers: + dependency: "direct main" + description: + name: audioplayers + sha256: c05c6147124cd63e725e861335a8b4d57300b80e6e92cea7c145c739223bbaef + url: "https://pub.dev" + source: hosted + version: "5.2.1" + audioplayers_android: + dependency: transitive + description: + name: audioplayers_android + sha256: b00e1a0e11365d88576320ec2d8c192bc21f1afb6c0e5995d1c57ae63156acb5 + url: "https://pub.dev" + source: hosted + version: "4.0.3" + audioplayers_darwin: + dependency: transitive + description: + name: audioplayers_darwin + sha256: "3034e99a6df8d101da0f5082dcca0a2a99db62ab1d4ddb3277bed3f6f81afe08" + url: "https://pub.dev" + source: hosted + version: "5.0.2" + audioplayers_linux: + dependency: transitive + description: + name: audioplayers_linux + sha256: "60787e73fefc4d2e0b9c02c69885402177e818e4e27ef087074cf27c02246c9e" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + audioplayers_platform_interface: + dependency: transitive + description: + name: audioplayers_platform_interface + sha256: "365c547f1bb9e77d94dd1687903a668d8f7ac3409e48e6e6a3668a1ac2982adb" + url: "https://pub.dev" + source: hosted + version: "6.1.0" + audioplayers_web: + dependency: transitive + description: + name: audioplayers_web + sha256: "22cd0173e54d92bd9b2c80b1204eb1eb159ece87475ab58c9788a70ec43c2a62" + url: "https://pub.dev" + source: hosted + version: "4.1.0" + audioplayers_windows: + dependency: transitive + description: + name: audioplayers_windows + sha256: "9536812c9103563644ada2ef45ae523806b0745f7a78e89d1b5fb1951de90e1a" + url: "https://pub.dev" + source: hosted + version: "3.1.0" boolean_selector: dependency: transitive description: @@ -73,6 +129,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.3" + file: + dependency: transitive + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" fixnum: dependency: "direct main" description: @@ -99,6 +163,11 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" google_fonts: dependency: "direct main" description: @@ -123,6 +192,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" leak_tracker: dependency: transitive description: @@ -288,6 +365,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" stack_trace: dependency: transitive description: @@ -312,6 +397,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" + url: "https://pub.dev" + source: hosted + version: "3.1.0+1" term_glyph: dependency: transitive description: @@ -336,6 +429,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + uuid: + dependency: transitive + description: + name: uuid + sha256: "83d37c7ad7aaf9aa8e275490669535c8080377cfa7a7004c24dfac53afffaa90" + url: "https://pub.dev" + source: hosted + version: "4.4.2" vector_math: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 218163a..69dd879 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -40,6 +40,7 @@ dependencies: protobuf: ^3.1.0 web_socket_channel: ^3.0.1 provider: ^6.1.2 + audioplayers: ^5.2.1 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 8b6d468..09e8e2c 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,9 @@ #include "generated_plugin_registrant.h" +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + AudioplayersWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index b93c4c3..375535c 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + audioplayers_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST From db99eeb43ad35259d3bc998d63afd94e8bcf84b5 Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Tue, 13 Aug 2024 21:03:42 -0400 Subject: [PATCH 12/19] big wip --- lib/controller/play.dart | 6 ++ lib/controller/stillbox.dart | 19 ++++++- lib/main.dart | 31 +++++----- lib/views/radio.dart | 1 - linux/flutter/generated_plugin_registrant.cc | 4 ++ linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 56 +++++++++++++++++++ pubspec.yaml | 1 + .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 11 files changed, 108 insertions(+), 17 deletions(-) diff --git a/lib/controller/play.dart b/lib/controller/play.dart index bf2ac3e..3207326 100644 --- a/lib/controller/play.dart +++ b/lib/controller/play.dart @@ -1,6 +1,12 @@ +import 'dart:typed_data'; + import 'package:audioplayers/audioplayers.dart'; +import '../pb/stillbox.pb.dart'; class Player { final player = AudioPlayer(); Player(); + Future play(Call call) { + return player.play(BytesSource(Uint8List.fromList(call.audio))); + } } diff --git a/lib/controller/stillbox.dart b/lib/controller/stillbox.dart index d34deda..3a17096 100644 --- a/lib/controller/stillbox.dart +++ b/lib/controller/stillbox.dart @@ -1,9 +1,13 @@ import 'package:flutter/material.dart'; import 'package:web_socket_channel/io.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import '../pb/stillbox.pb.dart'; import 'play.dart'; +class BadAuthException implements Exception {} + class Stillbox extends ChangeNotifier { + final storage = FlutterSecureStorage(); Player player = Player(); late IOWebSocketChannel channel; bool connected = false; @@ -11,6 +15,8 @@ class Stillbox extends ChangeNotifier { LiveState _state = LiveState.LS_LIVE; Filter? currentFilter; Call? _currentCall; + Map headers = {}; + set state(LiveState newState) { channel.sink.add(Live(state: newState, filter: currentFilter)); _state = newState; @@ -38,8 +44,9 @@ class Stillbox extends ChangeNotifier { _wsUri = Uri.parse(socketUrl); } - void connect() { - channel = IOWebSocketChannel.connect(_wsUri); + Future connect() async { + await setBearer(); + channel = IOWebSocketChannel.connect(_wsUri, headers: headers); channel.stream.listen((event) => _handleData(event), onDone: () { connected = false; }, onError: (error) { @@ -49,10 +56,18 @@ class Stillbox extends ChangeNotifier { notifyListeners(); } + Future setBearer() async { + String? storedToken = await storage.read(key: 'token'); + if (storedToken == null) { + throw (BadAuthException); + } + } + void _handleData(dynamic event) { final msg = Message.fromBuffer(event); switch (msg.whichToClientMessage()) { case Message_ToClientMessage.call: + player.play(msg.call); case Message_ToClientMessage.notification: case Message_ToClientMessage.popup: case Message_ToClientMessage.error: diff --git a/lib/main.dart b/lib/main.dart index 8402b08..61e88c1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -48,21 +48,24 @@ class CallsHomeState extends State { } Future loadData() async { - await Future.delayed(const Duration(seconds: 1)); // Simulate some delay - // Ensure the navigation happens in the context of this widget's subtree - if (mounted) { - Navigator.pushReplacement( - context, - PageRouteBuilder( - pageBuilder: (context, animation, secondaryAnimation) => - const Login(), - transitionsBuilder: (context, animation, secondaryAnimation, child) { - return child; - }, - transitionDuration: const Duration(milliseconds: 0), - ), - ); + try { + await Provider.of(context, listen: false).connect(); + } catch (e) { + if (mounted) { + Navigator.pushReplacement( + context, + PageRouteBuilder( + pageBuilder: (context, animation, secondaryAnimation) => + const Login(), + transitionsBuilder: + (context, animation, secondaryAnimation, child) { + return child; + }, + transitionDuration: const Duration(milliseconds: 0), + ), + ); + } } } diff --git a/lib/views/radio.dart b/lib/views/radio.dart index dc5b18f..40416ef 100644 --- a/lib/views/radio.dart +++ b/lib/views/radio.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import '../../views/lcd.dart'; import '../../views/keypad.dart'; -import '../controller/ws.dart'; class MainRadio extends StatefulWidget { const MainRadio({super.key, required this.title}); diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 1830e5c..14a06d2 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -7,9 +7,13 @@ #include "generated_plugin_registrant.h" #include +#include void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin"); audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar); + g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); + flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index e9abb91..188af9f 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST audioplayers_linux + flutter_secure_storage_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index a9f2f23..ae73647 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,9 +6,11 @@ import FlutterMacOS import Foundation import audioplayers_darwin +import flutter_secure_storage_macos import path_provider_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin")) + FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 4932418..e37280f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -158,6 +158,54 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.0" + flutter_secure_storage: + dependency: "direct main" + description: + name: flutter_secure_storage + sha256: "165164745e6afb5c0e3e3fcc72a012fb9e58496fb26ffb92cf22e16a821e85d0" + url: "https://pub.dev" + source: hosted + version: "9.2.2" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: "4d91bfc23047422cbcd73ac684bc169859ee766482517c22172c86596bf1464b" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_macos: + dependency: transitive + description: + name: flutter_secure_storage_macos + sha256: "1693ab11121a5f925bbea0be725abfcfbbcf36c1e29e571f84a0c0f436147a81" + url: "https://pub.dev" + source: hosted + version: "3.1.2" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 + url: "https://pub.dev" + source: hosted + version: "3.1.2" flutter_test: dependency: "direct dev" description: flutter @@ -477,6 +525,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.1" + win32: + dependency: transitive + description: + name: win32 + sha256: "68d1e89a91ed61ad9c370f9f8b6effed9ae5e0ede22a270bdfa6daf79fc2290a" + url: "https://pub.dev" + source: hosted + version: "5.5.4" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 69dd879..a3c57ed 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,6 +41,7 @@ dependencies: web_socket_channel: ^3.0.1 provider: ^6.1.2 audioplayers: ^5.2.1 + flutter_secure_storage: ^9.2.2 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 09e8e2c..5f23188 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,8 +7,11 @@ #include "generated_plugin_registrant.h" #include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { AudioplayersWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin")); + FlutterSecureStorageWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 375535c..1164d9f 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST audioplayers_windows + flutter_secure_storage_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST From 0d150d73d8f2631b7f6669e935ec212bbfb98ef8 Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Tue, 13 Aug 2024 23:07:35 -0400 Subject: [PATCH 13/19] big huge wip --- lib/controller/stillbox.dart | 54 ++++++++++++++++--- lib/main.dart | 4 +- lib/views/login.dart | 53 +++++++++++++++++- macos/Podfile.lock | 12 +++++ macos/Runner.xcodeproj/project.pbxproj | 4 +- .../xcshareddata/WorkspaceSettings.xcsettings | 8 +++ macos/Runner/DebugProfile.entitlements | 4 ++ macos/Runner/Release.entitlements | 4 ++ pubspec.lock | 2 +- pubspec.yaml | 1 + 10 files changed, 135 insertions(+), 11 deletions(-) create mode 100644 macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/lib/controller/stillbox.dart b/lib/controller/stillbox.dart index 3a17096..1371b24 100644 --- a/lib/controller/stillbox.dart +++ b/lib/controller/stillbox.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:web_socket_channel/io.dart'; +import 'package:http/http.dart' as http; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import '../pb/stillbox.pb.dart'; import 'play.dart'; @@ -16,6 +17,7 @@ class Stillbox extends ChangeNotifier { Filter? currentFilter; Call? _currentCall; Map headers = {}; + Uri? baseUri = Uri.base; set state(LiveState newState) { channel.sink.add(Live(state: newState, filter: currentFilter)); @@ -34,18 +36,56 @@ class Stillbox extends ChangeNotifier { } Stillbox() { - String socketUrl = 'ws://xenon:3050/ws'; - Uri baseUri = Uri.base; - if (baseUri.scheme == 'http' || baseUri.scheme == 'https') { - final port = (baseUri.hasPort ? ':${baseUri.port}' : ''); + setUris(); + } + + void setUris() { + if (baseUri != null && (baseUri!.scheme == 'http' || baseUri!.scheme == 'https')) { + String socketUrl; + final port = (baseUri!.hasPort ? ':${baseUri!.port}' : ''); socketUrl = - '${baseUri.scheme == 'http' ? 'ws' : 'wss'}://${baseUri.host}$port/ws'; + '${baseUri!.scheme == 'http' ? 'ws' : 'wss'}://${baseUri!.host}$port/ws'; + _wsUri = Uri.parse(socketUrl); + } else { + baseUri = null; } - _wsUri = Uri.parse(socketUrl); + } + + void updateCookie(http.Response response) { + String? rawCookie = response.headers['set-cookie']; + if (rawCookie != null) { + int index = rawCookie.indexOf(';'); + headers['cookie'] = + (index == -1) ? rawCookie : rawCookie.substring(0, index); + } + } + + Future doLogin(String uri, String username, String password) async { + if(baseUri == null) { + baseUri = Uri.parse(uri); + setUris(); + } + Uri loginUri = Uri.parse(baseUri!.toString() + '/login'); + final form = {}; + form['username'] = username; + form['password'] = password; + http.Response response = await http.post( + loginUri, + body: form, + ); + if(response.statusCode == 200) { + updateCookie(response); + await storage.write(key: 'token', value: headers['cookie']); + await connect(); + return true; + } + return false; } Future connect() async { - await setBearer(); + if (connected = true) { + return; + } channel = IOWebSocketChannel.connect(_wsUri, headers: headers); channel.stream.listen((event) => _handleData(event), onDone: () { connected = false; diff --git a/lib/main.dart b/lib/main.dart index 61e88c1..93f9d9b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -50,7 +50,9 @@ class CallsHomeState extends State { Future loadData() async { // Ensure the navigation happens in the context of this widget's subtree try { - await Provider.of(context, listen: false).connect(); + final sb = Provider.of(context, listen: false); + await sb.setBearer(); + await sb.connect(); } catch (e) { if (mounted) { Navigator.pushReplacement( diff --git a/lib/views/login.dart b/lib/views/login.dart index bf8eb79..75da807 100644 --- a/lib/views/login.dart +++ b/lib/views/login.dart @@ -1,4 +1,7 @@ +import 'package:calls/main.dart'; import 'package:flutter/material.dart'; +import '../controller/stillbox.dart'; +import 'package:provider/provider.dart'; class Login extends StatefulWidget { const Login({super.key}); @@ -9,6 +12,7 @@ class Login extends StatefulWidget { class _LoginState extends State { final _formKey = GlobalKey(); + TextEditingController uriController = TextEditingController(); TextEditingController userController = TextEditingController(); TextEditingController passwordController = TextEditingController(); @@ -24,6 +28,31 @@ class _LoginState extends State { child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16), child: Column(children: [ + Builder(builder: (context) { + if (!(Uri.base.scheme == 'http' || + Uri.base.scheme == 'https')) { + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8, vertical: 10), + child: TextFormField( + controller: uriController, + decoration: const InputDecoration( + border: OutlineInputBorder(), + labelText: "Server URL"), + validator: (value) { + if (value != null) { + return Uri.parse(value).isAbsolute + ? null + : 'Please enter a valid URL.'; + } else { + return 'Please enter a valid URL.'; + } + }, + )); + } + + return Container(); + }), Padding( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 10), @@ -60,7 +89,29 @@ class _LoginState extends State { child: ElevatedButton( onPressed: () { if (_formKey.currentState!.validate()) { - // TODO do login here + Provider.of(context, listen: false) + .doLogin( + uriController.text, + userController.text, + passwordController.text) + .then((result) { + if (result == true && mounted) { + Navigator.pushReplacement( + context, + PageRouteBuilder( + pageBuilder: (context, animation, + secondaryAnimation) => + const CallsHome(), + transitionsBuilder: (context, animation, + secondaryAnimation, child) { + return child; + }, + transitionDuration: + const Duration(milliseconds: 0), + ), + ); + } + }); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Please login.')), diff --git a/macos/Podfile.lock b/macos/Podfile.lock index a83eba5..085d79b 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -1,20 +1,32 @@ PODS: + - audioplayers_darwin (0.0.1): + - FlutterMacOS + - flutter_secure_storage_macos (6.1.1): + - FlutterMacOS - FlutterMacOS (1.0.0) - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS DEPENDENCIES: + - audioplayers_darwin (from `Flutter/ephemeral/.symlinks/plugins/audioplayers_darwin/macos`) + - flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) EXTERNAL SOURCES: + audioplayers_darwin: + :path: Flutter/ephemeral/.symlinks/plugins/audioplayers_darwin/macos + flutter_secure_storage_macos: + :path: Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos FlutterMacOS: :path: Flutter/ephemeral path_provider_foundation: :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin SPEC CHECKSUMS: + audioplayers_darwin: dcad41de4fbd0099cb3749f7ab3b0cb8f70b810c + flutter_secure_storage_macos: 59459653abe1adb92abbc8ea747d79f8d19866c9 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 6ed6bd6..cb09b21 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -195,7 +195,6 @@ 2C35E72338A581BA4B223314 /* Pods-RunnerTests.release.xcconfig */, 308BAD42558871EC233F8F66 /* Pods-RunnerTests.profile.xcconfig */, ); - name = Pods; path = Pods; sourceTree = ""; }; @@ -574,6 +573,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = HPWYU9LLXA; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -706,6 +706,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = HPWYU9LLXA; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -726,6 +727,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = HPWYU9LLXA; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", diff --git a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements index dddb8a3..d769771 100644 --- a/macos/Runner/DebugProfile.entitlements +++ b/macos/Runner/DebugProfile.entitlements @@ -8,5 +8,9 @@ com.apple.security.network.server + com.apple.security.network.client + + keychain-access-groups + diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements index 852fa1a..225aa48 100644 --- a/macos/Runner/Release.entitlements +++ b/macos/Runner/Release.entitlements @@ -4,5 +4,9 @@ com.apple.security.app-sandbox + com.apple.security.network.client + + keychain-access-groups + diff --git a/pubspec.lock b/pubspec.lock index e37280f..2ea478d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -225,7 +225,7 @@ packages: source: hosted version: "6.2.1" http: - dependency: transitive + dependency: "direct main" description: name: http sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 diff --git a/pubspec.yaml b/pubspec.yaml index a3c57ed..4fb0728 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -42,6 +42,7 @@ dependencies: provider: ^6.1.2 audioplayers: ^5.2.1 flutter_secure_storage: ^9.2.2 + http: ^1.2.2 dev_dependencies: flutter_test: From c776d37765b522b0ac87e3b5efb5561e65a218a6 Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Wed, 14 Aug 2024 08:28:26 -0400 Subject: [PATCH 14/19] wip --- android/app/build.gradle | 4 +-- .../kotlin/com/example/calls/MainActivity.kt | 2 +- ios/Runner.xcodeproj/project.pbxproj | 12 +++---- lib/controller/play.dart | 4 ++- lib/controller/stillbox.dart | 36 +++++++++++-------- lib/main.dart | 2 +- linux/CMakeLists.txt | 2 +- macos/Runner.xcodeproj/project.pbxproj | 6 ++-- macos/Runner/Configs/AppInfo.xcconfig | 2 +- 9 files changed, 39 insertions(+), 31 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 6d818d4..a0a9029 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -6,7 +6,7 @@ plugins { } android { - namespace = "com.example.calls" + namespace = "me.dynatron.calls" compileSdk = flutter.compileSdkVersion ndkVersion = flutter.ndkVersion @@ -21,7 +21,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId = "com.example.calls" + applicationId = "me.dynatron.calls" // You can update the following values to match your application needs. // For more information, see: https://flutter.dev/to/review-gradle-config. minSdk = flutter.minSdkVersion diff --git a/android/app/src/main/kotlin/com/example/calls/MainActivity.kt b/android/app/src/main/kotlin/com/example/calls/MainActivity.kt index c15203b..8727af2 100644 --- a/android/app/src/main/kotlin/com/example/calls/MainActivity.kt +++ b/android/app/src/main/kotlin/com/example/calls/MainActivity.kt @@ -1,4 +1,4 @@ -package com.example.calls +package me.dynatron.calls import io.flutter.embedding.android.FlutterActivity diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 762ca06..0dd6e46 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -369,7 +369,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.calls; + PRODUCT_BUNDLE_IDENTIFIER = me.dynatron.calls; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -385,7 +385,7 @@ CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.calls.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = me.dynatron.calls.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -402,7 +402,7 @@ CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.calls.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = me.dynatron.calls.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; @@ -417,7 +417,7 @@ CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.calls.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = me.dynatron.calls.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; @@ -549,7 +549,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.calls; + PRODUCT_BUNDLE_IDENTIFIER = me.dynatron.calls; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -572,7 +572,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.calls; + PRODUCT_BUNDLE_IDENTIFIER = me.dynatron.calls; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/lib/controller/play.dart b/lib/controller/play.dart index 3207326..044e533 100644 --- a/lib/controller/play.dart +++ b/lib/controller/play.dart @@ -7,6 +7,8 @@ class Player { final player = AudioPlayer(); Player(); Future play(Call call) { - return player.play(BytesSource(Uint8List.fromList(call.audio))); + print('play!'); + return Future.delayed(const Duration(seconds: 1)); +// return player.play(BytesSource(Uint8List.fromList(call.audio))); } } diff --git a/lib/controller/stillbox.dart b/lib/controller/stillbox.dart index 1371b24..e96c146 100644 --- a/lib/controller/stillbox.dart +++ b/lib/controller/stillbox.dart @@ -8,7 +8,7 @@ import 'play.dart'; class BadAuthException implements Exception {} class Stillbox extends ChangeNotifier { - final storage = FlutterSecureStorage(); + final storage = const FlutterSecureStorage(); Player player = Player(); late IOWebSocketChannel channel; bool connected = false; @@ -40,7 +40,8 @@ class Stillbox extends ChangeNotifier { } void setUris() { - if (baseUri != null && (baseUri!.scheme == 'http' || baseUri!.scheme == 'https')) { + if (baseUri != null && + (baseUri!.scheme == 'http' || baseUri!.scheme == 'https')) { String socketUrl; final port = (baseUri!.hasPort ? ':${baseUri!.port}' : ''); socketUrl = @@ -52,20 +53,20 @@ class Stillbox extends ChangeNotifier { } void updateCookie(http.Response response) { - String? rawCookie = response.headers['set-cookie']; - if (rawCookie != null) { - int index = rawCookie.indexOf(';'); - headers['cookie'] = - (index == -1) ? rawCookie : rawCookie.substring(0, index); - } - } + String? rawCookie = response.headers['set-cookie']; + if (rawCookie != null) { + int index = rawCookie.indexOf(';'); + headers['cookie'] = + (index == -1) ? rawCookie : rawCookie.substring(0, index); + } + } Future doLogin(String uri, String username, String password) async { - if(baseUri == null) { + if (baseUri == null) { baseUri = Uri.parse(uri); setUris(); } - Uri loginUri = Uri.parse(baseUri!.toString() + '/login'); + Uri loginUri = Uri.parse('${baseUri!}/login'); final form = {}; form['username'] = username; form['password'] = password; @@ -73,9 +74,10 @@ class Stillbox extends ChangeNotifier { loginUri, body: form, ); - if(response.statusCode == 200) { + if (response.statusCode == 200) { updateCookie(response); await storage.write(key: 'token', value: headers['cookie']); + await storage.write(key: 'baseURL', value: uri); await connect(); return true; } @@ -83,7 +85,7 @@ class Stillbox extends ChangeNotifier { } Future connect() async { - if (connected = true) { + if (connected == true) { return; } channel = IOWebSocketChannel.connect(_wsUri, headers: headers); @@ -96,11 +98,15 @@ class Stillbox extends ChangeNotifier { notifyListeners(); } - Future setBearer() async { + Future getBearer() async { String? storedToken = await storage.read(key: 'token'); - if (storedToken == null) { + String? storedUri = await storage.read(key: 'baseURL'); + if (storedToken == null || storedUri == null) { throw (BadAuthException); } + headers['cookie'] = storedToken; + baseUri = Uri.parse(storedUri); + setUris(); } void _handleData(dynamic event) { diff --git a/lib/main.dart b/lib/main.dart index 93f9d9b..dabd575 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -51,7 +51,7 @@ class CallsHomeState extends State { // Ensure the navigation happens in the context of this widget's subtree try { final sb = Provider.of(context, listen: false); - await sb.setBearer(); + await sb.getBearer(); await sb.connect(); } catch (e) { if (mounted) { diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index cb7283f..fd6fc66 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -7,7 +7,7 @@ project(runner LANGUAGES CXX) set(BINARY_NAME "calls") # The unique GTK application identifier for this application. See: # https://wiki.gnome.org/HowDoI/ChooseApplicationID -set(APPLICATION_ID "com.example.calls") +set(APPLICATION_ID "me.dynatron.calls") # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index cb09b21..f76426c 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -478,7 +478,7 @@ CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.calls.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = me.dynatron.calls.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/calls.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/calls"; @@ -493,7 +493,7 @@ CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.calls.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = me.dynatron.calls.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/calls.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/calls"; @@ -508,7 +508,7 @@ CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.calls.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = me.dynatron.calls.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/calls.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/calls"; diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig index 9b0c4b4..ea222a2 100644 --- a/macos/Runner/Configs/AppInfo.xcconfig +++ b/macos/Runner/Configs/AppInfo.xcconfig @@ -8,7 +8,7 @@ PRODUCT_NAME = calls // The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = com.example.calls +PRODUCT_BUNDLE_IDENTIFIER = me.dynatron.calls // The copyright displayed in application information PRODUCT_COPYRIGHT = Copyright © 2024 com.example. All rights reserved. From 5a8e7c7690d803a1d676a7e4a30ef9807cd17294 Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Wed, 14 Aug 2024 08:29:53 -0400 Subject: [PATCH 15/19] protoc --- lib/pb/stillbox.pb.dart | 24 +++++++++++++++++++----- lib/pb/stillbox.pbjson.dart | 10 +++++++--- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/lib/pb/stillbox.pb.dart b/lib/pb/stillbox.pb.dart index fb3bc72..ffea5ba 100644 --- a/lib/pb/stillbox.pb.dart +++ b/lib/pb/stillbox.pb.dart @@ -150,6 +150,7 @@ class Call extends $pb.GeneratedMessage { $core.Iterable<$fixnum.Int64>? frequencies, $core.Iterable<$core.int>? patches, $core.Iterable<$core.int>? sources, + $core.int? duration, $core.List<$core.int>? audio, }) { final $result = create(); @@ -183,6 +184,9 @@ class Call extends $pb.GeneratedMessage { if (sources != null) { $result.sources.addAll(sources); } + if (duration != null) { + $result.duration = duration; + } if (audio != null) { $result.audio = audio; } @@ -203,7 +207,8 @@ class Call extends $pb.GeneratedMessage { ..p<$fixnum.Int64>(8, _omitFieldNames ? '' : 'frequencies', $pb.PbFieldType.K6) ..p<$core.int>(9, _omitFieldNames ? '' : 'patches', $pb.PbFieldType.K3) ..p<$core.int>(10, _omitFieldNames ? '' : 'sources', $pb.PbFieldType.K3) - ..a<$core.List<$core.int>>(11, _omitFieldNames ? '' : 'audio', $pb.PbFieldType.OY) + ..a<$core.int>(11, _omitFieldNames ? '' : 'duration', $pb.PbFieldType.O3) + ..a<$core.List<$core.int>>(12, _omitFieldNames ? '' : 'audio', $pb.PbFieldType.OY) ..hasRequiredFields = false ; @@ -303,13 +308,22 @@ class Call extends $pb.GeneratedMessage { $core.List<$core.int> get sources => $_getList(9); @$pb.TagNumber(11) - $core.List<$core.int> get audio => $_getN(10); + $core.int get duration => $_getIZ(10); @$pb.TagNumber(11) - set audio($core.List<$core.int> v) { $_setBytes(10, v); } + set duration($core.int v) { $_setSignedInt32(10, v); } @$pb.TagNumber(11) - $core.bool hasAudio() => $_has(10); + $core.bool hasDuration() => $_has(10); @$pb.TagNumber(11) - void clearAudio() => clearField(11); + void clearDuration() => clearField(11); + + @$pb.TagNumber(12) + $core.List<$core.int> get audio => $_getN(11); + @$pb.TagNumber(12) + set audio($core.List<$core.int> v) { $_setBytes(11, v); } + @$pb.TagNumber(12) + $core.bool hasAudio() => $_has(11); + @$pb.TagNumber(12) + void clearAudio() => clearField(12); } class UserPopup extends $pb.GeneratedMessage { diff --git a/lib/pb/stillbox.pbjson.dart b/lib/pb/stillbox.pbjson.dart index f5cf222..adcd6bd 100644 --- a/lib/pb/stillbox.pbjson.dart +++ b/lib/pb/stillbox.pbjson.dart @@ -63,7 +63,11 @@ const Call$json = { {'1': 'frequencies', '3': 8, '4': 3, '5': 3, '10': 'frequencies'}, {'1': 'patches', '3': 9, '4': 3, '5': 5, '10': 'patches'}, {'1': 'sources', '3': 10, '4': 3, '5': 5, '10': 'sources'}, - {'1': 'audio', '3': 11, '4': 1, '5': 12, '10': 'audio'}, + {'1': 'duration', '3': 11, '4': 1, '5': 5, '9': 0, '10': 'duration', '17': true}, + {'1': 'audio', '3': 12, '4': 1, '5': 12, '10': 'audio'}, + ], + '8': [ + {'1': '_duration'}, ], }; @@ -74,8 +78,8 @@ final $typed_data.Uint8List callDescriptor = $convert.base64Decode( 'cFIIZGF0ZVRpbWUSFgoGc3lzdGVtGAQgASgFUgZzeXN0ZW0SHAoJdGFsa2dyb3VwGAUgASgFUg' 'l0YWxrZ3JvdXASFgoGc291cmNlGAYgASgFUgZzb3VyY2USHAoJZnJlcXVlbmN5GAcgASgDUglm' 'cmVxdWVuY3kSIAoLZnJlcXVlbmNpZXMYCCADKANSC2ZyZXF1ZW5jaWVzEhgKB3BhdGNoZXMYCS' - 'ADKAVSB3BhdGNoZXMSGAoHc291cmNlcxgKIAMoBVIHc291cmNlcxIUCgVhdWRpbxgLIAEoDFIF' - 'YXVkaW8='); + 'ADKAVSB3BhdGNoZXMSGAoHc291cmNlcxgKIAMoBVIHc291cmNlcxIfCghkdXJhdGlvbhgLIAEo' + 'BUgAUghkdXJhdGlvbogBARIUCgVhdWRpbxgMIAEoDFIFYXVkaW9CCwoJX2R1cmF0aW9u'); @$core.Deprecated('Use userPopupDescriptor instead') const UserPopup$json = { From c411a007e62d4bb4551ce7f142ce7c3ef02485f9 Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Wed, 14 Aug 2024 08:33:11 -0400 Subject: [PATCH 16/19] try to play --- lib/controller/play.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/controller/play.dart b/lib/controller/play.dart index 044e533..3207326 100644 --- a/lib/controller/play.dart +++ b/lib/controller/play.dart @@ -7,8 +7,6 @@ class Player { final player = AudioPlayer(); Player(); Future play(Call call) { - print('play!'); - return Future.delayed(const Duration(seconds: 1)); -// return player.play(BytesSource(Uint8List.fromList(call.audio))); + return player.play(BytesSource(Uint8List.fromList(call.audio))); } } From 3a6ac8868583a286c28f3d3de8b9a42ac52c5ba5 Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Wed, 14 Aug 2024 09:26:28 -0400 Subject: [PATCH 17/19] safearea --- lib/controller/play.dart | 1 + lib/views/login.dart | 32 +++++++++++++++++--------------- lib/views/radio.dart | 5 +++-- pubspec.lock | 2 +- pubspec.yaml | 1 + 5 files changed, 23 insertions(+), 18 deletions(-) diff --git a/lib/controller/play.dart b/lib/controller/play.dart index 3207326..eb7f6ab 100644 --- a/lib/controller/play.dart +++ b/lib/controller/play.dart @@ -7,6 +7,7 @@ class Player { final player = AudioPlayer(); Player(); Future play(Call call) { + // TODO make a queue return player.play(BytesSource(Uint8List.fromList(call.audio))); } } diff --git a/lib/views/login.dart b/lib/views/login.dart index 75da807..114b9aa 100644 --- a/lib/views/login.dart +++ b/lib/views/login.dart @@ -95,21 +95,23 @@ class _LoginState extends State { userController.text, passwordController.text) .then((result) { - if (result == true && mounted) { - Navigator.pushReplacement( - context, - PageRouteBuilder( - pageBuilder: (context, animation, - secondaryAnimation) => - const CallsHome(), - transitionsBuilder: (context, animation, - secondaryAnimation, child) { - return child; - }, - transitionDuration: - const Duration(milliseconds: 0), - ), - ); + if (result == true) { + if (context.mounted) { + Navigator.pushReplacement( + context, + PageRouteBuilder( + pageBuilder: (context, animation, + secondaryAnimation) => + const CallsHome(), + transitionsBuilder: (context, animation, + secondaryAnimation, child) { + return child; + }, + transitionDuration: + const Duration(milliseconds: 0), + ), + ); + } } }); } else { diff --git a/lib/views/radio.dart b/lib/views/radio.dart index 40416ef..070dcbb 100644 --- a/lib/views/radio.dart +++ b/lib/views/radio.dart @@ -14,7 +14,8 @@ class MainRadio extends StatefulWidget { class _MainRadioState extends State { @override Widget build(BuildContext context) { - return const Scaffold( + return const SafeArea( + child: Scaffold( body: Center( child: SizedBox( width: 500.0, @@ -27,6 +28,6 @@ class _MainRadioState extends State { ], )), ), - ); + )); } } diff --git a/pubspec.lock b/pubspec.lock index 2ea478d..903227a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -34,7 +34,7 @@ packages: source: hosted version: "5.0.2" audioplayers_linux: - dependency: transitive + dependency: "direct main" description: name: audioplayers_linux sha256: "60787e73fefc4d2e0b9c02c69885402177e818e4e27ef087074cf27c02246c9e" diff --git a/pubspec.yaml b/pubspec.yaml index 4fb0728..ac00848 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -43,6 +43,7 @@ dependencies: audioplayers: ^5.2.1 flutter_secure_storage: ^9.2.2 http: ^1.2.2 + audioplayers_linux: ^3.1.0 dev_dependencies: flutter_test: From b15263546eadbc86de233996c5c32ceb17e0a45d Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Wed, 14 Aug 2024 09:29:49 -0400 Subject: [PATCH 18/19] save bytes source class --- lib/controller/play.dart | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lib/controller/play.dart b/lib/controller/play.dart index eb7f6ab..1a030bf 100644 --- a/lib/controller/play.dart +++ b/lib/controller/play.dart @@ -11,3 +11,29 @@ class Player { return player.play(BytesSource(Uint8List.fromList(call.audio))); } } + +/* +for just_audio (add just_audio and just_audio_linux) +class CallBytesSource extends StreamAudioSource { + late Uint8List _buffer; + final Call _call; + + factory CallBytesSource(Call call) { + return CallBytesSource._(call, Uint8List.fromList(call.audio)); + } + + CallBytesSource._(this._call, this._buffer) : super(tag: 'CallBytesSource'); + + @override + Future request([int? start, int? end]) async { + // Returning the stream audio response with the parameters + return StreamAudioResponse( + sourceLength: _buffer.length, + contentLength: (end ?? _buffer.length) - (start ?? 0), + offset: start ?? 0, + stream: Stream.fromIterable([_buffer.sublist(start ?? 0, end)]), + contentType: _call.audioType, + ); + } +} +*/ \ No newline at end of file From e583b87e6846a25e6f443e053eaa1f898ca81538 Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Wed, 14 Aug 2024 10:01:55 -0400 Subject: [PATCH 19/19] trim slash --- lib/controller/stillbox.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/controller/stillbox.dart b/lib/controller/stillbox.dart index e96c146..2b4aa6c 100644 --- a/lib/controller/stillbox.dart +++ b/lib/controller/stillbox.dart @@ -66,7 +66,12 @@ class Stillbox extends ChangeNotifier { baseUri = Uri.parse(uri); setUris(); } - Uri loginUri = Uri.parse('${baseUri!}/login'); + String baseUriString = baseUri.toString(); + // trim trailing slash since gordio router really dislikes it + if (baseUriString.endsWith('/')) { + baseUriString = baseUriString.substring(0, baseUriString.length - 1); + } + Uri loginUri = Uri.parse('$baseUriString/login'); final form = {}; form['username'] = username; form['password'] = password;