- setup/driver_install.dart:检测已装(/Library/Printers/HiTi)+ 内置 dmg 定位 (从 resolvedExecutable 反推 flutter_assets/assets/drivers/,缺失降级官网链接) + 静默安装(hdiutil 挂载→定位 pkg→osascript 提权 installer→finally detach; 挂载点名带随机序号必须从 attach 输出解析) - 向导第 2 步重做状态机:驱动未装 → "一键安装驱动(内置安装包)"按钮 (安装中提示输管理员密码,失败显示原因);驱动已装但无队列 → 连接/添加队列 引导;无内置 dmg 的构建回退官网链接 - assets/drivers/ 收驱动 dmg(.gitignore 不入库,打包脚本检查缺失并警告, 当前从 card-printer-agent/dist 拷入 2.4.2 版,25MB) - 实测:挂载/pkg 定位/detach 免密链路全通过;打包 47MB dmg 内含驱动, 冒烟启动正常(服务已占主端口 38200);一键安装的提权弹窗待用户实测
79 lines
3.2 KiB
Dart
79 lines
3.2 KiB
Dart
import 'dart:io';
|
|
|
|
import '../utils/command_runner.dart';
|
|
|
|
/// HiTi Mac 驱动(CUPS 过滤器)内置安装:App 资源携带驱动 dmg,
|
|
/// 向导一键挂载静默安装(osascript 提权一次),已装判断 = /Library/Printers/HiTi 存在。
|
|
/// Windows 不适用(PavoApi 直打,官方驱动可选)。
|
|
class DriverInstall {
|
|
static const installedMarker = '/Library/Printers/HiTi';
|
|
|
|
static bool get isSupported => Platform.isMacOS;
|
|
|
|
/// 驱动是否已安装。
|
|
static bool isInstalled() => Directory(installedMarker).existsSync();
|
|
|
|
/// 内置驱动 dmg 的文件系统路径;未打包携带(如开发运行/产物缺失)返回 null。
|
|
///
|
|
/// Flutter assets 在 .app 内的真实路径:
|
|
/// [app]/Contents/Frameworks/App.framework/Resources/flutter_assets/[asset]
|
|
/// 从可执行文件位置反推(exe 在 [app]/Contents/MacOS/)。
|
|
static String? bundledDmgPath() {
|
|
if (!Platform.isMacOS) return null;
|
|
final exe = Platform.resolvedExecutable;
|
|
final exeDir = File(exe).parent.path; // .../Contents/MacOS
|
|
final driversDir =
|
|
'$exeDir/../Frameworks/App.framework/Resources/flutter_assets/assets/drivers';
|
|
final dir = Directory(driversDir);
|
|
if (!dir.existsSync()) return null;
|
|
final dmgs = dir
|
|
.listSync()
|
|
.whereType<File>()
|
|
.where((f) => f.path.toLowerCase().endsWith('.dmg'))
|
|
.toList();
|
|
return dmgs.isEmpty ? null : dmgs.first.path;
|
|
}
|
|
|
|
/// 从内置 dmg 静默安装驱动(挂载 → 找 pkg → osascript 提权 installer → 卸载卷)。
|
|
/// 提权弹窗由系统显示(一次管理员密码),超时 3 分钟。
|
|
static Future<void> installFromDmg(String dmgPath, {void Function(String line)? onLog}) async {
|
|
final log = onLog ?? (_) {};
|
|
|
|
// 1. 只读挂载(挂载点带随机序号,必须从输出解析,不能拼名字)
|
|
final attachOut = await runCommand('hdiutil', ['attach', '-nobrowse', '-readonly', dmgPath],
|
|
timeoutMs: 30000);
|
|
final match = RegExp(r'/Volumes/[^\t\n]+').allMatches(attachOut);
|
|
if (match.isEmpty) {
|
|
throw StateError('驱动 dmg 挂载失败');
|
|
}
|
|
final mountPoint = match.last.group(0)!;
|
|
log('驱动卷已挂载:$mountPoint');
|
|
|
|
try {
|
|
// 2. 找 pkg
|
|
final pkg = Directory(mountPoint)
|
|
.listSync()
|
|
.whereType<File>()
|
|
.where((f) => f.path.toLowerCase().endsWith('.pkg'))
|
|
.toList();
|
|
if (pkg.isEmpty) {
|
|
throw StateError('驱动 dmg 内未找到 .pkg 安装包');
|
|
}
|
|
final pkgPath = pkg.first.path;
|
|
|
|
// 3. osascript 提权安装(AppleScript 字符串内嵌套引号用 \" 转义)
|
|
final script =
|
|
'do shell script "installer -pkg \\"$pkgPath\\" -target /" '
|
|
'with administrator privileges with prompt "安装 HiTi 打印机驱动"';
|
|
log('开始安装驱动(请在系统弹窗中输入管理员密码)…');
|
|
await runCommand('osascript', ['-e', script], timeoutMs: 180000);
|
|
log('驱动安装完成');
|
|
} finally {
|
|
// 4. 卸载卷(失败不影响安装结果)
|
|
try {
|
|
await runCommand('hdiutil', ['detach', mountPoint, '-force'], tolerateExit: true);
|
|
} catch (_) {}
|
|
}
|
|
}
|
|
}
|