new: iperf

This commit is contained in:
lollipopkit
2024-01-21 18:53:17 +08:00
parent 50d6ed919b
commit 1434556e0b
34 changed files with 432 additions and 117 deletions

View File

@@ -0,0 +1,112 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:toolbox/core/extension/context/common.dart';
import 'package:toolbox/core/extension/context/dialog.dart';
import 'package:toolbox/core/extension/context/locale.dart';
import 'package:toolbox/core/extension/context/snackbar.dart';
import 'package:toolbox/core/utils/platform/auth.dart';
import 'package:toolbox/data/res/store.dart';
import 'package:toolbox/data/res/ui.dart';
import 'package:toolbox/view/page/setting/platform/platform_pub.dart';
import 'package:toolbox/view/widget/appbar.dart';
import 'package:toolbox/view/widget/input_field.dart';
import 'package:toolbox/view/widget/cardx.dart';
import 'package:toolbox/view/widget/store_switch.dart';
class AndroidSettingsPage extends StatefulWidget {
const AndroidSettingsPage({super.key});
@override
_AndroidSettingsPageState createState() => _AndroidSettingsPageState();
}
class _AndroidSettingsPageState extends State<AndroidSettingsPage> {
late SharedPreferences _sp;
@override
void initState() {
super.initState();
SharedPreferences.getInstance().then((value) => _sp = value);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const CustomAppBar(
title: Text('Android'),
),
body: ListView(
padding: const EdgeInsets.symmetric(horizontal: 17),
children: [
_buildBgRun(),
_buildAndroidWidgetSharedPreference(),
if (BioAuth.isPlatformSupported)
PlatformPublicSettings.buildBioAuth(),
].map((e) => CardX(child: e)).toList(),
),
);
}
Widget _buildBgRun() {
return ListTile(
title: Text(l10n.bgRun),
subtitle: Text(l10n.bgRunTip, style: UIs.textGrey),
trailing: StoreSwitch(prop: Stores.setting.bgRun),
);
}
void _saveWidgetSP(String data, Map<String, String> old) {
context.pop();
try {
final map = Map<String, String>.from(json.decode(data));
final keysDel = old.keys.toSet().difference(map.keys.toSet());
for (final key in keysDel) {
_sp.remove(key);
}
map.forEach((key, value) {
_sp.setString(key, value);
});
context.showSnackBar(l10n.success);
} catch (e) {
context.showSnackBar(e.toString());
}
}
Widget _buildAndroidWidgetSharedPreference() {
return ListTile(
title: Text(l10n.homeWidgetUrlConfig),
trailing: const Icon(Icons.keyboard_arrow_right),
onTap: () {
final data = <String, String>{};
_sp.getKeys().forEach((key) {
final val = _sp.getString(key);
if (val != null) {
data[key] = val;
}
});
final ctrl = TextEditingController(text: json.encode(data));
context.showRoundDialog(
title: Text(l10n.homeWidgetUrlConfig),
child: Input(
autoFocus: true,
controller: ctrl,
label: 'JSON',
type: TextInputType.visiblePassword,
maxLines: 7,
onSubmitted: (p0) => _saveWidgetSP(p0, data),
),
actions: [
TextButton(
onPressed: () {
_saveWidgetSP(ctrl.text, data);
},
child: Text(l10n.ok),
),
],
);
},
);
}
}

View File

