diff --git a/example/.gitignore b/example/.gitignore
new file mode 100644
index 0000000..a8e938c
--- /dev/null
+++ b/example/.gitignore
@@ -0,0 +1,47 @@
+# 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/
+
+# Web related
+lib/generated_plugin_registrant.dart
+
+# 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/example/.metadata b/example/.metadata
new file mode 100644
index 0000000..245a8e8
--- /dev/null
+++ b/example/.metadata
@@ -0,0 +1,30 @@
+# 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: 85684f9300908116a78138ea4c6036c35c9a1236
+ channel: stable
+
+project_type: app
+
+# Tracks metadata for the flutter migrate command
+migration:
+ platforms:
+ - platform: root
+ create_revision: 85684f9300908116a78138ea4c6036c35c9a1236
+ base_revision: 85684f9300908116a78138ea4c6036c35c9a1236
+ - platform: android
+ create_revision: 85684f9300908116a78138ea4c6036c35c9a1236
+ base_revision: 85684f9300908116a78138ea4c6036c35c9a1236
+
+ # 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/example/README.md b/example/README.md
new file mode 100644
index 0000000..2b3fce4
--- /dev/null
+++ b/example/README.md
@@ -0,0 +1,16 @@
+# example
+
+A new Flutter project.
+
+## 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/example/analysis_options.yaml b/example/analysis_options.yaml
new file mode 100644
index 0000000..61b6c4d
--- /dev/null
+++ b/example/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/example/android/.gitignore b/example/android/.gitignore
new file mode 100644
index 0000000..6f56801
--- /dev/null
+++ b/example/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/example/android/app/build.gradle b/example/android/app/build.gradle
new file mode 100644
index 0000000..ef7cb5a
--- /dev/null
+++ b/example/android/app/build.gradle
@@ -0,0 +1,71 @@
+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 33
+ 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.example.example"
+ // You can update the following values to match your application needs.
+ // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-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"
+}
diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 0000000..45d523a
--- /dev/null
+++ b/example/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..63577c2
--- /dev/null
+++ b/example/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt b/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt
new file mode 100644
index 0000000..e793a00
--- /dev/null
+++ b/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt
@@ -0,0 +1,6 @@
+package com.example.example
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity: FlutterActivity() {
+}
diff --git a/example/android/app/src/main/res/drawable-v21/launch_background.xml b/example/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 0000000..f74085f
--- /dev/null
+++ b/example/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/example/android/app/src/main/res/drawable/launch_background.xml b/example/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 0000000..304732f
--- /dev/null
+++ b/example/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..db77bb4
Binary files /dev/null and b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..17987b7
Binary files /dev/null and b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..09d4391
Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..d5f1c8d
Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4d6372e
Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/example/android/app/src/main/res/values-night/styles.xml b/example/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 0000000..06952be
--- /dev/null
+++ b/example/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..cb1ef88
--- /dev/null
+++ b/example/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/example/android/app/src/profile/AndroidManifest.xml b/example/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 0000000..45d523a
--- /dev/null
+++ b/example/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/example/android/build.gradle b/example/android/build.gradle
new file mode 100644
index 0000000..83ae220
--- /dev/null
+++ b/example/android/build.gradle
@@ -0,0 +1,31 @@
+buildscript {
+ ext.kotlin_version = '1.6.10'
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:7.1.2'
+ 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/example/android/gradle.properties b/example/android/gradle.properties
new file mode 100644
index 0000000..94adc3a
--- /dev/null
+++ b/example/android/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..cc5527d
--- /dev/null
+++ b/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jun 23 08:50:38 CEST 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
diff --git a/example/android/settings.gradle b/example/android/settings.gradle
new file mode 100644
index 0000000..44e62bc
--- /dev/null
+++ b/example/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/example/lib/main.dart b/example/lib/main.dart
new file mode 100644
index 0000000..d34c2ac
--- /dev/null
+++ b/example/lib/main.dart
@@ -0,0 +1,141 @@
+import 'dart:io';
+import 'dart:typed_data';
+import 'package:flutter/material.dart';
+import 'package:file_picker/file_picker.dart';
+import 'package:moxplatform/moxplatform.dart';
+import 'package:moxplatform_platform_interface/moxplatform_platform_interface.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 {
+ int _counter = 0;
+
+ Future _incrementCounter() async {
+ final result = await FilePicker.platform.pickFiles();
+ if (result == null) {
+ return;
+ }
+
+ final path = result.files.single.path;
+ await MoxplatformPlugin.crypto.encryptFile(
+ path!,
+ path + '.enc',
+ Uint8List.fromList(List.filled(32, 1)),
+ Uint8List.fromList(List.filled(16, 2)),
+ CipherAlgorithm.aes256CbcPkcs7,
+ );
+ print('DONE');
+ final lengthEnc = await File(path + ".enc").length();
+ final lengthOrig = await File(path).length();
+ print('Encrypted file is $lengthEnc Bytes large (Orig $lengthOrig)');
+
+ await MoxplatformPlugin.crypto.decryptFile(
+ path + '.enc',
+ path + '.dec',
+ Uint8List.fromList(List.filled(32, 1)),
+ Uint8List.fromList(List.filled(16, 2)),
+ CipherAlgorithm.aes256CbcPkcs7,
+ );
+ print('DONE');
+
+ final lengthDec = await File(path + ".dec").length();
+ print('Decrypted file is $lengthDec Bytes large (Orig $lengthOrig)');
+ }
+
+ @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.headline4,
+ ),
+ ],
+ ),
+ ),
+ floatingActionButton: FloatingActionButton(
+ onPressed: _incrementCounter,
+ tooltip: 'Increment',
+ child: const Icon(Icons.add),
+ ), // This trailing comma makes auto-formatting nicer for build methods.
+ );
+ }
+}
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
new file mode 100644
index 0000000..7c9ef67
--- /dev/null
+++ b/example/pubspec.yaml
@@ -0,0 +1,97 @@
+name: example
+description: A new Flutter project.
+
+# 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: ">=2.17.5 <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
+
+ moxplatform:
+ hosted: https://git.polynom.me/api/packages/Moxxy/pub
+ version: 0.1.11+2
+ moxplatform_android:
+ hosted: https://git.polynom.me/api/packages/Moxxy/pub
+ version: 0.1.11+2
+
+ file_picker: 5.2.0+1
+
+ # The following adds the Cupertino Icons font to your application.
+ # Use with the CupertinoIcons class for iOS style icons.
+ cupertino_icons: ^1.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:
+ # - 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/example/pubspec_overrides.yaml b/example/pubspec_overrides.yaml
new file mode 100644
index 0000000..ac056f1
--- /dev/null
+++ b/example/pubspec_overrides.yaml
@@ -0,0 +1,8 @@
+# melos_managed_dependency_overrides: moxplatform,moxplatform_android,moxplatform_platform_interface
+dependency_overrides:
+ moxplatform:
+ path: ../packages/moxplatform
+ moxplatform_android:
+ path: ../packages/moxplatform_android
+ moxplatform_platform_interface:
+ path: ../packages/moxplatform_platform_interface
diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart
new file mode 100644
index 0000000..092d222
--- /dev/null
+++ b/example/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:example/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);
+ });
+}
diff --git a/packages/moxplatform/lib/src/plugin.dart b/packages/moxplatform/lib/src/plugin.dart
index d99320e..eadc4c9 100644
--- a/packages/moxplatform/lib/src/plugin.dart
+++ b/packages/moxplatform/lib/src/plugin.dart
@@ -3,4 +3,5 @@ import 'package:moxplatform_platform_interface/moxplatform_platform_interface.da
class MoxplatformPlugin {
static IsolateHandler get handler => MoxplatformInterface.handler;
static MediaScannerImplementation get media => MoxplatformInterface.media;
+ static CryptographyImplementation get crypto => MoxplatformInterface.crypto;
}
diff --git a/packages/moxplatform_android/android/src/main/java/me/polynom/moxplatform_android/MoxplatformAndroidPlugin.java b/packages/moxplatform_android/android/src/main/java/me/polynom/moxplatform_android/MoxplatformAndroidPlugin.java
index cc95b33..f18904c 100644
--- a/packages/moxplatform_android/android/src/main/java/me/polynom/moxplatform_android/MoxplatformAndroidPlugin.java
+++ b/packages/moxplatform_android/android/src/main/java/me/polynom/moxplatform_android/MoxplatformAndroidPlugin.java
@@ -6,15 +6,23 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
+import android.os.Message;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
+import javax.crypto.Cipher;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.service.ServiceAware;
import io.flutter.embedding.engine.plugins.service.ServicePluginBinding;
@@ -136,6 +144,38 @@ public class MoxplatformAndroidPlugin extends BroadcastReceiver implements Flutt
}
result.success(true);
break;
+ case "encryptFile":
+ Thread encryptionThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ ArrayList args = (ArrayList) call.arguments;
+ String src = (String) args.get(0);
+ String dest = (String) args.get(1);
+ byte[] key = (byte[]) args.get(2);
+ byte[] iv = (byte[]) args.get(3);
+ int algorithm = (int) args.get(4);
+
+ result.success(encryptFile(src, dest, key, iv, algorithm));
+ }
+ });
+ encryptionThread.start();
+ break;
+ case "decryptFile":
+ Thread decryptionThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ ArrayList args = (ArrayList) call.arguments;
+ String src = (String) args.get(0);
+ String dest = (String) args.get(1);
+ byte[] key = (byte[]) args.get(2);
+ byte[] iv = (byte[]) args.get(3);
+ int algorithm = (int) args.get(4);
+
+ result.success(decryptFile(src, dest, key, iv, algorithm));
+ }
+ });
+ decryptionThread.start();
+ break;
default:
result.notImplemented();
break;
@@ -175,4 +215,84 @@ public class MoxplatformAndroidPlugin extends BroadcastReceiver implements Flutt
Log.d(TAG, "Detached from service");
this.service = null;
}
+
+ private String getCipherSpecFromInteger(int algorithm) {
+ switch (algorithm) {
+ case 0: return "AES_128/GCM/NoPadding";
+ case 1: return "AES_256/GCM/NoPadding";
+ case 2: return "AES_256/CBC/PKCS7PADDING";
+ default:
+ Log.d(TAG, "INVALID ALGORITHM");
+ return "";
+ }
+ }
+
+ public boolean encryptFile(String src, String dest, byte[] key, byte[] iv, int algorithm) {
+ String spec = getCipherSpecFromInteger(algorithm);
+ if (spec.isEmpty()) {
+ return false;
+ }
+
+ // Shamelessly stolen from https://github.com/hugo-pcl/native-crypto-flutter/pull/3
+ byte[] buffer = new byte[8096];
+ SecretKeySpec sk = new SecretKeySpec(key, spec);
+ try {
+ Cipher cipher = Cipher.getInstance(spec);
+ cipher.init(Cipher.ENCRYPT_MODE, sk, new IvParameterSpec(iv));
+ FileInputStream fin = new FileInputStream(src);
+ FileOutputStream fout = new FileOutputStream(dest);
+ CipherOutputStream cout = new CipherOutputStream(fout, cipher);
+ int len = 0;
+ while (true) {
+ len = fin.read(buffer);
+ if (len != 0 && len > 0) {
+ cout.write(buffer, 0, len);
+ } else {
+ break;
+ }
+ }
+ cout.flush();
+ cout.close();
+ fin.close();
+ return true;
+ } catch (Exception ex) {
+ Log.d(TAG, "ENC: " + ex.getMessage());
+ return false;
+ }
+ }
+
+ public boolean decryptFile(String src, String dest, byte[] key, byte[] iv, int algorithm) {
+ String spec = getCipherSpecFromInteger(algorithm);
+ if (spec.isEmpty()) {
+ return false;
+ }
+
+ // Shamelessly stolen from https://github.com/hugo-pcl/native-crypto-flutter/pull/3
+ byte[] buffer = new byte[8096];
+ SecretKeySpec sk = new SecretKeySpec(key, spec);
+ try {
+ Cipher cipher = Cipher.getInstance(spec);
+ cipher.init(Cipher.DECRYPT_MODE, sk, new IvParameterSpec(iv));
+ FileInputStream fin = new FileInputStream(src);
+ FileOutputStream fout = new FileOutputStream(dest);
+ CipherOutputStream cout = new CipherOutputStream(fout, cipher);
+ Log.d(TAG, "Reading from " + src + ", writing to " + dest);
+ int len = 0;
+ while (true) {
+ len = fin.read(buffer);
+ if (len != 0 && len > 0) {
+ cout.write(buffer, 0, len);
+ } else {
+ break;
+ }
+ }
+ cout.flush();
+ cout.close();
+ fin.close();
+ return true;
+ } catch (Exception ex) {
+ Log.d(TAG, "DEC: " + ex.getMessage());
+ return false;
+ }
+ }
}
diff --git a/packages/moxplatform_android/lib/src/crypto_android.dart b/packages/moxplatform_android/lib/src/crypto_android.dart
new file mode 100644
index 0000000..38fa9a5
--- /dev/null
+++ b/packages/moxplatform_android/lib/src/crypto_android.dart
@@ -0,0 +1,31 @@
+import 'dart:typed_data';
+import 'package:flutter/services.dart';
+import 'package:moxplatform_platform_interface/moxplatform_platform_interface.dart';
+
+class AndroidCryptographyImplementation extends CryptographyImplementation {
+ final _methodChannel = const MethodChannel('me.polynom.moxplatform_android');
+
+ @override
+ Future encryptFile(String sourcePath, String destPath, Uint8List key, Uint8List iv, CipherAlgorithm algorithm) async {
+ final result = await _methodChannel.invokeMethod('encryptFile', [
+ sourcePath,
+ destPath,
+ key,
+ iv,
+ algorithm.toInt(),
+ ]);
+ return result ?? false;
+ }
+
+ @override
+ Future decryptFile(String sourcePath, String destPath, Uint8List key, Uint8List iv, CipherAlgorithm algorithm) async {
+ final result = await _methodChannel.invokeMethod('decryptFile', [
+ sourcePath,
+ destPath,
+ key,
+ iv,
+ algorithm.toInt(),
+ ]);
+ return result ?? false;
+ }
+}
diff --git a/packages/moxplatform_android/lib/src/plugin_android.dart b/packages/moxplatform_android/lib/src/plugin_android.dart
index e5dd0ef..525bd80 100644
--- a/packages/moxplatform_android/lib/src/plugin_android.dart
+++ b/packages/moxplatform_android/lib/src/plugin_android.dart
@@ -1,3 +1,4 @@
+import 'package:moxplatform_android/src/crypto_android.dart';
import 'package:moxplatform_android/src/isolate_android.dart';
import 'package:moxplatform_android/src/media_android.dart';
import 'package:moxplatform_platform_interface/moxplatform_platform_interface.dart';
@@ -8,6 +9,7 @@ class MoxplatformAndroidPlugin extends MoxplatformInterface {
print('MoxplatformAndroidPlugin: Registering implementation');
MoxplatformInterface.handler = AndroidIsolateHandler();
MoxplatformInterface.media = AndroidMediaScannerImplementation();
+ MoxplatformInterface.crypto = AndroidCryptographyImplementation();
}
@override
diff --git a/packages/moxplatform_platform_interface/lib/moxplatform_platform_interface.dart b/packages/moxplatform_platform_interface/lib/moxplatform_platform_interface.dart
index 6422a37..88c3336 100644
--- a/packages/moxplatform_platform_interface/lib/moxplatform_platform_interface.dart
+++ b/packages/moxplatform_platform_interface/lib/moxplatform_platform_interface.dart
@@ -1,5 +1,7 @@
library moxplatform_platform_interface;
+export 'src/crypto.dart';
+export 'src/crypto_stub.dart';
export 'src/interface.dart';
export 'src/isolate.dart';
export 'src/isolate_stub.dart';
diff --git a/packages/moxplatform_platform_interface/lib/src/crypto.dart b/packages/moxplatform_platform_interface/lib/src/crypto.dart
new file mode 100644
index 0000000..5b3ab30
--- /dev/null
+++ b/packages/moxplatform_platform_interface/lib/src/crypto.dart
@@ -0,0 +1,32 @@
+import 'dart:typed_data';
+
+enum CipherAlgorithm {
+ aes128GcmNoPadding,
+ aes256GcmNoPadding,
+ aes256CbcPkcs7,
+}
+
+extension CipherAlgorithmToIntExtension on CipherAlgorithm {
+ int toInt() {
+ switch (this) {
+ case CipherAlgorithm.aes128GcmNoPadding: return 0;
+ case CipherAlgorithm.aes256GcmNoPadding: return 1;
+ case CipherAlgorithm.aes256CbcPkcs7: return 2;
+ }
+ }
+}
+
+/// Wrapper around platform-native cryptography APIs
+abstract class CryptographyImplementation {
+ /// Encrypt the file at [sourcePath] using [algorithm] and write the result back to
+ /// [destPath]. Note that this function runs off-thread as to not block the UI thread.
+ ///
+ /// Resolves to true if the encryption was successful. Resolves to fale on failure.
+ Future encryptFile(String sourcePath, String destPath, Uint8List key, Uint8List iv, CipherAlgorithm algorithm);
+
+ /// Decrypt the file at [sourcePath] using [algorithm] and write the result back to
+ /// [destPath]. Note that this function runs off-thread as to not block the UI thread.
+ ///
+ /// Resolves to true if the encryption was successful. Resolves to fale on failure.
+ Future decryptFile(String sourcePath, String destPath, Uint8List key, Uint8List iv, CipherAlgorithm algorithm);
+}
diff --git a/packages/moxplatform_platform_interface/lib/src/crypto_stub.dart b/packages/moxplatform_platform_interface/lib/src/crypto_stub.dart
new file mode 100644
index 0000000..4deadd0
--- /dev/null
+++ b/packages/moxplatform_platform_interface/lib/src/crypto_stub.dart
@@ -0,0 +1,14 @@
+import 'dart:typed_data';
+import 'package:moxplatform_platform_interface/src/crypto.dart';
+
+class StubCryptographyImplementation extends CryptographyImplementation {
+ @override
+ Future encryptFile(String sourcePath, String destPath, Uint8List key, Uint8List iv, CipherAlgorithm algorithm) async {
+ return false;
+ }
+
+ @override
+ Future decryptFile(String sourcePath, String destPath, Uint8List key, Uint8List iv, CipherAlgorithm algorithm) async {
+ return false;
+ }
+}
diff --git a/packages/moxplatform_platform_interface/lib/src/interface.dart b/packages/moxplatform_platform_interface/lib/src/interface.dart
index 7d348aa..208d626 100644
--- a/packages/moxplatform_platform_interface/lib/src/interface.dart
+++ b/packages/moxplatform_platform_interface/lib/src/interface.dart
@@ -1,3 +1,5 @@
+import 'package:moxplatform_platform_interface/src/crypto.dart';
+import 'package:moxplatform_platform_interface/src/crypto_stub.dart';
import 'package:moxplatform_platform_interface/src/isolate.dart';
import 'package:moxplatform_platform_interface/src/isolate_stub.dart';
import 'package:moxplatform_platform_interface/src/media.dart';
@@ -11,6 +13,7 @@ abstract class MoxplatformInterface extends PlatformInterface {
static IsolateHandler handler = StubIsolateHandler();
static MediaScannerImplementation media = StubMediaScannerImplementation();
+ static CryptographyImplementation crypto = StubCryptographyImplementation();
/// Return the current platform name.
Future getPlatformName();