generated from dellevin/template
压缩安装包体积
This commit is contained in:
@@ -32,6 +32,13 @@ android {
|
|||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
|
// 启用代码压缩和资源缩减
|
||||||
|
isMinifyEnabled = true
|
||||||
|
isShrinkResources = true
|
||||||
|
proguardFiles(
|
||||||
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
|
"proguard-rules.pro"
|
||||||
|
)
|
||||||
// TODO: Add your own signing config for the release build.
|
// TODO: Add your own signing config for the release build.
|
||||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||||
signingConfig = signingConfigs.getByName("debug")
|
signingConfig = signingConfigs.getByName("debug")
|
||||||
|
|||||||
33
android/app/proguard-rules.pro
vendored
Normal file
33
android/app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
|
||||||
|
# Flutter specific rules
|
||||||
|
-keep class io.flutter.app.** { *; }
|
||||||
|
-keep class io.flutter.plugin.** { *; }
|
||||||
|
-keep class io.flutter.util.** { *; }
|
||||||
|
-keep class io.flutter.view.** { *; }
|
||||||
|
-keep class io.flutter.** { *; }
|
||||||
|
-keep class io.flutter.plugins.** { *; }
|
||||||
|
|
||||||
|
# Keep classes used by reflection
|
||||||
|
-keep class * { @com.google.gson.annotations.SerializedName <fields>; }
|
||||||
|
|
||||||
|
# SQLite
|
||||||
|
-keep class net.sqlcipher.** { *; }
|
||||||
|
|
||||||
|
# Prevent obfuscation of model classes
|
||||||
|
-keep class top.iletter.mooknote.** { *; }
|
||||||
|
|
||||||
|
# Google Play Core - prevent R8 from removing these classes
|
||||||
|
-dontwarn com.google.android.play.core.splitcompat.SplitCompatApplication
|
||||||
|
-dontwarn com.google.android.play.core.splitinstall.SplitInstallException
|
||||||
|
-dontwarn com.google.android.play.core.splitinstall.SplitInstallManager
|
||||||
|
-dontwarn com.google.android.play.core.splitinstall.SplitInstallManagerFactory
|
||||||
|
-dontwarn com.google.android.play.core.splitinstall.SplitInstallRequest$Builder
|
||||||
|
-dontwarn com.google.android.play.core.splitinstall.SplitInstallRequest
|
||||||
|
-dontwarn com.google.android.play.core.splitinstall.SplitInstallSessionState
|
||||||
|
-dontwarn com.google.android.play.core.splitinstall.SplitInstallStateUpdatedListener
|
||||||
|
-dontwarn com.google.android.play.core.tasks.OnFailureListener
|
||||||
|
-dontwarn com.google.android.play.core.tasks.OnSuccessListener
|
||||||
|
-dontwarn com.google.android.play.core.tasks.Task
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 245 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.1 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.1 MiB |
@@ -5,10 +5,11 @@ import 'package:path/path.dart' as path;
|
|||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import '../utils/font_download_manager.dart';
|
import '../utils/font_download_manager.dart';
|
||||||
import '../utils/toast_util.dart';
|
import '../utils/toast_util.dart';
|
||||||
|
import '../utils/user_prefs.dart';
|
||||||
|
|
||||||
/// 字体选择页面
|
/// 字体选择页面
|
||||||
///
|
///
|
||||||
/// 用户输入或选择字体目录,遍历目录下的字体文件,点击即可加载使用。
|
/// 用户选择字体目录,遍历目录下的字体文件,点击即可加载使用。
|
||||||
class FontPickerPage extends StatefulWidget {
|
class FontPickerPage extends StatefulWidget {
|
||||||
final String? initialFamily;
|
final String? initialFamily;
|
||||||
const FontPickerPage({super.key, this.initialFamily});
|
const FontPickerPage({super.key, this.initialFamily});
|
||||||
@@ -18,27 +19,24 @@ class FontPickerPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _FontPickerPageState extends State<FontPickerPage> {
|
class _FontPickerPageState extends State<FontPickerPage> {
|
||||||
final TextEditingController _pathController = TextEditingController();
|
|
||||||
final FontDownloadManager _fontManager = FontDownloadManager();
|
final FontDownloadManager _fontManager = FontDownloadManager();
|
||||||
|
|
||||||
List<FontFileInfo> _fonts = [];
|
List<FontFileInfo> _fonts = [];
|
||||||
String? _loadingPath;
|
String? _loadingPath;
|
||||||
String? _selectedFamily;
|
String? _selectedFamily;
|
||||||
bool _isScanning = false;
|
bool _isScanning = false;
|
||||||
|
String? _currentDirPath;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_selectedFamily = widget.initialFamily;
|
_selectedFamily = widget.initialFamily;
|
||||||
// 默认填入内置字体目录
|
// 恢复上次选择的目录
|
||||||
_pathController.text = '/sdcard/Documents/mooknote/fonts';
|
final savedDir = UserPrefs().lastFontDir;
|
||||||
_checkPermissionAndScan();
|
if (savedDir != null && savedDir.isNotEmpty) {
|
||||||
|
_currentDirPath = savedDir;
|
||||||
|
_scanDirectory(savedDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_pathController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 请求存储权限(Android 11+ 需要 MANAGE_EXTERNAL_STORAGE)
|
/// 请求存储权限(Android 11+ 需要 MANAGE_EXTERNAL_STORAGE)
|
||||||
@@ -58,18 +56,6 @@ class _FontPickerPageState extends State<FontPickerPage> {
|
|||||||
return status.isGranted;
|
return status.isGranted;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 检查权限并扫描
|
|
||||||
Future<void> _checkPermissionAndScan() async {
|
|
||||||
final hasPermission = await _requestStoragePermission();
|
|
||||||
if (!hasPermission) {
|
|
||||||
if (mounted) {
|
|
||||||
_showPermissionDialog();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await _scanDirectory();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 显示权限提示弹窗
|
/// 显示权限提示弹窗
|
||||||
void _showPermissionDialog() {
|
void _showPermissionDialog() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
@@ -103,12 +89,8 @@ class _FontPickerPageState extends State<FontPickerPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 扫描目录字体
|
/// 扫描目录字体
|
||||||
Future<void> _scanDirectory() async {
|
Future<void> _scanDirectory(String dirPath) async {
|
||||||
final dirPath = _pathController.text.trim();
|
if (dirPath.isEmpty) return;
|
||||||
if (dirPath.isEmpty) {
|
|
||||||
if (mounted) ToastUtil.show(context, '请输入目录路径');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setState(() => _isScanning = true);
|
setState(() => _isScanning = true);
|
||||||
try {
|
try {
|
||||||
@@ -130,18 +112,30 @@ class _FontPickerPageState extends State<FontPickerPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 使用 file_picker 选择目录
|
/// 扫描当前目录字体
|
||||||
Future<void> _pickDirectory() async {
|
Future<void> _rescanDirectory() async {
|
||||||
|
if (_currentDirPath == null || _currentDirPath!.isEmpty) {
|
||||||
|
ToastUtil.show(context, '请先选择字体目录');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await _scanDirectory(_currentDirPath!);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 点击选择目录按钮
|
||||||
|
Future<void> _onSelectDirectory() async {
|
||||||
final hasPermission = await _requestStoragePermission();
|
final hasPermission = await _requestStoragePermission();
|
||||||
if (!hasPermission) {
|
if (!hasPermission) {
|
||||||
if (mounted) _showPermissionDialog();
|
if (mounted) _showPermissionDialog();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final result = await FilePicker.platform.getDirectoryPath();
|
final result = await FilePicker.platform.getDirectoryPath();
|
||||||
if (result != null && result.isNotEmpty) {
|
if (result != null && result.isNotEmpty) {
|
||||||
_pathController.text = result;
|
_currentDirPath = result;
|
||||||
await _scanDirectory();
|
// 保存到 SharedPreferences
|
||||||
|
await UserPrefs().setLastFontDir(result);
|
||||||
|
await _scanDirectory(result);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) ToastUtil.show(context, '选择目录失败: $e');
|
if (mounted) ToastUtil.show(context, '选择目录失败: $e');
|
||||||
@@ -195,7 +189,7 @@ class _FontPickerPageState extends State<FontPickerPage> {
|
|||||||
),
|
),
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
// 路径输入区
|
// 选择目录按钮区
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.fromLTRB(20, 16, 20, 12),
|
padding: const EdgeInsets.fromLTRB(20, 16, 20, 12),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@@ -219,40 +213,49 @@ class _FontPickerPageState extends State<FontPickerPage> {
|
|||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextField(
|
child: InkWell(
|
||||||
controller: _pathController,
|
onTap: _onSelectDirectory,
|
||||||
style: TextStyle(fontSize: 13, color: colors.onSurface),
|
borderRadius: BorderRadius.circular(8),
|
||||||
decoration: InputDecoration(
|
child: Container(
|
||||||
hintText: '输入字体目录路径',
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||||
hintStyle: TextStyle(
|
decoration: BoxDecoration(
|
||||||
fontSize: 13,
|
color: colors.surfaceContainerHighest,
|
||||||
color: colors.onSurface.withValues(alpha: 0.3),
|
|
||||||
),
|
|
||||||
filled: true,
|
|
||||||
fillColor: colors.surfaceContainerHighest,
|
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 12,
|
|
||||||
vertical: 10,
|
|
||||||
),
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
borderSide: BorderSide.none,
|
|
||||||
),
|
),
|
||||||
suffixIcon: IconButton(
|
child: Row(
|
||||||
icon: Icon(
|
children: [
|
||||||
|
Icon(
|
||||||
Icons.folder_open_outlined,
|
Icons.folder_open_outlined,
|
||||||
size: 18,
|
size: 18,
|
||||||
color: colors.onSurface.withValues(alpha: 0.5),
|
color: colors.onSurface.withValues(alpha: 0.5),
|
||||||
),
|
),
|
||||||
onPressed: _pickDirectory,
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
_currentDirPath ?? '点击选择字体目录',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
color: _currentDirPath != null
|
||||||
|
? colors.onSurface
|
||||||
|
: colors.onSurface.withValues(alpha: 0.3),
|
||||||
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Icon(
|
||||||
|
Icons.chevron_right,
|
||||||
|
size: 18,
|
||||||
|
color: colors.onSurface.withValues(alpha: 0.3),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onSubmitted: (_) => _scanDirectory(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: _scanDirectory,
|
onPressed: _rescanDirectory,
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: colors.primary,
|
backgroundColor: colors.primary,
|
||||||
foregroundColor: colors.onPrimary,
|
foregroundColor: colors.onPrimary,
|
||||||
|
|||||||
@@ -112,6 +112,8 @@ class _WebDAVSyncPageState extends State<WebDAVSyncPage> {
|
|||||||
path: path,
|
path: path,
|
||||||
);
|
);
|
||||||
setState(() => _isConfigured = true);
|
setState(() => _isConfigured = true);
|
||||||
|
// 保存成功后立即加载远程备份信息
|
||||||
|
_loadRemoteInfo();
|
||||||
ToastUtil.show(context, result['message'] ?? '连接成功,配置已保存');
|
ToastUtil.show(context, result['message'] ?? '连接成功,配置已保存');
|
||||||
} else {
|
} else {
|
||||||
ToastUtil.show(context, result['message'] ?? '连接失败,请检查配置');
|
ToastUtil.show(context, result['message'] ?? '连接失败,请检查配置');
|
||||||
|
|||||||
@@ -267,4 +267,10 @@ class UserPrefs {
|
|||||||
/// EPUB 句读列表视图模式: 0=瀑布流, 1=列表
|
/// EPUB 句读列表视图模式: 0=瀑布流, 1=列表
|
||||||
int get highlightsViewMode => prefs.getInt('highlightsViewMode') ?? 0;
|
int get highlightsViewMode => prefs.getInt('highlightsViewMode') ?? 0;
|
||||||
Future<bool> setHighlightsViewMode(int value) => prefs.setInt('highlightsViewMode', value);
|
Future<bool> setHighlightsViewMode(int value) => prefs.setInt('highlightsViewMode', value);
|
||||||
|
|
||||||
|
// ========== 字体选择器 ==========
|
||||||
|
|
||||||
|
/// 字体选择器上次使用的目录路径
|
||||||
|
String? get lastFontDir => prefs.getString('lastFontDir');
|
||||||
|
Future<bool> setLastFontDir(String value) => prefs.setString('lastFontDir', value);
|
||||||
}
|
}
|
||||||
|
|||||||
18
pubspec.lock
18
pubspec.lock
@@ -1,14 +1,6 @@
|
|||||||
# Generated by pub
|
# Generated by pub
|
||||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
packages:
|
packages:
|
||||||
android_intent_plus:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: android_intent_plus
|
|
||||||
sha256: "2329378af63f49b985cb2e110ac784d08374f1e2b1984be77ba9325b1c8cce11"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "5.3.1"
|
|
||||||
archive:
|
archive:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -271,7 +263,7 @@ packages:
|
|||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
flutter_colorpicker:
|
flutter_colorpicker:
|
||||||
dependency: "direct main"
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_colorpicker
|
name: flutter_colorpicker
|
||||||
sha256: "969de5f6f9e2a570ac660fb7b501551451ea2a1ab9e2097e89475f60e07816ea"
|
sha256: "969de5f6f9e2a570ac660fb7b501551451ea2a1ab9e2097e89475f60e07816ea"
|
||||||
@@ -453,14 +445,6 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
gbk_codec:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: gbk_codec
|
|
||||||
sha256: "3af5311fc9393115e3650ae6023862adf998051a804a08fb804f042724999f61"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.4.0"
|
|
||||||
glob:
|
glob:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -30,17 +30,14 @@ dependencies:
|
|||||||
flutter_staggered_grid_view: ^0.7.0
|
flutter_staggered_grid_view: ^0.7.0
|
||||||
http: ^1.2.0
|
http: ^1.2.0
|
||||||
url_launcher: ^6.2.5
|
url_launcher: ^6.2.5
|
||||||
android_intent_plus: ^5.0.0
|
|
||||||
webview_flutter: ^4.8.0
|
webview_flutter: ^4.8.0
|
||||||
flutter_inappwebview: ^6.1.5
|
flutter_inappwebview: ^6.1.5
|
||||||
dynamic_color: ^1.8.1
|
dynamic_color: ^1.8.1
|
||||||
package_info_plus: ^8.0.0
|
package_info_plus: ^8.0.0
|
||||||
extended_text_field: ^16.0.2
|
extended_text_field: ^16.0.2
|
||||||
uuid: ^4.5.0
|
uuid: ^4.5.0
|
||||||
gbk_codec: ^0.4.0
|
|
||||||
expandable: ^5.0.1
|
expandable: ^5.0.1
|
||||||
flutter_quill: ^11.5.0
|
flutter_quill: ^11.5.0
|
||||||
flutter_colorpicker: ^1.1.0
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Reference in New Issue
Block a user