From 51635b2070272c961f08b05569d6a7eafaa9a6fa Mon Sep 17 00:00:00 2001 From: Joe Ardent Date: Thu, 17 Aug 2023 16:14:32 -0700 Subject: [PATCH] PoC for rust/dart ffi working. --- mobile/.editorconfig | 2 + mobile/.flutter_rust_bridge.yml | 7 + mobile/.gitignore | 51 +++++ mobile/.metadata | 44 ++++ mobile/.tool-versions | 1 + mobile/LICENSE-MIT | 21 ++ mobile/LICENSE.md | 3 + mobile/analysis_options.yaml | 29 +++ mobile/android/.gitignore | 13 ++ mobile/android/app/build.gradle | 99 +++++++++ .../android/app/src/debug/AndroidManifest.xml | 8 + .../android/app/src/main/AndroidManifest.xml | 34 +++ .../com/nebcorp-hias/cuttle/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 ++ .../app/src/main/res/values/styles.xml | 18 ++ .../app/src/profile/AndroidManifest.xml | 8 + mobile/android/build.gradle | 31 +++ mobile/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + mobile/android/settings.gradle | 11 + mobile/justfile | 18 ++ mobile/lib/bridge_definitions.dart | 30 +++ mobile/lib/bridge_generated.dart | 207 ++++++++++++++++++ mobile/lib/bridge_generated.io.dart | 169 ++++++++++++++ mobile/lib/ffi.dart | 18 ++ mobile/lib/main.dart | 148 +++++++++++++ mobile/native/.rustfmt.toml | 4 + mobile/native/Cargo.toml | 13 ++ mobile/native/src/api.rs | 59 +++++ mobile/native/src/bridge_generated.io.rs | 41 ++++ mobile/native/src/bridge_generated.rs | 101 +++++++++ mobile/native/src/lib.rs | 2 + mobile/pubspec.yaml | 94 ++++++++ mobile/test/widget_test.dart | 6 + 41 files changed, 1346 insertions(+) create mode 100644 mobile/.editorconfig create mode 100644 mobile/.flutter_rust_bridge.yml create mode 100644 mobile/.gitignore create mode 100644 mobile/.metadata create mode 100644 mobile/.tool-versions create mode 100644 mobile/LICENSE-MIT create mode 100644 mobile/LICENSE.md create mode 100644 mobile/analysis_options.yaml create mode 100644 mobile/android/.gitignore create mode 100644 mobile/android/app/build.gradle create mode 100644 mobile/android/app/src/debug/AndroidManifest.xml create mode 100644 mobile/android/app/src/main/AndroidManifest.xml create mode 100644 mobile/android/app/src/main/kotlin/main/com/nebcorp-hias/cuttle/MainActivity.kt create mode 100644 mobile/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 mobile/android/app/src/main/res/drawable/launch_background.xml create mode 100644 mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 mobile/android/app/src/main/res/values-night/styles.xml create mode 100644 mobile/android/app/src/main/res/values/styles.xml create mode 100644 mobile/android/app/src/profile/AndroidManifest.xml create mode 100644 mobile/android/build.gradle create mode 100644 mobile/android/gradle.properties create mode 100644 mobile/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 mobile/android/settings.gradle create mode 100644 mobile/justfile create mode 100644 mobile/lib/bridge_definitions.dart create mode 100644 mobile/lib/bridge_generated.dart create mode 100644 mobile/lib/bridge_generated.io.dart create mode 100644 mobile/lib/ffi.dart create mode 100644 mobile/lib/main.dart create mode 100644 mobile/native/.rustfmt.toml create mode 100644 mobile/native/Cargo.toml create mode 100644 mobile/native/src/api.rs create mode 100644 mobile/native/src/bridge_generated.io.rs create mode 100644 mobile/native/src/bridge_generated.rs create mode 100644 mobile/native/src/lib.rs create mode 100644 mobile/pubspec.yaml create mode 100644 mobile/test/widget_test.dart diff --git a/mobile/.editorconfig b/mobile/.editorconfig new file mode 100644 index 0000000..9d4c2fa --- /dev/null +++ b/mobile/.editorconfig @@ -0,0 +1,2 @@ +[*.dart] +indent_size = 2 diff --git a/mobile/.flutter_rust_bridge.yml b/mobile/.flutter_rust_bridge.yml new file mode 100644 index 0000000..f2a5bc8 --- /dev/null +++ b/mobile/.flutter_rust_bridge.yml @@ -0,0 +1,7 @@ +rust_input: + - native/src/api.rs +dart_output: + - lib/bridge_generated.dart +dart_decl_output: lib/bridge_definitions.dart +dart_format_line_length: 120 +wasm: false diff --git a/mobile/.gitignore b/mobile/.gitignore new file mode 100644 index 0000000..09360f3 --- /dev/null +++ b/mobile/.gitignore @@ -0,0 +1,51 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# 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/ + +# Web related + +# 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 +native/target +xcuserdata +jniLibs + +Cargo.lock +pubspec.lock diff --git a/mobile/.metadata b/mobile/.metadata new file mode 100644 index 0000000..105695d --- /dev/null +++ b/mobile/.metadata @@ -0,0 +1,44 @@ +# 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: 9944297138845a94256f1cf37beb88ff9a8e811a + channel: stable + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a + base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a + - platform: android + create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a + base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a + - platform: ios + create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a + base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a + - platform: linux + create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a + base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a + - platform: macos + create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a + base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a + - platform: web + create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a + base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a + - platform: windows + create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a + base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a + + # 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' diff --git a/mobile/.tool-versions b/mobile/.tool-versions new file mode 100644 index 0000000..fd85870 --- /dev/null +++ b/mobile/.tool-versions @@ -0,0 +1 @@ +java liberica-8u322+6 diff --git a/mobile/LICENSE-MIT b/mobile/LICENSE-MIT new file mode 100644 index 0000000..ff9f307 --- /dev/null +++ b/mobile/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright 2022 Viet Dinh. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/mobile/LICENSE.md b/mobile/LICENSE.md new file mode 100644 index 0000000..23034d6 --- /dev/null +++ b/mobile/LICENSE.md @@ -0,0 +1,3 @@ +This portion of the software is covered under the terms of the [Chaos +License](../LICENSE.md). Portions of the code in this mobile module are used under the terms of the +[MIT License](./LICENSE-MIT). diff --git a/mobile/analysis_options.yaml b/mobile/analysis_options.yaml new file mode 100644 index 0000000..61b6c4d --- /dev/null +++ b/mobile/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/mobile/android/.gitignore b/mobile/android/.gitignore new file mode 100644 index 0000000..6f56801 --- /dev/null +++ b/mobile/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/mobile/android/app/build.gradle b/mobile/android/app/build.gradle new file mode 100644 index 0000000..8c5a631 --- /dev/null +++ b/mobile/android/app/build.gradle @@ -0,0 +1,99 @@ +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.nebcorp_hias.cuttle" + // 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 + 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" +} + +[ + Debug: null, + Profile: '--release', + Release: '--release' +].each { + def taskPostfix = it.key + def profileMode = it.value + tasks.whenTaskAdded { task -> + if (task.name == "javaPreCompile$taskPostfix") { + task.dependsOn "cargoBuild$taskPostfix" + } + } + tasks.register("cargoBuild$taskPostfix", Exec) { + workingDir "../../native" + environment ANDROID_NDK_HOME: "$ANDROID_NDK" + commandLine 'cargo', 'ndk', + // the 2 ABIs below are used by real Android devices + '-t', 'armeabi-v7a', + '-t', 'arm64-v8a', + // for the simulator + '-t', 'x86_64', + '-o', '../android/app/src/main/jniLibs', 'build' + if (profileMode != null) { + args profileMode + } + } +} diff --git a/mobile/android/app/src/debug/AndroidManifest.xml b/mobile/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..9b15c7c --- /dev/null +++ b/mobile/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + diff --git a/mobile/android/app/src/main/AndroidManifest.xml b/mobile/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..9d3008a --- /dev/null +++ b/mobile/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + diff --git a/mobile/android/app/src/main/kotlin/main/com/nebcorp-hias/cuttle/MainActivity.kt b/mobile/android/app/src/main/kotlin/main/com/nebcorp-hias/cuttle/MainActivity.kt new file mode 100644 index 0000000..10bcd27 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/main/com/nebcorp-hias/cuttle/MainActivity.kt @@ -0,0 +1,6 @@ +package com.nebcorp_hias.cuttle + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/mobile/android/app/src/main/res/drawable-v21/launch_background.xml b/mobile/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/mobile/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/mobile/android/app/src/main/res/drawable/launch_background.xml b/mobile/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/mobile/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/mobile/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/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/mobile/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/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/mobile/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/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/mobile/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/mobile/android/app/src/main/res/values-night/styles.xml b/mobile/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/mobile/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/mobile/android/app/src/main/res/values/styles.xml b/mobile/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..cb1ef88 --- /dev/null +++ b/mobile/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/mobile/android/app/src/profile/AndroidManifest.xml b/mobile/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..9b15c7c --- /dev/null +++ b/mobile/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + diff --git a/mobile/android/build.gradle b/mobile/android/build.gradle new file mode 100644 index 0000000..713d7f6 --- /dev/null +++ b/mobile/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') +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} diff --git a/mobile/android/gradle.properties b/mobile/android/gradle.properties new file mode 100644 index 0000000..94adc3a --- /dev/null +++ b/mobile/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/mobile/android/gradle/wrapper/gradle-wrapper.properties b/mobile/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..3c472b9 --- /dev/null +++ b/mobile/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/mobile/android/settings.gradle b/mobile/android/settings.gradle new file mode 100644 index 0000000..44e62bc --- /dev/null +++ b/mobile/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/mobile/justfile b/mobile/justfile new file mode 100644 index 0000000..a16d174 --- /dev/null +++ b/mobile/justfile @@ -0,0 +1,18 @@ +default: gen lint + +gen: + flutter pub get + flutter_rust_bridge_codegen + +lint: + cd native && cargo fmt + dart format . + +clean: + flutter clean + cd native && cargo clean + +serve *args='': + flutter pub run flutter_rust_bridge:serve {{args}} + +# vim:expandtab:sw=4:ts=4 diff --git a/mobile/lib/bridge_definitions.dart b/mobile/lib/bridge_definitions.dart new file mode 100644 index 0000000..a82851a --- /dev/null +++ b/mobile/lib/bridge_definitions.dart @@ -0,0 +1,30 @@ +// AUTO GENERATED FILE, DO NOT EDIT. +// Generated by `flutter_rust_bridge`@ 1.81.0. +// ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member, prefer_is_empty, unnecessary_const + +import 'dart:convert'; +import 'dart:async'; +import 'package:meta/meta.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'; +import 'package:uuid/uuid.dart'; + +abstract class Native { + Future platform({dynamic hint}); + + FlutterRustBridgeTaskConstMeta get kPlatformConstMeta; + + Future rustReleaseMode({dynamic hint}); + + FlutterRustBridgeTaskConstMeta get kRustReleaseModeConstMeta; +} + +enum Platform { + Unknown, + AndroidBish, + Ios, + Windows, + Unix, + MacIntel, + MacApple, + Wasm, +} diff --git a/mobile/lib/bridge_generated.dart b/mobile/lib/bridge_generated.dart new file mode 100644 index 0000000..bdbc6a2 --- /dev/null +++ b/mobile/lib/bridge_generated.dart @@ -0,0 +1,207 @@ +// AUTO GENERATED FILE, DO NOT EDIT. +// Generated by `flutter_rust_bridge`@ 1.81.0. +// ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member, prefer_is_empty, unnecessary_const + +import "bridge_definitions.dart"; +import 'dart:convert'; +import 'dart:async'; +import 'package:meta/meta.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'; +import 'package:uuid/uuid.dart'; + +import 'dart:convert'; +import 'dart:async'; +import 'package:meta/meta.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'; +import 'package:uuid/uuid.dart'; + +import 'dart:ffi' as ffi; + +class NativeImpl implements Native { + final NativePlatform _platform; + factory NativeImpl(ExternalLibrary dylib) => NativeImpl.raw(NativePlatform(dylib)); + + /// Only valid on web/WASM platforms. + factory NativeImpl.wasm(FutureOr module) => NativeImpl(module as ExternalLibrary); + NativeImpl.raw(this._platform); + Future platform({dynamic hint}) { + return _platform.executeNormal(FlutterRustBridgeTask( + callFfi: (port_) => _platform.inner.wire_platform(port_), + parseSuccessData: _wire2api_platform, + constMeta: kPlatformConstMeta, + argValues: [], + hint: hint, + )); + } + + FlutterRustBridgeTaskConstMeta get kPlatformConstMeta => const FlutterRustBridgeTaskConstMeta( + debugName: "platform", + argNames: [], + ); + + Future rustReleaseMode({dynamic hint}) { + return _platform.executeNormal(FlutterRustBridgeTask( + callFfi: (port_) => _platform.inner.wire_rust_release_mode(port_), + parseSuccessData: _wire2api_bool, + constMeta: kRustReleaseModeConstMeta, + argValues: [], + hint: hint, + )); + } + + FlutterRustBridgeTaskConstMeta get kRustReleaseModeConstMeta => const FlutterRustBridgeTaskConstMeta( + debugName: "rust_release_mode", + argNames: [], + ); + + void dispose() { + _platform.dispose(); + } +// Section: wire2api + + bool _wire2api_bool(dynamic raw) { + return raw as bool; + } + + int _wire2api_i32(dynamic raw) { + return raw as int; + } + + Platform _wire2api_platform(dynamic raw) { + return Platform.values[raw as int]; + } +} + +// Section: api2wire + +// Section: finalizer + +class NativePlatform extends FlutterRustBridgeBase { + NativePlatform(ffi.DynamicLibrary dylib) : super(NativeWire(dylib)); + +// Section: api2wire + +// Section: finalizer + +// Section: api_fill_to_wire +} + +// ignore_for_file: camel_case_types, non_constant_identifier_names, avoid_positional_boolean_parameters, annotate_overrides, constant_identifier_names + +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `package:ffigen`. +// ignore_for_file: type=lint + +/// generated by flutter_rust_bridge +class NativeWire implements FlutterRustBridgeWireBase { + @internal + late final dartApi = DartApiDl(init_frb_dart_api_dl); + + /// Holds the symbol lookup function. + final ffi.Pointer Function(String symbolName) _lookup; + + /// The symbols are looked up in [dynamicLibrary]. + NativeWire(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup; + + /// The symbols are looked up with [lookup]. + NativeWire.fromLookup(ffi.Pointer Function(String symbolName) lookup) : _lookup = lookup; + + void store_dart_post_cobject( + DartPostCObjectFnType ptr, + ) { + return _store_dart_post_cobject( + ptr, + ); + } + + late final _store_dart_post_cobjectPtr = + _lookup>('store_dart_post_cobject'); + late final _store_dart_post_cobject = _store_dart_post_cobjectPtr.asFunction(); + + Object get_dart_object( + int ptr, + ) { + return _get_dart_object( + ptr, + ); + } + + late final _get_dart_objectPtr = _lookup>('get_dart_object'); + late final _get_dart_object = _get_dart_objectPtr.asFunction(); + + void drop_dart_object( + int ptr, + ) { + return _drop_dart_object( + ptr, + ); + } + + late final _drop_dart_objectPtr = _lookup>('drop_dart_object'); + late final _drop_dart_object = _drop_dart_objectPtr.asFunction(); + + int new_dart_opaque( + Object handle, + ) { + return _new_dart_opaque( + handle, + ); + } + + late final _new_dart_opaquePtr = _lookup>('new_dart_opaque'); + late final _new_dart_opaque = _new_dart_opaquePtr.asFunction(); + + int init_frb_dart_api_dl( + ffi.Pointer obj, + ) { + return _init_frb_dart_api_dl( + obj, + ); + } + + late final _init_frb_dart_api_dlPtr = + _lookup)>>('init_frb_dart_api_dl'); + late final _init_frb_dart_api_dl = _init_frb_dart_api_dlPtr.asFunction)>(); + + void wire_platform( + int port_, + ) { + return _wire_platform( + port_, + ); + } + + late final _wire_platformPtr = _lookup>('wire_platform'); + late final _wire_platform = _wire_platformPtr.asFunction(); + + void wire_rust_release_mode( + int port_, + ) { + return _wire_rust_release_mode( + port_, + ); + } + + late final _wire_rust_release_modePtr = + _lookup>('wire_rust_release_mode'); + late final _wire_rust_release_mode = _wire_rust_release_modePtr.asFunction(); + + void free_WireSyncReturn( + WireSyncReturn ptr, + ) { + return _free_WireSyncReturn( + ptr, + ); + } + + late final _free_WireSyncReturnPtr = + _lookup>('free_WireSyncReturn'); + late final _free_WireSyncReturn = _free_WireSyncReturnPtr.asFunction(); +} + +final class _Dart_Handle extends ffi.Opaque {} + +typedef DartPostCObjectFnType + = ffi.Pointer message)>>; +typedef DartPort = ffi.Int64; diff --git a/mobile/lib/bridge_generated.io.dart b/mobile/lib/bridge_generated.io.dart new file mode 100644 index 0000000..ae84555 --- /dev/null +++ b/mobile/lib/bridge_generated.io.dart @@ -0,0 +1,169 @@ +// AUTO GENERATED FILE, DO NOT EDIT. +// Generated by `flutter_rust_bridge`@ 1.81.0. +// ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member, prefer_is_empty, unnecessary_const + +import "bridge_definitions.dart"; +import 'dart:convert'; +import 'dart:async'; +import 'package:meta/meta.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'; +import 'package:uuid/uuid.dart'; +import 'bridge_generated.dart'; +export 'bridge_generated.dart'; +import 'dart:ffi' as ffi; + +class NativePlatform extends FlutterRustBridgeBase { + NativePlatform(ffi.DynamicLibrary dylib) : super(NativeWire(dylib)); + +// Section: api2wire + +// Section: finalizer + +// Section: api_fill_to_wire +} + +// ignore_for_file: camel_case_types, non_constant_identifier_names, avoid_positional_boolean_parameters, annotate_overrides, constant_identifier_names + +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `package:ffigen`. +// ignore_for_file: type=lint + +/// generated by flutter_rust_bridge +class NativeWire implements FlutterRustBridgeWireBase { + @internal + late final dartApi = DartApiDl(init_frb_dart_api_dl); + + /// Holds the symbol lookup function. + final ffi.Pointer Function(String symbolName) + _lookup; + + /// The symbols are looked up in [dynamicLibrary]. + NativeWire(ffi.DynamicLibrary dynamicLibrary) + : _lookup = dynamicLibrary.lookup; + + /// The symbols are looked up with [lookup]. + NativeWire.fromLookup( + ffi.Pointer Function(String symbolName) + lookup) + : _lookup = lookup; + + void store_dart_post_cobject( + DartPostCObjectFnType ptr, + ) { + return _store_dart_post_cobject( + ptr, + ); + } + + late final _store_dart_post_cobjectPtr = + _lookup>( + 'store_dart_post_cobject'); + late final _store_dart_post_cobject = _store_dart_post_cobjectPtr + .asFunction(); + + Object get_dart_object( + int ptr, + ) { + return _get_dart_object( + ptr, + ); + } + + late final _get_dart_objectPtr = + _lookup>( + 'get_dart_object'); + late final _get_dart_object = + _get_dart_objectPtr.asFunction(); + + void drop_dart_object( + int ptr, + ) { + return _drop_dart_object( + ptr, + ); + } + + late final _drop_dart_objectPtr = + _lookup>( + 'drop_dart_object'); + late final _drop_dart_object = + _drop_dart_objectPtr.asFunction(); + + int new_dart_opaque( + Object handle, + ) { + return _new_dart_opaque( + handle, + ); + } + + late final _new_dart_opaquePtr = + _lookup>( + 'new_dart_opaque'); + late final _new_dart_opaque = + _new_dart_opaquePtr.asFunction(); + + int init_frb_dart_api_dl( + ffi.Pointer obj, + ) { + return _init_frb_dart_api_dl( + obj, + ); + } + + late final _init_frb_dart_api_dlPtr = + _lookup)>>( + 'init_frb_dart_api_dl'); + late final _init_frb_dart_api_dl = _init_frb_dart_api_dlPtr + .asFunction)>(); + + void wire_platform( + int port_, + ) { + return _wire_platform( + port_, + ); + } + + late final _wire_platformPtr = + _lookup>( + 'wire_platform'); + late final _wire_platform = + _wire_platformPtr.asFunction(); + + void wire_rust_release_mode( + int port_, + ) { + return _wire_rust_release_mode( + port_, + ); + } + + late final _wire_rust_release_modePtr = + _lookup>( + 'wire_rust_release_mode'); + late final _wire_rust_release_mode = + _wire_rust_release_modePtr.asFunction(); + + void free_WireSyncReturn( + WireSyncReturn ptr, + ) { + return _free_WireSyncReturn( + ptr, + ); + } + + late final _free_WireSyncReturnPtr = + _lookup>( + 'free_WireSyncReturn'); + late final _free_WireSyncReturn = + _free_WireSyncReturnPtr.asFunction(); +} + +final class _Dart_Handle extends ffi.Opaque {} + +typedef DartPostCObjectFnType = ffi.Pointer< + ffi.NativeFunction< + ffi.Bool Function(DartPort port_id, ffi.Pointer message)>>; +typedef DartPort = ffi.Int64; diff --git a/mobile/lib/ffi.dart b/mobile/lib/ffi.dart new file mode 100644 index 0000000..5e469a7 --- /dev/null +++ b/mobile/lib/ffi.dart @@ -0,0 +1,18 @@ +// This file initializes the dynamic library and connects it with the stub +// generated by flutter_rust_bridge_codegen. + +import 'dart:ffi'; + +import 'bridge_generated.dart'; +import 'bridge_definitions.dart'; +export 'bridge_definitions.dart'; + +// Re-export the bridge so it is only necessary to import this file. +export 'bridge_generated.dart'; +import 'dart:io' as io; + +const _base = 'native'; + +const _dylib = 'lib$_base.so'; + +final Native api = NativeImpl(DynamicLibrary.open(_dylib)); diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart new file mode 100644 index 0000000..5e2192a --- /dev/null +++ b/mobile/lib/main.dart @@ -0,0 +1,148 @@ +import 'package:flutter/material.dart'; +import 'ffi.dart' if (dart.library.html) 'ffi_web.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + 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.blue, + ), + home: const MyHomePage(title: 'Flutter Demo Home Page'), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({Key? key, required this.title}) : super(key: key); + + // 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 { + // These futures belong to the state and are only initialized once, + // in the initState method. + late Future platform; + late Future isRelease; + + @override + void initState() { + super.initState(); + platform = api.platform(); + isRelease = api.rustReleaseMode(); + } + + @override + Widget build(BuildContext context) { + // This method is rerun every time setState is called. + // + // 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're running on"), + // To render the results of a Future, a FutureBuilder is used which + // turns a Future into an AsyncSnapshot, which can be used to + // extract the error state, the loading state and the data if + // available. + // + // Here, the generic type that the FutureBuilder manages is + // explicitly named, because if omitted the snapshot will have the + // type of AsyncSnapshot. + FutureBuilder>( + // We await two unrelated futures here, so the type has to be + // List. + future: Future.wait([platform, isRelease]), + builder: (context, snap) { + final style = Theme.of(context).textTheme.headlineMedium; + if (snap.error != null) { + // An error has been encountered, so give an appropriate response and + // pass the error details to an unobstructive tooltip. + debugPrint(snap.error.toString()); + return Tooltip( + message: snap.error.toString(), + child: Text('Unknown OS', style: style), + ); + } + + // Guard return here, the data is not ready yet. + final data = snap.data; + if (data == null) return const CircularProgressIndicator(); + + // Finally, retrieve the data expected in the same order provided + // to the FutureBuilder.future. + final Platform platform = data[0]; + final release = data[1] ? 'Release' : 'Debug'; + final text = const { + Platform.AndroidBish: 'Android, bish', + Platform.Ios: 'iOS', + Platform.MacApple: 'MacOS with Apple Silicon', + Platform.MacIntel: 'MacOS', + Platform.Windows: 'Windows', + Platform.Unix: 'Unix', + Platform.Wasm: 'the Web', + }[platform] ?? + 'Unknown OS'; + return Text('$text ($release)', style: style); + }, + ) + ], + ), + ), + ); + } +} diff --git a/mobile/native/.rustfmt.toml b/mobile/native/.rustfmt.toml new file mode 100644 index 0000000..4c8d0e1 --- /dev/null +++ b/mobile/native/.rustfmt.toml @@ -0,0 +1,4 @@ +imports_granularity = "Crate" +group_imports = "StdExternalCrate" +wrap_comments = true +edition = "2021" diff --git a/mobile/native/Cargo.toml b/mobile/native/Cargo.toml new file mode 100644 index 0000000..fdf3002 --- /dev/null +++ b/mobile/native/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "native" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +crate-type = ["cdylib", "lib"] + +[dependencies] +anyhow = "1" +flutter_rust_bridge = "1" diff --git a/mobile/native/src/api.rs b/mobile/native/src/api.rs new file mode 100644 index 0000000..366517e --- /dev/null +++ b/mobile/native/src/api.rs @@ -0,0 +1,59 @@ +// This is the entry point of your Rust library. +// When adding new code to your project, note that only items used +// here will be transformed to their Dart equivalents. + +// A plain enum without any fields. This is similar to Dart- or C-style enums. +// flutter_rust_bridge is capable of generating code for enums with fields +// (@freezed classes in Dart and tagged unions in C). +pub enum Platform { + Unknown, + AndroidBish, + Ios, + Windows, + Unix, + MacIntel, + MacApple, + Wasm, +} + +// A function definition in Rust. Similar to Dart, the return type must always be named +// and is never inferred. +pub fn platform() -> Platform { + // This is a macro, a special expression that expands into code. In Rust, all macros + // end with an exclamation mark and can be invoked with all kinds of brackets (parentheses, + // brackets and curly braces). However, certain conventions exist, for example the + // vector macro is almost always invoked as vec![..]. + // + // The cfg!() macro returns a boolean value based on the current compiler configuration. + // When attached to expressions (#[cfg(..)] form), they show or hide the expression at compile time. + // Here, however, they evaluate to runtime values, which may or may not be optimized out + // by the compiler. A variety of configurations are demonstrated here which cover most of + // the modern oeprating systems. Try running the Flutter application on different machines + // and see if it matches your expected OS. + // + // Furthermore, in Rust, the last expression in a function is the return value and does + // not have the trailing semicolon. This entire if-else chain forms a single expression. + if cfg!(windows) { + Platform::Windows + } else if cfg!(target_os = "android") { + Platform::AndroidBish + } else if cfg!(target_os = "ios") { + Platform::Ios + } else if cfg!(all(target_os = "macos", target_arch = "aarch64")) { + Platform::MacApple + } else if cfg!(target_os = "macos") { + Platform::MacIntel + } else if cfg!(target_family = "wasm") { + Platform::Wasm + } else if cfg!(unix) { + Platform::Unix + } else { + Platform::Unknown + } +} + +// The convention for Rust identifiers is the snake_case, +// and they are automatically converted to camelCase on the Dart side. +pub fn rust_release_mode() -> bool { + cfg!(not(debug_assertions)) +} diff --git a/mobile/native/src/bridge_generated.io.rs b/mobile/native/src/bridge_generated.io.rs new file mode 100644 index 0000000..a4ec960 --- /dev/null +++ b/mobile/native/src/bridge_generated.io.rs @@ -0,0 +1,41 @@ +use super::*; +// Section: wire functions + +#[no_mangle] +pub extern "C" fn wire_platform(port_: i64) { + wire_platform_impl(port_) +} + +#[no_mangle] +pub extern "C" fn wire_rust_release_mode(port_: i64) { + wire_rust_release_mode_impl(port_) +} + +// Section: allocate functions + +// Section: related functions + +// Section: impl Wire2Api + +// Section: wire structs + +// Section: impl NewWithNullPtr + +pub trait NewWithNullPtr { + fn new_with_null_ptr() -> Self; +} + +impl NewWithNullPtr for *mut T { + fn new_with_null_ptr() -> Self { + std::ptr::null_mut() + } +} + +// Section: sync execution mode utility + +#[no_mangle] +pub extern "C" fn free_WireSyncReturn(ptr: support::WireSyncReturn) { + unsafe { + let _ = support::box_from_leak_ptr(ptr); + }; +} diff --git a/mobile/native/src/bridge_generated.rs b/mobile/native/src/bridge_generated.rs new file mode 100644 index 0000000..0ede1cf --- /dev/null +++ b/mobile/native/src/bridge_generated.rs @@ -0,0 +1,101 @@ +#![allow( + non_camel_case_types, + unused, + clippy::redundant_closure, + clippy::useless_conversion, + clippy::unit_arg, + clippy::double_parens, + non_snake_case, + clippy::too_many_arguments +)] +// AUTO GENERATED FILE, DO NOT EDIT. +// Generated by `flutter_rust_bridge`@ 1.81.0. + +use core::panic::UnwindSafe; +use std::{ffi::c_void, sync::Arc}; + +use flutter_rust_bridge::{rust2dart::IntoIntoDart, *}; + +use crate::api::*; + +// Section: imports + +// Section: wire functions + +fn wire_platform_impl(port_: MessagePort) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, Platform>( + WrapInfo { + debug_name: "platform", + port: Some(port_), + mode: FfiCallMode::Normal, + }, + move || move |task_callback| Ok(platform()), + ) +} +fn wire_rust_release_mode_impl(port_: MessagePort) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, bool>( + WrapInfo { + debug_name: "rust_release_mode", + port: Some(port_), + mode: FfiCallMode::Normal, + }, + move || move |task_callback| Ok(rust_release_mode()), + ) +} +// Section: wrapper structs + +// Section: static checks + +// Section: allocate functions + +// Section: related functions + +// Section: impl Wire2Api + +pub trait Wire2Api { + fn wire2api(self) -> T; +} + +impl Wire2Api> for *mut S +where + *mut S: Wire2Api, +{ + fn wire2api(self) -> Option { + (!self.is_null()).then(|| self.wire2api()) + } +} +// Section: impl IntoDart + +impl support::IntoDart for Platform { + fn into_dart(self) -> support::DartAbi { + match self { + Self::Unknown => 0, + Self::AndroidBish => 1, + Self::Ios => 2, + Self::Windows => 3, + Self::Unix => 4, + Self::MacIntel => 5, + Self::MacApple => 6, + Self::Wasm => 7, + } + .into_dart() + } +} +impl support::IntoDartExceptPrimitive for Platform {} +impl rust2dart::IntoIntoDart for Platform { + fn into_into_dart(self) -> Self { + self + } +} + +// Section: executor + +support::lazy_static! { + pub static ref FLUTTER_RUST_BRIDGE_HANDLER: support::DefaultHandler = Default::default(); +} + +#[cfg(not(target_family = "wasm"))] +#[path = "bridge_generated.io.rs"] +mod io; +#[cfg(not(target_family = "wasm"))] +pub use io::*; diff --git a/mobile/native/src/lib.rs b/mobile/native/src/lib.rs new file mode 100644 index 0000000..dfdd872 --- /dev/null +++ b/mobile/native/src/lib.rs @@ -0,0 +1,2 @@ +mod api; +mod bridge_generated; diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml new file mode 100644 index 0000000..672dd91 --- /dev/null +++ b/mobile/pubspec.yaml @@ -0,0 +1,94 @@ +name: cuttle +description: Optical data transfer with QR codes. + +# 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 used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +version: 1.0.0+1 + +environment: + sdk: ">=3.0.0 <4.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 + + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + ffi: ^2.0.1 + flutter_rust_bridge: ^1.45.0 + meta: ^1.8.0 + uuid: ^3.0.7 + +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 + ffigen: ">=8.0.0 <9.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: + # - 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/mobile/test/widget_test.dart b/mobile/test/widget_test.dart new file mode 100644 index 0000000..000f168 --- /dev/null +++ b/mobile/test/widget_test.dart @@ -0,0 +1,6 @@ +// import 'package:flutter/material.dart'; +// import 'package:flutter_test/flutter_test.dart'; + +void main() { + // Your tests go here. +}