generated from dellevin/template
压缩安装包体积
This commit is contained in:
@@ -32,6 +32,13 @@ android {
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
// 启用代码压缩和资源缩减
|
||||
isMinifyEnabled = true
|
||||
isShrinkResources = true
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
// TODO: Add your own signing config for the release build.
|
||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||
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 '../utils/font_download_manager.dart';
|
||||
import '../utils/toast_util.dart';
|
||||
import '../utils/user_prefs.dart';
|
||||
|
||||
/// 字体选择页面
|
||||
///
|
||||
/// 用户输入或选择字体目录,遍历目录下的字体文件,点击即可加载使用。
|
||||
/// 用户选择字体目录,遍历目录下的字体文件,点击即可加载使用。
|
||||
class FontPickerPage extends StatefulWidget {
|
||||
final String? initialFamily;
|
||||
const FontPickerPage({super.key, this.initialFamily});
|
||||
@@ -18,27 +19,24 @@ class FontPickerPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _FontPickerPageState extends State<FontPickerPage> {
|
||||
final TextEditingController _pathController = TextEditingController();
|
||||
final FontDownloadManager _fontManager = FontDownloadManager();
|
||||
|
||||
List<FontFileInfo> _fonts = [];
|
||||
String? _loadingPath;
|
||||
String? _selectedFamily;
|
||||
bool _isScanning = false;
|
||||
String? _currentDirPath;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_selectedFamily = widget.initialFamily;
|
||||
// 默认填入内置字体目录
|
||||
_pathController.text = '/sdcard/Documents/mooknote/fonts';
|
||||
_checkPermissionAndScan();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_pathController.dispose();
|
||||
super.dispose();
|
||||
// 恢复上次选择的目录
|
||||
final savedDir = UserPrefs().lastFontDir;
|
||||
if (savedDir != null && savedDir.isNotEmpty) {
|
||||
_currentDirPath = savedDir;
|
||||
_scanDirectory(savedDir);
|
||||
}
|
||||
}
|
||||
|
||||
/// 请求存储权限(Android 11+ 需要 MANAGE_EXTERNAL_STORAGE)
|
||||
@@ -58,18 +56,6 @@ class _FontPickerPageState extends State<FontPickerPage> {
|
||||
return status.isGranted;
|
||||
}
|
||||
|
||||
/// 检查权限并扫描
|
||||
Future<void> _checkPermissionAndScan() async {
|
||||
final hasPermission = await _requestStoragePermission();
|
||||
if (!hasPermission) {
|
||||
if (mounted) {
|
||||
_showPermissionDialog();
|
||||
}
|
||||
return;
|
||||
}
|
||||
await _scanDirectory();
|
||||
}
|
||||
|
||||
/// 显示权限提示弹窗
|
||||
void _showPermissionDialog() {
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
@@ -103,12 +89,8 @@ class _FontPickerPageState extends State<FontPickerPage> {
|
||||
}
|
||||
|
||||
/// 扫描目录字体
|
||||
Future<void> _scanDirectory() async {
|
||||
final dirPath = _pathController.text.trim();
|
||||
if (dirPath.isEmpty) {
|
||||
if (mounted) ToastUtil.show(context, '请输入目录路径');
|
||||
return;
|
||||
}
|
||||
Future<void> _scanDirectory(String dirPath) async {
|
||||
if (dirPath.isEmpty) return;
|
||||
|
||||
setState(() => _isScanning = true);
|
||||
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();
|
||||
if (!hasPermission) {
|
||||
if (mounted) _showPermissionDialog();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final result = await FilePicker.platform.getDirectoryPath();
|
||||
if (result != null && result.isNotEmpty) {
|
||||
_pathController.text = result;
|
||||
await _scanDirectory();
|
||||
_currentDirPath = result;
|
||||
// 保存到 SharedPreferences
|
||||
await UserPrefs().setLastFontDir(result);
|
||||
await _scanDirectory(result);
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) ToastUtil.show(context, '选择目录失败: $e');
|
||||
@@ -195,7 +189,7 @@ class _FontPickerPageState extends State<FontPickerPage> {
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
// 路径输入区
|
||||
// 选择目录按钮区
|
||||
Container(
|
||||
padding: const EdgeInsets.fromLTRB(20, 16, 20, 12),
|
||||
decoration: BoxDecoration(
|
||||
@@ -219,40 +213,49 @@ class _FontPickerPageState extends State<FontPickerPage> {
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _pathController,
|
||||
style: TextStyle(fontSize: 13, color: colors.onSurface),
|
||||
decoration: InputDecoration(
|
||||
hintText: '输入字体目录路径',
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 13,
|
||||
color: colors.onSurface.withValues(alpha: 0.3),
|
||||
),
|
||||
filled: true,
|
||||
fillColor: colors.surfaceContainerHighest,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 10,
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
child: InkWell(
|
||||
onTap: _onSelectDirectory,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: colors.surfaceContainerHighest,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
Icons.folder_open_outlined,
|
||||
size: 18,
|
||||
color: colors.onSurface.withValues(alpha: 0.5),
|
||||
),
|
||||
onPressed: _pickDirectory,
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.folder_open_outlined,
|
||||
size: 18,
|
||||
color: colors.onSurface.withValues(alpha: 0.5),
|
||||
),
|
||||
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),
|
||||
ElevatedButton(
|
||||
onPressed: _scanDirectory,
|
||||
onPressed: _rescanDirectory,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: colors.primary,
|
||||
foregroundColor: colors.onPrimary,
|
||||
|
||||
@@ -112,6 +112,8 @@ class _WebDAVSyncPageState extends State<WebDAVSyncPage> {
|
||||
path: path,
|
||||
);
|
||||
setState(() => _isConfigured = true);
|
||||
// 保存成功后立即加载远程备份信息
|
||||
_loadRemoteInfo();
|
||||
ToastUtil.show(context, result['message'] ?? '连接成功,配置已保存');
|
||||
} else {
|
||||
ToastUtil.show(context, result['message'] ?? '连接失败,请检查配置');
|
||||
|
||||
@@ -267,4 +267,10 @@ class UserPrefs {
|
||||
/// EPUB 句读列表视图模式: 0=瀑布流, 1=列表
|
||||
int get highlightsViewMode => prefs.getInt('highlightsViewMode') ?? 0;
|
||||
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
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
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:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -271,7 +263,7 @@ packages:
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_colorpicker:
|
||||
dependency: "direct main"
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_colorpicker
|
||||
sha256: "969de5f6f9e2a570ac660fb7b501551451ea2a1ab9e2097e89475f60e07816ea"
|
||||
@@ -453,14 +445,6 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -30,17 +30,14 @@ dependencies:
|
||||
flutter_staggered_grid_view: ^0.7.0
|
||||
http: ^1.2.0
|
||||
url_launcher: ^6.2.5
|
||||
android_intent_plus: ^5.0.0
|
||||
webview_flutter: ^4.8.0
|
||||
flutter_inappwebview: ^6.1.5
|
||||
dynamic_color: ^1.8.1
|
||||
package_info_plus: ^8.0.0
|
||||
extended_text_field: ^16.0.2
|
||||
uuid: ^4.5.0
|
||||
gbk_codec: ^0.4.0
|
||||
expandable: ^5.0.1
|
||||
flutter_quill: ^11.5.0
|
||||
flutter_colorpicker: ^1.1.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Reference in New Issue
Block a user