Compare commits
49 Commits
7a999cf860
...
42155d9e31
Author | SHA1 | Date | |
---|---|---|---|
42155d9e31 | |||
2813e72647 | |||
4167227c7b | |||
81e819b4a7 | |||
fe07b2a488 | |||
f2b140de18 | |||
7cc2d0e4be | |||
be58288025 | |||
27440067cd | |||
11ea1fae12 | |||
fe6d0a60c1 | |||
fb71ac330a | |||
2a9afdbb0c | |||
73008c95b6 | |||
0d4f0c59cc | |||
497ac279cc | |||
3c773e5270 | |||
173d5f5166 | |||
960bad46d4 | |||
43e88af803 | |||
b12e36da83 | |||
61de3cd565 | |||
271428219a | |||
6896b928e8 | |||
79938aa177 | |||
2f5a39416b | |||
2490a8ee9f | |||
c7ee2b6c6e | |||
6da35cd0ba | |||
30ef477999 | |||
8f93821617 | |||
daf40aed0b | |||
e975e749e4 | |||
adb8ee88d1 | |||
f90b3866ab | |||
fb9dab3d1e | |||
da851a985b | |||
864b868f45 | |||
1771c0e1b6 | |||
17642f9fab | |||
d5fdbe736b | |||
2486d846e8 | |||
3cd615aa3e | |||
8c9724055f | |||
f6e442fd4b | |||
563b0386d6 | |||
eab467ee1d | |||
052a4e4700 | |||
3bc880079c |
14
.gitlint
Normal file
14
.gitlint
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[general]
|
||||||
|
ignore=B5,B6,B7,B8
|
||||||
|
|
||||||
|
[title-max-length]
|
||||||
|
line-length=72
|
||||||
|
|
||||||
|
[title-trailing-punctuation]
|
||||||
|
[title-hard-tab]
|
||||||
|
[title-match-regex]
|
||||||
|
regex=^(feat|fix|chore|refactor)\((android|ios|linux|windows|macos|interface|base|repo)(,(android|ios|linux|windows|macos|interface|base))*\): [A-Z0-9].*$
|
||||||
|
|
||||||
|
|
||||||
|
[body-trailing-whitespace]
|
||||||
|
[body-first-line-empty]
|
278
CHANGELOG.md
Normal file
278
CHANGELOG.md
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
# Change Log
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## 2023-09-03
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Packages with breaking changes:
|
||||||
|
|
||||||
|
- There are no breaking changes in this release.
|
||||||
|
|
||||||
|
Packages with other changes:
|
||||||
|
|
||||||
|
- [`moxplatform_android` - `v0.1.22`](#moxplatform_android---v0122)
|
||||||
|
- [`moxplatform_platform_interface` - `v0.1.22`](#moxplatform_platform_interface---v0122)
|
||||||
|
- [`moxplatform` - `v0.1.17+6`](#moxplatform---v01176)
|
||||||
|
|
||||||
|
Packages with dependency updates only:
|
||||||
|
|
||||||
|
> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project.
|
||||||
|
|
||||||
|
- `moxplatform` - `v0.1.17+6`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### `moxplatform_android` - `v0.1.22`
|
||||||
|
|
||||||
|
- **FIX**(android): Fix notification grouping with the foreground notification.
|
||||||
|
- **FIX**(android): Fix creating notifications on Android 13.
|
||||||
|
- **FEAT**(interface,android): Allow passing an initial locale to the service.
|
||||||
|
- **FEAT**(base,interface,android): Move more logic to Moxxy.
|
||||||
|
- **FEAT**(android,interface): Implement video thumbnail generation.
|
||||||
|
|
||||||
|
#### `moxplatform_platform_interface` - `v0.1.22`
|
||||||
|
|
||||||
|
- **FIX**(repo): Remove notification examples.
|
||||||
|
- **FEAT**(interface,android): Allow passing an initial locale to the service.
|
||||||
|
- **FEAT**(base,interface,android): Move more logic to Moxxy.
|
||||||
|
- **FEAT**(android,interface): Implement video thumbnail generation.
|
||||||
|
|
||||||
|
|
||||||
|
## 2023-09-03
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Packages with breaking changes:
|
||||||
|
|
||||||
|
- There are no breaking changes in this release.
|
||||||
|
|
||||||
|
Packages with other changes:
|
||||||
|
|
||||||
|
- [`moxplatform_android` - `v0.1.21`](#moxplatform_android---v0121)
|
||||||
|
- [`moxplatform_platform_interface` - `v0.1.21`](#moxplatform_platform_interface---v0121)
|
||||||
|
- [`moxplatform` - `v0.1.17+5`](#moxplatform---v01175)
|
||||||
|
|
||||||
|
Packages with dependency updates only:
|
||||||
|
|
||||||
|
> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project.
|
||||||
|
|
||||||
|
- `moxplatform` - `v0.1.17+5`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### `moxplatform_android` - `v0.1.21`
|
||||||
|
|
||||||
|
- **FIX**(android): Fix notification grouping with the foreground notification.
|
||||||
|
- **FIX**(android): Fix creating notifications on Android 13.
|
||||||
|
- **FEAT**(base,interface,android): Move more logic to Moxxy.
|
||||||
|
- **FEAT**(android,interface): Implement video thumbnail generation.
|
||||||
|
|
||||||
|
#### `moxplatform_platform_interface` - `v0.1.21`
|
||||||
|
|
||||||
|
- **FIX**(repo): Remove notification examples.
|
||||||
|
- **FEAT**(base,interface,android): Move more logic to Moxxy.
|
||||||
|
- **FEAT**(android,interface): Implement video thumbnail generation.
|
||||||
|
|
||||||
|
|
||||||
|
## 2023-08-05
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Packages with breaking changes:
|
||||||
|
|
||||||
|
- There are no breaking changes in this release.
|
||||||
|
|
||||||
|
Packages with other changes:
|
||||||
|
|
||||||
|
- [`moxplatform_android` - `v0.1.20`](#moxplatform_android---v0120)
|
||||||
|
- [`moxplatform_platform_interface` - `v0.1.20`](#moxplatform_platform_interface---v0120)
|
||||||
|
- [`moxplatform` - `v0.1.17+4`](#moxplatform---v01174)
|
||||||
|
|
||||||
|
Packages with dependency updates only:
|
||||||
|
|
||||||
|
> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project.
|
||||||
|
|
||||||
|
- `moxplatform` - `v0.1.17+4`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### `moxplatform_android` - `v0.1.20`
|
||||||
|
|
||||||
|
- **FIX**(android): Fix FileProvider id.
|
||||||
|
- **FEAT**(android,interface): Handle battery optimisation.
|
||||||
|
|
||||||
|
#### `moxplatform_platform_interface` - `v0.1.20`
|
||||||
|
|
||||||
|
- **FEAT**(android,interface): Handle battery optimisation.
|
||||||
|
|
||||||
|
|
||||||
|
## 2023-08-05
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Packages with breaking changes:
|
||||||
|
|
||||||
|
- There are no breaking changes in this release.
|
||||||
|
|
||||||
|
Packages with other changes:
|
||||||
|
|
||||||
|
- [`moxplatform_android` - `v0.1.19`](#moxplatform_android---v0119)
|
||||||
|
- [`moxplatform_platform_interface` - `v0.1.19`](#moxplatform_platform_interface---v0119)
|
||||||
|
- [`moxplatform` - `v0.1.17+3`](#moxplatform---v01173)
|
||||||
|
|
||||||
|
Packages with dependency updates only:
|
||||||
|
|
||||||
|
> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project.
|
||||||
|
|
||||||
|
- `moxplatform` - `v0.1.17+3`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### `moxplatform_android` - `v0.1.19`
|
||||||
|
|
||||||
|
- **FEAT**(android,interface): Handle battery optimisation.
|
||||||
|
|
||||||
|
#### `moxplatform_platform_interface` - `v0.1.19`
|
||||||
|
|
||||||
|
- **FEAT**(android,interface): Handle battery optimisation.
|
||||||
|
|
||||||
|
|
||||||
|
## 2023-08-04
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Packages with breaking changes:
|
||||||
|
|
||||||
|
- There are no breaking changes in this release.
|
||||||
|
|
||||||
|
Packages with other changes:
|
||||||
|
|
||||||
|
- [`moxplatform` - `v0.1.17+2`](#moxplatform---v01172)
|
||||||
|
- [`moxplatform_android` - `v0.1.18`](#moxplatform_android---v0118)
|
||||||
|
- [`moxplatform_platform_interface` - `v0.1.18`](#moxplatform_platform_interface---v0118)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### `moxplatform` - `v0.1.17+2`
|
||||||
|
|
||||||
|
- **FIX**: Format and lint.
|
||||||
|
|
||||||
|
#### `moxplatform_android` - `v0.1.18`
|
||||||
|
|
||||||
|
- **FIX**: Format and lint.
|
||||||
|
- **FIX**: Fix self-replies after receiving another message.
|
||||||
|
- **FIX**: Add payload to all intents.
|
||||||
|
- **FIX**: Fix images disappearing after replying.
|
||||||
|
- **FEAT**: Move recordSentMessage to pigeon.
|
||||||
|
- **FEAT**: Move the crypto APIs to pigeon.
|
||||||
|
- **FEAT**: Adjust to Moxxy changes.
|
||||||
|
- **FEAT**: Store the avatar path also in the shared preferences.
|
||||||
|
- **FEAT**: Allow the sender's data being null.
|
||||||
|
- **FEAT**: Allow attaching arbitrary data to the notification.
|
||||||
|
- **FEAT**: Allow showing regular notifications.
|
||||||
|
- **FEAT**: Make i18n data a bit more persistent.
|
||||||
|
- **FEAT**: Color in the notification silhouette.
|
||||||
|
- **FEAT**: Allow setting the self-avatar.
|
||||||
|
- **FEAT**: Take care of i18n.
|
||||||
|
|
||||||
|
#### `moxplatform_platform_interface` - `v0.1.18`
|
||||||
|
|
||||||
|
- **FIX**: Format and lint.
|
||||||
|
- **FIX**: Add payload to all intents.
|
||||||
|
- **FEAT**: Move recordSentMessage to pigeon.
|
||||||
|
- **FEAT**: Move the crypto APIs to pigeon.
|
||||||
|
- **FEAT**: Allow the sender's data being null.
|
||||||
|
- **FEAT**: Allow attaching arbitrary data to the notification.
|
||||||
|
- **FEAT**: Allow showing regular notifications.
|
||||||
|
- **FEAT**: Color in the notification silhouette.
|
||||||
|
- **FEAT**: Allow setting the self-avatar.
|
||||||
|
- **FEAT**: Take care of i18n.
|
||||||
|
|
||||||
|
|
||||||
|
## 2023-07-21
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Packages with breaking changes:
|
||||||
|
|
||||||
|
- There are no breaking changes in this release.
|
||||||
|
|
||||||
|
Packages with other changes:
|
||||||
|
|
||||||
|
- [`moxplatform_android` - `v0.1.17+1`](#moxplatform_android---v01171)
|
||||||
|
- [`moxplatform` - `v0.1.17+1`](#moxplatform---v01171)
|
||||||
|
- [`moxplatform_platform_interface` - `v0.1.17+1`](#moxplatform_platform_interface---v01171)
|
||||||
|
|
||||||
|
Packages with dependency updates only:
|
||||||
|
|
||||||
|
> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project.
|
||||||
|
|
||||||
|
- `moxplatform` - `v0.1.17+1`
|
||||||
|
- `moxplatform_platform_interface` - `v0.1.17+1`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### `moxplatform_android` - `v0.1.17+1`
|
||||||
|
|
||||||
|
- **FIX**: Accidentally used the name as the target's key. Oops.
|
||||||
|
- **FIX**: Fix minor things.
|
||||||
|
|
||||||
|
|
||||||
|
## 2023-07-21
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Packages with breaking changes:
|
||||||
|
|
||||||
|
- There are no breaking changes in this release.
|
||||||
|
|
||||||
|
Packages with other changes:
|
||||||
|
|
||||||
|
- [`moxplatform` - `v0.1.17`](#moxplatform---v0117)
|
||||||
|
- [`moxplatform_android` - `v0.1.17`](#moxplatform_android---v0117)
|
||||||
|
- [`moxplatform_platform_interface` - `v0.1.17`](#moxplatform_platform_interface---v0117)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### `moxplatform` - `v0.1.17`
|
||||||
|
|
||||||
|
- **FIX**: Fix typecasting issue.
|
||||||
|
- **FEAT**: Add an API for creating direct share shortcuts.
|
||||||
|
- **FEAT**: Migrate to moxlib 0.2.0.
|
||||||
|
- **FEAT**: I forgot to bump dependency versions.
|
||||||
|
|
||||||
|
#### `moxplatform_android` - `v0.1.17`
|
||||||
|
|
||||||
|
- **FIX**: Fix typecasting issue.
|
||||||
|
- **FEAT**: Improve code quality of the cryptography.
|
||||||
|
- **FEAT**: Rewrite recordSentMessage in Kotlin.
|
||||||
|
- **FEAT**: Add an API for creating direct share shortcuts.
|
||||||
|
- **FEAT**: Migrate to moxlib 0.2.0.
|
||||||
|
- **FEAT**: I forgot to bump dependency versions.
|
||||||
|
- **FEAT**: Also hash the file on encryption and decryption.
|
||||||
|
|
||||||
|
#### `moxplatform_platform_interface` - `v0.1.17`
|
||||||
|
|
||||||
|
- **FIX**: Fix typecasting issue.
|
||||||
|
- **FEAT**: Add an API for creating direct share shortcuts.
|
||||||
|
- **FEAT**: Migrate to moxlib 0.2.0.
|
||||||
|
- **FEAT**: I forgot to bump dependency versions.
|
||||||
|
- **FEAT**: Also hash the file on encryption and decryption.
|
||||||
|
|
@ -9,9 +9,11 @@ This repo is based on [very_good_flutter_plugin](https://github.com/VeryGoodOpen
|
|||||||
The development of this package is based on [melos](https://pub.dev/packages/melos).
|
The development of this package is based on [melos](https://pub.dev/packages/melos).
|
||||||
|
|
||||||
To make all packages link to each other locally, begin by running `melos bootstrap`. After editing
|
To make all packages link to each other locally, begin by running `melos bootstrap`. After editing
|
||||||
the code and making your changes, please run `melos run analyze` to make sure that no linter warnings
|
the code and making your changes, please format the code using `melos run format` and lint using `melos run analyze`.
|
||||||
are left inside the code.
|
|
||||||
|
When done - and a version bump is appropriate - bump the version of all packages using `melos version` and
|
||||||
|
publish with `melos publish --no-dry-run --git-tag-version`.
|
||||||
|
|
||||||
## Acknowledgements
|
## Acknowledgements
|
||||||
|
|
||||||
- [ekasetiawans](https://github.com/ekasetiawans) for [flutter_background_service](https://github.com/ekasetiawans/flutter_background_service). moxplatform_android is basically just a copy and paste of [flutter_background_service](https://github.com/ekasetiawans/flutter_background_service).
|
- [ekasetiawans](https://github.com/ekasetiawans) for [flutter_background_service](https://github.com/ekasetiawans/flutter_background_service). moxplatform_android's service implementation is basically just a copy and paste of [flutter_background_service](https://github.com/ekasetiawans/flutter_background_service).
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
include: package:very_good_analysis/analysis_options.yaml
|
include: package:very_good_analysis/analysis_options.yaml
|
||||||
|
analyzer:
|
||||||
|
exclude:
|
||||||
|
- lib/src/api.g.dart
|
||||||
|
|
||||||
linter:
|
linter:
|
||||||
rules:
|
rules:
|
||||||
public_member_api_docs: false
|
public_member_api_docs: false
|
||||||
|
@ -47,7 +47,7 @@ android {
|
|||||||
applicationId "com.example.example"
|
applicationId "com.example.example"
|
||||||
// You can update the following values to match your application needs.
|
// 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.
|
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
|
||||||
minSdkVersion flutter.minSdkVersion
|
minSdkVersion 26
|
||||||
targetSdkVersion flutter.targetSdkVersion
|
targetSdkVersion flutter.targetSdkVersion
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
|
@ -34,5 +34,5 @@
|
|||||||
|
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.6.10'
|
ext.kotlin_version = '1.8.21'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
@ -26,6 +26,6 @@ subprojects {
|
|||||||
project.evaluationDependsOn(':app')
|
project.evaluationDependsOn(':app')
|
||||||
}
|
}
|
||||||
|
|
||||||
task clean(type: Delete) {
|
tasks.register("clean", Delete) {
|
||||||
delete rootProject.buildDir
|
delete rootProject.buildDir
|
||||||
}
|
}
|
||||||
|
@ -3,59 +3,60 @@ import 'dart:typed_data';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:moxplatform/moxplatform.dart';
|
import 'package:moxplatform/moxplatform.dart';
|
||||||
import 'package:moxplatform_platform_interface/moxplatform_platform_interface.dart';
|
import 'package:path/path.dart';
|
||||||
|
|
||||||
|
/// The id of the notification channel.
|
||||||
|
const channelId = "me.polynom.moxplatform.testing3";
|
||||||
|
const otherChannelId = "me.polynom.moxplatform.testing4";
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class Sender {
|
||||||
|
const Sender(this.name, this.jid);
|
||||||
|
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
final String jid;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyApp extends StatefulWidget {
|
||||||
const MyApp({Key? key}) : super(key: key);
|
const MyApp({Key? key}) : super(key: key);
|
||||||
|
|
||||||
// This widget is the root of your application.
|
@override
|
||||||
|
MyAppState createState() => MyAppState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyAppState extends State<MyApp> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: 'Flutter Demo',
|
title: 'Moxplatform Demo',
|
||||||
theme: ThemeData(
|
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,
|
primarySwatch: Colors.blue,
|
||||||
),
|
),
|
||||||
home: const MyHomePage(title: 'Flutter Demo Home Page'),
|
home: const MyHomePage(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyHomePage extends StatefulWidget {
|
class MyHomePage extends StatefulWidget {
|
||||||
const MyHomePage({Key? key, required this.title}) : super(key: key);
|
const MyHomePage({super.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
|
@override
|
||||||
State<MyHomePage> createState() => _MyHomePageState();
|
MyHomePageState createState() => MyHomePageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MyHomePageState extends State<MyHomePage> {
|
class MyHomePageState extends State<MyHomePage> {
|
||||||
int _counter = 0;
|
/// List of "Message senders".
|
||||||
|
final List<Sender> senders = const [
|
||||||
|
Sender('Mash Kyrielight', 'mash@example.org'),
|
||||||
|
Sender('Rio Tsukatsuki', 'rio@millenium'),
|
||||||
|
Sender('Raiden Shogun', 'raiden@tevhat'),
|
||||||
|
];
|
||||||
|
|
||||||
Future<void> _incrementCounter() async {
|
Future<void> _cryptoTest() async {
|
||||||
final result = await FilePicker.platform.pickFiles();
|
final result = await FilePicker.platform.pickFiles();
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
return;
|
return;
|
||||||
@ -65,7 +66,7 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||||||
final path = result.files.single.path;
|
final path = result.files.single.path;
|
||||||
final enc = await MoxplatformPlugin.crypto.encryptFile(
|
final enc = await MoxplatformPlugin.crypto.encryptFile(
|
||||||
path!,
|
path!,
|
||||||
path + '.enc',
|
'$path.enc',
|
||||||
Uint8List.fromList(List.filled(32, 1)),
|
Uint8List.fromList(List.filled(32, 1)),
|
||||||
Uint8List.fromList(List.filled(16, 2)),
|
Uint8List.fromList(List.filled(16, 2)),
|
||||||
CipherAlgorithm.aes256CbcPkcs7,
|
CipherAlgorithm.aes256CbcPkcs7,
|
||||||
@ -74,75 +75,165 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||||||
final end = DateTime.now();
|
final end = DateTime.now();
|
||||||
|
|
||||||
final diff = end.millisecondsSinceEpoch - start.millisecondsSinceEpoch;
|
final diff = end.millisecondsSinceEpoch - start.millisecondsSinceEpoch;
|
||||||
|
// ignore: avoid_print
|
||||||
print('TIME: ${diff / 1000}s');
|
print('TIME: ${diff / 1000}s');
|
||||||
|
// ignore: avoid_print
|
||||||
print('DONE (${enc != null})');
|
print('DONE (${enc != null})');
|
||||||
final lengthEnc = await File(path + ".enc").length();
|
final lengthEnc = await File('$path.enc').length();
|
||||||
final lengthOrig = await File(path).length();
|
final lengthOrig = await File(path).length();
|
||||||
|
// ignore: avoid_print
|
||||||
print('Encrypted file is $lengthEnc Bytes large (Orig $lengthOrig)');
|
print('Encrypted file is $lengthEnc Bytes large (Orig $lengthOrig)');
|
||||||
|
|
||||||
await MoxplatformPlugin.crypto.decryptFile(
|
await MoxplatformPlugin.crypto.decryptFile(
|
||||||
path + '.enc',
|
'$path.enc',
|
||||||
path + '.dec',
|
'$path.dec',
|
||||||
Uint8List.fromList(List.filled(32, 1)),
|
Uint8List.fromList(List.filled(32, 1)),
|
||||||
Uint8List.fromList(List.filled(16, 2)),
|
Uint8List.fromList(List.filled(16, 2)),
|
||||||
CipherAlgorithm.aes256CbcPkcs7,
|
CipherAlgorithm.aes256CbcPkcs7,
|
||||||
'SHA-256',
|
'SHA-256',
|
||||||
);
|
);
|
||||||
|
// ignore: avoid_print
|
||||||
print('DONE');
|
print('DONE');
|
||||||
|
|
||||||
final lengthDec = await File(path + ".dec").length();
|
final lengthDec = await File('$path.dec').length();
|
||||||
|
// ignore: avoid_print
|
||||||
print('Decrypted file is $lengthDec Bytes large (Orig $lengthOrig)');
|
print('Decrypted file is $lengthDec Bytes large (Orig $lengthOrig)');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
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(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
// Here we take the value from the MyHomePage object that was created by
|
title: const Text('Moxplatform Demo'),
|
||||||
// the App.build method, and use it to set our appbar title.
|
|
||||||
title: Text(widget.title),
|
|
||||||
),
|
),
|
||||||
body: Center(
|
body: Center(
|
||||||
// Center is a layout widget. It takes a single child and positions it
|
child: ListView(
|
||||||
// in the middle of the parent.
|
children: [
|
||||||
child: Column(
|
ElevatedButton(
|
||||||
// Column is also a layout widget. It takes a list of children and
|
onPressed: _cryptoTest,
|
||||||
// arranges them vertically. By default, it sizes itself to fit its
|
child: const Text('Test cryptography'),
|
||||||
// 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: <Widget>[
|
|
||||||
const Text(
|
|
||||||
'You have pushed the button this many times:',
|
|
||||||
),
|
),
|
||||||
Text(
|
ElevatedButton(
|
||||||
'$_counter',
|
onPressed: () {
|
||||||
style: Theme.of(context).textTheme.headline4,
|
MoxplatformPlugin.contacts.recordSentMessage('Hallo', 'Welt');
|
||||||
|
},
|
||||||
|
child: const Text('Test recordSentMessage (no fallback)'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
MoxplatformPlugin.contacts.recordSentMessage('Person', 'Person',
|
||||||
|
fallbackIcon: FallbackIconType.person);
|
||||||
|
},
|
||||||
|
child: const Text('Test recordSentMessage (person fallback)'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
MoxplatformPlugin.contacts.recordSentMessage('Notes', 'Notes',
|
||||||
|
fallbackIcon: FallbackIconType.notes);
|
||||||
|
},
|
||||||
|
child: const Text('Test recordSentMessage (notes fallback)'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
// ignore: avoid_print
|
||||||
|
print(await MoxplatformPlugin.platform.getPersistentDataPath());
|
||||||
|
},
|
||||||
|
child: const Text('Get data directory'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
// ignore: avoid_print
|
||||||
|
print(await MoxplatformPlugin.platform.getCacheDataPath());
|
||||||
|
},
|
||||||
|
child: const Text('Get cache directory'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
// ignore: avoid_print
|
||||||
|
print(await MoxplatformPlugin.platform
|
||||||
|
.isIgnoringBatteryOptimizations());
|
||||||
|
},
|
||||||
|
child: const Text('Is battery optimised?'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await MoxplatformPlugin.platform
|
||||||
|
.openBatteryOptimisationSettings();
|
||||||
|
},
|
||||||
|
child: const Text('Open battery optimisation page'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final result = await FilePicker.platform.pickFiles(
|
||||||
|
type: FileType.video,
|
||||||
|
);
|
||||||
|
if (result == null) return;
|
||||||
|
|
||||||
|
final path = result.files.single.path!;
|
||||||
|
final storagePath =
|
||||||
|
await MoxplatformPlugin.platform.getPersistentDataPath();
|
||||||
|
final mediaPath = join(storagePath, 'media');
|
||||||
|
if (!Directory(mediaPath).existsSync()) {
|
||||||
|
await Directory(mediaPath).create(recursive: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
final internalPath = join(mediaPath, basename(path));
|
||||||
|
// ignore: avoid_print
|
||||||
|
print('Copying file');
|
||||||
|
await File(path).copy(internalPath);
|
||||||
|
|
||||||
|
// ignore: avoid_print
|
||||||
|
print('Generating thumbnail');
|
||||||
|
final thumbResult =
|
||||||
|
await MoxplatformPlugin.platform.generateVideoThumbnail(
|
||||||
|
internalPath,
|
||||||
|
'$internalPath.thumbnail.jpg',
|
||||||
|
720,
|
||||||
|
);
|
||||||
|
// ignore: avoid_print
|
||||||
|
print('Success: $thumbResult');
|
||||||
|
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
await showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => Image.file(
|
||||||
|
File('$internalPath.thumbnail.jpg'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: const Text('Thumbnail'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final result = await MoxplatformPlugin.platform.pickFiles(FilePickerType.image, false);
|
||||||
|
print('Picked files $result');
|
||||||
|
},
|
||||||
|
child: const Text('Pick image'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final result = await MoxplatformPlugin.platform.pickFiles(FilePickerType.image, true);
|
||||||
|
print('Picked files $result');
|
||||||
|
},
|
||||||
|
child: const Text('Pick multiple images'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final result = await MoxplatformPlugin.platform.pickFiles(FilePickerType.imageAndVideo, true);
|
||||||
|
print('Picked files $result');
|
||||||
|
},
|
||||||
|
child: const Text('Pick multiple images and videos'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final result = await MoxplatformPlugin.platform.pickFiles(FilePickerType.generic, true);
|
||||||
|
print('Picked files $result');
|
||||||
|
},
|
||||||
|
child: const Text('Pick multiple generic files'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
|
||||||
onPressed: _incrementCounter,
|
|
||||||
tooltip: 'Increment',
|
|
||||||
child: const Icon(Icons.add),
|
|
||||||
), // This trailing comma makes auto-formatting nicer for build methods.
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,13 +32,15 @@ dependencies:
|
|||||||
|
|
||||||
moxplatform:
|
moxplatform:
|
||||||
hosted: https://git.polynom.me/api/packages/Moxxy/pub
|
hosted: https://git.polynom.me/api/packages/Moxxy/pub
|
||||||
version: 0.1.15
|
version: 0.1.17+6
|
||||||
moxplatform_android:
|
moxplatform_android:
|
||||||
hosted: https://git.polynom.me/api/packages/Moxxy/pub
|
hosted: https://git.polynom.me/api/packages/Moxxy/pub
|
||||||
version: 0.1.15
|
version: 0.1.22
|
||||||
|
|
||||||
file_picker: 5.2.0+1
|
file_picker: 5.2.0+1
|
||||||
|
|
||||||
|
path: 1.8.3
|
||||||
|
|
||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: ^1.0.2
|
cupertino_icons: ^1.0.2
|
||||||
|
117
flake.lock
117
flake.lock
@ -1,6 +1,66 @@
|
|||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
|
"android-nixpkgs": {
|
||||||
|
"inputs": {
|
||||||
|
"devshell": "devshell",
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1689798050,
|
||||||
|
"narHash": "sha256-ZyFPra7N0MF803o55dYQQyX9b/BmXr6QTCyN7slRThY=",
|
||||||
|
"owner": "tadfisher",
|
||||||
|
"repo": "android-nixpkgs",
|
||||||
|
"rev": "9aa0e2990da86de8ca203af313668851dcb9ea6e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "tadfisher",
|
||||||
|
"repo": "android-nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"devshell": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"android-nixpkgs",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1688380630,
|
||||||
|
"narHash": "sha256-8ilApWVb1mAi4439zS3iFeIT0ODlbrifm/fegWwgHjA=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "devshell",
|
||||||
|
"rev": "f9238ec3d75cefbb2b42a44948c4e8fb1ae9a205",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "devshell",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"flake-utils": {
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems_2"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1689068808,
|
||||||
|
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1649676176,
|
"lastModified": 1649676176,
|
||||||
"narHash": "sha256-OWKJratjt2RW151VUlJPRALb7OU2S5s+f0vLj4o1bHM=",
|
"narHash": "sha256-OWKJratjt2RW151VUlJPRALb7OU2S5s+f0vLj4o1bHM=",
|
||||||
@ -17,11 +77,27 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1660551188,
|
"lastModified": 1689679375,
|
||||||
"narHash": "sha256-a1LARMMYQ8DPx1BgoI/UN4bXe12hhZkCNqdxNi6uS0g=",
|
"narHash": "sha256-LHUC52WvyVDi9PwyL1QCpaxYWBqp4ir4iL6zgOkmcb8=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "441dc5d512153039f19ef198e662e4f3dbb9fd65",
|
"rev": "684c17c429c42515bafb3ad775d2a710947f3d67",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1689752456,
|
||||||
|
"narHash": "sha256-VOChdECcEI8ixz8QY+YC4JaNEFwQd1V8bA0G4B28Ki0=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "7f256d7da238cb627ef189d56ed590739f42f13b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -33,8 +109,39 @@
|
|||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": "flake-utils",
|
"android-nixpkgs": "android-nixpkgs",
|
||||||
"nixpkgs": "nixpkgs"
|
"flake-utils": "flake-utils_2",
|
||||||
|
"nixpkgs": "nixpkgs_2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
54
flake.nix
54
flake.nix
@ -3,9 +3,10 @@
|
|||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
android-nixpkgs.url = "github:tadfisher/android-nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system: let
|
outputs = { self, nixpkgs, flake-utils, android-nixpkgs }: flake-utils.lib.eachDefaultSystem (system: let
|
||||||
pkgs = import nixpkgs {
|
pkgs = import nixpkgs {
|
||||||
inherit system;
|
inherit system;
|
||||||
config = {
|
config = {
|
||||||
@ -13,29 +14,32 @@
|
|||||||
allowUnfree = true;
|
allowUnfree = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
android = pkgs.androidenv.composeAndroidPackages {
|
# Everything to make Flutter happy
|
||||||
# TODO: Find a way to pin these
|
sdk = android-nixpkgs.sdk.${system} (sdkPkgs: with sdkPkgs; [
|
||||||
#toolsVersion = "26.1.1";
|
cmdline-tools-latest
|
||||||
#platformToolsVersion = "31.0.3";
|
build-tools-30-0-3
|
||||||
#buildToolsVersions = [ "31.0.0" ];
|
build-tools-33-0-2
|
||||||
#includeEmulator = true;
|
build-tools-34-0-0
|
||||||
#emulatorVersion = "30.6.3";
|
platform-tools
|
||||||
platformVersions = [ "28" ];
|
emulator
|
||||||
includeSources = false;
|
patcher-v4
|
||||||
includeSystemImages = true;
|
platforms-android-30
|
||||||
systemImageTypes = [ "default" ];
|
platforms-android-31
|
||||||
abiVersions = [ "x86_64" ];
|
platforms-android-33
|
||||||
includeNDK = false;
|
]);
|
||||||
useGoogleAPIs = false;
|
pinnedJDK = pkgs.jdk17;
|
||||||
useGoogleTVAddOns = false;
|
|
||||||
};
|
|
||||||
pinnedJDK = pkgs.jdk;
|
|
||||||
in {
|
in {
|
||||||
devShell = pkgs.mkShell {
|
devShell = pkgs.mkShell {
|
||||||
buildInputs = with pkgs; [
|
buildInputs = with pkgs; [
|
||||||
flutter pinnedJDK android.platform-tools dart # Flutter
|
# Android
|
||||||
gitlint jq # Code hygiene
|
pinnedJDK
|
||||||
ripgrep # General utilities
|
sdk
|
||||||
|
|
||||||
|
# Flutter
|
||||||
|
flutter dart
|
||||||
|
|
||||||
|
# Code hygiene
|
||||||
|
gitlint
|
||||||
|
|
||||||
# Flutter dependencies for linux desktop
|
# Flutter dependencies for linux desktop
|
||||||
atk
|
atk
|
||||||
@ -59,9 +63,13 @@
|
|||||||
CPATH = "${pkgs.xorg.libX11.dev}/include:${pkgs.xorg.xorgproto}/include";
|
CPATH = "${pkgs.xorg.libX11.dev}/include:${pkgs.xorg.xorgproto}/include";
|
||||||
LD_LIBRARY_PATH = with pkgs; lib.makeLibraryPath [ atk cairo epoxy gdk-pixbuf glib gtk3 harfbuzz pango ];
|
LD_LIBRARY_PATH = with pkgs; lib.makeLibraryPath [ atk cairo epoxy gdk-pixbuf glib gtk3 harfbuzz pango ];
|
||||||
|
|
||||||
ANDROID_HOME = "${android.androidsdk}/libexec/android-sdk";
|
ANDROID_HOME = "${sdk}/share/android-sdk";
|
||||||
|
ANDROID_SDK_ROOT = "${sdk}/share/android-sdk";
|
||||||
JAVA_HOME = pinnedJDK;
|
JAVA_HOME = pinnedJDK;
|
||||||
ANDROID_AVD_HOME = (toString ./.) + "/.android/avd";
|
|
||||||
|
# Fix an issue with Flutter using an older version of aapt2, which does not know
|
||||||
|
# an used parameter.
|
||||||
|
GRADLE_OPTS = "-Dorg.gradle.project.android.aapt2FromMavenOverride=${sdk}/share/android-sdk/build-tools/34.0.0/aapt2";
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -8,5 +8,7 @@ command:
|
|||||||
usePubspecOverrides: true
|
usePubspecOverrides: true
|
||||||
|
|
||||||
scripts:
|
scripts:
|
||||||
|
format:
|
||||||
|
exec: dart format .
|
||||||
analyze:
|
analyze:
|
||||||
exec: dart analyze .
|
exec: flutter analyze
|
||||||
|
@ -1,3 +1,34 @@
|
|||||||
|
## 0.1.17+6
|
||||||
|
|
||||||
|
- Update a dependency to the latest release.
|
||||||
|
|
||||||
|
## 0.1.17+5
|
||||||
|
|
||||||
|
- Update a dependency to the latest release.
|
||||||
|
|
||||||
|
## 0.1.17+4
|
||||||
|
|
||||||
|
- Update a dependency to the latest release.
|
||||||
|
|
||||||
|
## 0.1.17+3
|
||||||
|
|
||||||
|
- Update a dependency to the latest release.
|
||||||
|
|
||||||
|
## 0.1.17+2
|
||||||
|
|
||||||
|
- **FIX**: Format and lint.
|
||||||
|
|
||||||
|
## 0.1.17+1
|
||||||
|
|
||||||
|
- Update a dependency to the latest release.
|
||||||
|
|
||||||
|
## 0.1.17
|
||||||
|
|
||||||
|
- **FIX**: Fix typecasting issue.
|
||||||
|
- **FEAT**: Add an API for creating direct share shortcuts.
|
||||||
|
- **FEAT**: Migrate to moxlib 0.2.0.
|
||||||
|
- **FEAT**: I forgot to bump dependency versions.
|
||||||
|
|
||||||
## 0.1.11+2
|
## 0.1.11+2
|
||||||
|
|
||||||
- Update a dependency to the latest release.
|
- Update a dependency to the latest release.
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
library moxplatform;
|
library moxplatform;
|
||||||
|
|
||||||
|
export 'package:moxplatform_platform_interface/moxplatform_platform_interface.dart';
|
||||||
|
|
||||||
export 'src/plugin.dart';
|
export 'src/plugin.dart';
|
||||||
export 'src/types.dart';
|
export 'src/types.dart';
|
||||||
|
@ -2,6 +2,7 @@ import 'package:moxplatform_platform_interface/moxplatform_platform_interface.da
|
|||||||
|
|
||||||
class MoxplatformPlugin {
|
class MoxplatformPlugin {
|
||||||
static IsolateHandler get handler => MoxplatformInterface.handler;
|
static IsolateHandler get handler => MoxplatformInterface.handler;
|
||||||
static MediaScannerImplementation get media => MoxplatformInterface.media;
|
|
||||||
static CryptographyImplementation get crypto => MoxplatformInterface.crypto;
|
static CryptographyImplementation get crypto => MoxplatformInterface.crypto;
|
||||||
|
static ContactsImplementation get contacts => MoxplatformInterface.contacts;
|
||||||
|
static PlatformImplementation get platform => MoxplatformInterface.platform;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:moxlib/awaitabledatasender.dart';
|
import 'package:moxlib/moxlib.dart';
|
||||||
|
|
||||||
abstract class BackgroundCommand implements JsonImplementation {}
|
abstract class BackgroundCommand implements JsonImplementation {}
|
||||||
|
|
||||||
abstract class BackgroundEvent implements JsonImplementation {}
|
abstract class BackgroundEvent implements JsonImplementation {}
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
|
@ -0,0 +1 @@
|
|||||||
|
|
@ -1,11 +1,11 @@
|
|||||||
name: moxplatform
|
name: moxplatform
|
||||||
description: Moxxy platform-specific code
|
description: Moxxy platform-specific code
|
||||||
version: 0.1.15
|
version: 0.1.17+6
|
||||||
publish_to: https://git.polynom.me/api/packages/Moxxy/pub
|
publish_to: https://git.polynom.me/api/packages/Moxxy/pub
|
||||||
homepage: https://codeberg.org/moxxy/moxplatform
|
homepage: https://codeberg.org/moxxy/moxplatform
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.16.0 <3.0.0"
|
sdk: ">=2.17.0 <3.0.0"
|
||||||
flutter: ">=2.10.0"
|
flutter: ">=2.10.0"
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
@ -22,17 +22,17 @@ dependencies:
|
|||||||
|
|
||||||
moxlib:
|
moxlib:
|
||||||
hosted: https://git.polynom.me/api/packages/Moxxy/pub
|
hosted: https://git.polynom.me/api/packages/Moxxy/pub
|
||||||
version: ^0.1.4
|
version: ^0.2.0
|
||||||
|
|
||||||
moxplatform_android:
|
moxplatform_android:
|
||||||
hosted: https://git.polynom.me/api/packages/Moxxy/pub
|
hosted: https://git.polynom.me/api/packages/Moxxy/pub
|
||||||
version: ^0.1.15
|
version: ^0.1.22
|
||||||
moxplatform_platform_interface:
|
moxplatform_platform_interface:
|
||||||
hosted: https://git.polynom.me/api/packages/Moxxy/pub
|
hosted: https://git.polynom.me/api/packages/Moxxy/pub
|
||||||
version: ^0.1.15
|
version: ^0.1.22
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
plugin_platform_interface: ^2.1.2
|
plugin_platform_interface: ^2.1.2
|
||||||
very_good_analysis: ^2.4.0
|
very_good_analysis: ^3.0.1
|
1
packages/moxplatform_android/.pubignore
Normal file
1
packages/moxplatform_android/.pubignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
pubspec_overrides.yaml
|
@ -1,3 +1,60 @@
|
|||||||
|
## 0.1.22
|
||||||
|
|
||||||
|
- **FIX**(android): Fix notification grouping with the foreground notification.
|
||||||
|
- **FIX**(android): Fix creating notifications on Android 13.
|
||||||
|
- **FEAT**(interface,android): Allow passing an initial locale to the service.
|
||||||
|
- **FEAT**(base,interface,android): Move more logic to Moxxy.
|
||||||
|
- **FEAT**(android,interface): Implement video thumbnail generation.
|
||||||
|
|
||||||
|
## 0.1.21
|
||||||
|
|
||||||
|
- **FIX**(android): Fix notification grouping with the foreground notification.
|
||||||
|
- **FIX**(android): Fix creating notifications on Android 13.
|
||||||
|
- **FEAT**(base,interface,android): Move more logic to Moxxy.
|
||||||
|
- **FEAT**(android,interface): Implement video thumbnail generation.
|
||||||
|
|
||||||
|
## 0.1.20
|
||||||
|
|
||||||
|
- **FIX**(android): Fix FileProvider id.
|
||||||
|
- **FEAT**(android,interface): Handle battery optimisation.
|
||||||
|
|
||||||
|
## 0.1.19
|
||||||
|
|
||||||
|
- **FEAT**(android,interface): Handle battery optimisation.
|
||||||
|
|
||||||
|
## 0.1.18
|
||||||
|
|
||||||
|
- **FIX**: Format and lint.
|
||||||
|
- **FIX**: Fix self-replies after receiving another message.
|
||||||
|
- **FIX**: Add payload to all intents.
|
||||||
|
- **FIX**: Fix images disappearing after replying.
|
||||||
|
- **FEAT**: Move recordSentMessage to pigeon.
|
||||||
|
- **FEAT**: Move the crypto APIs to pigeon.
|
||||||
|
- **FEAT**: Adjust to Moxxy changes.
|
||||||
|
- **FEAT**: Store the avatar path also in the shared preferences.
|
||||||
|
- **FEAT**: Allow the sender's data being null.
|
||||||
|
- **FEAT**: Allow attaching arbitrary data to the notification.
|
||||||
|
- **FEAT**: Allow showing regular notifications.
|
||||||
|
- **FEAT**: Make i18n data a bit more persistent.
|
||||||
|
- **FEAT**: Color in the notification silhouette.
|
||||||
|
- **FEAT**: Allow setting the self-avatar.
|
||||||
|
- **FEAT**: Take care of i18n.
|
||||||
|
|
||||||
|
## 0.1.17+1
|
||||||
|
|
||||||
|
- **FIX**: Accidentally used the name as the target's key. Oops.
|
||||||
|
- **FIX**: Fix minor things.
|
||||||
|
|
||||||
|
## 0.1.17
|
||||||
|
|
||||||
|
- **FIX**: Fix typecasting issue.
|
||||||
|
- **FEAT**: Improve code quality of the cryptography.
|
||||||
|
- **FEAT**: Rewrite recordSentMessage in Kotlin.
|
||||||
|
- **FEAT**: Add an API for creating direct share shortcuts.
|
||||||
|
- **FEAT**: Migrate to moxlib 0.2.0.
|
||||||
|
- **FEAT**: I forgot to bump dependency versions.
|
||||||
|
- **FEAT**: Also hash the file on encryption and decryption.
|
||||||
|
|
||||||
## 0.1.11+2
|
## 0.1.11+2
|
||||||
|
|
||||||
- **REFACTOR**: Make version constraints looser.
|
- **REFACTOR**: Make version constraints looser.
|
||||||
|
@ -2,13 +2,17 @@ group 'me.polynom.moxplatform_android'
|
|||||||
version '1.0'
|
version '1.0'
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
|
ext {
|
||||||
|
kotlin_version = '1.8.21'
|
||||||
|
}
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
classpath 'com.android.tools.build:gradle:4.1.3'
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,9 +24,10 @@ rootProject.allprojects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'com.android.library'
|
apply plugin: 'com.android.library'
|
||||||
|
apply plugin: 'org.jetbrains.kotlin.android'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 31
|
compileSdkVersion 33
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
@ -30,10 +35,14 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
// What Moxxy currently uses
|
||||||
|
minSdkVersion 26
|
||||||
|
targetSdkVersion 33
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
|
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
|
||||||
|
implementation 'androidx.core:core:1.10.1'
|
||||||
|
implementation 'androidx.activity:activity:1.7.2'
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="me.polynom.moxplatform_android">
|
package="me.polynom.moxplatform_android">
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
|
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||||
|
|
||||||
|
|
||||||
<application>
|
<application>
|
||||||
@ -26,6 +28,5 @@
|
|||||||
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
@ -0,0 +1,507 @@
|
|||||||
|
// Autogenerated from Pigeon (v10.1.4), do not edit directly.
|
||||||
|
// See also: https://pub.dev/packages/pigeon
|
||||||
|
|
||||||
|
package me.polynom.moxplatform_android;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import io.flutter.plugin.common.BasicMessageChannel;
|
||||||
|
import io.flutter.plugin.common.BinaryMessenger;
|
||||||
|
import io.flutter.plugin.common.MessageCodec;
|
||||||
|
import io.flutter.plugin.common.StandardMessageCodec;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/** Generated class from Pigeon. */
|
||||||
|
@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression", "serial"})
|
||||||
|
public class Api {
|
||||||
|
|
||||||
|
/** Error class for passing custom error details to Flutter via a thrown PlatformException. */
|
||||||
|
public static class FlutterError extends RuntimeException {
|
||||||
|
|
||||||
|
/** The error code. */
|
||||||
|
public final String code;
|
||||||
|
|
||||||
|
/** The error details. Must be a datatype supported by the api codec. */
|
||||||
|
public final Object details;
|
||||||
|
|
||||||
|
public FlutterError(@NonNull String code, @Nullable String message, @Nullable Object details)
|
||||||
|
{
|
||||||
|
super(message);
|
||||||
|
this.code = code;
|
||||||
|
this.details = details;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
protected static ArrayList<Object> wrapError(@NonNull Throwable exception) {
|
||||||
|
ArrayList<Object> errorList = new ArrayList<Object>(3);
|
||||||
|
if (exception instanceof FlutterError) {
|
||||||
|
FlutterError error = (FlutterError) exception;
|
||||||
|
errorList.add(error.code);
|
||||||
|
errorList.add(error.getMessage());
|
||||||
|
errorList.add(error.details);
|
||||||
|
} else {
|
||||||
|
errorList.add(exception.toString());
|
||||||
|
errorList.add(exception.getClass().getSimpleName());
|
||||||
|
errorList.add(
|
||||||
|
"Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception));
|
||||||
|
}
|
||||||
|
return errorList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum CipherAlgorithm {
|
||||||
|
AES128GCM_NO_PADDING(0),
|
||||||
|
AES256GCM_NO_PADDING(1),
|
||||||
|
AES256CBC_PKCS7(2);
|
||||||
|
|
||||||
|
final int index;
|
||||||
|
|
||||||
|
private CipherAlgorithm(final int index) {
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum FallbackIconType {
|
||||||
|
NONE(0),
|
||||||
|
PERSON(1),
|
||||||
|
NOTES(2);
|
||||||
|
|
||||||
|
final int index;
|
||||||
|
|
||||||
|
private FallbackIconType(final int index) {
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum FilePickerType {
|
||||||
|
IMAGE(0),
|
||||||
|
VIDEO(1),
|
||||||
|
IMAGE_AND_VIDEO(2),
|
||||||
|
GENERIC(3);
|
||||||
|
|
||||||
|
final int index;
|
||||||
|
|
||||||
|
private FilePickerType(final int index) {
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generated class from Pigeon that represents data sent in messages. */
|
||||||
|
public static final class CryptographyResult {
|
||||||
|
private @NonNull byte[] plaintextHash;
|
||||||
|
|
||||||
|
public @NonNull byte[] getPlaintextHash() {
|
||||||
|
return plaintextHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlaintextHash(@NonNull byte[] setterArg) {
|
||||||
|
if (setterArg == null) {
|
||||||
|
throw new IllegalStateException("Nonnull field \"plaintextHash\" is null.");
|
||||||
|
}
|
||||||
|
this.plaintextHash = setterArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @NonNull byte[] ciphertextHash;
|
||||||
|
|
||||||
|
public @NonNull byte[] getCiphertextHash() {
|
||||||
|
return ciphertextHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCiphertextHash(@NonNull byte[] setterArg) {
|
||||||
|
if (setterArg == null) {
|
||||||
|
throw new IllegalStateException("Nonnull field \"ciphertextHash\" is null.");
|
||||||
|
}
|
||||||
|
this.ciphertextHash = setterArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constructor is non-public to enforce null safety; use Builder. */
|
||||||
|
CryptographyResult() {}
|
||||||
|
|
||||||
|
public static final class Builder {
|
||||||
|
|
||||||
|
private @Nullable byte[] plaintextHash;
|
||||||
|
|
||||||
|
public @NonNull Builder setPlaintextHash(@NonNull byte[] setterArg) {
|
||||||
|
this.plaintextHash = setterArg;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable byte[] ciphertextHash;
|
||||||
|
|
||||||
|
public @NonNull Builder setCiphertextHash(@NonNull byte[] setterArg) {
|
||||||
|
this.ciphertextHash = setterArg;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull CryptographyResult build() {
|
||||||
|
CryptographyResult pigeonReturn = new CryptographyResult();
|
||||||
|
pigeonReturn.setPlaintextHash(plaintextHash);
|
||||||
|
pigeonReturn.setCiphertextHash(ciphertextHash);
|
||||||
|
return pigeonReturn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
ArrayList<Object> toList() {
|
||||||
|
ArrayList<Object> toListResult = new ArrayList<Object>(2);
|
||||||
|
toListResult.add(plaintextHash);
|
||||||
|
toListResult.add(ciphertextHash);
|
||||||
|
return toListResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
static @NonNull CryptographyResult fromList(@NonNull ArrayList<Object> list) {
|
||||||
|
CryptographyResult pigeonResult = new CryptographyResult();
|
||||||
|
Object plaintextHash = list.get(0);
|
||||||
|
pigeonResult.setPlaintextHash((byte[]) plaintextHash);
|
||||||
|
Object ciphertextHash = list.get(1);
|
||||||
|
pigeonResult.setCiphertextHash((byte[]) ciphertextHash);
|
||||||
|
return pigeonResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Result<T> {
|
||||||
|
@SuppressWarnings("UnknownNullness")
|
||||||
|
void success(T result);
|
||||||
|
|
||||||
|
void error(@NonNull Throwable error);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MoxplatformApiCodec extends StandardMessageCodec {
|
||||||
|
public static final MoxplatformApiCodec INSTANCE = new MoxplatformApiCodec();
|
||||||
|
|
||||||
|
private MoxplatformApiCodec() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) {
|
||||||
|
switch (type) {
|
||||||
|
case (byte) 128:
|
||||||
|
return CryptographyResult.fromList((ArrayList<Object>) readValue(buffer));
|
||||||
|
default:
|
||||||
|
return super.readValueOfType(type, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) {
|
||||||
|
if (value instanceof CryptographyResult) {
|
||||||
|
stream.write(128);
|
||||||
|
writeValue(stream, ((CryptographyResult) value).toList());
|
||||||
|
} else {
|
||||||
|
super.writeValue(stream, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
||||||
|
public interface MoxplatformApi {
|
||||||
|
/** Platform APIs */
|
||||||
|
@NonNull
|
||||||
|
String getPersistentDataPath();
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
String getCacheDataPath();
|
||||||
|
|
||||||
|
void openBatteryOptimisationSettings();
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
Boolean isIgnoringBatteryOptimizations();
|
||||||
|
/** Contacts APIs */
|
||||||
|
void recordSentMessage(@NonNull String name, @NonNull String jid, @Nullable String avatarPath, @NonNull FallbackIconType fallbackIcon);
|
||||||
|
/** Cryptography APIs */
|
||||||
|
void encryptFile(@NonNull String sourcePath, @NonNull String destPath, @NonNull byte[] key, @NonNull byte[] iv, @NonNull CipherAlgorithm algorithm, @NonNull String hashSpec, @NonNull Result<CryptographyResult> result);
|
||||||
|
|
||||||
|
void decryptFile(@NonNull String sourcePath, @NonNull String destPath, @NonNull byte[] key, @NonNull byte[] iv, @NonNull CipherAlgorithm algorithm, @NonNull String hashSpec, @NonNull Result<CryptographyResult> result);
|
||||||
|
|
||||||
|
void hashFile(@NonNull String sourcePath, @NonNull String hashSpec, @NonNull Result<byte[]> result);
|
||||||
|
/** Media APIs */
|
||||||
|
@NonNull
|
||||||
|
Boolean generateVideoThumbnail(@NonNull String src, @NonNull String dest, @NonNull Long maxWidth);
|
||||||
|
/** Picker */
|
||||||
|
void pickFiles(@NonNull FilePickerType type, @NonNull Boolean pickMultiple, @NonNull Result<List<String>> result);
|
||||||
|
|
||||||
|
/** The codec used by MoxplatformApi. */
|
||||||
|
static @NonNull MessageCodec<Object> getCodec() {
|
||||||
|
return MoxplatformApiCodec.INSTANCE;
|
||||||
|
}
|
||||||
|
/**Sets up an instance of `MoxplatformApi` to handle messages through the `binaryMessenger`. */
|
||||||
|
static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable MoxplatformApi api) {
|
||||||
|
{
|
||||||
|
BasicMessageChannel<Object> channel =
|
||||||
|
new BasicMessageChannel<>(
|
||||||
|
binaryMessenger, "dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.getPersistentDataPath", getCodec());
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler(
|
||||||
|
(message, reply) -> {
|
||||||
|
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||||
|
try {
|
||||||
|
String output = api.getPersistentDataPath();
|
||||||
|
wrapped.add(0, output);
|
||||||
|
}
|
||||||
|
catch (Throwable exception) {
|
||||||
|
ArrayList<Object> wrappedError = wrapError(exception);
|
||||||
|
wrapped = wrappedError;
|
||||||
|
}
|
||||||
|
reply.reply(wrapped);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
BasicMessageChannel<Object> channel =
|
||||||
|
new BasicMessageChannel<>(
|
||||||
|
binaryMessenger, "dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.getCacheDataPath", getCodec());
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler(
|
||||||
|
(message, reply) -> {
|
||||||
|
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||||
|
try {
|
||||||
|
String output = api.getCacheDataPath();
|
||||||
|
wrapped.add(0, output);
|
||||||
|
}
|
||||||
|
catch (Throwable exception) {
|
||||||
|
ArrayList<Object> wrappedError = wrapError(exception);
|
||||||
|
wrapped = wrappedError;
|
||||||
|
}
|
||||||
|
reply.reply(wrapped);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
BasicMessageChannel<Object> channel =
|
||||||
|
new BasicMessageChannel<>(
|
||||||
|
binaryMessenger, "dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.openBatteryOptimisationSettings", getCodec());
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler(
|
||||||
|
(message, reply) -> {
|
||||||
|
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||||
|
try {
|
||||||
|
api.openBatteryOptimisationSettings();
|
||||||
|
wrapped.add(0, null);
|
||||||
|
}
|
||||||
|
catch (Throwable exception) {
|
||||||
|
ArrayList<Object> wrappedError = wrapError(exception);
|
||||||
|
wrapped = wrappedError;
|
||||||
|
}
|
||||||
|
reply.reply(wrapped);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
BasicMessageChannel<Object> channel =
|
||||||
|
new BasicMessageChannel<>(
|
||||||
|
binaryMessenger, "dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.isIgnoringBatteryOptimizations", getCodec());
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler(
|
||||||
|
(message, reply) -> {
|
||||||
|
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||||
|
try {
|
||||||
|
Boolean output = api.isIgnoringBatteryOptimizations();
|
||||||
|
wrapped.add(0, output);
|
||||||
|
}
|
||||||
|
catch (Throwable exception) {
|
||||||
|
ArrayList<Object> wrappedError = wrapError(exception);
|
||||||
|
wrapped = wrappedError;
|
||||||
|
}
|
||||||
|
reply.reply(wrapped);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
BasicMessageChannel<Object> channel =
|
||||||
|
new BasicMessageChannel<>(
|
||||||
|
binaryMessenger, "dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.recordSentMessage", getCodec());
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler(
|
||||||
|
(message, reply) -> {
|
||||||
|
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||||
|
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||||
|
String nameArg = (String) args.get(0);
|
||||||
|
String jidArg = (String) args.get(1);
|
||||||
|
String avatarPathArg = (String) args.get(2);
|
||||||
|
FallbackIconType fallbackIconArg = args.get(3) == null ? null : FallbackIconType.values()[(int) args.get(3)];
|
||||||
|
try {
|
||||||
|
api.recordSentMessage(nameArg, jidArg, avatarPathArg, fallbackIconArg);
|
||||||
|
wrapped.add(0, null);
|
||||||
|
}
|
||||||
|
catch (Throwable exception) {
|
||||||
|
ArrayList<Object> wrappedError = wrapError(exception);
|
||||||
|
wrapped = wrappedError;
|
||||||
|
}
|
||||||
|
reply.reply(wrapped);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
BasicMessageChannel<Object> channel =
|
||||||
|
new BasicMessageChannel<>(
|
||||||
|
binaryMessenger, "dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.encryptFile", getCodec());
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler(
|
||||||
|
(message, reply) -> {
|
||||||
|
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||||
|
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||||
|
String sourcePathArg = (String) args.get(0);
|
||||||
|
String destPathArg = (String) args.get(1);
|
||||||
|
byte[] keyArg = (byte[]) args.get(2);
|
||||||
|
byte[] ivArg = (byte[]) args.get(3);
|
||||||
|
CipherAlgorithm algorithmArg = args.get(4) == null ? null : CipherAlgorithm.values()[(int) args.get(4)];
|
||||||
|
String hashSpecArg = (String) args.get(5);
|
||||||
|
Result<CryptographyResult> resultCallback =
|
||||||
|
new Result<CryptographyResult>() {
|
||||||
|
public void success(CryptographyResult result) {
|
||||||
|
wrapped.add(0, result);
|
||||||
|
reply.reply(wrapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void error(Throwable error) {
|
||||||
|
ArrayList<Object> wrappedError = wrapError(error);
|
||||||
|
reply.reply(wrappedError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
api.encryptFile(sourcePathArg, destPathArg, keyArg, ivArg, algorithmArg, hashSpecArg, resultCallback);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
BasicMessageChannel<Object> channel =
|
||||||
|
new BasicMessageChannel<>(
|
||||||
|
binaryMessenger, "dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.decryptFile", getCodec());
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler(
|
||||||
|
(message, reply) -> {
|
||||||
|
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||||
|
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||||
|
String sourcePathArg = (String) args.get(0);
|
||||||
|
String destPathArg = (String) args.get(1);
|
||||||
|
byte[] keyArg = (byte[]) args.get(2);
|
||||||
|
byte[] ivArg = (byte[]) args.get(3);
|
||||||
|
CipherAlgorithm algorithmArg = args.get(4) == null ? null : CipherAlgorithm.values()[(int) args.get(4)];
|
||||||
|
String hashSpecArg = (String) args.get(5);
|
||||||
|
Result<CryptographyResult> resultCallback =
|
||||||
|
new Result<CryptographyResult>() {
|
||||||
|
public void success(CryptographyResult result) {
|
||||||
|
wrapped.add(0, result);
|
||||||
|
reply.reply(wrapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void error(Throwable error) {
|
||||||
|
ArrayList<Object> wrappedError = wrapError(error);
|
||||||
|
reply.reply(wrappedError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
api.decryptFile(sourcePathArg, destPathArg, keyArg, ivArg, algorithmArg, hashSpecArg, resultCallback);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
BasicMessageChannel<Object> channel =
|
||||||
|
new BasicMessageChannel<>(
|
||||||
|
binaryMessenger, "dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.hashFile", getCodec());
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler(
|
||||||
|
(message, reply) -> {
|
||||||
|
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||||
|
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||||
|
String sourcePathArg = (String) args.get(0);
|
||||||
|
String hashSpecArg = (String) args.get(1);
|
||||||
|
Result<byte[]> resultCallback =
|
||||||
|
new Result<byte[]>() {
|
||||||
|
public void success(byte[] result) {
|
||||||
|
wrapped.add(0, result);
|
||||||
|
reply.reply(wrapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void error(Throwable error) {
|
||||||
|
ArrayList<Object> wrappedError = wrapError(error);
|
||||||
|
reply.reply(wrappedError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
api.hashFile(sourcePathArg, hashSpecArg, resultCallback);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
BasicMessageChannel<Object> channel =
|
||||||
|
new BasicMessageChannel<>(
|
||||||
|
binaryMessenger, "dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.generateVideoThumbnail", getCodec());
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler(
|
||||||
|
(message, reply) -> {
|
||||||
|
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||||
|
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||||
|
String srcArg = (String) args.get(0);
|
||||||
|
String destArg = (String) args.get(1);
|
||||||
|
Number maxWidthArg = (Number) args.get(2);
|
||||||
|
try {
|
||||||
|
Boolean output = api.generateVideoThumbnail(srcArg, destArg, (maxWidthArg == null) ? null : maxWidthArg.longValue());
|
||||||
|
wrapped.add(0, output);
|
||||||
|
}
|
||||||
|
catch (Throwable exception) {
|
||||||
|
ArrayList<Object> wrappedError = wrapError(exception);
|
||||||
|
wrapped = wrappedError;
|
||||||
|
}
|
||||||
|
reply.reply(wrapped);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
BasicMessageChannel<Object> channel =
|
||||||
|
new BasicMessageChannel<>(
|
||||||
|
binaryMessenger, "dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.pickFiles", getCodec());
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler(
|
||||||
|
(message, reply) -> {
|
||||||
|
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||||
|
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||||
|
FilePickerType typeArg = args.get(0) == null ? null : FilePickerType.values()[(int) args.get(0)];
|
||||||
|
Boolean pickMultipleArg = (Boolean) args.get(1);
|
||||||
|
Result<List<String>> resultCallback =
|
||||||
|
new Result<List<String>>() {
|
||||||
|
public void success(List<String> result) {
|
||||||
|
wrapped.add(0, result);
|
||||||
|
reply.reply(wrapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void error(Throwable error) {
|
||||||
|
ArrayList<Object> wrappedError = wrapError(error);
|
||||||
|
reply.reply(wrappedError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
api.pickFiles(typeArg, pickMultipleArg, resultCallback);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,13 @@
|
|||||||
package me.polynom.moxplatform_android;
|
package me.polynom.moxplatform_android;
|
||||||
|
|
||||||
|
import static me.polynom.moxplatform_android.ConstantsKt.GROUP_KEY_FOREGROUND;
|
||||||
|
import static me.polynom.moxplatform_android.ConstantsKt.GROUP_KEY_MESSAGES;
|
||||||
|
import static me.polynom.moxplatform_android.ConstantsKt.GROUP_KEY_OTHER;
|
||||||
|
import static me.polynom.moxplatform_android.ConstantsKt.SHARED_PREFERENCES_KEY;
|
||||||
|
|
||||||
import android.app.AlarmManager;
|
import android.app.AlarmManager;
|
||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
|
import android.app.NotificationChannelGroup;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
@ -85,29 +91,15 @@ public class BackgroundService extends Service implements MethodChannel.MethodCa
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isManuallyStopped(Context context) {
|
public static boolean isManuallyStopped(Context context) {
|
||||||
return context.getSharedPreferences(MoxplatformAndroidPlugin.sharedPrefKey, MODE_PRIVATE).getBoolean(manuallyStoppedKey, false);
|
return context.getSharedPreferences(SHARED_PREFERENCES_KEY, MODE_PRIVATE).getBoolean(manuallyStoppedKey, false);
|
||||||
}
|
}
|
||||||
public void setManuallyStopped(Context context, boolean value) {
|
public void setManuallyStopped(Context context, boolean value) {
|
||||||
context.getSharedPreferences(MoxplatformAndroidPlugin.sharedPrefKey, MODE_PRIVATE)
|
context.getSharedPreferences(SHARED_PREFERENCES_KEY, MODE_PRIVATE)
|
||||||
.edit()
|
.edit()
|
||||||
.putBoolean(manuallyStoppedKey, value)
|
.putBoolean(manuallyStoppedKey, value)
|
||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createNotificationChannel() {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
CharSequence name = "Moxxy Background Service";
|
|
||||||
String description = "Executing Moxxy in the background";
|
|
||||||
|
|
||||||
int importance = NotificationManager.IMPORTANCE_LOW;
|
|
||||||
NotificationChannel channel = new NotificationChannel("FOREGROUND_DEFAULT", name, importance);
|
|
||||||
channel.setDescription(description);
|
|
||||||
|
|
||||||
NotificationManager notificationManager = getSystemService(NotificationManager.class);
|
|
||||||
notificationManager.createNotificationChannel(channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void updateNotificationInfo() {
|
protected void updateNotificationInfo() {
|
||||||
String packageName = getApplicationContext().getPackageName();
|
String packageName = getApplicationContext().getPackageName();
|
||||||
Intent i = getPackageManager().getLaunchIntentForPackage(packageName);
|
Intent i = getPackageManager().getLaunchIntentForPackage(packageName);
|
||||||
@ -122,7 +114,7 @@ public class BackgroundService extends Service implements MethodChannel.MethodCa
|
|||||||
| (PendingIntent.FLAG_MUTABLE & (aboveS ? PendingIntent.FLAG_MUTABLE : 0))
|
| (PendingIntent.FLAG_MUTABLE & (aboveS ? PendingIntent.FLAG_MUTABLE : 0))
|
||||||
);
|
);
|
||||||
|
|
||||||
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, "FOREGROUND_DEFAULT")
|
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, "foreground_service")
|
||||||
.setSmallIcon(R.drawable.ic_service_icon)
|
.setSmallIcon(R.drawable.ic_service_icon)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.setOngoing(true)
|
.setOngoing(true)
|
||||||
@ -151,7 +143,7 @@ public class BackgroundService extends Service implements MethodChannel.MethodCa
|
|||||||
FlutterInjector.instance().flutterLoader().startInitialization(getApplicationContext());
|
FlutterInjector.instance().flutterLoader().startInitialization(getApplicationContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
long entrypointHandle = getSharedPreferences(MoxplatformAndroidPlugin.sharedPrefKey, MODE_PRIVATE)
|
long entrypointHandle = getSharedPreferences(SHARED_PREFERENCES_KEY, MODE_PRIVATE)
|
||||||
.getLong(MoxplatformAndroidPlugin.entrypointKey, 0);
|
.getLong(MoxplatformAndroidPlugin.entrypointKey, 0);
|
||||||
FlutterInjector.instance().flutterLoader().ensureInitializationComplete(getApplicationContext(), null);
|
FlutterInjector.instance().flutterLoader().ensureInitializationComplete(getApplicationContext(), null);
|
||||||
FlutterCallbackInformation callback = FlutterCallbackInformation.lookupCallbackInformation(entrypointHandle);
|
FlutterCallbackInformation callback = FlutterCallbackInformation.lookupCallbackInformation(entrypointHandle);
|
||||||
@ -237,7 +229,6 @@ public class BackgroundService extends Service implements MethodChannel.MethodCa
|
|||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
||||||
createNotificationChannel();
|
|
||||||
notificationBody = "Preparing...";
|
notificationBody = "Preparing...";
|
||||||
updateNotificationInfo();
|
updateNotificationInfo();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
package me.polynom.moxplatform_android
|
||||||
|
|
||||||
|
// The tag we use for logging.
|
||||||
|
const val TAG = "Moxplatform"
|
||||||
|
|
||||||
|
// The size of the buffer to hashing, encryption, and decryption in bytes.
|
||||||
|
const val BUFFER_SIZE = 8096
|
||||||
|
|
||||||
|
const val GROUP_KEY_FOREGROUND = "foreground"
|
||||||
|
const val GROUP_KEY_MESSAGES = "messages"
|
||||||
|
const val GROUP_KEY_OTHER = "other"
|
||||||
|
|
||||||
|
// The data key for text entered in the notification's reply field
|
||||||
|
const val REPLY_TEXT_KEY = "key_reply_text"
|
||||||
|
|
||||||
|
// The key for the notification id to mark as read
|
||||||
|
const val MARK_AS_READ_ID_KEY = "notification_id"
|
||||||
|
|
||||||
|
// Values for actions performed through the notification
|
||||||
|
const val REPLY_ACTION = "reply"
|
||||||
|
const val MARK_AS_READ_ACTION = "mark_as_read"
|
||||||
|
const val TAP_ACTION = "tap"
|
||||||
|
|
||||||
|
// Extra data keys for the intents that reach the NotificationReceiver
|
||||||
|
const val NOTIFICATION_EXTRA_JID_KEY = "jid"
|
||||||
|
const val NOTIFICATION_EXTRA_ID_KEY = "notification_id"
|
||||||
|
|
||||||
|
// Extra data keys for messages embedded inside the notification style
|
||||||
|
const val NOTIFICATION_MESSAGE_EXTRA_MIME = "mime"
|
||||||
|
const val NOTIFICATION_MESSAGE_EXTRA_PATH = "path"
|
||||||
|
|
||||||
|
const val MOXPLATFORM_FILEPROVIDER_ID = "me.polynom.moxplatform_android.fileprovider"
|
||||||
|
|
||||||
|
// Shared preferences keys
|
||||||
|
const val SHARED_PREFERENCES_KEY = "me.polynom.moxplatform_android"
|
||||||
|
const val SHARED_PREFERENCES_YOU_KEY = "you"
|
||||||
|
const val SHARED_PREFERENCES_MARK_AS_READ_KEY = "mark_as_read"
|
||||||
|
const val SHARED_PREFERENCES_REPLY_KEY = "reply"
|
||||||
|
const val SHARED_PREFERENCES_AVATAR_KEY = "avatar_path"
|
||||||
|
|
||||||
|
// TODO: Maybe try again to rewrite the entire plugin in Kotlin
|
||||||
|
//const val METHOD_CHANNEL_KEY = "me.polynom.moxplatform_android"
|
||||||
|
//const val BACKGROUND_METHOD_CHANNEL_KEY = METHOD_CHANNEL_KEY + "_bg"
|
||||||
|
|
||||||
|
// https://github.com/ekasetiawans/flutter_background_service/blob/e427f3b70138ec26f9671c2617f9061f25eade6f/packages/flutter_background_service_android/android/src/main/java/id/flutter/flutter_background_service/BootReceiver.java#L20
|
||||||
|
//const val WAKELOCK_DURATION = 10*60*1000L;
|
||||||
|
|
||||||
|
// The name of the wakelock the background service manager holds.
|
||||||
|
//const val SERVICE_WAKELOCK_NAME = "BackgroundService.Lock"
|
||||||
|
|
||||||
|
//const val DATA_RECEIVER_METHOD_NAME = "dataReceived"
|
||||||
|
|
||||||
|
// Shared preferences keys
|
||||||
|
//const val SHARED_PREFERENCES_KEY = "me.polynom.moxplatform_android"
|
||||||
|
//const val SP_MANUALLY_STOPPED_KEY = "manually_stopped"
|
||||||
|
//const val SP_ENTRYPOINT_KEY = "entrypoint_handle"
|
||||||
|
//const val SP_EXTRA_DATA_KEY = "extra_data"
|
||||||
|
//const val SP_AUTO_START_AT_BOOT_KEY = "auto_start_at_boot"
|
@ -0,0 +1,158 @@
|
|||||||
|
package me.polynom.moxplatform_android
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import me.polynom.moxplatform_android.Api.CipherAlgorithm
|
||||||
|
import me.polynom.moxplatform_android.Api.CryptographyResult
|
||||||
|
|
||||||
|
import java.io.FileInputStream
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.lang.Exception
|
||||||
|
import java.security.MessageDigest
|
||||||
|
import javax.crypto.Cipher
|
||||||
|
import javax.crypto.CipherOutputStream
|
||||||
|
import javax.crypto.spec.IvParameterSpec
|
||||||
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
|
// A FileOutputStream that continuously hashes whatever it writes to the file.
|
||||||
|
private class HashedFileOutputStream(name: String, hashAlgorithm: String) : FileOutputStream(name) {
|
||||||
|
private val digest: MessageDigest
|
||||||
|
|
||||||
|
init {
|
||||||
|
this.digest = MessageDigest.getInstance(hashAlgorithm)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun write(buffer: ByteArray, offset: Int, length: Int) {
|
||||||
|
super.write(buffer, offset, length)
|
||||||
|
|
||||||
|
digest.update(buffer, offset, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun digest() : ByteArray {
|
||||||
|
return digest.digest()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCipherSpecFromInteger(algorithm: CipherAlgorithm): String {
|
||||||
|
return when (algorithm) {
|
||||||
|
CipherAlgorithm.AES128GCM_NO_PADDING -> "AES_128/GCM/NoPadding"
|
||||||
|
CipherAlgorithm.AES256GCM_NO_PADDING -> "AES_256/GCM/NoPadding"
|
||||||
|
CipherAlgorithm.AES256CBC_PKCS7 -> "AES_256/CBC/PKCS7PADDING"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the hash, specified by @algorithm, of the file at path @srcFile. If an exception
|
||||||
|
// occurs, returns null. If everything went well, returns the raw hash of @srcFile.
|
||||||
|
fun hashFile(srcFile: String, algorithm: String, result: Api.Result<ByteArray?>) {
|
||||||
|
thread(start = true) {
|
||||||
|
val buffer = ByteArray(BUFFER_SIZE)
|
||||||
|
try {
|
||||||
|
val digest = MessageDigest.getInstance(algorithm)
|
||||||
|
val fInputStream = FileInputStream(srcFile)
|
||||||
|
var length: Int
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
length = fInputStream.read()
|
||||||
|
if (length <= 0) break
|
||||||
|
|
||||||
|
// Only update the digest if we read more than 0 bytes
|
||||||
|
digest.update(buffer, 0, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
fInputStream.close()
|
||||||
|
|
||||||
|
result.success(digest.digest())
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "[hashFile]: " + e.stackTraceToString())
|
||||||
|
result.success(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt the plaintext file at @src to @dest using the secret key @key and the IV @iv. The algorithm is chosen using @cipherAlgorithm. The file is additionally
|
||||||
|
// hashed before and after encryption using the hash algorithm specified by @hashAlgorithm.
|
||||||
|
fun encryptAndHash(src: String, dest: String, key: ByteArray, iv: ByteArray, cipherAlgorithm: CipherAlgorithm, hashAlgorithm: String, result: Api.Result<CryptographyResult?>) {
|
||||||
|
thread(start = true) {
|
||||||
|
val cipherSpec = getCipherSpecFromInteger(cipherAlgorithm)
|
||||||
|
val buffer = ByteArray(BUFFER_SIZE)
|
||||||
|
val secretKey = SecretKeySpec(key, cipherSpec)
|
||||||
|
try {
|
||||||
|
val digest = MessageDigest.getInstance(hashAlgorithm)
|
||||||
|
val cipher = Cipher.getInstance(cipherSpec)
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, secretKey, IvParameterSpec(iv))
|
||||||
|
|
||||||
|
val fileInputStream = FileInputStream(src)
|
||||||
|
val fileOutputStream = HashedFileOutputStream(dest, hashAlgorithm)
|
||||||
|
val cipherOutputStream = CipherOutputStream(fileOutputStream, cipher)
|
||||||
|
|
||||||
|
var length: Int
|
||||||
|
while (true) {
|
||||||
|
length = fileInputStream.read(buffer)
|
||||||
|
if (length <= 0) break
|
||||||
|
|
||||||
|
digest.update(buffer, 0, length)
|
||||||
|
cipherOutputStream.write(buffer, 0, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush and close
|
||||||
|
cipherOutputStream.flush()
|
||||||
|
cipherOutputStream.close()
|
||||||
|
fileInputStream.close()
|
||||||
|
|
||||||
|
result.success(
|
||||||
|
CryptographyResult().apply {
|
||||||
|
plaintextHash = digest.digest()
|
||||||
|
ciphertextHash = fileOutputStream.digest()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "[encryptAndHash]: " + e.stackTraceToString())
|
||||||
|
result.success(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt the ciphertext file at @src to @dest using the secret key @key and the IV @iv. The algorithm is chosen using @cipherAlgorithm. The file is additionally
|
||||||
|
// hashed before and after decryption using the hash algorithm specified by @hashAlgorithm.
|
||||||
|
fun decryptAndHash(src: String, dest: String, key: ByteArray, iv: ByteArray, cipherAlgorithm: CipherAlgorithm, hashAlgorithm: String, result: Api.Result<CryptographyResult?>) {
|
||||||
|
thread(start = true) {
|
||||||
|
val cipherSpec = getCipherSpecFromInteger(cipherAlgorithm)
|
||||||
|
// Shamelessly stolen from https://github.com/hugo-pcl/native-crypto-flutter/pull/3
|
||||||
|
val buffer = ByteArray(BUFFER_SIZE)
|
||||||
|
val secretKey = SecretKeySpec(key, cipherSpec)
|
||||||
|
try {
|
||||||
|
val digest = MessageDigest.getInstance(hashAlgorithm)
|
||||||
|
val cipher = Cipher.getInstance(cipherSpec)
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, secretKey, IvParameterSpec(iv))
|
||||||
|
|
||||||
|
val fileInputStream = FileInputStream(src)
|
||||||
|
val fileOutputStream = HashedFileOutputStream(dest, hashAlgorithm)
|
||||||
|
val cipherOutputStream = CipherOutputStream(fileOutputStream, cipher)
|
||||||
|
|
||||||
|
// Read, decrypt, and hash until we read 0 bytes
|
||||||
|
var length: Int
|
||||||
|
while (true) {
|
||||||
|
length = fileInputStream.read(buffer)
|
||||||
|
if (length <= 0) break
|
||||||
|
|
||||||
|
digest.update(buffer, 0, length)
|
||||||
|
cipherOutputStream.write(buffer, 0, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush
|
||||||
|
cipherOutputStream.flush()
|
||||||
|
cipherOutputStream.close()
|
||||||
|
fileInputStream.close()
|
||||||
|
|
||||||
|
result.success(
|
||||||
|
CryptographyResult().apply {
|
||||||
|
plaintextHash = digest.digest()
|
||||||
|
ciphertextHash = fileOutputStream.digest()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "[hashAndDecrypt]: " + e.stackTraceToString())
|
||||||
|
result.success(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,34 +0,0 @@
|
|||||||
package me.polynom.moxplatform_android;
|
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
public class HashedFileOutputStream extends FileOutputStream {
|
|
||||||
public MessageDigest digest;
|
|
||||||
|
|
||||||
public HashedFileOutputStream(String name, String hashSpec) throws FileNotFoundException, NoSuchAlgorithmException {
|
|
||||||
super(name);
|
|
||||||
|
|
||||||
digest = MessageDigest.getInstance(hashSpec);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(byte[] b, int off, int len) throws IOException {
|
|
||||||
super.write(b, off, len);
|
|
||||||
|
|
||||||
digest.update(b, off, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getHexHash() {
|
|
||||||
StringBuffer result = new StringBuffer();
|
|
||||||
for (byte b : digest.digest()) result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
|
|
||||||
return result.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getHash() {
|
|
||||||
return digest.digest();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,43 +1,79 @@
|
|||||||
package me.polynom.moxplatform_android;
|
package me.polynom.moxplatform_android;
|
||||||
|
|
||||||
|
import static android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS;
|
||||||
|
import static androidx.core.content.ContextCompat.getSystemService;
|
||||||
|
import static androidx.core.content.ContextCompat.startActivity;
|
||||||
|
import static me.polynom.moxplatform_android.ConstantsKt.MOXPLATFORM_FILEPROVIDER_ID;
|
||||||
|
import static me.polynom.moxplatform_android.ConstantsKt.SHARED_PREFERENCES_KEY;
|
||||||
|
import static me.polynom.moxplatform_android.CryptoKt.*;
|
||||||
|
import static me.polynom.moxplatform_android.PickerKt.filePickerRequest;
|
||||||
|
import static me.polynom.moxplatform_android.PickerKt.onActivityResultImpl;
|
||||||
|
import static me.polynom.moxplatform_android.RecordSentMessageKt.*;
|
||||||
|
import static me.polynom.moxplatform_android.ThumbnailsKt.generateVideoThumbnailImplementation;
|
||||||
|
|
||||||
|
import me.polynom.moxplatform_android.Api.*;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
|
import android.app.NotificationChannel;
|
||||||
|
import android.app.NotificationManager;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.media.MediaMetadataRetriever;
|
||||||
|
import android.media.ThumbnailUtils;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.PowerManager;
|
||||||
|
import android.provider.MediaStore;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.Size;
|
||||||
|
|
||||||
|
import androidx.activity.result.PickVisualMediaRequest;
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.core.app.NotificationManagerCompat;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.core.content.FileProvider;
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.security.MessageDigest;
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import javax.crypto.Cipher;
|
import java.util.Set;
|
||||||
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.FlutterPlugin;
|
||||||
import io.flutter.embedding.engine.plugins.service.ServiceAware;
|
import io.flutter.embedding.engine.plugins.service.ServiceAware;
|
||||||
import io.flutter.embedding.engine.plugins.service.ServicePluginBinding;
|
import io.flutter.embedding.engine.plugins.service.ServicePluginBinding;
|
||||||
|
import io.flutter.plugin.common.EventChannel;
|
||||||
|
import io.flutter.plugin.common.EventChannel.EventSink;
|
||||||
|
import io.flutter.plugin.common.EventChannel.StreamHandler;
|
||||||
import io.flutter.plugin.common.MethodCall;
|
import io.flutter.plugin.common.MethodCall;
|
||||||
import io.flutter.plugin.common.MethodChannel;
|
import io.flutter.plugin.common.MethodChannel;
|
||||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
||||||
import io.flutter.plugin.common.MethodChannel.Result;
|
import io.flutter.plugin.common.MethodChannel.Result;
|
||||||
import io.flutter.plugin.common.PluginRegistry.Registrar;
|
import io.flutter.plugin.common.PluginRegistry.Registrar;
|
||||||
|
import io.flutter.plugin.common.PluginRegistry;
|
||||||
import io.flutter.plugin.common.JSONMethodCodec;
|
import io.flutter.plugin.common.JSONMethodCodec;
|
||||||
|
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
|
||||||
|
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
|
||||||
|
import kotlin.Unit;
|
||||||
|
import kotlin.jvm.functions.Function1;
|
||||||
|
|
||||||
public class MoxplatformAndroidPlugin extends BroadcastReceiver implements FlutterPlugin, MethodCallHandler, ServiceAware {
|
public class MoxplatformAndroidPlugin extends BroadcastReceiver implements FlutterPlugin, MethodCallHandler, ServiceAware, ActivityAware, PluginRegistry.ActivityResultListener, MoxplatformApi {
|
||||||
public static final String entrypointKey = "entrypoint_handle";
|
public static final String entrypointKey = "entrypoint_handle";
|
||||||
public static final String extraDataKey = "extra_data";
|
public static final String extraDataKey = "extra_data";
|
||||||
private static final String autoStartAtBootKey = "auto_start_at_boot";
|
private static final String autoStartAtBootKey = "auto_start_at_boot";
|
||||||
public static final String sharedPrefKey = "me.polynom.moxplatform_android";
|
|
||||||
private static final String TAG = "moxplatform_android";
|
private static final String TAG = "moxplatform_android";
|
||||||
public static final String methodChannelKey = "me.polynom.moxplatform_android";
|
public static final String methodChannelKey = "me.polynom.moxplatform_android";
|
||||||
public static final String dataReceivedMethodName = "dataReceived";
|
public static final String dataReceivedMethodName = "dataReceived";
|
||||||
@ -45,12 +81,19 @@ public class MoxplatformAndroidPlugin extends BroadcastReceiver implements Flutt
|
|||||||
private static final List<MoxplatformAndroidPlugin> _instances = new ArrayList<>();
|
private static final List<MoxplatformAndroidPlugin> _instances = new ArrayList<>();
|
||||||
private BackgroundService service;
|
private BackgroundService service;
|
||||||
private MethodChannel channel;
|
private MethodChannel channel;
|
||||||
|
|
||||||
|
public static Activity activity;
|
||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
public MoxplatformAndroidPlugin() {
|
public MoxplatformAndroidPlugin() {
|
||||||
_instances.add(this);
|
_instances.add(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||||
|
return onActivityResultImpl(context, requestCode, resultCode, data);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
|
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
|
||||||
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), methodChannelKey);
|
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), methodChannelKey);
|
||||||
@ -60,6 +103,8 @@ public class MoxplatformAndroidPlugin extends BroadcastReceiver implements Flutt
|
|||||||
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this.context);
|
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this.context);
|
||||||
localBroadcastManager.registerReceiver(this, new IntentFilter(methodChannelKey));
|
localBroadcastManager.registerReceiver(this, new IntentFilter(methodChannelKey));
|
||||||
|
|
||||||
|
MoxplatformApi.setup(flutterPluginBinding.getBinaryMessenger(), this);
|
||||||
|
|
||||||
Log.d(TAG, "Attached to engine");
|
Log.d(TAG, "Attached to engine");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,6 +112,7 @@ public class MoxplatformAndroidPlugin extends BroadcastReceiver implements Flutt
|
|||||||
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(registrar.context());
|
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(registrar.context());
|
||||||
final MoxplatformAndroidPlugin plugin = new MoxplatformAndroidPlugin();
|
final MoxplatformAndroidPlugin plugin = new MoxplatformAndroidPlugin();
|
||||||
localBroadcastManager.registerReceiver(plugin, new IntentFilter(methodChannelKey));
|
localBroadcastManager.registerReceiver(plugin, new IntentFilter(methodChannelKey));
|
||||||
|
activity = registrar.activity();
|
||||||
|
|
||||||
final MethodChannel channel = new MethodChannel(registrar.messenger(), "me.polynom/background_service_android", JSONMethodCodec.INSTANCE);
|
final MethodChannel channel = new MethodChannel(registrar.messenger(), "me.polynom/background_service_android", JSONMethodCodec.INSTANCE);
|
||||||
channel.setMethodCallHandler(plugin);
|
channel.setMethodCallHandler(plugin);
|
||||||
@ -77,29 +123,24 @@ public class MoxplatformAndroidPlugin extends BroadcastReceiver implements Flutt
|
|||||||
|
|
||||||
/// Store the entrypoint handle and extra data for the background service.
|
/// Store the entrypoint handle and extra data for the background service.
|
||||||
private void configure(long entrypointHandle, String extraData) {
|
private void configure(long entrypointHandle, String extraData) {
|
||||||
SharedPreferences prefs = context.getSharedPreferences(sharedPrefKey, Context.MODE_PRIVATE);
|
SharedPreferences prefs = context.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE);
|
||||||
prefs.edit()
|
prefs.edit().putLong(entrypointKey, entrypointHandle).putString(extraDataKey, extraData).apply();
|
||||||
.putLong(entrypointKey, entrypointHandle)
|
|
||||||
.putString(extraDataKey, extraData)
|
|
||||||
.apply();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long getHandle(Context c) {
|
public static long getHandle(Context c) {
|
||||||
return c.getSharedPreferences(sharedPrefKey, Context.MODE_PRIVATE).getLong(entrypointKey, 0);
|
return c.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE).getLong(entrypointKey, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getExtraData(Context c) {
|
public static String getExtraData(Context c) {
|
||||||
return c.getSharedPreferences(sharedPrefKey, Context.MODE_PRIVATE).getString(extraDataKey, "");
|
return c.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE).getString(extraDataKey, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setStartAtBoot(Context c, boolean value) {
|
public static void setStartAtBoot(Context c, boolean value) {
|
||||||
c.getSharedPreferences(sharedPrefKey, Context.MODE_PRIVATE)
|
c.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE).edit().putBoolean(autoStartAtBootKey, value).apply();
|
||||||
.edit()
|
|
||||||
.putBoolean(autoStartAtBootKey, value)
|
|
||||||
.apply();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean getStartAtBoot(Context c) {
|
public static boolean getStartAtBoot(Context c) {
|
||||||
return c.getSharedPreferences(sharedPrefKey, Context.MODE_PRIVATE).getBoolean(autoStartAtBootKey, false);
|
return c.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE).getBoolean(autoStartAtBootKey, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isRunning() {
|
private boolean isRunning() {
|
||||||
@ -114,7 +155,7 @@ public class MoxplatformAndroidPlugin extends BroadcastReceiver implements Flutt
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
|
public void onMethodCall(@NonNull MethodCall call, @NonNull io.flutter.plugin.common.MethodChannel.Result result) {
|
||||||
switch (call.method) {
|
switch (call.method) {
|
||||||
case "configure":
|
case "configure":
|
||||||
ArrayList args = (ArrayList) call.arguments;
|
ArrayList args = (ArrayList) call.arguments;
|
||||||
@ -144,53 +185,6 @@ public class MoxplatformAndroidPlugin extends BroadcastReceiver implements Flutt
|
|||||||
}
|
}
|
||||||
result.success(true);
|
result.success(true);
|
||||||
break;
|
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);
|
|
||||||
String hashSpec = (String) args.get(5);
|
|
||||||
|
|
||||||
result.success(encryptFile(src, dest, key, iv, algorithm, hashSpec));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
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);
|
|
||||||
String hashSpec = (String) args.get(5);
|
|
||||||
|
|
||||||
result.success(decryptFile(src, dest, key, iv, algorithm, hashSpec));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
decryptionThread.start();
|
|
||||||
break;
|
|
||||||
case "hashFile":
|
|
||||||
Thread hashingThread = new Thread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
ArrayList args = (ArrayList) call.arguments;
|
|
||||||
String src = (String) args.get(0);
|
|
||||||
String hashSpec = (String) args.get(1);
|
|
||||||
|
|
||||||
result.success(hashFile(src, hashSpec));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
hashingThread.start();
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
result.notImplemented();
|
result.notImplemented();
|
||||||
break;
|
break;
|
||||||
@ -231,118 +225,94 @@ public class MoxplatformAndroidPlugin extends BroadcastReceiver implements Flutt
|
|||||||
this.service = null;
|
this.service = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getCipherSpecFromInteger(int algorithm) {
|
@NonNull
|
||||||
switch (algorithm) {
|
@Override
|
||||||
case 0: return "AES_128/GCM/NoPadding";
|
public String getPersistentDataPath() {
|
||||||
case 1: return "AES_256/GCM/NoPadding";
|
return context.getFilesDir().getPath();
|
||||||
case 2: return "AES_256/CBC/PKCS7PADDING";
|
|
||||||
default:
|
|
||||||
Log.d(TAG, "INVALID ALGORITHM");
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public HashMap<String, byte[]> encryptFile(String src, String dest, byte[] key, byte[] iv, int algorithm, String hashSpec) {
|
@NonNull
|
||||||
String spec = getCipherSpecFromInteger(algorithm);
|
@Override
|
||||||
if (spec.isEmpty()) {
|
public String getCacheDataPath() {
|
||||||
return null;
|
return context.getCacheDir().getPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shamelessly stolen from https://github.com/hugo-pcl/native-crypto-flutter/pull/3
|
@Override
|
||||||
byte[] buffer = new byte[8096];
|
public void openBatteryOptimisationSettings() {
|
||||||
SecretKeySpec sk = new SecretKeySpec(key, spec);
|
final Uri packageUri = Uri.parse("package:" + context.getPackageName());
|
||||||
try {
|
Log.d(TAG, packageUri.toString());
|
||||||
MessageDigest md = MessageDigest.getInstance(hashSpec);
|
final Intent intent = new Intent(ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, packageUri);
|
||||||
Cipher cipher = Cipher.getInstance(spec);
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, sk, new IvParameterSpec(iv));
|
context.startActivity(intent);
|
||||||
FileInputStream fin = new FileInputStream(src);
|
|
||||||
HashedFileOutputStream fout = new HashedFileOutputStream(dest, hashSpec);
|
|
||||||
CipherOutputStream cout = new CipherOutputStream(fout, cipher);
|
|
||||||
int len = 0;
|
|
||||||
int bufLen = 0;
|
|
||||||
while (true) {
|
|
||||||
len = fin.read(buffer);
|
|
||||||
if (len != 0 && len > 0) {
|
|
||||||
md.update(buffer, 0, len);
|
|
||||||
cout.write(buffer, 0, len);
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cout.flush();
|
|
||||||
cout.close();
|
|
||||||
fin.close();
|
|
||||||
|
|
||||||
return new HashMap<String, byte[]>() {{
|
|
||||||
put("plaintext_hash", md.digest());
|
|
||||||
put("ciphertext_hash", fout.getHash());
|
|
||||||
}};
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Log.d(TAG, "ENC: " + ex.getMessage());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public HashMap<String, byte[]> decryptFile(String src, String dest, byte[] key, byte[] iv, int algorithm, String hashSpec) {
|
@NonNull
|
||||||
String spec = getCipherSpecFromInteger(algorithm);
|
@Override
|
||||||
if (spec.isEmpty()) {
|
public Boolean isIgnoringBatteryOptimizations() {
|
||||||
return null;
|
final PowerManager pm = context.getSystemService(PowerManager.class);
|
||||||
|
return pm.isIgnoringBatteryOptimizations(context.getPackageName());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shamelessly stolen from https://github.com/hugo-pcl/native-crypto-flutter/pull/3
|
@Override
|
||||||
byte[] buffer = new byte[8096];
|
public void recordSentMessage(@NonNull String name, @NonNull String jid, @Nullable String avatarPath, @NonNull FallbackIconType fallbackIcon) {
|
||||||
SecretKeySpec sk = new SecretKeySpec(key, spec);
|
systemRecordSentMessage(context, name, jid, avatarPath, fallbackIcon);
|
||||||
try {
|
|
||||||
Cipher cipher = Cipher.getInstance(spec);
|
|
||||||
cipher.init(Cipher.DECRYPT_MODE, sk, new IvParameterSpec(iv));
|
|
||||||
FileInputStream fin = new FileInputStream(src);
|
|
||||||
HashedFileOutputStream fout = new HashedFileOutputStream(dest, hashSpec);
|
|
||||||
CipherOutputStream cout = new CipherOutputStream(fout, cipher);
|
|
||||||
MessageDigest md = MessageDigest.getInstance(hashSpec);
|
|
||||||
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);
|
|
||||||
md.update(buffer, 0, len);
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cout.flush();
|
|
||||||
cout.close();
|
|
||||||
fin.close();
|
|
||||||
|
|
||||||
return new HashMap<String, byte[]>() {{
|
|
||||||
put("plaintext_hash", md.digest());
|
|
||||||
put("ciphertext_hash", fout.getHash());
|
|
||||||
}};
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Log.d(TAG, "DEC: " + ex.getMessage());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] hashFile(String src, String algorithm) {
|
@Override
|
||||||
byte[] buffer = new byte[8096];
|
public void encryptFile(@NonNull String sourcePath, @NonNull String destPath, @NonNull byte[] key, @NonNull byte[] iv, @NonNull CipherAlgorithm algorithm, @NonNull String hashSpec, @NonNull Api.Result<CryptographyResult> result) {
|
||||||
try {
|
CryptoKt.encryptAndHash(
|
||||||
MessageDigest md = MessageDigest.getInstance(algorithm);
|
sourcePath,
|
||||||
FileInputStream fin = new FileInputStream(src);
|
destPath,
|
||||||
int len = 0;
|
key,
|
||||||
while (true) {
|
iv,
|
||||||
len = fin.read(buffer);
|
algorithm,
|
||||||
if (len != 0 && len > 0) {
|
hashSpec,
|
||||||
md.update(buffer, 0, len);
|
result
|
||||||
} else {
|
);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return md.digest();
|
@Override
|
||||||
} catch (Exception ex) {
|
public void decryptFile(@NonNull String sourcePath, @NonNull String destPath, @NonNull byte[] key, @NonNull byte[] iv, @NonNull CipherAlgorithm algorithm, @NonNull String hashSpec, @NonNull Api.Result<CryptographyResult> result) {
|
||||||
Log.d(TAG, "Hash: " + ex.getMessage());
|
CryptoKt.decryptAndHash(
|
||||||
return null;
|
sourcePath,
|
||||||
|
destPath,
|
||||||
|
key,
|
||||||
|
iv,
|
||||||
|
algorithm,
|
||||||
|
hashSpec,
|
||||||
|
result
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hashFile(@NonNull String sourcePath, @NonNull String hashSpec, @NonNull Api.Result<byte[]> result) {
|
||||||
|
CryptoKt.hashFile(sourcePath, hashSpec, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Boolean generateVideoThumbnail(@NonNull String src, @NonNull String dest, @NonNull Long maxWidth) {
|
||||||
|
return generateVideoThumbnailImplementation(src, dest, maxWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pickFiles(@NonNull FilePickerType type, @NonNull Boolean pickMultiple, @NonNull Api.Result<List<String>> result) {
|
||||||
|
filePickerRequest(context, activity, type, pickMultiple, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttachedToActivity(ActivityPluginBinding binding) {
|
||||||
|
activity = binding.getActivity();
|
||||||
|
binding.addActivityResultListener(this);
|
||||||
|
Log.d(TAG, "Activity attached");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetachedFromActivity() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetachedFromActivityForConfigChanges() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) {}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,226 @@
|
|||||||
|
package me.polynom.moxplatform_android
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.ContentResolver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.database.Cursor
|
||||||
|
import android.net.Uri
|
||||||
|
import android.provider.MediaStore
|
||||||
|
import android.util.Log
|
||||||
|
import android.webkit.MimeTypeMap
|
||||||
|
import androidx.activity.result.PickVisualMediaRequest
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
|
||||||
|
object RequestTracker {
|
||||||
|
val requests: MutableMap<Int, Api.Result<Any>> = mutableMapOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
const val PICK_FILE_REQUEST = 41;
|
||||||
|
const val PICK_FILES_REQUEST = 42;
|
||||||
|
|
||||||
|
fun genericFilePickerRequest(activity: Activity?, pickMultiple: Boolean, result: Api.Result<List<String>>) {
|
||||||
|
val pickIntent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||||
|
addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
|
type = "*/*"
|
||||||
|
|
||||||
|
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, pickMultiple);
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestTracker.requests[PICK_FILE_REQUEST] = result as Api.Result<Any>;
|
||||||
|
activity?.startActivityForResult(pickIntent, PICK_FILE_REQUEST)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun filePickerRequest(
|
||||||
|
context: Context,
|
||||||
|
activity: Activity?,
|
||||||
|
type: Api.FilePickerType,
|
||||||
|
pickMultiple: Boolean,
|
||||||
|
result: Api.Result<List<String>>
|
||||||
|
) {
|
||||||
|
if (type == Api.FilePickerType.GENERIC) {
|
||||||
|
return genericFilePickerRequest(activity, pickMultiple, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
val pickerType = when (type) {
|
||||||
|
Api.FilePickerType.IMAGE -> ActivityResultContracts.PickVisualMedia.ImageOnly
|
||||||
|
Api.FilePickerType.VIDEO -> ActivityResultContracts.PickVisualMedia.VideoOnly
|
||||||
|
Api.FilePickerType.IMAGE_AND_VIDEO -> ActivityResultContracts.PickVisualMedia.ImageAndVideo
|
||||||
|
// TODO
|
||||||
|
Api.FilePickerType.GENERIC -> ActivityResultContracts.PickVisualMedia.ImageAndVideo
|
||||||
|
}
|
||||||
|
|
||||||
|
val pick = when (pickMultiple) {
|
||||||
|
false -> ActivityResultContracts.PickVisualMedia()
|
||||||
|
true -> ActivityResultContracts.PickMultipleVisualMedia()
|
||||||
|
}
|
||||||
|
|
||||||
|
val requestCode = if (pickMultiple) PICK_FILES_REQUEST else PICK_FILE_REQUEST
|
||||||
|
val pickIntent = pick.createIntent(context, PickVisualMediaRequest(pickerType))
|
||||||
|
RequestTracker.requests[requestCode] = result as Api.Result<Any>
|
||||||
|
Log.d(TAG, "Tracked size ${RequestTracker.requests.size}")
|
||||||
|
|
||||||
|
if (activity == null) {
|
||||||
|
Log.w(TAG, "Activity is null")
|
||||||
|
}
|
||||||
|
activity?.startActivityForResult(pickIntent, requestCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies the file from the given content URI to a temporary directory, retaining the original
|
||||||
|
* file name if possible.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Each file is placed in its own directory to avoid conflicts according to the following
|
||||||
|
* scheme: {cacheDir}/{randomUuid}/{fileName}
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* File extension is changed to match MIME type of the file, if known. Otherwise, the extension
|
||||||
|
* is left unchanged.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* If the original file name is unknown, a predefined "image_picker" filename is used and the
|
||||||
|
* file extension is deduced from the mime type (with fallback to ".jpg" in case of failure).
|
||||||
|
*/
|
||||||
|
fun getPathFromUri(context: Context, uri: Uri): String? {
|
||||||
|
try {
|
||||||
|
context.contentResolver.openInputStream(uri).use { inputStream ->
|
||||||
|
val uuid = UUID.randomUUID().toString()
|
||||||
|
val targetDirectory = File(context.cacheDir, uuid)
|
||||||
|
targetDirectory.mkdir()
|
||||||
|
// TODO(SynSzakala) according to the docs, `deleteOnExit` does not work reliably on Android; we should preferably
|
||||||
|
// just clear the picked files after the app startup.
|
||||||
|
targetDirectory.deleteOnExit()
|
||||||
|
var fileName = getImageName(context, uri)
|
||||||
|
var extension = getImageExtension(context, uri)
|
||||||
|
if (fileName == null) {
|
||||||
|
Log.w("FileUtils", "Cannot get file name for $uri")
|
||||||
|
if (extension == null) extension = ".jpg"
|
||||||
|
fileName = "image_picker$extension"
|
||||||
|
} else if (extension != null) {
|
||||||
|
fileName = getBaseName(fileName) + extension
|
||||||
|
}
|
||||||
|
val file = File(targetDirectory, fileName)
|
||||||
|
FileOutputStream(file).use { outputStream ->
|
||||||
|
copy(inputStream!!, outputStream)
|
||||||
|
return file.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
// If closing the output stream fails, we cannot be sure that the
|
||||||
|
// target file was written in full. Flushing the stream merely moves
|
||||||
|
// the bytes into the OS, not necessarily to the file.
|
||||||
|
return null
|
||||||
|
} catch (e: SecurityException) {
|
||||||
|
// Calling `ContentResolver#openInputStream()` has been reported to throw a
|
||||||
|
// `SecurityException` on some devices in certain circumstances. Instead of crashing, we
|
||||||
|
// return `null`.
|
||||||
|
//
|
||||||
|
// See https://github.com/flutter/flutter/issues/100025 for more details.
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return extension of image with dot, or null if it's empty.
|
||||||
|
*/
|
||||||
|
private fun getImageExtension(context: Context, uriImage: Uri): String? {
|
||||||
|
val extension: String?
|
||||||
|
extension = try {
|
||||||
|
if (uriImage.scheme == ContentResolver.SCHEME_CONTENT) {
|
||||||
|
val mime = MimeTypeMap.getSingleton()
|
||||||
|
mime.getExtensionFromMimeType(context.contentResolver.getType(uriImage))
|
||||||
|
} else {
|
||||||
|
MimeTypeMap.getFileExtensionFromUrl(
|
||||||
|
Uri.fromFile(File(uriImage.path)).toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return if (extension == null || extension.isEmpty()) {
|
||||||
|
null
|
||||||
|
} else ".$extension"
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return name of the image provided by ContentResolver; this may be null.
|
||||||
|
*/
|
||||||
|
private fun getImageName(context: Context, uriImage: Uri): String? {
|
||||||
|
queryImageName(context, uriImage).use { cursor ->
|
||||||
|
return if (cursor == null || !cursor.moveToFirst() || (cursor.columnCount < 1)) null else cursor.getString(
|
||||||
|
0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun queryImageName(context: Context, uriImage: Uri): Cursor? {
|
||||||
|
return context
|
||||||
|
.contentResolver
|
||||||
|
.query(uriImage, arrayOf(MediaStore.MediaColumns.DISPLAY_NAME), null, null, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
private fun copy(`in`: InputStream, out: OutputStream) {
|
||||||
|
val buffer = ByteArray(4 * 1024)
|
||||||
|
var bytesRead: Int
|
||||||
|
while (`in`.read(buffer).also { bytesRead = it } != -1) {
|
||||||
|
out.write(buffer, 0, bytesRead)
|
||||||
|
}
|
||||||
|
out.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getBaseName(fileName: String): String {
|
||||||
|
val lastDotIndex = fileName.lastIndexOf('.')
|
||||||
|
return if (lastDotIndex < 0) {
|
||||||
|
fileName
|
||||||
|
} else fileName.substring(0, lastDotIndex)
|
||||||
|
// Basename is everything before the last '.'.
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onActivityResultImpl(context: Context, requestCode: Int, resultCode: Int, data: Intent?): Boolean {
|
||||||
|
Log.d(TAG, "Got result for $requestCode with result $resultCode (${data?.action})")
|
||||||
|
if (requestCode == PICK_FILE_REQUEST || requestCode == PICK_FILES_REQUEST) {
|
||||||
|
Log.d(TAG, "Extra data ${data?.data}")
|
||||||
|
val result = RequestTracker.requests.remove(requestCode);
|
||||||
|
if (result == null) {
|
||||||
|
Log.w(TAG, "Untracked response.")
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultCode != Activity.RESULT_OK) {
|
||||||
|
// No files picked
|
||||||
|
result!!.success(listOf<String>())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
val pickedMultiple = requestCode == PICK_FILES_REQUEST
|
||||||
|
val pickedFiles = mutableListOf<String>()
|
||||||
|
if (pickedMultiple) {
|
||||||
|
val intentUris = data!!.clipData
|
||||||
|
if (data!!.clipData != null) {
|
||||||
|
for (i in 0 until data!!.clipData!!.itemCount) {
|
||||||
|
val path = getPathFromUri(context, data!!.clipData!!.getItemAt(i).uri)
|
||||||
|
if (path != null) {
|
||||||
|
pickedFiles.add(path )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val path = getPathFromUri(context, data!!.data!!)
|
||||||
|
if (path != null) {
|
||||||
|
pickedFiles.add(path )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result!!.success(pickedFiles)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package me.polynom.moxplatform_android
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import androidx.core.app.Person
|
||||||
|
import androidx.core.content.pm.ShortcutInfoCompat
|
||||||
|
import androidx.core.content.pm.ShortcutManagerCompat
|
||||||
|
import androidx.core.graphics.drawable.IconCompat
|
||||||
|
import me.polynom.moxplatform_android.Api.FallbackIconType
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Uses Android's direct share API to create dynamic share targets that are compatible
|
||||||
|
* with share_handler's media handling.
|
||||||
|
* NOTE: The "system" prefix is to prevent confusion between pigeon's abstract recordSentMessage
|
||||||
|
* method and this one.
|
||||||
|
* */
|
||||||
|
fun systemRecordSentMessage(context: Context, name: String, jid: String, avatarPath: String?, fallbackIcon: FallbackIconType) {
|
||||||
|
val pkgName = context.packageName
|
||||||
|
val intent = Intent(context, Class.forName("$pkgName.MainActivity")).apply {
|
||||||
|
action = Intent.ACTION_SEND
|
||||||
|
|
||||||
|
// Compatibility with share_handler
|
||||||
|
putExtra("conversationIdentifier", jid)
|
||||||
|
}
|
||||||
|
|
||||||
|
val shortcutTarget = "$pkgName.dynamic_share_target"
|
||||||
|
val shortcutBuilder = ShortcutInfoCompat.Builder(context, jid).apply {
|
||||||
|
setShortLabel(name)
|
||||||
|
setIsConversation()
|
||||||
|
setCategories(setOf(shortcutTarget))
|
||||||
|
setIntent(intent)
|
||||||
|
setLongLived(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
val personBuilder = Person.Builder().apply {
|
||||||
|
setKey(jid)
|
||||||
|
setName(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Either set an avatar image OR a fallback icon
|
||||||
|
if (avatarPath != null) {
|
||||||
|
val icon = IconCompat.createWithAdaptiveBitmap(
|
||||||
|
BitmapFactory.decodeFile(avatarPath),
|
||||||
|
)
|
||||||
|
shortcutBuilder.setIcon(icon)
|
||||||
|
personBuilder.setIcon(icon)
|
||||||
|
} else {
|
||||||
|
val resourceId = when(fallbackIcon) {
|
||||||
|
FallbackIconType.PERSON -> R.mipmap.person
|
||||||
|
FallbackIconType.NOTES -> R.mipmap.notes
|
||||||
|
// "Fallthrough"
|
||||||
|
else -> R.mipmap.person
|
||||||
|
}
|
||||||
|
val icon = IconCompat.createWithResource(context, resourceId)
|
||||||
|
shortcutBuilder.setIcon(icon)
|
||||||
|
personBuilder.setIcon(icon)
|
||||||
|
}
|
||||||
|
|
||||||
|
shortcutBuilder.setPerson(personBuilder.build())
|
||||||
|
ShortcutManagerCompat.addDynamicShortcuts(
|
||||||
|
context,
|
||||||
|
listOf(shortcutBuilder.build()),
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package me.polynom.moxplatform_android
|
||||||
|
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.media.MediaMetadataRetriever
|
||||||
|
import android.util.Log
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate a video thumbnail using the first frame of the video at @src. Afterwards, scale it
|
||||||
|
* down such that its width is equal to @maxWidth (while keeping the aspect ratio) and write it to
|
||||||
|
* @dest.
|
||||||
|
*
|
||||||
|
* If everything went well, returns true. If we're unable to generate the thumbnail, returns false.
|
||||||
|
* */
|
||||||
|
fun generateVideoThumbnailImplementation(src: String, dest: String, maxWidth: Long): Boolean {
|
||||||
|
try {
|
||||||
|
val mmr = MediaMetadataRetriever().apply {
|
||||||
|
setDataSource(src)
|
||||||
|
}
|
||||||
|
val unscaledThumbnail = mmr.getFrameAtTime(0) ?: return false
|
||||||
|
|
||||||
|
// Scale down the thumbnail while keeping the aspect ratio
|
||||||
|
val scalingFactor = maxWidth.toDouble() / unscaledThumbnail.width;
|
||||||
|
Log.d(TAG, "Scaling to $maxWidth from ${unscaledThumbnail.width} with scalingFactor $scalingFactor");
|
||||||
|
val thumbnail = Bitmap.createScaledBitmap(
|
||||||
|
unscaledThumbnail,
|
||||||
|
(unscaledThumbnail.width * scalingFactor).toInt(),
|
||||||
|
(unscaledThumbnail.height * scalingFactor).toInt(),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Write it to the destination file
|
||||||
|
val fos = FileOutputStream(dest)
|
||||||
|
thumbnail.compress(Bitmap.CompressFormat.JPEG, 75, fos)
|
||||||
|
fos.flush()
|
||||||
|
fos.close()
|
||||||
|
return true;
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
Log.e(TAG, "Failed to create thumbnail for $src: ${ex.message}")
|
||||||
|
ex.printStackTrace()
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-2h2v2zM13,13h-2L11,7h2v6z"/>
|
||||||
|
</vector>
|
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M17.34,20l-3.54,-3.54l1.41,-1.41l2.12,2.12l4.24,-4.24L23,14.34L17.34,20zM12,17c0,-3.87 3.13,-7 7,-7c1.08,0 2.09,0.25 3,0.68V4c0,-1.1 -0.9,-2 -2,-2H4C2.9,2 2,2.9 2,4v18l4,-4h6v0c0,-0.17 0.01,-0.33 0.03,-0.5C12.01,17.34 12,17.17 12,17z"/>
|
||||||
|
</vector>
|
@ -0,0 +1,14 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="120dp"
|
||||||
|
android:height="120dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<group android:scaleX="0.42988887"
|
||||||
|
android:scaleY="0.42988887"
|
||||||
|
android:translateX="6.8413334"
|
||||||
|
android:translateY="6.8413334">
|
||||||
|
<path
|
||||||
|
android:pathData="M3,18h12v-2L3,16v2zM3,6v2h18L21,6L3,6zM3,13h18v-2L3,11v2z"
|
||||||
|
android:fillColor="#ffffff"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
@ -0,0 +1,14 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="120dp"
|
||||||
|
android:height="120dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<group android:scaleX="0.483625"
|
||||||
|
android:scaleY="0.483625"
|
||||||
|
android:translateX="6.1965"
|
||||||
|
android:translateY="6.1965">
|
||||||
|
<path
|
||||||
|
android:pathData="m12,12c2.21,0 4,-1.79 4,-4C16,5.79 14.21,4 12,4 9.79,4 8,5.79 8,8c0,2.21 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"
|
||||||
|
android:fillColor="#fffff9"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:autoMirrored="true" android:height="24dp"
|
||||||
|
android:tint="#FFFFFF" android:viewportHeight="24"
|
||||||
|
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M10,9V5l-7,7 7,7v-4.1c5,0 8.5,1.6 11,5.1 -1,-5 -4,-10 -11,-11z"/>
|
||||||
|
</vector>
|
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M1,21h22L12,2 1,21zM13,18h-2v-2h2v2zM13,14h-2v-4h2v4z"/>
|
||||||
|
</vector>
|
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/notes_background"/>
|
||||||
|
<foreground android:drawable="@drawable/notes_foreground"/>
|
||||||
|
</adaptive-icon>
|
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/person_background"/>
|
||||||
|
<foreground android:drawable="@drawable/person_foreground"/>
|
||||||
|
</adaptive-icon>
|
Binary file not shown.
After Width: | Height: | Size: 650 B |
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="notes_background">#CF4AFF</color>
|
||||||
|
</resources>
|
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="person_background">#CF4AFF</color>
|
||||||
|
</resources>
|
@ -1,6 +1,5 @@
|
|||||||
library moxplatform_android;
|
library moxplatform_android;
|
||||||
|
|
||||||
export 'src/isolate_android.dart';
|
export 'src/isolate_android.dart';
|
||||||
export 'src/media_android.dart';
|
|
||||||
export 'src/plugin_android.dart';
|
export 'src/plugin_android.dart';
|
||||||
export 'src/service_android.dart';
|
export 'src/service_android.dart';
|
||||||
|
28
packages/moxplatform_android/lib/src/contacts_android.dart
Normal file
28
packages/moxplatform_android/lib/src/contacts_android.dart
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import 'package:moxplatform_platform_interface/moxplatform_platform_interface.dart';
|
||||||
|
|
||||||
|
class AndroidContactsImplementation extends ContactsImplementation {
|
||||||
|
final MoxplatformApi _api = MoxplatformApi();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> recordSentMessage(
|
||||||
|
String name,
|
||||||
|
String jid, {
|
||||||
|
String? avatarPath,
|
||||||
|
FallbackIconType fallbackIcon = FallbackIconType.none,
|
||||||
|
}) async {
|
||||||
|
// Ensure we always have an icon
|
||||||
|
if (avatarPath != null) {
|
||||||
|
assert(
|
||||||
|
fallbackIcon != FallbackIconType.none,
|
||||||
|
'If no avatar is specified, then a fallbackIcon must be set',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _api.recordSentMessage(
|
||||||
|
name,
|
||||||
|
jid,
|
||||||
|
avatarPath,
|
||||||
|
fallbackIcon,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,59 +1,49 @@
|
|||||||
import 'dart:typed_data';
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:moxplatform_platform_interface/moxplatform_platform_interface.dart';
|
import 'package:moxplatform_platform_interface/moxplatform_platform_interface.dart';
|
||||||
|
|
||||||
class AndroidCryptographyImplementation extends CryptographyImplementation {
|
class AndroidCryptographyImplementation extends CryptographyImplementation {
|
||||||
final _methodChannel = const MethodChannel('me.polynom.moxplatform_android');
|
final MoxplatformApi _api = MoxplatformApi();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<CryptographyResult?> encryptFile(String sourcePath, String destPath, Uint8List key, Uint8List iv, CipherAlgorithm algorithm, String hashSpec) async {
|
Future<CryptographyResult?> encryptFile(
|
||||||
final dynamic resultRaw = await _methodChannel.invokeMethod<dynamic>('encryptFile', [
|
String sourcePath,
|
||||||
|
String destPath,
|
||||||
|
Uint8List key,
|
||||||
|
Uint8List iv,
|
||||||
|
CipherAlgorithm algorithm,
|
||||||
|
String hashSpec,
|
||||||
|
) async {
|
||||||
|
return _api.encryptFile(
|
||||||
sourcePath,
|
sourcePath,
|
||||||
destPath,
|
destPath,
|
||||||
key,
|
key,
|
||||||
iv,
|
iv,
|
||||||
algorithm.toInt(),
|
algorithm,
|
||||||
hashSpec,
|
hashSpec,
|
||||||
]);
|
|
||||||
if (resultRaw == null) return null;
|
|
||||||
|
|
||||||
// ignore: argument_type_not_assignable
|
|
||||||
final result = Map<String, dynamic>.from(resultRaw);
|
|
||||||
return CryptographyResult(
|
|
||||||
result['plaintext_hash']! as Uint8List,
|
|
||||||
result['ciphertext_hash']! as Uint8List,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<CryptographyResult?> decryptFile(String sourcePath, String destPath, Uint8List key, Uint8List iv, CipherAlgorithm algorithm, String hashSpec) async {
|
Future<CryptographyResult?> decryptFile(
|
||||||
final dynamic resultRaw = await _methodChannel.invokeMethod<dynamic>('decryptFile', [
|
String sourcePath,
|
||||||
|
String destPath,
|
||||||
|
Uint8List key,
|
||||||
|
Uint8List iv,
|
||||||
|
CipherAlgorithm algorithm,
|
||||||
|
String hashSpec,
|
||||||
|
) async {
|
||||||
|
return _api.decryptFile(
|
||||||
sourcePath,
|
sourcePath,
|
||||||
destPath,
|
destPath,
|
||||||
key,
|
key,
|
||||||
iv,
|
iv,
|
||||||
algorithm.toInt(),
|
algorithm,
|
||||||
hashSpec,
|
hashSpec,
|
||||||
]);
|
|
||||||
if (resultRaw == null) return null;
|
|
||||||
|
|
||||||
// ignore: argument_type_not_assignable
|
|
||||||
final result = Map<String, dynamic>.from(resultRaw);
|
|
||||||
return CryptographyResult(
|
|
||||||
result['plaintext_hash']! as Uint8List,
|
|
||||||
result['ciphertext_hash']! as Uint8List,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Uint8List?> hashFile(String path, String hashSpec) async {
|
Future<Uint8List?> hashFile(String sourcePath, String hashSpec) async {
|
||||||
final dynamic resultsRaw = await _methodChannel.invokeMethod<dynamic>('hashFile', [
|
return _api.hashFile(sourcePath, hashSpec);
|
||||||
path,
|
|
||||||
hashSpec,
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (resultsRaw == null) return null;
|
|
||||||
|
|
||||||
return resultsRaw as Uint8List;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,16 +4,16 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:moxlib/awaitabledatasender.dart';
|
import 'package:moxlib/moxlib.dart';
|
||||||
import 'package:moxplatform/moxplatform.dart';
|
import 'package:moxplatform/moxplatform.dart';
|
||||||
import 'package:moxplatform_android/src/service_android.dart';
|
import 'package:moxplatform_android/src/service_android.dart';
|
||||||
import 'package:moxplatform_platform_interface/moxplatform_platform_interface.dart';
|
|
||||||
|
|
||||||
/// An [AwaitableDataSender] that uses flutter_background_service.
|
/// An [AwaitableDataSender] that uses flutter_background_service.
|
||||||
class BackgroundServiceDataSender extends AwaitableDataSender<BackgroundCommand, BackgroundEvent> {
|
class BackgroundServiceDataSender
|
||||||
|
extends AwaitableDataSender<BackgroundCommand, BackgroundEvent> {
|
||||||
BackgroundServiceDataSender()
|
BackgroundServiceDataSender()
|
||||||
: _channel = const MethodChannel('me.polynom.moxplatform_android'), super();
|
: _channel = const MethodChannel('me.polynom.moxplatform_android'),
|
||||||
|
super();
|
||||||
final MethodChannel _channel;
|
final MethodChannel _channel;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -39,23 +39,27 @@ Future<void> androidEntrypoint() async {
|
|||||||
);
|
);
|
||||||
final data = jsonDecode(result!) as Map<String, dynamic>;
|
final data = jsonDecode(result!) as Map<String, dynamic>;
|
||||||
final entrypointHandle = data['genericEntrypoint']! as int;
|
final entrypointHandle = data['genericEntrypoint']! as int;
|
||||||
final entrypointCallbackHandle = CallbackHandle.fromRawHandle(entrypointHandle);
|
final entrypointCallbackHandle =
|
||||||
final entrypoint = PluginUtilities.getCallbackFromHandle(entrypointCallbackHandle);
|
CallbackHandle.fromRawHandle(entrypointHandle);
|
||||||
|
final entrypoint =
|
||||||
|
PluginUtilities.getCallbackFromHandle(entrypointCallbackHandle);
|
||||||
final handleUIEventHandle = data['eventHandle']! as int;
|
final handleUIEventHandle = data['eventHandle']! as int;
|
||||||
final handleUIEventCallbackHandle = CallbackHandle.fromRawHandle(handleUIEventHandle);
|
final handleUIEventCallbackHandle =
|
||||||
final handleUIEvent = PluginUtilities.getCallbackFromHandle(handleUIEventCallbackHandle);
|
CallbackHandle.fromRawHandle(handleUIEventHandle);
|
||||||
|
final handleUIEvent =
|
||||||
|
PluginUtilities.getCallbackFromHandle(handleUIEventCallbackHandle);
|
||||||
|
|
||||||
final srv = AndroidBackgroundService();
|
final srv = AndroidBackgroundService();
|
||||||
GetIt.I.registerSingleton<BackgroundService>(srv);
|
GetIt.I.registerSingleton<BackgroundService>(srv);
|
||||||
srv.init(
|
srv.init(
|
||||||
entrypoint! as Future<void> Function(),
|
entrypoint! as Future<void> Function(String),
|
||||||
handleUIEvent! as Future<void> Function(Map<String, dynamic>? data),
|
handleUIEvent! as Future<void> Function(Map<String, dynamic>? data),
|
||||||
|
data['initialLocale']! as String,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The Android specific implementation of the [IsolateHandler].
|
/// The Android specific implementation of the [IsolateHandler].
|
||||||
class AndroidIsolateHandler extends IsolateHandler {
|
class AndroidIsolateHandler extends IsolateHandler {
|
||||||
|
|
||||||
AndroidIsolateHandler()
|
AndroidIsolateHandler()
|
||||||
: _channel = const MethodChannel('me.polynom.moxplatform_android'),
|
: _channel = const MethodChannel('me.polynom.moxplatform_android'),
|
||||||
_dataSender = BackgroundServiceDataSender(),
|
_dataSender = BackgroundServiceDataSender(),
|
||||||
@ -66,7 +70,9 @@ class AndroidIsolateHandler extends IsolateHandler {
|
|||||||
final Logger _log;
|
final Logger _log;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> attach(Future<void> Function(Map<String, dynamic>? data) handleIsolateEvent) async {
|
Future<void> attach(
|
||||||
|
Future<void> Function(Map<String, dynamic>? data) handleIsolateEvent,
|
||||||
|
) async {
|
||||||
_channel.setMethodCallHandler((MethodCall call) async {
|
_channel.setMethodCallHandler((MethodCall call) async {
|
||||||
final args = call.arguments as String;
|
final args = call.arguments as String;
|
||||||
await handleIsolateEvent(jsonDecode(args) as Map<String, dynamic>);
|
await handleIsolateEvent(jsonDecode(args) as Map<String, dynamic>);
|
||||||
@ -75,20 +81,25 @@ class AndroidIsolateHandler extends IsolateHandler {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> start(
|
Future<void> start(
|
||||||
Future<void> Function() entrypoint,
|
Future<void> Function(String initialLocale) entrypoint,
|
||||||
Future<void> Function(Map<String, dynamic>? data) handleUIEvent,
|
Future<void> Function(Map<String, dynamic>? data) handleUIEvent,
|
||||||
Future<void> Function(Map<String, dynamic>? data) handleIsolateEvent,
|
Future<void> Function(Map<String, dynamic>? data) handleIsolateEvent,
|
||||||
|
String initialLocale,
|
||||||
) async {
|
) async {
|
||||||
_log.finest('Called start');
|
_log.finest('Called start');
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
final androidEntrypointHandle = PluginUtilities.getCallbackHandle(androidEntrypoint)!.toRawHandle();
|
final androidEntrypointHandle =
|
||||||
|
PluginUtilities.getCallbackHandle(androidEntrypoint)!.toRawHandle();
|
||||||
_log.finest('androidEntrypointHandle: $androidEntrypointHandle');
|
_log.finest('androidEntrypointHandle: $androidEntrypointHandle');
|
||||||
await _channel.invokeMethod<void>('configure', <dynamic>[
|
await _channel.invokeMethod<void>('configure', <dynamic>[
|
||||||
androidEntrypointHandle,
|
androidEntrypointHandle,
|
||||||
jsonEncode({
|
jsonEncode({
|
||||||
'genericEntrypoint': PluginUtilities.getCallbackHandle(entrypoint)!.toRawHandle(),
|
'genericEntrypoint':
|
||||||
'eventHandle': PluginUtilities.getCallbackHandle(handleUIEvent)!.toRawHandle()
|
PluginUtilities.getCallbackHandle(entrypoint)!.toRawHandle(),
|
||||||
|
'eventHandle':
|
||||||
|
PluginUtilities.getCallbackHandle(handleUIEvent)!.toRawHandle(),
|
||||||
|
'initialLocale': initialLocale,
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
import 'package:media_scanner/media_scanner.dart';
|
|
||||||
import 'package:moxplatform_platform_interface/moxplatform_platform_interface.dart';
|
|
||||||
|
|
||||||
class AndroidMediaScannerImplementation extends MediaScannerImplementation {
|
|
||||||
@override
|
|
||||||
void scanFile(String path) {
|
|
||||||
MediaScanner.loadMedia(path: path);
|
|
||||||
}
|
|
||||||
}
|
|
39
packages/moxplatform_android/lib/src/platform_android.dart
Normal file
39
packages/moxplatform_android/lib/src/platform_android.dart
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import 'package:moxplatform/moxplatform.dart';
|
||||||
|
import 'package:moxplatform_platform_interface/moxplatform_platform_interface.dart';
|
||||||
|
|
||||||
|
class AndroidPlatformImplementation extends PlatformImplementation {
|
||||||
|
@override
|
||||||
|
Future<String> getCacheDataPath() {
|
||||||
|
return MoxplatformInterface.api.getCacheDataPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String> getPersistentDataPath() {
|
||||||
|
return MoxplatformInterface.api.getPersistentDataPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> isIgnoringBatteryOptimizations() {
|
||||||
|
return MoxplatformInterface.api.isIgnoringBatteryOptimizations();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> openBatteryOptimisationSettings() {
|
||||||
|
return MoxplatformInterface.api.openBatteryOptimisationSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> generateVideoThumbnail(
|
||||||
|
String src,
|
||||||
|
String dest,
|
||||||
|
int width,
|
||||||
|
) async {
|
||||||
|
return MoxplatformInterface.api.generateVideoThumbnail(src, dest, width);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<String>> pickFiles(FilePickerType type, bool pickMultiple) async {
|
||||||
|
final result = await MoxplatformInterface.api.pickFiles(type, pickMultiple);
|
||||||
|
return result.cast<String>();
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,17 @@
|
|||||||
|
import 'package:moxplatform_android/src/contacts_android.dart';
|
||||||
import 'package:moxplatform_android/src/crypto_android.dart';
|
import 'package:moxplatform_android/src/crypto_android.dart';
|
||||||
import 'package:moxplatform_android/src/isolate_android.dart';
|
import 'package:moxplatform_android/src/isolate_android.dart';
|
||||||
import 'package:moxplatform_android/src/media_android.dart';
|
import 'package:moxplatform_android/src/platform_android.dart';
|
||||||
import 'package:moxplatform_platform_interface/moxplatform_platform_interface.dart';
|
import 'package:moxplatform_platform_interface/moxplatform_platform_interface.dart';
|
||||||
|
|
||||||
class MoxplatformAndroidPlugin extends MoxplatformInterface {
|
class MoxplatformAndroidPlugin extends MoxplatformInterface {
|
||||||
static void registerWith() {
|
static void registerWith() {
|
||||||
// ignore: avoid_print
|
// ignore: avoid_print
|
||||||
print('MoxplatformAndroidPlugin: Registering implementation');
|
print('MoxplatformAndroidPlugin: Registering implementation');
|
||||||
MoxplatformInterface.handler = AndroidIsolateHandler();
|
MoxplatformInterface.contacts = AndroidContactsImplementation();
|
||||||
MoxplatformInterface.media = AndroidMediaScannerImplementation();
|
|
||||||
MoxplatformInterface.crypto = AndroidCryptographyImplementation();
|
MoxplatformInterface.crypto = AndroidCryptographyImplementation();
|
||||||
|
MoxplatformInterface.handler = AndroidIsolateHandler();
|
||||||
|
MoxplatformInterface.platform = AndroidPlatformImplementation();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -4,19 +4,18 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:moxlib/awaitabledatasender.dart';
|
import 'package:moxlib/moxlib.dart';
|
||||||
import 'package:moxplatform/moxplatform.dart';
|
import 'package:moxplatform/moxplatform.dart';
|
||||||
import 'package:moxplatform_platform_interface/moxplatform_platform_interface.dart';
|
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
class AndroidBackgroundService extends BackgroundService {
|
class AndroidBackgroundService extends BackgroundService {
|
||||||
|
|
||||||
AndroidBackgroundService()
|
AndroidBackgroundService()
|
||||||
: _log = Logger('AndroidBackgroundService'),
|
: _log = Logger('AndroidBackgroundService'),
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@internal
|
@internal
|
||||||
static const MethodChannel channel = MethodChannel('me.polynom.moxplatform_android_bg');
|
static const MethodChannel channel =
|
||||||
|
MethodChannel('me.polynom.moxplatform_android_bg');
|
||||||
final Logger _log;
|
final Logger _log;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -28,20 +27,21 @@ class AndroidBackgroundService extends BackgroundService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void sendEvent(BackgroundEvent event, { String? id }) {
|
void sendEvent(BackgroundEvent event, {String? id}) {
|
||||||
final data = DataWrapper(
|
final data = DataWrapper(
|
||||||
id ?? const Uuid().v4(),
|
id ?? const Uuid().v4(),
|
||||||
event,
|
event,
|
||||||
);
|
);
|
||||||
// NOTE: *S*erver to *F*oreground
|
// NOTE: *S*erver to *F*oreground
|
||||||
_log.fine('S2F: ${data.toJson().toString()}');
|
_log.fine('S2F: ${data.toJson()}');
|
||||||
channel.invokeMethod<void>('sendData', jsonEncode(data.toJson()));
|
channel.invokeMethod<void>('sendData', jsonEncode(data.toJson()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void init(
|
void init(
|
||||||
Future<void> Function() entrypoint,
|
Future<void> Function(String initialLocale) entrypoint,
|
||||||
Future<void> Function(Map<String, dynamic>? data) handleEvent,
|
Future<void> Function(Map<String, dynamic>? data) handleEvent,
|
||||||
|
String initialLocale,
|
||||||
) {
|
) {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
@ -59,6 +59,6 @@ class AndroidBackgroundService extends BackgroundService {
|
|||||||
|
|
||||||
_log.finest('Running...');
|
_log.finest('Running...');
|
||||||
|
|
||||||
entrypoint();
|
entrypoint(initialLocale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
name: moxplatform_android
|
name: moxplatform_android
|
||||||
description: Android implementation of moxplatform
|
description: Android implementation of moxplatform
|
||||||
version: 0.1.15
|
version: 0.1.22
|
||||||
homepage: https://codeberg.org/moxxy/moxplatform
|
homepage: https://codeberg.org/moxxy/moxplatform
|
||||||
publish_to: https://git.polynom.me/api/packages/Moxxy/pub
|
publish_to: https://git.polynom.me/api/packages/Moxxy/pub
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.16.0 <3.0.0"
|
sdk: ">=2.17.0 <4.0.0"
|
||||||
flutter: ">=2.10.0"
|
flutter: ">=2.10.0"
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
@ -22,18 +22,17 @@ dependencies:
|
|||||||
sdk: flutter
|
sdk: flutter
|
||||||
get_it: ^7.2.0
|
get_it: ^7.2.0
|
||||||
logging: ^1.0.2
|
logging: ^1.0.2
|
||||||
media_scanner: ^2.0.0
|
|
||||||
meta: ^1.7.0
|
meta: ^1.7.0
|
||||||
moxlib:
|
moxlib:
|
||||||
hosted: https://git.polynom.me/api/packages/Moxxy/pub
|
hosted: https://git.polynom.me/api/packages/Moxxy/pub
|
||||||
version: ^0.1.4
|
version: ^0.2.0
|
||||||
|
|
||||||
moxplatform:
|
moxplatform:
|
||||||
hosted: https://git.polynom.me/api/packages/Moxxy/pub
|
hosted: https://git.polynom.me/api/packages/Moxxy/pub
|
||||||
version: ^0.1.15
|
version: ^0.1.17+6
|
||||||
moxplatform_platform_interface:
|
moxplatform_platform_interface:
|
||||||
hosted: https://git.polynom.me/api/packages/Moxxy/pub
|
hosted: https://git.polynom.me/api/packages/Moxxy/pub
|
||||||
version: ^0.1.15
|
version: ^0.1.22
|
||||||
|
|
||||||
plugin_platform_interface: ^2.1.2
|
plugin_platform_interface: ^2.1.2
|
||||||
uuid: ^3.0.5
|
uuid: ^3.0.5
|
||||||
@ -41,4 +40,5 @@ dependencies:
|
|||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
very_good_analysis: ^2.4.0
|
pigeon: 10.1.4
|
||||||
|
very_good_analysis: ^3.0.1
|
||||||
|
@ -1,3 +1,49 @@
|
|||||||
|
## 0.1.22
|
||||||
|
|
||||||
|
- **FIX**(repo): Remove notification examples.
|
||||||
|
- **FEAT**(interface,android): Allow passing an initial locale to the service.
|
||||||
|
- **FEAT**(base,interface,android): Move more logic to Moxxy.
|
||||||
|
- **FEAT**(android,interface): Implement video thumbnail generation.
|
||||||
|
|
||||||
|
## 0.1.21
|
||||||
|
|
||||||
|
- **FIX**(repo): Remove notification examples.
|
||||||
|
- **FEAT**(base,interface,android): Move more logic to Moxxy.
|
||||||
|
- **FEAT**(android,interface): Implement video thumbnail generation.
|
||||||
|
|
||||||
|
## 0.1.20
|
||||||
|
|
||||||
|
- **FEAT**(android,interface): Handle battery optimisation.
|
||||||
|
|
||||||
|
## 0.1.19
|
||||||
|
|
||||||
|
- **FEAT**(android,interface): Handle battery optimisation.
|
||||||
|
|
||||||
|
## 0.1.18
|
||||||
|
|
||||||
|
- **FIX**: Format and lint.
|
||||||
|
- **FIX**: Add payload to all intents.
|
||||||
|
- **FEAT**: Move recordSentMessage to pigeon.
|
||||||
|
- **FEAT**: Move the crypto APIs to pigeon.
|
||||||
|
- **FEAT**: Allow the sender's data being null.
|
||||||
|
- **FEAT**: Allow attaching arbitrary data to the notification.
|
||||||
|
- **FEAT**: Allow showing regular notifications.
|
||||||
|
- **FEAT**: Color in the notification silhouette.
|
||||||
|
- **FEAT**: Allow setting the self-avatar.
|
||||||
|
- **FEAT**: Take care of i18n.
|
||||||
|
|
||||||
|
## 0.1.17+1
|
||||||
|
|
||||||
|
- Update a dependency to the latest release.
|
||||||
|
|
||||||
|
## 0.1.17
|
||||||
|
|
||||||
|
- **FIX**: Fix typecasting issue.
|
||||||
|
- **FEAT**: Add an API for creating direct share shortcuts.
|
||||||
|
- **FEAT**: Migrate to moxlib 0.2.0.
|
||||||
|
- **FEAT**: I forgot to bump dependency versions.
|
||||||
|
- **FEAT**: Also hash the file on encryption and decryption.
|
||||||
|
|
||||||
## 0.1.11+2
|
## 0.1.11+2
|
||||||
|
|
||||||
- **REFACTOR**: Make version constraints looser.
|
- **REFACTOR**: Make version constraints looser.
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
library moxplatform_platform_interface;
|
library moxplatform_platform_interface;
|
||||||
|
|
||||||
|
export 'src/api.g.dart';
|
||||||
|
export 'src/contacts.dart';
|
||||||
|
export 'src/contacts_stub.dart';
|
||||||
export 'src/crypto.dart';
|
export 'src/crypto.dart';
|
||||||
export 'src/crypto_stub.dart';
|
export 'src/crypto_stub.dart';
|
||||||
export 'src/interface.dart';
|
export 'src/interface.dart';
|
||||||
export 'src/isolate.dart';
|
export 'src/isolate.dart';
|
||||||
export 'src/isolate_stub.dart';
|
export 'src/isolate_stub.dart';
|
||||||
export 'src/media.dart';
|
export 'src/platform.dart';
|
||||||
export 'src/media_stub.dart';
|
export 'src/platform_stub.dart';
|
||||||
export 'src/service.dart';
|
export 'src/service.dart';
|
||||||
|
338
packages/moxplatform_platform_interface/lib/src/api.g.dart
Normal file
338
packages/moxplatform_platform_interface/lib/src/api.g.dart
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
// Autogenerated from Pigeon (v10.1.4), do not edit directly.
|
||||||
|
// See also: https://pub.dev/packages/pigeon
|
||||||
|
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
enum CipherAlgorithm {
|
||||||
|
aes128GcmNoPadding,
|
||||||
|
aes256GcmNoPadding,
|
||||||
|
aes256CbcPkcs7,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum FallbackIconType {
|
||||||
|
none,
|
||||||
|
person,
|
||||||
|
notes,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum FilePickerType {
|
||||||
|
image,
|
||||||
|
video,
|
||||||
|
imageAndVideo,
|
||||||
|
generic,
|
||||||
|
}
|
||||||
|
|
||||||
|
class CryptographyResult {
|
||||||
|
CryptographyResult({
|
||||||
|
required this.plaintextHash,
|
||||||
|
required this.ciphertextHash,
|
||||||
|
});
|
||||||
|
|
||||||
|
Uint8List plaintextHash;
|
||||||
|
|
||||||
|
Uint8List ciphertextHash;
|
||||||
|
|
||||||
|
Object encode() {
|
||||||
|
return <Object?>[
|
||||||
|
plaintextHash,
|
||||||
|
ciphertextHash,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
static CryptographyResult decode(Object result) {
|
||||||
|
result as List<Object?>;
|
||||||
|
return CryptographyResult(
|
||||||
|
plaintextHash: result[0]! as Uint8List,
|
||||||
|
ciphertextHash: result[1]! as Uint8List,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MoxplatformApiCodec extends StandardMessageCodec {
|
||||||
|
const _MoxplatformApiCodec();
|
||||||
|
@override
|
||||||
|
void writeValue(WriteBuffer buffer, Object? value) {
|
||||||
|
if (value is CryptographyResult) {
|
||||||
|
buffer.putUint8(128);
|
||||||
|
writeValue(buffer, value.encode());
|
||||||
|
} else {
|
||||||
|
super.writeValue(buffer, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object? readValueOfType(int type, ReadBuffer buffer) {
|
||||||
|
switch (type) {
|
||||||
|
case 128:
|
||||||
|
return CryptographyResult.decode(readValue(buffer)!);
|
||||||
|
default:
|
||||||
|
return super.readValueOfType(type, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MoxplatformApi {
|
||||||
|
/// Constructor for [MoxplatformApi]. The [binaryMessenger] named argument is
|
||||||
|
/// available for dependency injection. If it is left null, the default
|
||||||
|
/// BinaryMessenger will be used which routes to the host platform.
|
||||||
|
MoxplatformApi({BinaryMessenger? binaryMessenger})
|
||||||
|
: _binaryMessenger = binaryMessenger;
|
||||||
|
final BinaryMessenger? _binaryMessenger;
|
||||||
|
|
||||||
|
static const MessageCodec<Object?> codec = _MoxplatformApiCodec();
|
||||||
|
|
||||||
|
/// Platform APIs
|
||||||
|
Future<String> getPersistentDataPath() async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.getPersistentDataPath', codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(null) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else if (replyList[0] == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'null-error',
|
||||||
|
message: 'Host platform returned null value for non-null return value.',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (replyList[0] as String?)!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> getCacheDataPath() async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.getCacheDataPath', codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(null) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else if (replyList[0] == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'null-error',
|
||||||
|
message: 'Host platform returned null value for non-null return value.',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (replyList[0] as String?)!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> openBatteryOptimisationSettings() async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.openBatteryOptimisationSettings', codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(null) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> isIgnoringBatteryOptimizations() async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.isIgnoringBatteryOptimizations', codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(null) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else if (replyList[0] == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'null-error',
|
||||||
|
message: 'Host platform returned null value for non-null return value.',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (replyList[0] as bool?)!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Contacts APIs
|
||||||
|
Future<void> recordSentMessage(String arg_name, String arg_jid, String? arg_avatarPath, FallbackIconType arg_fallbackIcon) async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.recordSentMessage', codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(<Object?>[arg_name, arg_jid, arg_avatarPath, arg_fallbackIcon.index]) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cryptography APIs
|
||||||
|
Future<CryptographyResult?> encryptFile(String arg_sourcePath, String arg_destPath, Uint8List arg_key, Uint8List arg_iv, CipherAlgorithm arg_algorithm, String arg_hashSpec) async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.encryptFile', codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(<Object?>[arg_sourcePath, arg_destPath, arg_key, arg_iv, arg_algorithm.index, arg_hashSpec]) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (replyList[0] as CryptographyResult?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<CryptographyResult?> decryptFile(String arg_sourcePath, String arg_destPath, Uint8List arg_key, Uint8List arg_iv, CipherAlgorithm arg_algorithm, String arg_hashSpec) async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.decryptFile', codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(<Object?>[arg_sourcePath, arg_destPath, arg_key, arg_iv, arg_algorithm.index, arg_hashSpec]) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (replyList[0] as CryptographyResult?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Uint8List?> hashFile(String arg_sourcePath, String arg_hashSpec) async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.hashFile', codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(<Object?>[arg_sourcePath, arg_hashSpec]) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (replyList[0] as Uint8List?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Media APIs
|
||||||
|
Future<bool> generateVideoThumbnail(String arg_src, String arg_dest, int arg_maxWidth) async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.generateVideoThumbnail', codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(<Object?>[arg_src, arg_dest, arg_maxWidth]) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else if (replyList[0] == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'null-error',
|
||||||
|
message: 'Host platform returned null value for non-null return value.',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (replyList[0] as bool?)!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Picker
|
||||||
|
Future<List<String?>> pickFiles(FilePickerType arg_type, bool arg_pickMultiple) async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.pickFiles', codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(<Object?>[arg_type.index, arg_pickMultiple]) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else if (replyList[0] == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'null-error',
|
||||||
|
message: 'Host platform returned null value for non-null return value.',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (replyList[0] as List<Object?>?)!.cast<String?>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
import 'package:moxplatform_platform_interface/src/api.g.dart';
|
||||||
|
|
||||||
|
// Wrapper around various contact APIs.
|
||||||
|
// ignore: one_member_abstracts
|
||||||
|
abstract class ContactsImplementation {
|
||||||
|
Future<void> recordSentMessage(
|
||||||
|
String name,
|
||||||
|
String jid, {
|
||||||
|
String? avatarPath,
|
||||||
|
FallbackIconType fallbackIcon = FallbackIconType.none,
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
import 'package:moxplatform_platform_interface/src/api.g.dart';
|
||||||
|
import 'package:moxplatform_platform_interface/src/contacts.dart';
|
||||||
|
|
||||||
|
class StubContactsImplementation extends ContactsImplementation {
|
||||||
|
@override
|
||||||
|
Future<void> recordSentMessage(
|
||||||
|
String name,
|
||||||
|
String jid, {
|
||||||
|
String? avatarPath,
|
||||||
|
FallbackIconType fallbackIcon = FallbackIconType.none,
|
||||||
|
}) async {}
|
||||||
|
}
|
@ -1,27 +1,5 @@
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
import 'package:moxplatform_platform_interface/src/api.g.dart';
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CryptographyResult {
|
|
||||||
|
|
||||||
const CryptographyResult(this.plaintextHash, this.ciphertextHash);
|
|
||||||
final Uint8List plaintextHash;
|
|
||||||
final Uint8List ciphertextHash;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wrapper around platform-native cryptography APIs
|
/// Wrapper around platform-native cryptography APIs
|
||||||
abstract class CryptographyImplementation {
|
abstract class CryptographyImplementation {
|
||||||
@ -30,18 +8,32 @@ abstract class CryptographyImplementation {
|
|||||||
/// Note that this function runs off-thread as to not block the UI thread.
|
/// 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.
|
/// Resolves to true if the encryption was successful. Resolves to fale on failure.
|
||||||
Future<CryptographyResult?> encryptFile(String sourcePath, String destPath, Uint8List key, Uint8List iv, CipherAlgorithm algorithm, String hashSpec);
|
Future<CryptographyResult?> encryptFile(
|
||||||
|
String sourcePath,
|
||||||
|
String destPath,
|
||||||
|
Uint8List key,
|
||||||
|
Uint8List iv,
|
||||||
|
CipherAlgorithm algorithm,
|
||||||
|
String hashSpec,
|
||||||
|
);
|
||||||
|
|
||||||
/// Decrypt the file at [sourcePath] using [algorithm] and write the result back to
|
/// Decrypt the file at [sourcePath] using [algorithm] and write the result back to
|
||||||
/// [destPath]. [hashSpec] is the name of the Hash function to use, i.e. "SHA-256".
|
/// [destPath]. [hashSpec] is the name of the Hash function to use, i.e. "SHA-256".
|
||||||
/// Note that this function runs off-thread as to not block the UI thread.
|
/// 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.
|
/// Resolves to true if the encryption was successful. Resolves to fale on failure.
|
||||||
Future<CryptographyResult?> decryptFile(String sourcePath, String destPath, Uint8List key, Uint8List iv, CipherAlgorithm algorithm, String hashSpec);
|
Future<CryptographyResult?> decryptFile(
|
||||||
|
String sourcePath,
|
||||||
|
String destPath,
|
||||||
|
Uint8List key,
|
||||||
|
Uint8List iv,
|
||||||
|
CipherAlgorithm algorithm,
|
||||||
|
String hashSpec,
|
||||||
|
);
|
||||||
|
|
||||||
/// Hashes the file at [path] using the Hash function with name [hashSpec].
|
/// Hashes the file at [sourcePath] using the Hash function with name [hashSpec].
|
||||||
/// Note that this function runs off-thread as to not block the UI thread.
|
/// Note that this function runs off-thread as to not block the UI thread.
|
||||||
///
|
///
|
||||||
/// Returns the hash of the file.
|
/// Returns the hash of the file.
|
||||||
Future<Uint8List?> hashFile(String path, String hashSpec);
|
Future<Uint8List?> hashFile(String sourcePath, String hashSpec);
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,34 @@
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
import 'package:moxplatform_platform_interface/src/api.g.dart';
|
||||||
import 'package:moxplatform_platform_interface/src/crypto.dart';
|
import 'package:moxplatform_platform_interface/src/crypto.dart';
|
||||||
|
|
||||||
class StubCryptographyImplementation extends CryptographyImplementation {
|
class StubCryptographyImplementation extends CryptographyImplementation {
|
||||||
@override
|
@override
|
||||||
Future<CryptographyResult?> encryptFile(String sourcePath, String destPath, Uint8List key, Uint8List iv, CipherAlgorithm algorithm, String hashSpec) async {
|
Future<CryptographyResult?> encryptFile(
|
||||||
|
String sourcePath,
|
||||||
|
String destPath,
|
||||||
|
Uint8List key,
|
||||||
|
Uint8List iv,
|
||||||
|
CipherAlgorithm algorithm,
|
||||||
|
String hashSpec,
|
||||||
|
) async {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<CryptographyResult?> decryptFile(String sourcePath, String destPath, Uint8List key, Uint8List iv, CipherAlgorithm algorithm, String hashSpec) async {
|
Future<CryptographyResult?> decryptFile(
|
||||||
|
String sourcePath,
|
||||||
|
String destPath,
|
||||||
|
Uint8List key,
|
||||||
|
Uint8List iv,
|
||||||
|
CipherAlgorithm algorithm,
|
||||||
|
String hashSpec,
|
||||||
|
) async {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Uint8List?> hashFile(String path, String hashSpec) async {
|
Future<Uint8List?> hashFile(String sourcePath, String hashSpec) async {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
|
import 'package:moxplatform_platform_interface/src/api.g.dart';
|
||||||
|
import 'package:moxplatform_platform_interface/src/contacts.dart';
|
||||||
|
import 'package:moxplatform_platform_interface/src/contacts_stub.dart';
|
||||||
import 'package:moxplatform_platform_interface/src/crypto.dart';
|
import 'package:moxplatform_platform_interface/src/crypto.dart';
|
||||||
import 'package:moxplatform_platform_interface/src/crypto_stub.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.dart';
|
||||||
import 'package:moxplatform_platform_interface/src/isolate_stub.dart';
|
import 'package:moxplatform_platform_interface/src/isolate_stub.dart';
|
||||||
import 'package:moxplatform_platform_interface/src/media.dart';
|
import 'package:moxplatform_platform_interface/src/platform.dart';
|
||||||
import 'package:moxplatform_platform_interface/src/media_stub.dart';
|
import 'package:moxplatform_platform_interface/src/platform_stub.dart';
|
||||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||||
|
|
||||||
abstract class MoxplatformInterface extends PlatformInterface {
|
abstract class MoxplatformInterface extends PlatformInterface {
|
||||||
@ -11,9 +14,12 @@ abstract class MoxplatformInterface extends PlatformInterface {
|
|||||||
|
|
||||||
static final Object _token = Object();
|
static final Object _token = Object();
|
||||||
|
|
||||||
|
static MoxplatformApi api = MoxplatformApi();
|
||||||
|
|
||||||
static IsolateHandler handler = StubIsolateHandler();
|
static IsolateHandler handler = StubIsolateHandler();
|
||||||
static MediaScannerImplementation media = StubMediaScannerImplementation();
|
|
||||||
static CryptographyImplementation crypto = StubCryptographyImplementation();
|
static CryptographyImplementation crypto = StubCryptographyImplementation();
|
||||||
|
static ContactsImplementation contacts = StubContactsImplementation();
|
||||||
|
static PlatformImplementation platform = StubPlatformImplementation();
|
||||||
|
|
||||||
/// Return the current platform name.
|
/// Return the current platform name.
|
||||||
Future<String?> getPlatformName();
|
Future<String?> getPlatformName();
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:moxlib/awaitabledatasender.dart';
|
import 'package:moxlib/moxlib.dart';
|
||||||
|
|
||||||
/// A class abstracting the interaction between the UI isolate and the background
|
/// A class abstracting the interaction between the UI isolate and the background
|
||||||
/// service, which is either a regular isolate or an Android foreground service.
|
/// service, which is either a regular isolate or an Android foreground service.
|
||||||
@ -8,10 +8,12 @@ abstract class IsolateHandler {
|
|||||||
/// [entrypoint] is the entrypoint that is run inside the new isolate.
|
/// [entrypoint] is the entrypoint that is run inside the new isolate.
|
||||||
/// [handleUIEvent] is a handler function that is called when the isolate receives data from the UI.
|
/// [handleUIEvent] is a handler function that is called when the isolate receives data from the UI.
|
||||||
/// [handleIsolateEvent] is a handler function that is called when the UI receives data from the service.
|
/// [handleIsolateEvent] is a handler function that is called when the UI receives data from the service.
|
||||||
|
/// [initialLocale] the locale to pass to the background service when first starting.
|
||||||
Future<void> start(
|
Future<void> start(
|
||||||
Future<void> Function() entrypoint,
|
Future<void> Function(String initialLocale) entrypoint,
|
||||||
Future<void> Function(Map<String, dynamic>? data) handleUIEvent,
|
Future<void> Function(Map<String, dynamic>? data) handleUIEvent,
|
||||||
Future<void> Function(Map<String, dynamic>? data) handleIsolateEvent,
|
Future<void> Function(Map<String, dynamic>? data) handleIsolateEvent,
|
||||||
|
String initialLocale,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Make sure that the UI event handler is registered without starting the isolate.
|
/// Make sure that the UI event handler is registered without starting the isolate.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:moxlib/awaitabledatasender.dart';
|
import 'package:moxlib/moxlib.dart';
|
||||||
import 'package:moxplatform_platform_interface/src/isolate.dart';
|
import 'package:moxplatform_platform_interface/src/isolate.dart';
|
||||||
|
|
||||||
class StubDataSender extends AwaitableDataSender {
|
class StubDataSender extends AwaitableDataSender {
|
||||||
@ -22,9 +22,10 @@ class StubIsolateHandler extends IsolateHandler {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> start(
|
Future<void> start(
|
||||||
Future<void> Function() entrypoint,
|
Future<void> Function(String initialLocale) entrypoint,
|
||||||
Future<void> Function(Map<String, dynamic>? data) handleUIEvent,
|
Future<void> Function(Map<String, dynamic>? data) handleUIEvent,
|
||||||
Future<void> Function(Map<String, dynamic>? data) handleIsolateEvent,
|
Future<void> Function(Map<String, dynamic>? data) handleIsolateEvent,
|
||||||
|
String initialLocale,
|
||||||
) async {
|
) async {
|
||||||
// ignore: avoid_print
|
// ignore: avoid_print
|
||||||
print('STUB STARTED!!!!!!');
|
print('STUB STARTED!!!!!!');
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
/// Wrapper around platform-specific media scanning
|
|
||||||
// ignore: one_member_abstracts
|
|
||||||
abstract class MediaScannerImplementation {
|
|
||||||
/// Let the platform-specific media scanner scan the file at [path].
|
|
||||||
void scanFile(String path);
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
import 'package:moxplatform_platform_interface/src/media.dart';
|
|
||||||
|
|
||||||
class StubMediaScannerImplementation extends MediaScannerImplementation {
|
|
||||||
@override
|
|
||||||
void scanFile(String path) {}
|
|
||||||
}
|
|
@ -0,0 +1,24 @@
|
|||||||
|
import 'package:moxplatform_platform_interface/src/api.g.dart';
|
||||||
|
|
||||||
|
abstract class PlatformImplementation {
|
||||||
|
/// Returns the path where persistent data should be stored.
|
||||||
|
Future<String> getPersistentDataPath();
|
||||||
|
|
||||||
|
/// Returns the path where cache data should be stored.
|
||||||
|
Future<String> getCacheDataPath();
|
||||||
|
|
||||||
|
/// Returns whether the app is battery-optimised (false) or
|
||||||
|
/// excluded from battery savings (true).
|
||||||
|
Future<bool> isIgnoringBatteryOptimizations();
|
||||||
|
|
||||||
|
/// Opens the page for battery optimisations. If not supported on the
|
||||||
|
/// platform, does nothing.
|
||||||
|
Future<void> openBatteryOptimisationSettings();
|
||||||
|
|
||||||
|
/// Attempt to generate a thumbnail for the video file at [src], scale it, while keeping the
|
||||||
|
/// aspect ratio in tact to [width], and write it to [dest]. If we were successful, returns true.
|
||||||
|
/// If no thumbnail was generated, returns false.
|
||||||
|
Future<bool> generateVideoThumbnail(String src, String dest, int width);
|
||||||
|
|
||||||
|
Future<List<String>> pickFiles(FilePickerType type, bool pickMultiple);
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
import 'package:moxplatform_platform_interface/src/api.g.dart';
|
||||||
|
import 'package:moxplatform_platform_interface/src/platform.dart';
|
||||||
|
|
||||||
|
class StubPlatformImplementation extends PlatformImplementation {
|
||||||
|
/// Returns the path where persistent data should be stored.
|
||||||
|
@override
|
||||||
|
Future<String> getPersistentDataPath() async => '';
|
||||||
|
|
||||||
|
/// Returns the path where cache data should be stored.
|
||||||
|
@override
|
||||||
|
Future<String> getCacheDataPath() async => '';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> isIgnoringBatteryOptimizations() async => false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> openBatteryOptimisationSettings() async {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> generateVideoThumbnail(
|
||||||
|
String src,
|
||||||
|
String dest,
|
||||||
|
int width,
|
||||||
|
) async =>
|
||||||
|
false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<String>> pickFiles(FilePickerType type, bool pickMultiple) async => [];
|
||||||
|
}
|
@ -5,13 +5,14 @@ abstract class BackgroundService {
|
|||||||
void setNotification(String title, String body);
|
void setNotification(String title, String body);
|
||||||
|
|
||||||
/// Send data from the background service to the UI.
|
/// Send data from the background service to the UI.
|
||||||
void sendEvent(BackgroundEvent event, { String? id });
|
void sendEvent(BackgroundEvent event, {String? id});
|
||||||
|
|
||||||
/// Called before [entrypoint]. Sets up whatever it needs to set up.
|
/// Called before [entrypoint]. Sets up whatever it needs to set up.
|
||||||
/// [handleEvent] is a function that is called whenever the service receives
|
/// [handleEvent] is a function that is called whenever the service receives
|
||||||
/// data.
|
/// data.
|
||||||
void init(
|
void init(
|
||||||
Future<void> Function() entrypoint,
|
Future<void> Function(String initialLocale) entrypoint,
|
||||||
Future<void> Function(Map<String, dynamic>? data) handleEvent,
|
Future<void> Function(Map<String, dynamic>? data) handleEvent,
|
||||||
|
String initialLocale,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
name: moxplatform_platform_interface
|
name: moxplatform_platform_interface
|
||||||
description: A common platform interface for the my_plugin plugin.
|
description: A common platform interface for the my_plugin plugin.
|
||||||
version: 0.1.15
|
version: 0.1.22
|
||||||
homepage: https://codeberg.org/moxxy/moxplatform
|
homepage: https://codeberg.org/moxxy/moxplatform
|
||||||
publish_to: https://git.polynom.me/api/packages/Moxxy/pub
|
publish_to: https://git.polynom.me/api/packages/Moxxy/pub
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.16.0 <3.0.0"
|
sdk: ">=2.17.0 <3.0.0"
|
||||||
flutter: ">=2.10.0"
|
flutter: ">=2.10.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -14,14 +14,14 @@ dependencies:
|
|||||||
|
|
||||||
moxlib:
|
moxlib:
|
||||||
hosted: https://git.polynom.me/api/packages/Moxxy/pub
|
hosted: https://git.polynom.me/api/packages/Moxxy/pub
|
||||||
version: ^0.1.4
|
version: ^0.2.0
|
||||||
moxplatform:
|
moxplatform:
|
||||||
hosted: https://git.polynom.me/api/packages/Moxxy/pub
|
hosted: https://git.polynom.me/api/packages/Moxxy/pub
|
||||||
version: ^0.1.15
|
version: ^0.1.17+6
|
||||||
|
|
||||||
plugin_platform_interface: ^2.1.2
|
plugin_platform_interface: ^2.1.2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
very_good_analysis: ^2.4.0
|
very_good_analysis: ^3.0.1
|
||||||
|
64
pigeons/api.dart
Normal file
64
pigeons/api.dart
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import 'package:pigeon/pigeon.dart';
|
||||||
|
|
||||||
|
@ConfigurePigeon(
|
||||||
|
PigeonOptions(
|
||||||
|
dartOut: 'packages/moxplatform_platform_interface/lib/src/api.g.dart',
|
||||||
|
//kotlinOut: 'packages/moxplatform_android/android/src/main/java/me/polynom/moxplatform_android/Notifications.g.kt',
|
||||||
|
//kotlinOptions: KotlinOptions(
|
||||||
|
// package: 'me.polynom.moxplatform_android',
|
||||||
|
//),
|
||||||
|
javaOut: 'packages/moxplatform_android/android/src/main/java/me/polynom/moxplatform_android/Api.java',
|
||||||
|
javaOptions: JavaOptions(
|
||||||
|
package: 'me.polynom.moxplatform_android',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
enum CipherAlgorithm {
|
||||||
|
aes128GcmNoPadding,
|
||||||
|
aes256GcmNoPadding,
|
||||||
|
aes256CbcPkcs7;
|
||||||
|
}
|
||||||
|
|
||||||
|
class CryptographyResult {
|
||||||
|
const CryptographyResult(this.plaintextHash, this.ciphertextHash);
|
||||||
|
final Uint8List plaintextHash;
|
||||||
|
final Uint8List ciphertextHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The type of icon to use when no avatar path is provided.
|
||||||
|
enum FallbackIconType {
|
||||||
|
none,
|
||||||
|
person,
|
||||||
|
notes;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum FilePickerType {
|
||||||
|
image,
|
||||||
|
video,
|
||||||
|
imageAndVideo,
|
||||||
|
generic,
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostApi()
|
||||||
|
abstract class MoxplatformApi {
|
||||||
|
/// Platform APIs
|
||||||
|
String getPersistentDataPath();
|
||||||
|
String getCacheDataPath();
|
||||||
|
void openBatteryOptimisationSettings();
|
||||||
|
bool isIgnoringBatteryOptimizations();
|
||||||
|
|
||||||
|
/// Contacts APIs
|
||||||
|
void recordSentMessage(String name, String jid, String? avatarPath, FallbackIconType fallbackIcon);
|
||||||
|
|
||||||
|
/// Cryptography APIs
|
||||||
|
@async CryptographyResult? encryptFile(String sourcePath, String destPath, Uint8List key, Uint8List iv, CipherAlgorithm algorithm, String hashSpec);
|
||||||
|
@async CryptographyResult? decryptFile(String sourcePath, String destPath, Uint8List key, Uint8List iv, CipherAlgorithm algorithm, String hashSpec);
|
||||||
|
@async Uint8List? hashFile(String sourcePath, String hashSpec);
|
||||||
|
|
||||||
|
/// Media APIs
|
||||||
|
bool generateVideoThumbnail(String src, String dest, int maxWidth);
|
||||||
|
|
||||||
|
/// Picker
|
||||||
|
@async
|
||||||
|
List<String> pickFiles(FilePickerType type, bool pickMultiple);
|
||||||
|
}
|
8
pubspec.yaml
Normal file
8
pubspec.yaml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
name: moxplatform_workspace
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: '>=2.18.0 <3.0.0'
|
||||||
|
dev_dependencies:
|
||||||
|
melos: ^3.1.1
|
||||||
|
pigeon: 10.1.4
|
||||||
|
very_good_analysis: ^3.0.1
|
Reference in New Issue
Block a user