Desktop builds

This commit is contained in:
Hanh 2022-03-09 21:19:42 +08:00
parent 41c0913dd3
commit fa80d57b1e
11 changed files with 151 additions and 66 deletions

View File

@ -80,3 +80,50 @@ This will create a `root` directory will all the various apks (arm32, arm64, i32
- Add arm64 to Excluded Architecture / Any IOS Simulator SDK
- Build warp with x86_64-apple-ios target
- debugShowCheckedModeBanner: false in MaterialApp()
## Desktop builds
Desktop builds must be made on their native platform, i.e. MacOX
needs a Mac, etc.
First checkout how to use flutter desktop as there are requirements
for each platform.
The repository does not include the project generated files.
Run `flutter create --platform=windows,linux,macos .` to generate them.
First compile the rust code. In `native/warp_api_ffi`, edit `Cargo.toml`
to change the library crate type to `cdylib`. Then `cargo build --release`.
This should produce a dynamic library under `target/release` in the project root
directory.
Depending on the platform, the output library has a different name and
a different way to include in the build.
Build the flutter project: `flutter build windows` (macos or linux).
The result is in `build/windows/runner/Release`.
### Linux
Copy `libwarp_api_ffi.so` into the `lib/` directory. Then tar/gzip and that's it.
### Windows
Copy `warp_api_ffi.dll` into the Release directory and also add:
- `sqlite3.dll`
- `msvcp140.dll`
- `vcruntime140.dll`
- `vcruntime140_1.dll`
Then zip.
### MacOS
On M1 macs, run rustup default stable-x86_64-apple-darwin
MacOS is the trickiest one. Open the xcode workspace.
Add in Signing & Capabilities: Network server & Client and R/W file access
to user selected files
Go to the Runner project, Build Stages
Drag `libwarp_api_ffi.so` into Runner/Frameworks
Remove it from the link stage (we don't want to statically link)
Add to Bundle Framework, Code Sign
Then exit xcode and run `flutter build macos`
If you want to create a DMG, use the npm package appdmg

View File

@ -12,10 +12,14 @@ Future<void> showAbout(BuildContext context) async {
final contentTemplate = await rootBundle.loadString('assets/about.md');
final template = Template(contentTemplate);
var content = template.renderString({'APP': APP_NAME});
PackageInfo packageInfo = await PackageInfo.fromPlatform();
String version = packageInfo.version;
String code = packageInfo.buildNumber;
content += "`${S.of(context).version}: $version+$code`";
if (isMobile()) {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
String version = packageInfo.version;
String code = packageInfo.buildNumber;
content += "`${S
.of(context)
.version}: $version+$code`";
}
showDialog(
context: context,
barrierDismissible: false,

View File

@ -205,7 +205,7 @@ class HomeState extends State<HomePageInner> with TickerProviderStateMixin {
}
_backup() async {
final didAuthenticate = await authenticate(context, S.of(context).pleaseAuthenticateToShowAccountSeed);
final didAuthenticate = !isMobile() || await authenticate(context, S.of(context).pleaseAuthenticateToShowAccountSeed);
if (didAuthenticate) {
Navigator.of(context).pushNamed('/backup');
}

View File

@ -6,6 +6,7 @@ import 'dart:ui';
import 'package:csv/csv.dart';
import 'package:currency_text_input_formatter/currency_text_input_formatter.dart';
import 'package:decimal/decimal.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_barcode_scanner/flutter_barcode_scanner.dart';
@ -17,6 +18,8 @@ import 'package:qr_flutter/qr_flutter.dart';
import 'package:rate_my_app/rate_my_app.dart';
import 'package:share_plus/share_plus.dart';
import 'package:sqflite/sqflite.dart';
import 'package:sqflite_common/sqlite_api.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import 'package:warp_api/warp_api.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
@ -213,12 +216,18 @@ class ZWalletAppState extends State<ZWalletApp> {
@override
void initState() {
super.initState();
WidgetsBinding.instance?.addPostFrameCallback((_) async {
await rateMyApp.init();
if (mounted && rateMyApp.shouldOpenDialog) {
rateMyApp.showRateDialog(this.context);
}
});
if (isMobile()) {
WidgetsBinding.instance?.addPostFrameCallback((_) async {
await rateMyApp.init();
if (mounted && rateMyApp.shouldOpenDialog) {
rateMyApp.showRateDialog(this.context);
}
});
}
else {
sqfliteFfiInit();
databaseFactory = databaseFactoryFfi;
}
init = _init();
}
@ -241,28 +250,30 @@ class ZWalletAppState extends State<ZWalletApp> {
}
}
await initUniLinks(this.context);
final quickActions = QuickActions();
quickActions.initialize((type) {
handleQuickAction(this.context, type);
});
if (!settings.linkHooksInitialized) {
Future.microtask(() {
final s = S.of(this.context);
List<ShortcutItem> shortcuts = [];
for (var c in settings.coins) {
final coin = c.coin;
final ticker = c.def.ticker;
shortcuts.add(ShortcutItem(type: '$coin.receive',
localizedTitle: s.receive(ticker),
icon: 'receive'));
shortcuts.add(ShortcutItem(type: '$coin.send',
localizedTitle: s.sendCointicker(ticker),
icon: 'send'));
}
quickActions.setShortcutItems(shortcuts);
if (isMobile()) {
await initUniLinks(this.context);
final quickActions = QuickActions();
quickActions.initialize((type) {
handleQuickAction(this.context, type);
});
await settings.setLinkHooksInitialized();
if (!settings.linkHooksInitialized) {
Future.microtask(() {
final s = S.of(this.context);
List<ShortcutItem> shortcuts = [];
for (var c in settings.coins) {
final coin = c.coin;
final ticker = c.def.ticker;
shortcuts.add(ShortcutItem(type: '$coin.receive',
localizedTitle: s.receive(ticker),
icon: 'receive'));
shortcuts.add(ShortcutItem(type: '$coin.send',
localizedTitle: s.sendCointicker(ticker),
icon: 'send'));
}
quickActions.setShortcutItems(shortcuts);
});
await settings.setLinkHooksInitialized();
}
}
}
return true;
@ -396,6 +407,7 @@ Future<bool> rescanDialog(BuildContext context) async {
}
Future<bool> confirmWifi(BuildContext context) async {
if (!isMobile()) return true;
final connectivity = await Connectivity().checkConnectivity();
if (connectivity == ConnectivityResult.mobile) {
return await showDialog<bool?>(
@ -576,10 +588,24 @@ Future<void> shieldTAddr(BuildContext context) async {
Future<void> shareCsv(List<List> data, String filename, String title) async {
final csvConverter = ListToCsvConverter();
final csv = csvConverter.convert(data);
Directory tempDir = await getTemporaryDirectory();
String fn = "${tempDir.path}/$filename";
final file = File(fn);
await file.writeAsString(csv);
await Share.shareFiles([fn], subject: title);
await saveFile(csv, filename, title);
}
Future<void> saveFile(String data, String filename, String title) async {
if (isMobile()) {
Directory tempDir = await getTemporaryDirectory();
String fn = "${tempDir.path}/$filename";
final file = File(fn);
await file.writeAsString(data);
return Share.shareFiles([filename], subject: title);
}
else {
final fn = await FilePicker.platform.saveFile();
if (fn != null) {
final file = File(fn);
await file.writeAsString(data);
}
}
}
bool isMobile() => Platform.isAndroid || Platform.isIOS;

View File

@ -100,11 +100,7 @@ class FullBackupPage extends StatelessWidget {
}
_onSave(BuildContext context) async {
Directory tempDir = await getTemporaryDirectory();
String filename = "${tempDir.path}/$APP_NAME.bak";
final file = File(filename);
await file.writeAsString(backup);
Share.shareFiles([filename], subject: S.of(context).encryptedBackup(APP_NAME));
await saveFile(backup, "$APP_NAME.bak", S.of(context).encryptedBackup(APP_NAME));
}
}

View File

@ -451,18 +451,13 @@ Future<void> send(BuildContext context, List<Recipient> recipients, bool useTran
rootScaffoldMessengerKey.currentState?.showSnackBar(snackBar2);
await active.update();
} else {
Directory tempDir = await getTemporaryDirectory();
String filename = "${tempDir.path}/tx.json";
final txjson = WarpApi.prepareTx(active.coin, active.id, recipients,
useTransparent, settings.anchorOffset, filename);
final file = File(filename);
await file.writeAsString(txjson);
Share.shareFiles([filename], subject: s.unsignedTransactionFile);
useTransparent, settings.anchorOffset);
await saveFile(txjson, "tx.json", s.unsignedTransactionFile);
final snackBar2 = SnackBar(content: Text(s.fileSaved));
rootScaffoldMessengerKey.currentState?.showSnackBar(snackBar2);
Navigator.of(context).pop();
}
}

View File

@ -17,7 +17,7 @@ import 'package:http/http.dart' as http;
import 'dart:convert' as convert;
import 'package:convert/convert.dart';
import 'package:flex_color_scheme/flex_color_scheme.dart';
import 'package:sensors_plus/sensors_plus.dart';
// import 'package:sensors_plus/sensors_plus.dart';
import 'coin/coin.dart';
import 'generated/l10n.dart';
@ -198,7 +198,7 @@ abstract class _Settings with Store {
_updateThemeData();
Future.microtask(_loadCurrencies); // lazily
accelerometerEvents.listen(_handleAccel);
// accelerometerEvents.listen(_handleAccel);
return true;
}

View File

@ -18,14 +18,14 @@ use zcash_multisig::{
};
use zcash_primitives::transaction::builder::Progress;
static RUNTIME: OnceCell<Mutex<Runtime>> = OnceCell::new();
static YWALLET: OnceCell<Mutex<Wallet>> = OnceCell::new();
static ZWALLET: OnceCell<Mutex<Wallet>> = OnceCell::new();
static YMEMPOOL: OnceCell<Mutex<MemPool>> = OnceCell::new();
static ZMEMPOOL: OnceCell<Mutex<MemPool>> = OnceCell::new();
static SYNCLOCK: OnceCell<Mutex<()>> = OnceCell::new();
static MULTISIG_AGG_LOCK: OnceCell<Mutex<MultisigAggregator>> = OnceCell::new();
static MULTISIG_SIGN_LOCK: OnceCell<Mutex<MultisigClient>> = OnceCell::new();
#[used] static RUNTIME: OnceCell<Mutex<Runtime>> = OnceCell::new();
#[used] static YWALLET: OnceCell<Mutex<Wallet>> = OnceCell::new();
#[used] static ZWALLET: OnceCell<Mutex<Wallet>> = OnceCell::new();
#[used] static YMEMPOOL: OnceCell<Mutex<MemPool>> = OnceCell::new();
#[used] static ZMEMPOOL: OnceCell<Mutex<MemPool>> = OnceCell::new();
#[used] static SYNCLOCK: OnceCell<Mutex<()>> = OnceCell::new();
#[used] static MULTISIG_AGG_LOCK: OnceCell<Mutex<MultisigAggregator>> = OnceCell::new();
#[used] static MULTISIG_SIGN_LOCK: OnceCell<Mutex<MultisigClient>> = OnceCell::new();
fn get_lock<T>(cell: &OnceCell<Mutex<T>>) -> anyhow::Result<MutexGuard<T>> {
cell.get()

View File

@ -81,6 +81,9 @@ class WarpApi {
static open() {
if (Platform.isAndroid) return DynamicLibrary.open('libwarp_api_ffi.so');
if (Platform.isIOS) return DynamicLibrary.executable();
if (Platform.isWindows) return DynamicLibrary.open('warp_api_ffi.dll');
if (Platform.isLinux) return DynamicLibrary.open('./lib/libwarp_api_ffi.so');
if (Platform.isMacOS) return DynamicLibrary.open('libwarp_api_ffi.dylib');
throw UnsupportedError('This platform is not supported.');
}
@ -194,8 +197,7 @@ class WarpApi {
int account,
List<Recipient> recipients,
bool useTransparent,
int anchorOffset,
String txFilename) {
int anchorOffset) {
final recipientsJson = jsonEncode(recipients);
final res = warp_api_lib.prepare_multi_payment(coin, account,
recipientsJson.toNativeUtf8().cast<Int8>(),

View File

@ -990,7 +990,7 @@ packages:
name: share_plus
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.0"
version: "3.1.0"
share_plus_linux:
dependency: transitive
description:
@ -1143,6 +1143,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.0"
sqflite_common_ffi:
dependency: "direct main"
description:
name: sqflite_common_ffi
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0+2"
sqlite3:
dependency: transitive
description:
name: sqlite3
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.1"
stack_trace:
dependency: transitive
description:

View File

@ -25,7 +25,8 @@ dependencies:
sdk: flutter
warp_api:
path: packages/warp_api_ffi
sqflite: ^2.0.0+4
sqflite: ^2.0.2
sqflite_common_ffi: ^2.1.0
flutter_mobx: ^2.0.2
qr_flutter: ^4.0.0
http: ^0.13.3
@ -51,9 +52,9 @@ dependencies:
ref: 821f81681f8ee819cd721498f7b28d290f4ebe38
grouped_list: ^4.1.0
json_annotation: ^4.1.0
share_plus: ^2.1.4
share_plus: ^3.1.0
path_provider: ^2.0.3
file_picker: ^4.0.2
file_picker: ^4.5.0
mustache_template: ^2.0.0
rate_my_app: ^1.1.1
flutter_palette: ^1.1.0+1