mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2026-01-31 13:25:10 +01:00
new: wearos detect (#358)
This commit is contained in:
22
lib/app.dart
22
lib/app.dart
@@ -18,7 +18,7 @@ class MyApp extends StatelessWidget {
|
||||
builder: (_, __) {
|
||||
if (!Stores.setting.useSystemPrimaryColor.fetch()) {
|
||||
UIs.colorSeed = Color(Stores.setting.primaryColor.fetch());
|
||||
return _buildApp();
|
||||
return _buildApp(context);
|
||||
}
|
||||
return DynamicColorBuilder(
|
||||
builder: (light, dark) {
|
||||
@@ -36,14 +36,14 @@ class MyApp extends StatelessWidget {
|
||||
} else if (!context.isDark && dark != null) {
|
||||
UIs.primaryColor = dark.primary;
|
||||
}
|
||||
return _buildApp(light: lightTheme, dark: darkTheme);
|
||||
return _buildApp(context, light: lightTheme, dark: darkTheme);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildApp({ThemeData? light, ThemeData? dark}) {
|
||||
Widget _buildApp(BuildContext ctx, {ThemeData? light, ThemeData? dark}) {
|
||||
final tMode = Stores.setting.themeMode.fetch();
|
||||
// Issue #57
|
||||
final themeMode = switch (tMode) {
|
||||
@@ -71,15 +71,17 @@ class MyApp extends StatelessWidget {
|
||||
themeMode: themeMode,
|
||||
theme: light,
|
||||
darkTheme: tMode < 3 ? dark : _getAmoledTheme(dark),
|
||||
home: Stores.setting.fullScreen.fetch()
|
||||
? OrientationBuilder(
|
||||
builder: (_, ori) {
|
||||
return HomePage(fullScreen: ori == Orientation.landscape);
|
||||
},
|
||||
)
|
||||
: const HomePage(),
|
||||
home: _buildAppContent(ctx),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAppContent(BuildContext ctx) {
|
||||
//if (Pros.app.isWearOS) return const WearHome();
|
||||
return const HomePage();
|
||||
}
|
||||
|
||||
// bool _isSmallDevice(BuildContext ctx) =>
|
||||
// MediaQuery.of(ctx).size.shortestSide < 600;
|
||||
}
|
||||
|
||||
void _setup(BuildContext context) async {
|
||||
|
||||
@@ -82,9 +82,9 @@ chmod +x $_installShellPath
|
||||
case ShellFunc.status:
|
||||
return '''
|
||||
if [ "\$macSign" = "" ] && [ "\$bsdSign" = "" ]; then
|
||||
\t${_statusCmds.join(cmdDivider)}
|
||||
\t${StatusCmdType.values.map((e) => e.cmd).join(cmdDivider)}
|
||||
else
|
||||
\t${_bsdStatusCmd.join(cmdDivider)}
|
||||
\t${BSDStatusCmdType.values.map((e) => e.cmd).join(cmdDivider)}
|
||||
fi''';
|
||||
// case ShellFunc.docker:
|
||||
// return '''
|
||||
@@ -196,72 +196,47 @@ extension EnumX on Enum {
|
||||
}
|
||||
|
||||
enum StatusCmdType {
|
||||
echo,
|
||||
time,
|
||||
net,
|
||||
sys,
|
||||
cpu,
|
||||
uptime,
|
||||
conn,
|
||||
disk,
|
||||
mem,
|
||||
tempType,
|
||||
tempVal,
|
||||
host,
|
||||
diskio,
|
||||
battery,
|
||||
nvidia,
|
||||
sensors,
|
||||
echo._('echo ${SystemType.linuxSign}'),
|
||||
time._('date +%s'),
|
||||
net._('cat /proc/net/dev'),
|
||||
sys._('cat /etc/*-release | grep PRETTY_NAME'),
|
||||
cpu._('cat /proc/stat | grep cpu'),
|
||||
uptime._('uptime'),
|
||||
conn._('cat /proc/net/snmp'),
|
||||
disk._('df'),
|
||||
mem._("cat /proc/meminfo | grep -E 'Mem|Swap'"),
|
||||
tempType._('cat /sys/class/thermal/thermal_zone*/type'),
|
||||
tempVal._('cat /sys/class/thermal/thermal_zone*/temp'),
|
||||
host._('cat /etc/hostname'),
|
||||
diskio._('cat /proc/diskstats'),
|
||||
battery._(
|
||||
'for f in /sys/class/power_supply/*/uevent; do cat "\$f"; echo; done'),
|
||||
nvidia._('nvidia-smi -q -x'),
|
||||
sensors._('sensors'),
|
||||
;
|
||||
}
|
||||
|
||||
/// Cmds for linux server
|
||||
const _statusCmds = [
|
||||
'echo ${SystemType.linuxSign}',
|
||||
'date +%s',
|
||||
'cat /proc/net/dev',
|
||||
'cat /etc/*-release | grep PRETTY_NAME',
|
||||
'cat /proc/stat | grep cpu',
|
||||
'uptime',
|
||||
'cat /proc/net/snmp',
|
||||
'df',
|
||||
"cat /proc/meminfo | grep -E 'Mem|Swap'",
|
||||
'cat /sys/class/thermal/thermal_zone*/type',
|
||||
'cat /sys/class/thermal/thermal_zone*/temp',
|
||||
'cat /etc/hostname',
|
||||
'cat /proc/diskstats',
|
||||
'for f in /sys/class/power_supply/*/uevent; do cat "\$f"; echo; done',
|
||||
'nvidia-smi -q -x',
|
||||
'sensors',
|
||||
];
|
||||
final String cmd;
|
||||
|
||||
const StatusCmdType._(this.cmd);
|
||||
}
|
||||
|
||||
enum BSDStatusCmdType {
|
||||
echo,
|
||||
time,
|
||||
net,
|
||||
sys,
|
||||
cpu,
|
||||
uptime,
|
||||
disk,
|
||||
mem,
|
||||
echo._('echo ${SystemType.bsdSign}'),
|
||||
time._('date +%s'),
|
||||
net._('netstat -ibn'),
|
||||
sys._('uname -or'),
|
||||
cpu._('top -l 1 | grep "CPU usage"'),
|
||||
uptime._('uptime'),
|
||||
disk._('df -k'),
|
||||
mem._('top -l 1 | grep PhysMem'),
|
||||
//temp,
|
||||
host,
|
||||
host._('hostname'),
|
||||
;
|
||||
}
|
||||
|
||||
/// Cmds for BSD server
|
||||
const _bsdStatusCmd = [
|
||||
'echo ${SystemType.bsdSign}',
|
||||
'date +%s',
|
||||
'netstat -ibn',
|
||||
'uname -or',
|
||||
'top -l 1 | grep "CPU usage"',
|
||||
'uptime',
|
||||
'df -k',
|
||||
'top -l 1 | grep PhysMem',
|
||||
//'sysctl -a | grep temperature',
|
||||
'hostname',
|
||||
];
|
||||
final String cmd;
|
||||
|
||||
const BSDStatusCmdType._(this.cmd);
|
||||
}
|
||||
|
||||
extension StatusCmdTypeX on StatusCmdType {
|
||||
String get i18n => switch (this) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:fl_lib/fl_lib.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AppProvider extends ChangeNotifier {
|
||||
@@ -8,7 +10,28 @@ class AppProvider extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool moveBg = true;
|
||||
|
||||
BuildContext? ctx;
|
||||
|
||||
bool isWearOS = false;
|
||||
|
||||
Future<void> init() async {
|
||||
await _initIsWearOS();
|
||||
}
|
||||
|
||||
Future<void> _initIsWearOS() async {
|
||||
if (!isAndroid) {
|
||||
isWearOS = false;
|
||||
return;
|
||||
}
|
||||
|
||||
final deviceInfo = DeviceInfoPlugin();
|
||||
final androidInfo = await deviceInfo.androidInfo;
|
||||
|
||||
const feat = 'android.hardware.type.watch';
|
||||
final hasFeat = androidInfo.systemFeatures.contains(feat);
|
||||
if (hasFeat) {
|
||||
isWearOS = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
class BuildData {
|
||||
static const String name = "ServerBox";
|
||||
static const int build = 897;
|
||||
static const String engine = "3.19.6";
|
||||
static const String buildAt = "2024-05-14 22:32:07";
|
||||
static const int modifications = 12;
|
||||
static const int build = 904;
|
||||
static const String engine = "3.22.0";
|
||||
static const String buildAt = "2024-05-20 16:10:12";
|
||||
static const int modifications = 11;
|
||||
static const int script = 47;
|
||||
}
|
||||
|
||||
@@ -65,36 +65,17 @@ Future<void> _initApp() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
await Paths.init(BuildData.name);
|
||||
|
||||
// Base of all data.
|
||||
await _initDb();
|
||||
|
||||
await _initData();
|
||||
_setupDebug();
|
||||
SystemUIs.initDesktopWindow(Stores.setting.hideTitleBar.fetch());
|
||||
|
||||
// Load font
|
||||
SystemUIs.initDesktopWindow(Stores.setting.hideTitleBar.fetch());
|
||||
FontUtils.loadFrom(Stores.setting.fontPath.fetch());
|
||||
|
||||
if (isAndroid) {
|
||||
// SharedPreferences is only used on Android for saving home widgets settings.
|
||||
SharedPreferences.setPrefix('');
|
||||
// try switch to highest refresh rate
|
||||
await FlutterDisplayMode.setHighRefreshRate();
|
||||
}
|
||||
|
||||
final serversCount = Stores.server.box.keys.length;
|
||||
// Plus 1 to avoid 0.
|
||||
Computer.shared.turnOn(workersCount: (serversCount / 3).round() + 1);
|
||||
|
||||
if (isIOS || isMacOS) {
|
||||
if (Stores.setting.icloudSync.fetch()) ICloud.sync();
|
||||
}
|
||||
if (Stores.setting.webdavSync.fetch()) Webdav.sync();
|
||||
|
||||
_doPlatformRelated();
|
||||
_doVersionRelated();
|
||||
}
|
||||
|
||||
Future<void> _initDb() async {
|
||||
Future<void> _initData() async {
|
||||
// await SecureStore.init();
|
||||
await Hive.initFlutter();
|
||||
// Ordered by typeId
|
||||
@@ -115,6 +96,7 @@ Future<void> _initDb() async {
|
||||
|
||||
Pros.snippet.load();
|
||||
Pros.key.load();
|
||||
await Pros.app.init();
|
||||
}
|
||||
|
||||
void _setupDebug() {
|
||||
@@ -131,6 +113,24 @@ void _setupDebug() {
|
||||
}
|
||||
}
|
||||
|
||||
void _doPlatformRelated() async {
|
||||
if (isAndroid) {
|
||||
// SharedPreferences is only used on Android for saving home widgets settings.
|
||||
SharedPreferences.setPrefix('');
|
||||
// try switch to highest refresh rate
|
||||
FlutterDisplayMode.setHighRefreshRate();
|
||||
}
|
||||
|
||||
final serversCount = Stores.server.box.keys.length;
|
||||
// Plus 1 to avoid 0.
|
||||
Computer.shared.turnOn(workersCount: (serversCount / 3).round() + 1);
|
||||
|
||||
if (isIOS || isMacOS) {
|
||||
if (Stores.setting.icloudSync.fetch()) ICloud.sync();
|
||||
}
|
||||
if (Stores.setting.webdavSync.fetch()) Webdav.sync();
|
||||
}
|
||||
|
||||
// It may contains some async heavy funcs.
|
||||
Future<void> _doVersionRelated() async {
|
||||
final curVer = Stores.setting.lastVer.fetch();
|
||||
@@ -138,7 +138,6 @@ Future<void> _doVersionRelated() async {
|
||||
// It's only the version upgrade trigger logic.
|
||||
// How to upgrade the data is inside each own func.
|
||||
if (curVer < newVer) {
|
||||
// DO version check inside each func.
|
||||
ServerDetailCards.autoAddNewCards(newVer);
|
||||
Stores.setting.lastVer.put(newVer);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import 'package:fl_lib/fl_lib.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:icons_plus/icons_plus.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:toolbox/core/channel/home_widget.dart';
|
||||
import 'package:toolbox/core/extension/build.dart';
|
||||
import 'package:toolbox/core/extension/context/locale.dart';
|
||||
@@ -23,9 +22,7 @@ import 'package:wakelock_plus/wakelock_plus.dart';
|
||||
part 'appbar.dart';
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
final bool fullScreen;
|
||||
|
||||
const HomePage({super.key, this.fullScreen = false});
|
||||
const HomePage({super.key});
|
||||
|
||||
@override
|
||||
State<HomePage> createState() => _HomePageState();
|
||||
@@ -157,17 +154,17 @@ class _HomePageState extends State<HomePage>
|
||||
}
|
||||
},
|
||||
),
|
||||
bottomNavigationBar: widget.fullScreen
|
||||
? null
|
||||
: ValBuilder(
|
||||
listenable: _isLandscape,
|
||||
builder: (ls) {
|
||||
return ListenableBuilder(
|
||||
bottomNavigationBar: ValBuilder(
|
||||
listenable: _isLandscape,
|
||||
builder: (ls) {
|
||||
return Stores.setting.fullScreen.fetch()
|
||||
? UIs.placeholder
|
||||
: ListenableBuilder(
|
||||
listenable: _selectIndex,
|
||||
builder: (_, __) => _buildBottomBar(ls),
|
||||
);
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -337,7 +334,7 @@ ${GithubIds.participants.map((e) => '[$e](${e.url})').join(' ')}
|
||||
// Auth required for first launch
|
||||
if (Stores.setting.useBioAuth.fetch()) BioAuth.go();
|
||||
|
||||
_reqNotiPerm();
|
||||
//_reqNotiPerm();
|
||||
|
||||
if (Stores.setting.autoCheckAppUpdate.fetch()) {
|
||||
AppUpdateIface.doUpdate(
|
||||
@@ -352,28 +349,27 @@ ${GithubIds.participants.map((e) => '[$e](${e.url})').join(' ')}
|
||||
await Pros.server.refresh();
|
||||
}
|
||||
|
||||
// It's required by RUpgrade to send update progress
|
||||
Future<void> _reqNotiPerm() async {
|
||||
if (!isAndroid) return;
|
||||
final suc = await PermUtils.request(Permission.notification);
|
||||
if (!suc) {
|
||||
final noNotiPerm = Stores.setting.noNotiPerm;
|
||||
if (noNotiPerm.fetch()) return;
|
||||
context.showRoundDialog(
|
||||
title: l10n.error,
|
||||
child: Text(l10n.noNotiPerm),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
noNotiPerm.put(true);
|
||||
context.pop();
|
||||
},
|
||||
child: Text(l10n.ok),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
// Future<void> _reqNotiPerm() async {
|
||||
// if (!isAndroid) return;
|
||||
// final suc = await PermUtils.request(Permission.notification);
|
||||
// if (!suc) {
|
||||
// final noNotiPerm = Stores.setting.noNotiPerm;
|
||||
// if (noNotiPerm.fetch()) return;
|
||||
// context.showRoundDialog(
|
||||
// title: l10n.error,
|
||||
// child: Text(l10n.noNotiPerm),
|
||||
// actions: [
|
||||
// TextButton(
|
||||
// onPressed: () {
|
||||
// noNotiPerm.put(true);
|
||||
// context.pop();
|
||||
// },
|
||||
// child: Text(l10n.ok),
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
Future<void> _onLongPressSetting() async {
|
||||
final map = Stores.setting.box.toJson(includeInternal: false);
|
||||
|
||||
86
lib/view/page/home/wear.dart
Normal file
86
lib/view/page/home/wear.dart
Normal file
@@ -0,0 +1,86 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:after_layout/after_layout.dart';
|
||||
import 'package:fl_lib/fl_lib.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:toolbox/core/extension/context/locale.dart';
|
||||
import 'package:toolbox/data/model/server/server.dart';
|
||||
import 'package:toolbox/data/provider/server.dart';
|
||||
import 'package:toolbox/data/res/build_data.dart';
|
||||
import 'package:toolbox/data/res/provider.dart';
|
||||
import 'package:toolbox/data/res/store.dart';
|
||||
import 'package:toolbox/data/res/url.dart';
|
||||
|
||||
final class WearHome extends StatefulWidget {
|
||||
const WearHome({super.key});
|
||||
|
||||
@override
|
||||
_WearHomeState createState() => _WearHomeState();
|
||||
}
|
||||
|
||||
final class _WearHomeState extends State<WearHome> with AfterLayoutMixin {
|
||||
late final _pageCtrl =
|
||||
PageController(initialPage: Pros.server.servers.isNotEmpty ? 1 : 0);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _buildBody();
|
||||
}
|
||||
|
||||
Widget _buildBody() {
|
||||
return Consumer<ServerProvider>(builder: (_, pro, __) {
|
||||
if (pro.servers.isEmpty) {
|
||||
return const Center(child: Text('No server'));
|
||||
}
|
||||
return PageView.builder(
|
||||
controller: _pageCtrl,
|
||||
itemCount: pro.servers.length + 1,
|
||||
itemBuilder: (_, index) {
|
||||
if (index == 0) return _buildInit();
|
||||
|
||||
final id = pro.serverOrder[index];
|
||||
final server = Pros.server.pick(id: id);
|
||||
if (server == null) return UIs.placeholder;
|
||||
return _buildEachSever(server);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildInit() {
|
||||
return Center(
|
||||
child: Column(
|
||||
children: [
|
||||
IconButton(onPressed: () {}, icon: const Icon(Icons.add)),
|
||||
UIs.height7,
|
||||
Text(l10n.restore)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEachSever(Server srv) {
|
||||
return const Padding(
|
||||
padding: EdgeInsets.all(7),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<void> afterFirstLayout(BuildContext context) async {
|
||||
if (Stores.setting.autoCheckAppUpdate.fetch()) {
|
||||
AppUpdateIface.doUpdate(
|
||||
build: BuildData.build,
|
||||
url: '${Urls.cdnBase}/update.json',
|
||||
context: context,
|
||||
updateL10n: l10n.update,
|
||||
);
|
||||
}
|
||||
await Pros.server.load();
|
||||
await Pros.server.refresh();
|
||||
}
|
||||
}
|
||||
@@ -802,7 +802,6 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
||||
return CardX(
|
||||
child: ListTile(
|
||||
title: const Text('PVE'),
|
||||
subtitle: Text(addr, style: UIs.textGrey),
|
||||
leading: const Icon(FontAwesome.server_solid, size: 17),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () => AppRoutes.pve(spi: widget.spi).go(context),
|
||||
|
||||
Reference in New Issue
Block a user