@@ -0,0 +1,148 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:toolbox/core/extension/context/dialog.dart';
import 'package:toolbox/core/extension/context/locale.dart';
import 'package:toolbox/core/extension/context/snackbar.dart';
import 'package:toolbox/core/route.dart';
import 'package:toolbox/core/utils/misc.dart';
import 'package:toolbox/core/utils/platform/auth.dart';
import 'package:toolbox/core/utils/share.dart';
import 'package:toolbox/data/res/logger.dart';
import 'package:toolbox/data/res/misc.dart';
import 'package:toolbox/data/res/store.dart';
import 'package:toolbox/data/res/ui.dart';
import 'package:toolbox/view/page/setting/platform/platform_pub.dart';
import 'package:toolbox/view/widget/appbar.dart';
import 'package:toolbox/view/widget/future_widget.dart';
import 'package:toolbox/view/widget/cardx.dart';
import 'package:toolbox/view/widget/store_switch.dart';
import 'package:watch_connectivity/watch_connectivity.dart';
class IOSSettingsPage extends StatefulWidget {
const IOSSettingsPage({super.key});
@override
_IOSSettingsPageState createState() => _IOSSettingsPageState();
}
class _IOSSettingsPageState extends State<IOSSettingsPage> {
final _pushToken = ValueNotifier<String?>(null);
final wc = WatchConnectivity();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const CustomAppBar(
title: Text('iOS'),
),
body: ListView(
padding: const EdgeInsets.symmetric(horizontal: 17),
children: [
_buildPushToken(),
_buildAutoUpdateHomeWidget(),
_buildWatchApp(),
if (BioAuth.isPlatformSupported)
PlatformPublicSettings.buildBioAuth(),
].map((e) => CardX(child: e)).toList(),
),
);
}
Widget _buildPushToken() {
return ListTile(
title: Text(l10n.pushToken),
trailing: IconButton(
icon: const Icon(Icons.copy),
alignment: Alignment.centerRight,
padding: EdgeInsets.zero,
onPressed: () {
if (_pushToken.value != null) {
Shares.copy(_pushToken.value!);
context.showSnackBar(l10n.success);
} else {
context.showSnackBar(l10n.getPushTokenFailed);
}
},
),
subtitle: FutureWidget<String?>(
future: getToken(),
loading: Text(l10n.gettingToken),
error: (error, trace) => Text('${l10n.error}: $error'),
success: (text) {
_pushToken.value = text;
return Text(
text ?? l10n.nullToken,
style: UIs.textGrey,
overflow: TextOverflow.ellipsis,
maxLines: 1,
);
},
),
);
}
Widget _buildAutoUpdateHomeWidget() {
return ListTile(
title: Text(l10n.autoUpdateHomeWidget),
subtitle: Text(l10n.whenOpenApp, style: UIs.textGrey),
trailing: StoreSwitch(prop: Stores.setting.autoUpdateHomeWidget),
);
}
Widget _buildWatchApp() {
return FutureWidget<Map<String, dynamic>?>(
future: () async {
if (!await wc.isPaired) {
return null;
}
return await wc.applicationContext;
}(),
loading: UIs.centerLoading,
error: (e, trace) {
Loggers.app.warning('WatchOS error', e, trace);
return ListTile(
title: const Text('Watch app'),
subtitle: Text('${l10n.error}: $e', style: UIs.textGrey),
);
},
success: (ctx) {
if (ctx == null) {
return ListTile(
title: const Text('Watch app'),
subtitle: Text(l10n.watchNotPaired, style: UIs.textGrey),
);
}
return ListTile(
title: const Text('Watch app'),
trailing: const Icon(Icons.keyboard_arrow_right),
onTap: () async => _onTapWatchApp(ctx),
);
},
);
}
void _onTapWatchApp(Map<String, dynamic> map) async {
/// Encode [map] to String with indent `\t`
final text = Miscs.jsonEncoder.convert(map);
final result = await AppRoute.editor(
text: text,
langCode: 'json',
title: 'Watch app',
).go<String>(context);
if (result == null) {
return;
}
try {
final newCtx = json.decode(result) as Map<String, dynamic>;
await wc.updateApplicationContext(newCtx);
} catch (e, trace) {
context.showRoundDialog(
title: Text(l10n.error),
child: Text('${l10n.save}:\n$e'),
);
Loggers.app.warning('Update watch config failed', e, trace);
}
}
}

View File

@@ -0,0 +1,51 @@
import 'package:flutter/material.dart';
import 'package:toolbox/core/extension/context/locale.dart';
import 'package:toolbox/core/utils/platform/auth.dart';
import 'package:toolbox/data/res/store.dart';
import 'package:toolbox/data/res/ui.dart';
import 'package:toolbox/view/widget/future_widget.dart';
import 'package:toolbox/view/widget/store_switch.dart';
abstract final class PlatformPublicSettings {
static Widget buildBioAuth() {
return FutureWidget<bool>(
future: BioAuth.isAvail,
loading: ListTile(
title: Text(l10n.bioAuth),
subtitle: Text(l10n.serverTabLoading, style: UIs.textGrey),
),
error: (e, __) => ListTile(
title: Text(l10n.bioAuth),
subtitle: Text('${l10n.failed}: $e', style: UIs.textGrey),
),
success: (can) {
return ListTile(
title: Text(l10n.bioAuth),
subtitle: can == true
? null
: const Text(
'Not available',
style: UIs.textGrey,
),
trailing: can == true
? StoreSwitch(
prop: Stores.setting.useBioAuth,
callback: (val) async {
if (val) {
Stores.setting.useBioAuth.put(false);
return;
}
// Only auth when turn off (val == false)
final result = await BioAuth.auth(l10n.authRequired);
// If failed, turn on again
if (result != AuthResult.success) {
Stores.setting.useBioAuth.put(true);
}
},
)
: null,
);
},
);
}
}