From e1850b1b59000691420f1416e98d40136afb3ae0 Mon Sep 17 00:00:00 2001 From: Michael Ketchel Date: Wed, 10 May 2023 16:52:00 -0400 Subject: [PATCH] First commit --- $HOME/fish/config.fish | 3 + $HOME/fish/fish_variables | 35 + $HOME/tool_state | 4 + .gitignore | 44 ++ .metadata | 33 + README.md | 16 + analysis_options.yaml | 29 + android/.gitignore | 13 + android/app/build.gradle | 72 ++ android/app/src/debug/AndroidManifest.xml | 8 + android/app/src/main/AndroidManifest.xml | 34 + .../com/rgnets/pttoc_test/MainActivity.kt | 6 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + android/app/src/main/res/values/styles.xml | 18 + android/app/src/profile/AndroidManifest.xml | 8 + android/build.gradle | 31 + android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + android/settings.gradle | 11 + assets/dumble_cert.pem | 23 + assets/dumble_key.pem | 27 + lib/main.dart | 140 ++++ lib/providers/connection_options.dart | 36 + lib/providers/lib/mumble_log.dart | 38 ++ lib/providers/mumble_provider.dart | 356 ++++++++++ lib/views/mumble_ui/mumble_ui.dart | 106 +++ lib/views/mumble_ui/mumble_ui_view_model.dart | 34 + linux/.gitignore | 1 + linux/CMakeLists.txt | 138 ++++ linux/flutter/CMakeLists.txt | 88 +++ linux/flutter/generated_plugin_registrant.cc | 11 + linux/flutter/generated_plugin_registrant.h | 15 + linux/flutter/generated_plugins.cmake | 23 + linux/main.cc | 6 + linux/my_application.cc | 104 +++ linux/my_application.h | 18 + pubspec.lock | 641 ++++++++++++++++++ pubspec.yaml | 109 +++ test/widget_test.dart | 30 + 46 files changed, 2359 insertions(+) create mode 100644 $HOME/fish/config.fish create mode 100644 $HOME/fish/fish_variables create mode 100644 $HOME/tool_state create mode 100644 .gitignore create mode 100644 .metadata create mode 100644 README.md create mode 100644 analysis_options.yaml create mode 100644 android/.gitignore create mode 100644 android/app/build.gradle create mode 100644 android/app/src/debug/AndroidManifest.xml create mode 100644 android/app/src/main/AndroidManifest.xml create mode 100644 android/app/src/main/kotlin/com/rgnets/pttoc_test/MainActivity.kt create mode 100644 android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 android/app/src/main/res/drawable/launch_background.xml create mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/values-night/styles.xml create mode 100644 android/app/src/main/res/values/styles.xml create mode 100644 android/app/src/profile/AndroidManifest.xml create mode 100644 android/build.gradle create mode 100644 android/gradle.properties create mode 100644 android/gradle/wrapper/gradle-wrapper.properties create mode 100644 android/settings.gradle create mode 100644 assets/dumble_cert.pem create mode 100644 assets/dumble_key.pem create mode 100644 lib/main.dart create mode 100644 lib/providers/connection_options.dart create mode 100644 lib/providers/lib/mumble_log.dart create mode 100644 lib/providers/mumble_provider.dart create mode 100644 lib/views/mumble_ui/mumble_ui.dart create mode 100644 lib/views/mumble_ui/mumble_ui_view_model.dart create mode 100644 linux/.gitignore create mode 100644 linux/CMakeLists.txt create mode 100644 linux/flutter/CMakeLists.txt create mode 100644 linux/flutter/generated_plugin_registrant.cc create mode 100644 linux/flutter/generated_plugin_registrant.h create mode 100644 linux/flutter/generated_plugins.cmake create mode 100644 linux/main.cc create mode 100644 linux/my_application.cc create mode 100644 linux/my_application.h create mode 100644 pubspec.lock create mode 100644 pubspec.yaml create mode 100644 test/widget_test.dart diff --git a/$HOME/fish/config.fish b/$HOME/fish/config.fish new file mode 100644 index 0000000..d714361 --- /dev/null +++ b/$HOME/fish/config.fish @@ -0,0 +1,3 @@ +if status is-interactive + # Commands to run in interactive sessions can go here +end diff --git a/$HOME/fish/fish_variables b/$HOME/fish/fish_variables new file mode 100644 index 0000000..fdca294 --- /dev/null +++ b/$HOME/fish/fish_variables @@ -0,0 +1,35 @@ +# This file contains fish universal variable definitions. +# VERSION: 3.0 +SETUVAR __fish_initialized:3400 +SETUVAR fish_color_autosuggestion:555\x1ebrblack +SETUVAR fish_color_cancel:\x2dr +SETUVAR fish_color_command:blue +SETUVAR fish_color_comment:red +SETUVAR fish_color_cwd:green +SETUVAR fish_color_cwd_root:red +SETUVAR fish_color_end:green +SETUVAR fish_color_error:brred +SETUVAR fish_color_escape:brcyan +SETUVAR fish_color_history_current:\x2d\x2dbold +SETUVAR fish_color_host:normal +SETUVAR fish_color_host_remote:yellow +SETUVAR fish_color_normal:normal +SETUVAR fish_color_operator:brcyan +SETUVAR fish_color_param:cyan +SETUVAR fish_color_quote:yellow +SETUVAR fish_color_redirection:cyan\x1e\x2d\x2dbold +SETUVAR fish_color_search_match:bryellow\x1e\x2d\x2dbackground\x3dbrblack +SETUVAR fish_color_selection:white\x1e\x2d\x2dbold\x1e\x2d\x2dbackground\x3dbrblack +SETUVAR fish_color_status:red +SETUVAR fish_color_user:brgreen +SETUVAR fish_color_valid_path:\x2d\x2dunderline +SETUVAR fish_key_bindings:fish_default_key_bindings +SETUVAR fish_pager_color_completion:normal +SETUVAR fish_pager_color_description:B3A06D\x1eyellow\x1e\x2di +SETUVAR fish_pager_color_prefix:normal\x1e\x2d\x2dbold\x1e\x2d\x2dunderline +SETUVAR fish_pager_color_progress:brwhite\x1e\x2d\x2dbackground\x3dcyan +SETUVAR fish_pager_color_selected_background:\x2dr +SETUVAR fry_auto_switch:0 +SETUVAR fry_installer:ruby\x2dinstall +SETUVAR fry_prepend_path:0 +SETUVAR fry_rubies:/home/michael/\x2erubies diff --git a/$HOME/tool_state b/$HOME/tool_state new file mode 100644 index 0000000..f8e1ab1 --- /dev/null +++ b/$HOME/tool_state @@ -0,0 +1,4 @@ +{ + "is-bot": true, + "license-hash": "3e8c85e63b26ce223cda96a9a8fbb410" +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..24476c5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,44 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..066e29e --- /dev/null +++ b/.metadata @@ -0,0 +1,33 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled. + +version: + revision: 4b12645012342076800eb701bcdfe18f87da21cf + channel: stable + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 4b12645012342076800eb701bcdfe18f87da21cf + base_revision: 4b12645012342076800eb701bcdfe18f87da21cf + - platform: android + create_revision: 4b12645012342076800eb701bcdfe18f87da21cf + base_revision: 4b12645012342076800eb701bcdfe18f87da21cf + - platform: linux + create_revision: 4b12645012342076800eb701bcdfe18f87da21cf + base_revision: 4b12645012342076800eb701bcdfe18f87da21cf + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/README.md b/README.md new file mode 100644 index 0000000..f295406 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# pttoc_test + +A PTToC test + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..61b6c4d --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..6f56801 --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 0000000..559b23c --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,72 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion flutter.compileSdkVersion + ndkVersion flutter.ndkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.rgnets.pttoc_test" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. +// minSdkVersion flutter.minSdkVersion + minSdkVersion 18 + targetSdkVersion flutter.targetSdkVersion + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..6b67872 --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..cfd4abb --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + diff --git a/android/app/src/main/kotlin/com/rgnets/pttoc_test/MainActivity.kt b/android/app/src/main/kotlin/com/rgnets/pttoc_test/MainActivity.kt new file mode 100644 index 0000000..03c097f --- /dev/null +++ b/android/app/src/main/kotlin/com/rgnets/pttoc_test/MainActivity.kt @@ -0,0 +1,6 @@ +package com.rgnets.pttoc_test + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..cb1ef88 --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..6b67872 --- /dev/null +++ b/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..58a8c74 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.7.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.2.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..94adc3a --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..3c472b9 --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000..44e62bc --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/assets/dumble_cert.pem b/assets/dumble_cert.pem new file mode 100644 index 0000000..4663b21 --- /dev/null +++ b/assets/dumble_cert.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID6zCCAtMCFGBSwUB4UtUMYJ9IXVNeU9+ukG25MA0GCSqGSIb3DQEBCwUAMIGx +MQswCQYDVQQGEwJERTEbMBkGA1UECAwSQmFkZW4tV3VlcnR0ZW1iZXJnMRMwEQYD +VQQHDApTY2hvcm5kb3JmMQ0wCwYDVQQKDARFUE5XMQ8wDQYDVQQLDAZEdW1ibGUx +GTAXBgNVBAMMEER1bWJsZSBUZXN0IFVzZXIxNTAzBgkqhkiG9w0BCQEWJnByb2tv +cHdpZWxlci5oYXJkdW5kc29mdHdhcmVAZ21haWwuY29tMB4XDTIwMDcwMzE3MTcw +MVoXDTMwMDcwMTE3MTcwMVowgbExCzAJBgNVBAYTAkRFMRswGQYDVQQIDBJCYWRl +bi1XdWVydHRlbWJlcmcxEzARBgNVBAcMClNjaG9ybmRvcmYxDTALBgNVBAoMBEVQ +TlcxDzANBgNVBAsMBkR1bWJsZTEZMBcGA1UEAwwQRHVtYmxlIFRlc3QgVXNlcjE1 +MDMGCSqGSIb3DQEJARYmcHJva29wd2llbGVyLmhhcmR1bmRzb2Z0d2FyZUBnbWFp +bC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCRTau3umshBt5C +axK+DXgWh1FWT+ROBMjw0xDC/tiWs7rjORV27q2j1MYorVcxCnh9GSixr0G3uePu +GT2b2Ng70R2yKvaoOBfLZdf1onxPFxOIFjN+psn5qXb5av3PqVoxx70fX5O07Z8J +khHx2tMqvBPjzgHBcqXCEY8kp95ca7jgRQ3g5YQA1DKB65mZUxsCreLmcmDGKfLZ +gHQXdDx+qG091q9M1vpsJP/vTDEYv7jtAie7oDQVAz2BZxgxgzeSJSS5k7qcMjjH +m67nnmYMan264sOPVz8XtYxlMc5VeMKG0rUYyCUBfcjlINGB4gUuKLABc423kWAn +YsuoeszbAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAEdyqdd3lg2muxixmyewU8ZU +mTE9d/BgfKPmCBp7xSi5ucF7bsHNgC+E+vX27uli25nukOZ5/SXyvxXIKJR65le0 +yGQLa6izKamDYpUgzGISZcbfxFGs9r2mOFq4HVBvkdLUp4zfoyKNiIxInk9Efasm +Ajmf1vPFwJzsaT25CNkVFoeJTV1iNm6cAyC/Slwei0gtTjPqL+EGz/9Dh52NG2ej +YtJ+cCwxQAKgrKa250hCD4U12nxU94pRBxcweFP91jKr4o8LJPGb+lKY1bFBbLBQ +o0sDIUb91T6AwhHgjMOBBjiFrdBsAAsqWNYFeg/jUXX+QoS/JdHUCqB/5sSet+c= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/assets/dumble_key.pem b/assets/dumble_key.pem new file mode 100644 index 0000000..e604613 --- /dev/null +++ b/assets/dumble_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEoAIBAAKCAQEAkU2rt7prIQbeQmsSvg14FodRVk/kTgTI8NMQwv7YlrO64zkV +du6to9TGKK1XMQp4fRkosa9Bt7nj7hk9m9jYO9Edsir2qDgXy2XX9aJ8TxcTiBYz +fqbJ+al2+Wr9z6laMce9H1+TtO2fCZIR8drTKrwT484BwXKlwhGPJKfeXGu44EUN +4OWEANQygeuZmVMbAq3i5nJgxiny2YB0F3Q8fqhtPdavTNb6bCT/70wxGL+47QIn +u6A0FQM9gWcYMYM3kiUkuZO6nDI4x5uu555mDGp9uuLDj1c/F7WMZTHOVXjChtK1 +GMglAX3I5SDRgeIFLiiwAXONt5FgJ2LLqHrM2wIDAQABAoIBAFroByj5fpC5JVDU +gHNbJeiiXGbtsN6G69iPlIsKuiL+60vBqQYxqX04X4mPeV97N4q8tNMF3M2/hTSv +ofXhIte5qySO8KaqAWkFnKlXMQKiY1ve1/TDENYcCg1dvNGxGHbQiigYueaWS8vH +EWcJCthPtvntJvgKbF4QckfyppGW+kyA3rH4Di/Gc/vPe4asnqEL2L1PBIdgfDpO +R/OLJjrh888EQJikz8yhapCGHSOVAuGE8HXjDxIuelDXbXQmf2UkUo/BL8DcLiKs +ILADunv4BhtvMlWkmgbhHmdzmF6j/wPYmXhK2tmj0jO1b9xQNaktOVZ9mtQTXIL3 +8n3jKLECgYEAwXh3j/AuDcuVZJoZhTZjUg4KTWdJlwzWwR8gkmT/z7BU0U72zaSz +tehZLg5krZZ1+RIwmGPCUolp7d0y4/rfbDSHJAxdf/u0lkcjsTvFxj8WtOOjaCoG +BEaYV6HZM+oiIxMr38itjScS6i1/KRTeA8N63U63nOgS86Bsl/vJ8B8CgYEAwEPo +PpmZ7bYli1J6Iu7eBSItROWrEWKutyLjoeaujejJK/eD6mrN6jX+TFDcPWhgT5dw +VbJ6EJ1CMoNW13LdqbPUn5z3sjqe4cgX9+a7s2V31afzSVWD4T/bGnYjRYFASRD6 +84a+7hYwgX2n2rA4ptajRKVHkHLmbRZJceMGW8UCgYAd7yGwp5y4jbNDqYQhVFTx +sTJu33hHYrknkEVd4TqjS8kTyX/uOXmEv7psodV59jmym9ng+i5qztAQ+ZSSKZLA +DzCJp1AgKTW7uq4PEgx7q+87da0mxLnBl5qilGwi2CsH+kNjkI0ptc37ZaazGINx +WaF89Fz8bfvgrgWfTlnkSQJ/eysFnRTJh38ojhhIJhjgImnClmCLNpgBVVEEgrNF +H6QBPClUWNgTEfeE8V7buHslN9fMHxo3GG2l+rYo69yqJA0QKTBlfcgQAJQuxqEJ +neNi4Xy2XkRqN+KwekM0VVJ33FcEbP3lUqDkchANeAtsP3VBIri/H8p702ZzpoHJ +kQKBgD/5FiTSYYCW5jxbDYPaNsQBahW8bhCZdTQxCbq9cbOZAY7iYwVbtjVqp98I +zmwEBLb1syA+riHTTAAgVWi8hPwOfkaCQBIuI8eSuG3Nshrde2RJLJw7RRRogKVs +Oh6TFUnBfeztbIHxOtzv8xBpXs7UBDy21SKSgq4PPUwm7LJ9 +-----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..d75929a --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,140 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_loggy/flutter_loggy.dart'; +import 'package:loggy/loggy.dart'; +import 'package:provider/provider.dart'; + +import '/providers/mumble_provider.dart'; +import '/views/mumble_ui/mumble_ui.dart'; + +void main() { + Loggy.initLoggy( + filters: [ + // BlacklistFilter([NetworkLoggy]) + ], + logPrinter: const PrettyDeveloperPrinter(), + logOptions: const LogOptions( + LogLevel.debug, + stackTraceLevel: LogLevel.error, + )); + + // runApp(const MyApp()); + + runApp(MultiProvider( + providers: [ + ChangeNotifierProvider(create: (_) => MumbleProvider()) + ], + child: const MyApp(), + )); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + debugShowCheckedModeBanner: false, + title: 'Flutter Demo', + theme: ThemeData( + // This is the theme of your application. + // + // Try running your application with "flutter run". You'll see the + // application has a blue toolbar. Then, without quitting the app, try + // changing the primarySwatch below to Colors.green and then invoke + // "hot reload" (press "r" in the console where you ran "flutter run", + // or simply save your changes to "hot reload" in a Flutter IDE). + // Notice that the counter didn't reset back to zero; the application + // is not restarted. + primarySwatch: Colors.green, + ), + home: const MumbleUiView(), + // home: const MyHomePage(title: 'Flutter Demo Home Page'), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({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 + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + int _counter = 0; + + void _incrementCounter() { + setState(() { + // This call to setState tells the Flutter framework that something has + // changed in this State, which causes it to rerun the build method below + // so that the display can reflect the updated values. If we changed + // _counter without calling setState(), then the build method would not be + // called again, and so nothing would appear to happen. + _counter++; + }); + } + + @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( + appBar: AppBar( + // Here we take the value from the MyHomePage object that was created by + // the App.build method, and use it to set our appbar title. + title: Text(widget.title), + ), + body: Center( + // Center is a layout widget. It takes a single child and positions it + // in the middle of the parent. + 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. + // + // Invoke "debug painting" (press "p" in the console, choose the + // "Toggle Debug Paint" action from the Flutter Inspector in Android + // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) + // to see the wireframe for each widget. + // + // 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). + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text( + 'You have pushed the button this many times:', + ), + Text( + '$_counter', + style: Theme.of(context).textTheme.headlineMedium, + ), + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: _incrementCounter, + tooltip: 'Increment', + child: const Icon(Icons.add), + ), // This trailing comma makes auto-formatting nicer for build methods. + ); + } +} diff --git a/lib/providers/connection_options.dart b/lib/providers/connection_options.dart new file mode 100644 index 0000000..c4e7835 --- /dev/null +++ b/lib/providers/connection_options.dart @@ -0,0 +1,36 @@ +import 'dart:io'; + +import 'package:dumble/dumble.dart'; +import 'package:flutter/services.dart'; + +final ConnectionOptions defaultConnectionOptions = ConnectionOptions( + host: '192.168.7.15', + port: 64738, + name: 'dumble_test', + password: '', + pingTimeout: const Duration(seconds: 5)); + +/// Connect a Mumble server with a user certificate. +/// If you connect with certificate, you can register your self. +/// Instead of passwords, Mumble uses certificates to identify users. +/// + +Future createConnectionsOptionsWithCertificate( + ConnectionOptions connectionOptions) async { + ByteData certData = await rootBundle.load('assets/dumble_cert.pem'); + ByteData keyData = await rootBundle.load('assets/dumble_key.pem'); + + SecurityContext securityContext = SecurityContext.defaultContext; + // securityContext.setTrustedCertificatesBytes(data.buffer.asUint8List()); + // securityContext..usePrivateKeyBytes(keyData.buffer.asUint8List())..useCertificateChainBytes(certData.buffer.asUint8List()); + return ConnectionOptions( + host: connectionOptions.host, + name: connectionOptions.name, + port: connectionOptions.port, + password: connectionOptions.password, + pingTimeout: connectionOptions.pingTimeout, + incomingAudioStreamTimeout: connectionOptions.incomingAudioStreamTimeout, + context: SecurityContext(withTrustedRoots: true) + ..usePrivateKeyBytes(keyData.buffer.asUint8List()) + ..useCertificateChainBytes(certData.buffer.asUint8List())); +} diff --git a/lib/providers/lib/mumble_log.dart b/lib/providers/lib/mumble_log.dart new file mode 100644 index 0000000..2fa05a0 --- /dev/null +++ b/lib/providers/lib/mumble_log.dart @@ -0,0 +1,38 @@ +import 'dart:collection'; + +import 'package:loggy/loggy.dart'; + +enum MumbleLogEntryType { internalEvent, message, userEvent, server, error } + +class MumbleLogEntry extends LinkedListEntry { + final MumbleLogEntryType entryType; + final String message; + late final DateTime timestamp; + + MumbleLogEntry(this.entryType, this.message) { + timestamp = DateTime.now(); + } +} + +class MumbleLog with UiLoggy { + LinkedList entries = LinkedList(); + final int maxEntries; + + MumbleLog({ + this.maxEntries = 1000, + }); + + void addEntry(MumbleLogEntryType type, String message) { + loggy.info("MumbleLog: $type: $message"); + entries.add(MumbleLogEntry(type, message)); + } + + void message(String message) => addEntry(MumbleLogEntryType.message, message); + + void internal(String message) => + addEntry(MumbleLogEntryType.internalEvent, message); + + void user(String message) => addEntry(MumbleLogEntryType.userEvent, message); + void server(String message) => addEntry(MumbleLogEntryType.server, message); + void error(String message) => addEntry(MumbleLogEntryType.error, message); +} diff --git a/lib/providers/mumble_provider.dart b/lib/providers/mumble_provider.dart new file mode 100644 index 0000000..be65d4e --- /dev/null +++ b/lib/providers/mumble_provider.dart @@ -0,0 +1,356 @@ +import 'dart:io'; + +import 'package:dumble/dumble.dart'; +import 'package:flutter/widgets.dart'; +import 'package:loggy/loggy.dart'; +import 'package:pttoc_test/providers/connection_options.dart'; +import 'package:pttoc_test/providers/lib/mumble_log.dart'; + +class MumbleProvider extends ChangeNotifier with NetworkLoggy { + MumbleClient? client; + + MumbleLog mumbleLog = MumbleLog(); + + Future connect( + {required String host, + int port = 64738, + required String name, + String? password}) async { + mumbleLog.internal("Attempting to connect to $host:$port as $name"); + ConnectionOptions connectionOptions = ConnectionOptions( + host: host, + port: port, + name: name, + password: password, + pingTimeout: const Duration(seconds: 20)); + + return MumbleClient.connect( + options: + await createConnectionsOptionsWithCertificate(connectionOptions), + onBadCertificate: (X509Certificate certificate) { + //Accept every certificate + // TODO: This is example behavior--we should probably change it. + return true; + }).then((newClient) { + client = newClient; + mumbleLog.internal("Connected to $host:$port as $name"); + + // MumbleExampleCallback callback = MumbleExampleCallback(client!); + MumbleClientCallback clientCallback = MumbleClientCallback( + mumbleClient: client!, + onBanListReceived: onBanListReceived, + onChannelAdded: onChannelAdded, + onCryptStateChanged: onCryptStateChanged, + onDone: onDone, + onDropAllChannelPermissions: onDropAllChannelPermissions, + onError: onError, + onPermissionDenied: onPermissionDenied, + onQueryUsersResult: onQueryUsersResult, + onTextMessage: onTextMessage, + onUserAdded: onUserAdded, + onUserListReceived: onUserListReceived); + + client!.add(clientCallback); + // print('Client synced with server!'); + notifyListeners(); + }); + // .onError((error, stackTrace) { + // print(error); + // print(stackTrace); + // }); + +// +// print('Listing channels...'); +// print(client.getChannels()); +// print('Listing users...'); +// print(client.getUsers()); +// // Watch all users that are already on the server +// // New users will reported to the callback (because of line 14) and we will +// // watch these new users in onUserAdded below +// client.getUsers().values.forEach((User element) => element.add(callback)); +// // Also, watch self +// client.self.add(callback); +// // Set a comment for us +// client.self.setComment(comment: 'I\'m a bot!'); +// // Create a channel. If the channel is succesfully created, our callback is invoked. +// // client.createChannel(name: 'Dumble Test Channel'); +// //await new Future.delayed(const Duration(seconds: 30)); +// //await client.close(); +// //print('Bye!'); + } + + bool get connected { + if (client == null) { + return false; + } else { + return client!.closed == false; + } + } + + // Client callback functions + + void onBanListReceived(List bans) { + mumbleLog.internal("Ban list received: ${bans.join(',')}"); + } + + void onChannelAdded(Channel channel) { + // if (channel.name == 'Dumble Test Channel') { + // // This is our channel + // // join it + // client.self.moveToChannel(channel: channel); + // } + mumbleLog.internal("Channel added: ${channel.name}"); + } + + void onCryptStateChanged() {} + + void onDone() { + // print('onDone'); + mumbleLog.internal("Done received."); + } + + void onDropAllChannelPermissions() {} + + void onError(error, [StackTrace? stackTrace]) { + mumbleLog.error(error); + loggy.error('An error occurred!'); + loggy.error(error); + if (stackTrace != null) { + loggy.error(stackTrace); + } + } + + void onQueryUsersResult(Map idToName) { + mumbleLog.internal("QueryUsersResult: $idToName"); + } + + void onTextMessage(IncomingTextMessage message) { + mumbleLog.message('${message.actor?.name}: ${message.message}'); + // print('[${new DateTime.now()}] ${message.actor?.name}: ${message.message}'); + } + + void onUserAdded(User user) { + mumbleLog.user('${user.name} appeared'); + //Keep an eye on the user + // user.add(this); + + // Todo: Implement user listener, the "this" above. + } + + void onUserListReceived(List users) { + mumbleLog.user('User list: ${users.map((e) => e.name)}'); + } + + void onUserChanged(User? user, User? actor, UserChanges changes) { + mumbleLog.user('User $user changed $changes'); + // The user changed + // if (changes.channel) { + // // ...his channel + // if (user?.channel == client.self.channel) { + // // ... to our channel + // // So greet him + // client.self.channel + // .sendMessageToChannel(message: 'Hello ${user?.name}!'); + // } + // } + } + + void onUserRemoved(User user, User? actor, String? reason, bool? ban) { + // If the removed user is the actor that is responsible for this, the + // user simply left the server. Same is ture if the actor is null. + if (actor == null || user == actor) { + // print('${user.name} left the server'); + mumbleLog.user('${user.name} left the server'); + } else if (ban ?? false) { + // The user was baned from the server + mumbleLog + .user('${user.name} was banned by ${actor.name}, reason $reason.'); + } else { + // The user was kicked from the server + mumbleLog + .user('${user.name} was kicked by ${actor.name}, reason $reason.'); + } + } + + void onUserStats(User user, UserStats stats) {} + + void onPermissionDenied(PermissionDeniedException e) { + mumbleLog.internal('Permission denied!'); + // print( + // 'This will occur if this example is run a second time, since it will try to create a channel that already exists!'); + mumbleLog.internal('The concrete exception was: $e'); + } +} + +class MumbleClientCallback with MumbleClientListener { + final MumbleClient mumbleClient; + final void Function(List bans) _onBanListReceived; + final void Function(Channel channel) _onChannelAdded; + final void Function() _onCryptStateChanged; + final void Function() _onDone; + final void Function() _onDropAllChannelPermissions; + final void Function(Object error, [StackTrace? stackTrace]) _onError; + final void Function(PermissionDeniedException e) _onPermissionDenied; + final void Function(Map idToName) _onQueryUsersResult; + final void Function(IncomingTextMessage message) _onTextMessage; + final void Function(User user) _onUserAdded; + final void Function(List users) _onUserListReceived; + + MumbleClientCallback({ + required this.mumbleClient, + required void Function(List bans) onBanListReceived, + required void Function(Channel channel) onChannelAdded, + required void Function() onCryptStateChanged, + required void Function() onDone, + required void Function() onDropAllChannelPermissions, + required void Function(Object error, [StackTrace? stackTrace]) onError, + required void Function(PermissionDeniedException e) onPermissionDenied, + required void Function(Map idToName) onQueryUsersResult, + required void Function(IncomingTextMessage message) onTextMessage, + required void Function(User user) onUserAdded, + required void Function(List users) onUserListReceived, + }) : _onBanListReceived = onBanListReceived, + _onChannelAdded = onChannelAdded, + _onCryptStateChanged = onCryptStateChanged, + _onDone = onDone, + _onDropAllChannelPermissions = onDropAllChannelPermissions, + _onError = onError, + _onPermissionDenied = onPermissionDenied, + _onQueryUsersResult = onQueryUsersResult, + _onTextMessage = onTextMessage, + _onUserAdded = onUserAdded, + _onUserListReceived = onUserListReceived; + + @override + void onBanListReceived(List bans) => _onBanListReceived(bans); + + @override + void onChannelAdded(Channel channel) => _onChannelAdded(channel); + + @override + void onCryptStateChanged() => _onCryptStateChanged(); + + @override + void onDone() => _onDone(); + + @override + void onDropAllChannelPermissions() => _onDropAllChannelPermissions(); + + @override + void onError(Object error, [StackTrace? stackTrace]) => + _onError(error, stackTrace); + + @override + void onPermissionDenied(PermissionDeniedException e) => + _onPermissionDenied(e); + + @override + void onQueryUsersResult(Map idToName) => + _onQueryUsersResult(idToName); + + @override + void onTextMessage(IncomingTextMessage message) => _onTextMessage(message); + + @override + void onUserAdded(User user) => _onUserAdded(user); + + @override + void onUserListReceived(List users) => + _onUserListReceived(users); +} +// +// class MumbleExampleCallback with MumbleClientListener, UserListener { +// final MumbleClient client; +// +// const MumbleExampleCallback(this.client); +// +// @override +// void onBanListReceived(List bans) {} +// +// @override +// void onChannelAdded(Channel channel) { +// if (channel.name == 'Dumble Test Channel') { +// // This is our channel +// // join it +// client.self.moveToChannel(channel: channel); +// } +// } +// +// @override +// void onCryptStateChanged() {} +// +// @override +// void onDone() { +// print('onDone'); +// } +// +// @override +// void onDropAllChannelPermissions() {} +// +// @override +// void onError(error, [StackTrace? stackTrace]) { +// print('An error occured!'); +// print(error); +// if (stackTrace != null) { +// print(stackTrace); +// } +// } +// +// @override +// void onQueryUsersResult(Map idToName) {} +// +// @override +// void onTextMessage(IncomingTextMessage message) { +// print('[${new DateTime.now()}] ${message.actor?.name}: ${message.message}'); +// } +// +// @override +// void onUserAdded(User user) { +// //Keep an eye on the user +// user.add(this); +// } +// +// @override +// void onUserListReceived(List users) {} +// +// @override +// void onUserChanged(User? user, User? actor, UserChanges changes) { +// print('User $user changed $changes'); +// // The user changed +// if (changes.channel) { +// // ...his channel +// if (user?.channel == client.self.channel) { +// // ... to our channel +// // So greet him +// client.self.channel +// .sendMessageToChannel(message: 'Hello ${user?.name}!'); +// } +// } +// } +// +// @override +// void onUserRemoved(User user, User? actor, String? reason, bool? ban) { +// // If the removed user is the actor that is responsible for this, the +// // user simply left the server. Same is ture if the actor is null. +// if (actor == null || user == actor) { +// print('${user.name} left the server'); +// } else if (ban ?? false) { +// // The user was baned from the server +// print('${user.name} was banned by ${actor.name}, reason $reason.'); +// } else { +// // The user was kicked from the server +// print('${user.name} was kicked by ${actor.name}, reason $reason.'); +// } +// } +// +// @override +// void onUserStats(User user, UserStats stats) {} +// +// @override +// void onPermissionDenied(PermissionDeniedException e) { +// print('Permission denied!'); +// print( +// 'This will occur if this example is run a second time, since it will try to create a channel that already exists!'); +// print('The concrete exception was: $e'); +// } +// } diff --git a/lib/views/mumble_ui/mumble_ui.dart b/lib/views/mumble_ui/mumble_ui.dart new file mode 100644 index 0000000..c3a889d --- /dev/null +++ b/lib/views/mumble_ui/mumble_ui.dart @@ -0,0 +1,106 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:provider/provider.dart'; +import 'package:pttoc_test/views/mumble_ui/mumble_ui_view_model.dart'; +import 'package:rg_widgets/gui_utils.dart'; + +import '../../providers/mumble_provider.dart'; + +class MumbleUiView extends StatelessWidget { + const MumbleUiView({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return ChangeNotifierProvider( + create: (_) => MumbleUiViewModel( + Provider.of(context, listen: false)), + builder: (context, child) { + return Consumer( + builder: (context, mumbleUiVM, child) { + return Scaffold( + appBar: AppBar( + // Here we take the value from the MyHomePage object that was created by + // the App.build method, and use it to set our appbar title. + title: Text("Mumble Test"), + ), + body: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextField( + controller: mumbleUiVM.hostTextController, + decoration: const InputDecoration( + icon: Icon(Icons.storage), label: Text("Host")), + ), + TextField( + controller: mumbleUiVM.portTextController, + keyboardType: TextInputType.number, + maxLength: 5, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly + ], + decoration: const InputDecoration( + icon: Icon(Icons.lan), label: Text("Port"))), + TextField( + controller: mumbleUiVM.nameTextController, + decoration: const InputDecoration( + icon: Icon(Icons.person), label: Text("Name")), + ), + TextField( + controller: mumbleUiVM.passwordTextController, + decoration: const InputDecoration( + icon: Icon(Icons.key), label: Text("Password")), + obscureText: true, + obscuringCharacter: '*', + ), + + if (!mumbleUiVM.connected) + TextButton( + onPressed: () { + mumbleUiVM + .connect() + .then((value) => GuiUtils() + .showTextSnackbar(context, 'Connected!')) + .onError((error, stackTrace) { + GuiUtils().showTextSnackbar( + context, "Failed to connect!", + isError: true); + print(error); + print(stackTrace); + }); + }, + child: Text("Connect")), + + if (mumbleUiVM.connected) + TextButton( + onPressed: () { + mumbleUiVM + .disconnect() + .then((value) => GuiUtils() + .showTextSnackbar(context, 'Disconnected!')) + .onError((error, stackTrace) { + GuiUtils().showTextSnackbar( + context, "Failed to disconnect!", + isError: true); + print(error); + print(stackTrace); + }); + }, + child: Text("Disconnect")) + // Text( + // '$_counter', + // style: Theme.of(context).textTheme.headlineMedium, + // ), + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: () {}, + tooltip: 'Increment', + child: const Icon(Icons.add), + ), // This trailing comma makes auto-formatting nicer for build methods. + ); + }); + }); + } +} diff --git a/lib/views/mumble_ui/mumble_ui_view_model.dart b/lib/views/mumble_ui/mumble_ui_view_model.dart new file mode 100644 index 0000000..4bf296b --- /dev/null +++ b/lib/views/mumble_ui/mumble_ui_view_model.dart @@ -0,0 +1,34 @@ +import 'package:flutter/widgets.dart'; +import 'package:pttoc_test/providers/mumble_provider.dart'; + +class MumbleUiViewModel extends ChangeNotifier { + MumbleProvider mumbleProvider; + + TextEditingController hostTextController = + TextEditingController(text: "dr130.ketchel.xyz"); + TextEditingController portTextController = + TextEditingController(text: "64738"); + TextEditingController nameTextController = + TextEditingController(text: "Dumble Test"); + TextEditingController passwordTextController = TextEditingController(); + // Controller + + MumbleUiViewModel(this.mumbleProvider); + + Future connect() async { + // TODO: Validate form fields + return mumbleProvider + .connect( + host: hostTextController.text, + name: nameTextController.text, + port: int.parse(portTextController.text), + password: passwordTextController.text) + .whenComplete(() => notifyListeners()); + } + + Future disconnect() async { + return mumbleProvider.client!.close().whenComplete(() => notifyListeners()); + } + + bool get connected => mumbleProvider.connected; +} diff --git a/linux/.gitignore b/linux/.gitignore new file mode 100644 index 0000000..d3896c9 --- /dev/null +++ b/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt new file mode 100644 index 0000000..2dd25a6 --- /dev/null +++ b/linux/CMakeLists.txt @@ -0,0 +1,138 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "pttoc_test") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "com.rgnets.pttoc_test") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Define the application target. To change its name, change BINARY_NAME above, +# not the value here, or `flutter run` will no longer work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/linux/flutter/CMakeLists.txt b/linux/flutter/CMakeLists.txt new file mode 100644 index 0000000..d5bd016 --- /dev/null +++ b/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000..e71a16d --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,11 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + + +void fl_register_plugins(FlPluginRegistry* registry) { +} diff --git a/linux/flutter/generated_plugin_registrant.h b/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000..e0f0a47 --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake new file mode 100644 index 0000000..2e1de87 --- /dev/null +++ b/linux/flutter/generated_plugins.cmake @@ -0,0 +1,23 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/linux/main.cc b/linux/main.cc new file mode 100644 index 0000000..e7c5c54 --- /dev/null +++ b/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/linux/my_application.cc b/linux/my_application.cc new file mode 100644 index 0000000..88d5841 --- /dev/null +++ b/linux/my_application.cc @@ -0,0 +1,104 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "pttoc_test"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "pttoc_test"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + "flags", G_APPLICATION_NON_UNIQUE, + nullptr)); +} diff --git a/linux/my_application.h b/linux/my_application.h new file mode 100644 index 0000000..72271d5 --- /dev/null +++ b/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..6ab0aec --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,641 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + animated_toggle_switch: + dependency: transitive + description: + name: animated_toggle_switch + sha256: "3c6d98dcd53cb93253a9418b888b3fcd9452a55fe1a7a390bd48c0a410a41634" + url: "https://pub.dev" + source: hosted + version: "0.6.2" + async: + dependency: transitive + description: + name: async + sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + url: "https://pub.dev" + source: hosted + version: "2.10.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + url: "https://pub.dev" + source: hosted + version: "1.2.1" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + url: "https://pub.dev" + source: hosted + version: "1.17.0" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be + url: "https://pub.dev" + source: hosted + version: "1.0.5" + data_table_2: + dependency: transitive + description: + name: data_table_2 + sha256: daea1a67128e88b6b5537ee0752c67ecdc2fba9cd9303b3554361c648549618d + url: "https://pub.dev" + source: hosted + version: "2.4.2" + date_format: + dependency: transitive + description: + name: date_format + sha256: "8e5154ca363411847220c8cbc43afcf69c08e8debe40ba09d57710c25711760c" + url: "https://pub.dev" + source: hosted + version: "2.0.7" + dropdown_button2: + dependency: transitive + description: + name: dropdown_button2 + sha256: "604b87283e251e0e4a0cb1d0fdfa91fd527702b06e80999c4054cecc603e10e7" + url: "https://pub.dev" + source: hosted + version: "1.9.4" + dropdown_search: + dependency: transitive + description: + name: dropdown_search + sha256: "55106e8290acaa97ed15bea1fdad82c3cf0c248dd410e651f5a8ac6870f783ab" + url: "https://pub.dev" + source: hosted + version: "5.0.6" + dumble: + dependency: "direct main" + description: + name: dumble + sha256: "7b06fe246b185d180282c6304b89869f11897243474f52882c1ba7b0efc53838" + url: "https://pub.dev" + source: hosted + version: "0.8.9" + equatable: + dependency: transitive + description: + name: equatable + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" + source: hosted + version: "2.0.5" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99 + url: "https://pub.dev" + source: hosted + version: "2.0.2" + file: + dependency: transitive + description: + name: file + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" + source: hosted + version: "6.1.4" + file_picker: + dependency: transitive + description: + name: file_picker + sha256: c7a8e25ca60e7f331b153b0cb3d405828f18d3e72a6fa1d9440c86556fffc877 + url: "https://pub.dev" + source: hosted + version: "5.3.0" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + fl_chart: + dependency: transitive + description: + name: fl_chart + sha256: e97c5b850ad056e9b3a85d3afeb44c239a83aa994a90723940dac82234f2efaf + url: "https://pub.dev" + source: hosted + version: "0.61.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + url: "https://pub.dev" + source: hosted + version: "2.0.1" + flutter_loggy: + dependency: "direct main" + description: + name: flutter_loggy + sha256: "21b515977deefe37817cce35b0e420c7cde930b9dcfdcbeb05730ed24ee74e3a" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: "96af49aa6b57c10a312106ad6f71deed5a754029c24789bbf620ba784f0bd0b0" + url: "https://pub.dev" + source: hosted + version: "2.0.14" + flutter_sound: + dependency: "direct main" + description: + name: flutter_sound + sha256: "090a4694b11ecc744c2010621c4ffc5fe7c3079d304ea014961a72c7b72cfe6c" + url: "https://pub.dev" + source: hosted + version: "9.2.13" + flutter_sound_platform_interface: + dependency: transitive + description: + name: flutter_sound_platform_interface + sha256: "4537eaeb58a32748c42b621ad6116f7f4c6ee0a8d6ffaa501b165fe1c9df4753" + url: "https://pub.dev" + source: hosted + version: "9.2.13" + flutter_sound_web: + dependency: transitive + description: + name: flutter_sound_web + sha256: ad4ca92671a1879e1f613e900bbbdb8170b20d57d1e4e6363018fe56b055594f + url: "https://pub.dev" + source: hosted + version: "9.2.13" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + gauge_indicator: + dependency: transitive + description: + path: "." + ref: HEAD + resolved-ref: c945669d3b6bc706ef10aebcd99a733b0b0d85f0 + url: "git@github.com:rgnets/rg_gauges.git" + source: git + version: "0.3.4" + inject_js: + dependency: transitive + description: + name: inject_js + sha256: "3ab213e2c76375a611f57eb0044d32c6e18c5091d52510e7fe9f88f6bee83d19" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + intl: + dependency: transitive + description: + name: intl + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + url: "https://pub.dev" + source: hosted + version: "0.18.1" + jiffy: + dependency: transitive + description: + name: jiffy + sha256: "9d1ea8fa2955d62d3fd5f83317db4d565350deea13d7884212aa7d0ddb1c9982" + url: "https://pub.dev" + source: hosted + version: "6.1.0" + js: + dependency: transitive + description: + name: js + sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + url: "https://pub.dev" + source: hosted + version: "0.6.5" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + url: "https://pub.dev" + source: hosted + version: "4.8.1" + lints: + dependency: transitive + description: + name: lints + sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + url: "https://pub.dev" + source: hosted + version: "2.0.1" + logger: + dependency: transitive + description: + name: logger + sha256: db2ff852ed77090ba9f62d3611e4208a3d11dfa35991a81ae724c113fcb3e3f7 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + loggy: + dependency: "direct main" + description: + name: loggy + sha256: "981e03162bbd3a5a843026f75f73d26e4a0d8aa035ae060456ca7b30dfd1e339" + url: "https://pub.dev" + source: hosted + version: "2.0.3" + matcher: + dependency: transitive + description: + name: matcher + sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + url: "https://pub.dev" + source: hosted + version: "0.12.13" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + meta: + dependency: transitive + description: + name: meta + sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + url: "https://pub.dev" + source: hosted + version: "1.8.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + opus_flutter: + dependency: "direct main" + description: + name: opus_flutter + sha256: "0c55234291cfde7f8646ded9b296fbacf8a2712e27314b62cfca5c069805a6bd" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + opus_flutter_android: + dependency: transitive + description: + name: opus_flutter_android + sha256: bb2a317536a32cdaae1dde4be4286fd5bfaa65fc0249d5a624a1984c36600199 + url: "https://pub.dev" + source: hosted + version: "3.0.0" + opus_flutter_ios: + dependency: transitive + description: + name: opus_flutter_ios + sha256: "6f6d8379009c4959b738128102d67e491da8ce80394473d8e49663d31d551a41" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + opus_flutter_platform_interface: + dependency: transitive + description: + name: opus_flutter_platform_interface + sha256: "41180b74f1dacc131270d49815fd4eb46bd7cc5ad57aac84eab748d08de59138" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + opus_flutter_web: + dependency: transitive + description: + name: opus_flutter_web + sha256: "037a4cdb27c3a1c05c85aed3dbe0a4b8c2f49ccce33cef259ef34f1815af6785" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + opus_flutter_windows: + dependency: transitive + description: + name: opus_flutter_windows + sha256: "6a26862dd331c9c4b5e4c805efe3dd4e4a049e4a18bee230d994c25d72928a3b" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + path: + dependency: transitive + description: + name: path + sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + url: "https://pub.dev" + source: hosted + version: "1.8.2" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2" + url: "https://pub.dev" + source: hosted + version: "2.0.15" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86" + url: "https://pub.dev" + source: hosted + version: "2.0.27" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "1995d88ec2948dac43edf8fe58eb434d35d22a2940ecee1a9fefcd62beee6eb3" + url: "https://pub.dev" + source: hosted + version: "2.2.3" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: "2ae08f2216225427e64ad224a24354221c2c7907e448e6e0e8b57b1eb9f10ad1" + url: "https://pub.dev" + source: hosted + version: "2.1.10" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec" + url: "https://pub.dev" + source: hosted + version: "2.0.6" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: d3f80b32e83ec208ac95253e0cd4d298e104fbc63cb29c5c69edaed43b0c69d6 + url: "https://pub.dev" + source: hosted + version: "2.1.6" + platform: + dependency: transitive + description: + name: platform + sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" + url: "https://pub.dev" + source: hosted + version: "3.7.3" + process: + dependency: transitive + description: + name: process + sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + url: "https://pub.dev" + source: hosted + version: "4.2.4" + protobuf: + dependency: transitive + description: + name: protobuf + sha256: "01dd9bd0fa02548bf2ceee13545d4a0ec6046459d847b6b061d8a27237108a08" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + provider: + dependency: "direct main" + description: + name: provider + sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + url: "https://pub.dev" + source: hosted + version: "6.0.5" + recase: + dependency: transitive + description: + name: recase + sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 + url: "https://pub.dev" + source: hosted + version: "4.1.0" + responsive_toolkit: + dependency: transitive + description: + name: responsive_toolkit + sha256: "1509dfef1542d5da909b895ebed9d720901cbd38a3e6b728c7e92942eef280cc" + url: "https://pub.dev" + source: hosted + version: "0.0.5" + rg_common: + dependency: "direct main" + description: + path: "../../Repos/flutter/rxg_flutter_common/rg_common" + relative: true + source: path + version: "0.0.1" + rg_widgets: + dependency: "direct main" + description: + path: "../../Repos/flutter/rxg_flutter_common/rg_widgets" + relative: true + source: path + version: "0.0.1-dev.1" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" + url: "https://pub.dev" + source: hosted + version: "0.27.7" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + url: "https://pub.dev" + source: hosted + version: "1.9.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" + source: hosted + version: "1.11.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + url: "https://pub.dev" + source: hosted + version: "0.4.16" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + uuid: + dependency: transitive + description: + name: uuid + sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" + url: "https://pub.dev" + source: hosted + version: "3.0.7" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + web_ffi: + dependency: transitive + description: + name: web_ffi + sha256: "48ef8037f7bc051d11b88d0f2903e02bec21092c51833d37c3361c36e3edc4f7" + url: "https://pub.dev" + source: hosted + version: "0.7.2" + win32: + dependency: transitive + description: + name: win32 + sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c" + url: "https://pub.dev" + source: hosted + version: "4.1.4" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1 + url: "https://pub.dev" + source: hosted + version: "1.0.0" +sdks: + dart: ">=2.19.6 <3.0.0" + flutter: ">=3.7.0" diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..5794143 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,109 @@ +name: pttoc_test +description: A PTToC test +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: '>=2.19.6 <3.0.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + rg_widgets: + #git: # RG_WIDGETS_GIT + #url: git@github.com:rgnets/rg_widgets.git # RG_WIDGETS_GIT + ## ref: e4063f2 # RG_WIDGETS_GIT + path: ../../Repos/flutter/rxg_flutter_common/rg_widgets # RG_WIDGETS_PATH + + rg_common: + #git: # RG_COMMON_GIT + #url: git@github.com:rgnets/rg_common.git # RG_COMMON_GIT + ## ref: f727693 # RG_COMMON_GIT + path: ../../Repos/flutter/rxg_flutter_common/rg_common # RG_COMMON_PATH + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + provider: ^6.0.5 + dumble: ^0.8.9 + opus_flutter: ^3.0.1 + flutter_sound: ^9.2.13 + loggy: ^2.0.3 + flutter_loggy: ^2.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + assets: + - assets/dumble_cert.pem + - assets/dumble_key.pem + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/test/widget_test.dart b/test/widget_test.dart new file mode 100644 index 0000000..864b2bd --- /dev/null +++ b/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:pttoc_test/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +}