diff --git a/.gitignore b/.gitignore index 1837e33..3d7099a 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,4 @@ app.*.map.json /android/app/profile /android/app/release pubspec.lock +deb_builder/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e06ba5e --- /dev/null +++ b/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2023, dx8917312@gmail.com +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/README.md b/README.md index b6d4e79..26fb052 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# dde_qrcode_detector +# DDE二维码识别 A new Flutter project. diff --git a/build_deb.sh b/build_deb.sh new file mode 100644 index 0000000..16696a6 --- /dev/null +++ b/build_deb.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash + +flutter packages pub get + +VERSION=$(dart version.dart) + +flutter clean + +flutter build linux + +if [ -e deb_builder ]; then + rm -rf deb_builder +fi + + +mkdir "deb_builder" + +cp -r debian deb_builder/DEBIAN +chmod -R 755 deb_builder/DEBIAN + +cp LICENSE deb_builder/DEBIAN/copyright + +echo "设置版本号为: $VERSION" + +echo Version: "$VERSION" >> deb_builder/DEBIAN/control + +mkdir -p deb_builder/opt/apps/com.debuggerx.dde-qrcode-detector/ + +cp -r dde_package_info/* deb_builder/opt/apps/com.debuggerx.dde-qrcode-detector/ + +ARCH="x64" + +if [[ $(uname -m) == aarch64 ]]; then + ARCH="arm64" + sed -i "s/amd64/$ARCH/g" deb_builder/opt/apps/com.debuggerx.dde-qrcode-detector/info + sed -i "s/amd64/$ARCH/g" deb_builder/DEBIAN/control +fi + +cp -r build/linux/"$ARCH"/release/bundle deb_builder/opt/apps/com.debuggerx.dde-qrcode-detector/files + +mkdir -p deb_builder/opt/apps/com.debuggerx.dde-qrcode-detector/entries/icons/hicolor/512x512/apps/ + +cp logo.png deb_builder/opt/apps/com.debuggerx.dde-qrcode-detector/entries/icons/hicolor/512x512/apps/dde_qrcode_detector.png + +sed -i "s/VERSION/$VERSION/g" deb_builder/opt/apps/com.debuggerx.dde-qrcode-detector/info + +sed -i "s/VERSION/$VERSION/g" deb_builder/opt/apps/com.debuggerx.dde-qrcode-detector/entries/applications/com.debuggerx.dde-qrcode-detector.desktop + +echo "开始打包 $ARCH deb" + +fakeroot dpkg-deb -b deb_builder + +if [[ $ARCH == "x64" ]]; then + ARCH="amd64" +fi + +mv deb_builder.deb com.debuggerx.dde-qrcode-detector_"$VERSION"_"$ARCH".deb + +echo "打包完成!" \ No newline at end of file diff --git a/dde_package_info/entries/applications/com.debuggerx.dde-qrcode-detector.desktop b/dde_package_info/entries/applications/com.debuggerx.dde-qrcode-detector.desktop new file mode 100644 index 0000000..abbc117 --- /dev/null +++ b/dde_package_info/entries/applications/com.debuggerx.dde-qrcode-detector.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Categories=Utility; +Comment=专为 DDE 桌面环境打造的屏幕二维码识别工具,使用 Flutter 构建。 +Exec=/opt/apps/com.debuggerx.dde-qrcode-detector/files/dde_qrcode_detector +Icon=dde_qrcode_detector +Name=DDE Qrcode Detector +Name[zh_CN]=DDE二维码识别 +Type=Application +Version=VERSION +X-Deepin-Vendor=user-custom \ No newline at end of file diff --git a/dde_package_info/info b/dde_package_info/info new file mode 100644 index 0000000..e7dd794 --- /dev/null +++ b/dde_package_info/info @@ -0,0 +1,17 @@ +{ + "appid": "com.debuggerx.dde-qrcode-detector", + "name": "dde-qrcode-detector", + "version": "VERSION", + "arch": ["amd64"], + "permissions": { + "autostart": false, + "notification": false, + "trayicon": false, + "clipboard": true, + "account": false, + "bluetooth": false, + "camera": false, + "audio_record": false, + "installed_apps": false + } +} \ No newline at end of file diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..e69de29 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..55e6a7a --- /dev/null +++ b/debian/control @@ -0,0 +1,15 @@ +Source: dde-qrcode-detector +Section: utils +Priority: optional +Maintainer: DebuggerX +Build-Depends: + clang, + cmake, + libgtk-3-dev, + ninja-build, + libzbar-dev, +Homepage: https://github.com/debuggerx01/dde_qrcode_detector +Package: com.debuggerx.dde-qrcode-detector +Architecture: amd64 +Depends: libzbar0 +Description: 专为 DDE 桌面环境打造的屏幕二维码识别工具,使用 Flutter 构建。 diff --git a/lib/main.dart b/lib/main.dart index c8f180d..ce0cff9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'dart:math'; import 'package:auto_size_text/auto_size_text.dart'; +import 'package:dde_qrcode_detector/utils.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -65,6 +66,7 @@ enum Status { scan, found, notFound, + finish, } class _MyHomePageState extends State { @@ -85,14 +87,28 @@ class _MyHomePageState extends State { }); WindowManager.instance.focus(); WindowManager.instance.grabKeyboard(); - Future.delayed(const Duration(seconds: 1), () { - setState(() { - status = Status.scan; - }); - compute(scan, null).then((codes) { + Future.delayed(const Duration(milliseconds: 500), () { + if (context.mounted) { setState(() { - codes = codes; - status = codes.isEmpty ? Status.notFound : Status.found; + status = Status.scan; + }); + } + compute(scan, null).then((result) { + if (context.mounted) { + setState(() { + codes = result; + status = codes.isEmpty ? Status.notFound : Status.found; + }); + } + Future.delayed(const Duration(seconds: 3), () { + if (status == Status.notFound) { + exit(0); + } + if (context.mounted) { + setState(() { + status = Status.finish; + }); + } }); }); }); @@ -102,118 +118,155 @@ class _MyHomePageState extends State { static Future> scan(dynamic _) async { var imagePath = '/tmp/${DateTime.now().toIso8601String()}.png'; Process.runSync('scrot', [imagePath]); - return ZBar.scan(imagePath); + var result = ZBar.scan(imagePath); + File(imagePath).delete(); + return result; } @override Widget build(BuildContext context) { - late Rect rect; - if (pos != null) { - rect = Rect.fromPoints( - Offset(pos!.bottomLeft.x, pos!.bottomLeft.y), - Offset(pos!.topRight.x, pos!.topRight.y), - ); - rect = rect.translate(-currentWindowPos.dx, -currentWindowPos.dy); - } return GestureDetector( onTap: () { - if ([Status.found, Status.notFound].contains(status)) { + if ([ + Status.found, + Status.notFound, + Status.finish, + ].contains(status)) { exit(0); } }, child: Scaffold( + appBar: AppBar(), backgroundColor: status == Status.standBy ? Colors.transparent : Colors.black12.withOpacity( status == Status.scan ? 0.4 : 0.2, ), body: Stack( + fit: StackFit.expand, children: [ - if (status == Status.scan) + if (![Status.standBy, Status.finish].contains(status)) Center( child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(24), color: Colors.grey.shade700.withOpacity(0.9), ), - child: RepaintBoundary( - child: Image.asset( - 'assets/doubt.gif', - width: 200, - height: 200, - ), - ), - ), - ), - if (status == Status.found) - Positioned( - left: rect.left - rect.width / 2, - top: rect.top - rect.height / 2, - child: Container( - width: rect.width * 2, - height: rect.height * 2, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular( - max(rect.width, rect.height), - ), - color: Colors.black38.withOpacity(.6), - ), + width: 460, + height: 360, child: Center( child: Column( - mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, children: [ - const Text( - '二维码内容:', - style: TextStyle( - fontSize: 28, - color: Colors.white, + RepaintBoundary( + child: Image.asset( + { + Status.scan: 'assets/doubt.gif', + Status.found: 'assets/ok.gif', + Status.notFound: 'assets/no.gif', + }[status]!, + width: 200, + height: 200, + fit: BoxFit.fitHeight, ), ), - AutoSizeText( - content?.text ?? '', + Text( + { + Status.scan: '小浣熊正在努力寻找二维码~', + Status.found: '小浣熊成功找到${codes.length}个二维码!', + Status.notFound: '小浣熊找了一圈,啥也没发现……', + }[status]!, style: const TextStyle( - fontSize: 28, color: Colors.white, + fontSize: 28, ), - minFontSize: 16, - maxFontSize: 46, - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - FilledButton( - onPressed: () { - Clipboard.setData( - ClipboardData(text: content?.text), - ); - }, - child: const Text( - '复制', - style: TextStyle(fontSize: 28), - ), - ), - FilledButton( - onPressed: () async { - var url = content?.text ?? ''; - if (await canLaunchUrlString(url)) { - launchUrlString(url).then((value) { - exit(0); - }); - } - }, - child: const Text( - '打开', - style: TextStyle(fontSize: 28), - ), - ), - ], ), ], ), ), ), ), + ...codes.map( + (code) { + var centerAndSize = getCenterAndSizeOfPoints(code.points); + var center = centerAndSize.center - currentWindowPos; + var size = max(centerAndSize.size, 300); + return Positioned( + left: center.dx - size / 2, + top: center.dy - size / 2, + child: Container( + width: size.toDouble(), + height: size.toDouble(), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(size / 2), + color: Colors.black38.withOpacity(.6), + ), + padding: EdgeInsets.all(size / 6), + child: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Text( + '二维码内容:', + style: TextStyle( + fontSize: 28, + color: Colors.white, + fontWeight: FontWeight.w600, + ), + ), + Flexible( + child: AutoSizeText( + code.content.split('').join('\u{200B}'), + style: const TextStyle( + fontSize: 28, + color: Colors.white, + ), + minFontSize: 16, + maxFontSize: 46, + ), + ), + SizedBox(height: size / 16), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + FilledButton( + onPressed: () { + Clipboard.setData( + ClipboardData(text: code.content), + ); + exit(0); + }, + child: const Text( + '复制', + style: TextStyle(fontSize: 28), + ), + ), + SizedBox(width: size / 20), + FilledButton( + onPressed: () async { + var url = code.content; + if (await canLaunchUrlString(url)) { + launchUrlString(url).then((value) { + exit(0); + }); + } + }, + child: const Text( + '打开', + style: TextStyle(fontSize: 28), + ), + ), + ], + ), + ], + ), + ), + ), + ); + }, + ), ], ), ), diff --git a/lib/utils.dart b/lib/utils.dart new file mode 100644 index 0000000..de050df --- /dev/null +++ b/lib/utils.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:zbar_scan_plugin/zbar_scan_plugin.dart'; +import 'package:collection/collection.dart'; +import 'dart:math'; + +class CenterAndSize { + final Offset center; + final double size; + + CenterAndSize({ + required this.center, + required this.size, + }); +} + +CenterAndSize getCenterAndSizeOfPoints(List points) { + if (points.isEmpty) { + return CenterAndSize( + center: Offset.zero, + size: 0, + ); + } + var center = Offset( + points.map((p) => p.x).average, + points.map((p) => p.y).average, + ); + + var size = sqrt( + pow(points.first.x - center.dx, 2) + pow(points.first.y - center.dy, 2), + ); + return CenterAndSize( + center: center, + size: size * 1.2, + ); + +} diff --git a/logo.png b/logo.png new file mode 100644 index 0000000..ae3cce2 Binary files /dev/null and b/logo.png differ diff --git a/pubspec.yaml b/pubspec.yaml index 4736d07..a0295d4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -43,6 +43,7 @@ dependencies: zbar_scan_plugin: git: url: https://github.com/debuggerx01/zbar_scan_plugin.git + collection: ^1.17.0 dev_dependencies: flutter_test: @@ -54,6 +55,7 @@ dev_dependencies: # package. See that file for information about deactivating specific lint # rules and activating additional ones. flutter_lints: ^2.0.0 + yaml: any # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/version.dart b/version.dart new file mode 100644 index 0000000..1ddf4e9 --- /dev/null +++ b/version.dart @@ -0,0 +1,8 @@ +import 'dart:io'; + +import 'package:yaml/yaml.dart'; + +void main() async { + var document = loadYaml(File('pubspec.yaml').readAsStringSync()); + print(document['version'].replaceAll('+', '.')); +}