mirror of
https://github.com/ZhuJHua/moodiary.git
synced 2026-04-05 16:31:45 +08:00
@@ -1,63 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>心绪日记</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>心绪日记</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>NSFaceIDUsageDescription</key>
|
||||
<string>Used to apply locks</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Used to apply locks</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Used to apply locks</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Used to apply locks</string>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string>This app needs access to location when open.</string>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<true/>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>心绪日记</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>心绪日记</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Used to apply locks</string>
|
||||
<key>NSFaceIDUsageDescription</key>
|
||||
<string>Used to apply locks</string>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string>This app needs access to location when open.</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Used to apply locks</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string></string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string></string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<true/>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -7,8 +7,6 @@ import 'package:mood_diary/api/api.dart';
|
||||
import 'package:mood_diary/utils/cache_util.dart';
|
||||
|
||||
class WindowButtons extends StatelessWidget {
|
||||
final ColorScheme colorScheme;
|
||||
|
||||
final RxString hitokoto = ''.obs;
|
||||
|
||||
//获取一言
|
||||
@@ -21,10 +19,11 @@ class WindowButtons extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
WindowButtons({super.key, required this.colorScheme});
|
||||
WindowButtons({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
//getHitokoto();
|
||||
final buttonColors = WindowButtonColors(
|
||||
iconNormal: colorScheme.secondary,
|
||||
|
||||
@@ -25,16 +25,18 @@ import 'package:video_player_media_kit/video_player_media_kit.dart';
|
||||
|
||||
import 'components/window_buttons/window_buttons.dart';
|
||||
|
||||
late final AppLocalizations l10n;
|
||||
|
||||
Future<void> initSystem() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await findSystemLocale();
|
||||
await RustLib.init();
|
||||
await PrefUtil.initPref();
|
||||
await IsarUtil.initIsar();
|
||||
await WebDavUtil().initWebDav();
|
||||
VideoPlayerMediaKit.ensureInitialized(android: true, iOS: true, macOS: true, windows: true);
|
||||
await FMTCObjectBoxBackend().initialise();
|
||||
await const FMTCStore('mapStore').manage.create();
|
||||
await RustLib.init();
|
||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
||||
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
|
||||
systemNavigationBarColor: Colors.transparent,
|
||||
@@ -81,13 +83,13 @@ void main() async {
|
||||
onGenerateTitle: (context) => AppLocalizations.of(context)!.appName,
|
||||
backButtonDispatcher: GetRootBackButtonDispatcher(),
|
||||
builder: (context, child) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
l10n = AppLocalizations.of(context)!;
|
||||
final mediaQuery = MediaQuery(
|
||||
data: MediaQuery.of(context).copyWith(textScaler: TextScaler.linear(PrefUtil.getValue<double>('fontScale')!)),
|
||||
child: FToastBuilder()(context, child!),
|
||||
);
|
||||
final windowChild = (Platform.isWindows || Platform.isMacOS || Platform.isLinux)
|
||||
? Column(children: [WindowButtons(colorScheme: colorScheme), Expanded(child: mediaQuery)])
|
||||
? Column(children: [WindowButtons(), Expanded(child: mediaQuery)])
|
||||
: mediaQuery;
|
||||
return windowChild;
|
||||
},
|
||||
|
||||
@@ -5,9 +5,9 @@ import 'package:get/get.dart';
|
||||
import 'package:mood_diary/common/values/border.dart';
|
||||
import 'package:mood_diary/common/values/colors.dart';
|
||||
import 'package:mood_diary/components/diary_card/calendar_diary_card/calendar_diary_card_view.dart';
|
||||
import 'package:mood_diary/components/loading/loading.dart';
|
||||
import 'package:mood_diary/components/time_line/time_line_view.dart';
|
||||
import 'package:mood_diary/utils/array_util.dart';
|
||||
import 'package:rive_animated_icon/rive_animated_icon.dart';
|
||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||
|
||||
import 'calendar_logic.dart';
|
||||
@@ -230,17 +230,7 @@ class CalendarPage extends StatelessWidget {
|
||||
Expanded(child: Obx(() {
|
||||
return AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 400),
|
||||
child: state.isFetching.value
|
||||
? Center(
|
||||
child: RiveAnimatedIcon(
|
||||
riveIcon: RiveIcon.search,
|
||||
height: 80,
|
||||
width: 80,
|
||||
loopAnimation: true,
|
||||
strokeWidth: 4,
|
||||
color: colorScheme.onSurface,
|
||||
))
|
||||
: buildCardList(),
|
||||
child: state.isFetching.value ? const Center(child: Processing()) : buildCardList(),
|
||||
);
|
||||
})),
|
||||
],
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import 'package:flutter/animation.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:mood_diary/common/values/media_type.dart';
|
||||
import 'package:mood_diary/components/audio_player/audio_player_logic.dart';
|
||||
import 'package:mood_diary/router/app_routes.dart';
|
||||
|
||||
import '../../../utils/data/isar.dart';
|
||||
import '../../../utils/file_util.dart';
|
||||
import '../../../utils/notice_util.dart';
|
||||
import 'media_state.dart';
|
||||
@@ -85,50 +83,7 @@ class MediaLogic extends GetxController with GetSingleTickerProviderStateMixin {
|
||||
state.isCleaning = true;
|
||||
update(['modal']);
|
||||
|
||||
// 获取各类型的所有文件路径并转换为Set以提高查找效率
|
||||
final imageFiles = (await FileUtil.getDirFileName(MediaType.image.value)).toSet();
|
||||
final audioFiles = (await FileUtil.getDirFileName(MediaType.audio.value)).toSet();
|
||||
final videoFiles = (await FileUtil.getDirFileName(MediaType.video.value)).toSet();
|
||||
|
||||
// 用于存储日记中引用的文件名的Set
|
||||
final usedImages = <String>{};
|
||||
final usedAudios = <String>{};
|
||||
final usedVideos = <String>{};
|
||||
|
||||
// 获取日记总数
|
||||
final count = IsarUtil.countAllDiary();
|
||||
|
||||
// 分批获取日记并收集引用的文件名
|
||||
const batchSize = 50;
|
||||
for (int i = 0; i < count; i += batchSize) {
|
||||
final diaryList = await IsarUtil.getDiary(i, batchSize);
|
||||
for (var diary in diaryList) {
|
||||
usedImages.addAll(diary.imageName);
|
||||
usedAudios.addAll(diary.audioName);
|
||||
usedVideos.addAll(diary.videoName);
|
||||
for (var name in diary.videoName) {
|
||||
var thumbnailName = 'thumbnail-${name.substring(6, 42)}.jpeg';
|
||||
usedVideos.add(thumbnailName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 计算需要删除的文件
|
||||
final imagesToDelete = imageFiles.difference(usedImages);
|
||||
final audiosToDelete = audioFiles.difference(usedAudios);
|
||||
final videosToDelete = videoFiles.difference(usedVideos);
|
||||
|
||||
// delete controller when need
|
||||
for (var path in audiosToDelete) {
|
||||
Bind.delete<AudioPlayerLogic>(tag: path);
|
||||
}
|
||||
|
||||
// 并行删除文件
|
||||
await Future.wait([
|
||||
FileUtil.deleteMediaFiles(imagesToDelete, MediaType.image.value),
|
||||
FileUtil.deleteMediaFiles(audiosToDelete, MediaType.audio.value),
|
||||
FileUtil.deleteMediaFiles(videosToDelete, MediaType.video.value),
|
||||
]);
|
||||
await FileUtil.cleanFile();
|
||||
await getFilePath(state.mediaType.value);
|
||||
state.isCleaning = false;
|
||||
update(['modal']);
|
||||
|
||||
@@ -2,6 +2,8 @@ import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:mood_diary/common/values/media_type.dart';
|
||||
import 'package:mood_diary/utils/media_util.dart';
|
||||
import 'package:photo_view/photo_view.dart';
|
||||
import 'package:photo_view/photo_view_gallery.dart';
|
||||
|
||||
@@ -26,6 +28,13 @@ class ImagePage extends StatelessWidget {
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
iconTheme: const IconThemeData(color: Colors.white),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
MediaUtil.saveToGallery(path: state.imagePathList[state.imageIndex], type: MediaType.image);
|
||||
},
|
||||
icon: const Icon(Icons.save_alt)),
|
||||
],
|
||||
),
|
||||
body: PhotoViewGallery.builder(
|
||||
scrollPhysics: const PageScrollPhysics(),
|
||||
|
||||
@@ -4,13 +4,16 @@ import 'dart:io';
|
||||
import 'package:fc_native_video_thumbnail/fc_native_video_thumbnail.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_image_compress/flutter_image_compress.dart';
|
||||
import 'package:gal/gal.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:mime/mime.dart';
|
||||
import 'package:mood_diary/src/rust/api/compress.dart';
|
||||
import 'package:mood_diary/utils/log_util.dart';
|
||||
import 'package:mood_diary/utils/notice_util.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
import '../common/values/media_type.dart';
|
||||
import '../src/rust/api/constants.dart' as r_type;
|
||||
import 'data/pref.dart';
|
||||
import 'file_util.dart';
|
||||
@@ -113,9 +116,10 @@ class MediaUtil {
|
||||
// 遍历视频文件
|
||||
final videoFiles = videoDir.listSync().whereType<File>();
|
||||
for (final videoFile in videoFiles) {
|
||||
//如果是缩略图则跳过
|
||||
if (videoFile.path.contains('thumbnail')) continue;
|
||||
final videoName = basename(videoFile.path);
|
||||
final thumbnailPath = getThumbnailPath(videoName);
|
||||
|
||||
// 检查是否存在缩略图
|
||||
if (!File(thumbnailPath).existsSync()) {
|
||||
LogUtil.printInfo("Thumbnail missing for $videoName. Regenerating...");
|
||||
@@ -268,4 +272,20 @@ class MediaUtil {
|
||||
return await _thumbnail.getVideoThumbnail(
|
||||
srcFile: xFile.path, destFile: destPath, width: height, height: height, format: 'jpeg', quality: 90);
|
||||
}
|
||||
|
||||
// 保存视频或者图片到相册
|
||||
static Future<void> saveToGallery({required String path, required MediaType type}) async {
|
||||
final hasAccess = await Gal.hasAccess(toAlbum: true);
|
||||
if (!hasAccess) await Gal.requestAccess(toAlbum: true);
|
||||
try {
|
||||
if (type == MediaType.video) {
|
||||
await Gal.putVideo(path, album: 'Moodiary');
|
||||
} else {
|
||||
await Gal.putImage(path, album: 'Moodiary');
|
||||
}
|
||||
NoticeUtil.showToast('已保存到相册');
|
||||
} catch (e) {
|
||||
NoticeUtil.showToast('保存失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,13 +24,17 @@
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>$(PRODUCT_COPYRIGHT)</string>
|
||||
<key>NSLocationUsageDescription</key>
|
||||
<string></string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Some message to describe why you need this permission</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>This app needs access to location.</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string></string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Some message to describe why you need this permission</string>
|
||||
<key>NSLocationUsageDescription</key>
|
||||
<string>This app needs access to location.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
13
pubspec.lock
13
pubspec.lock
@@ -971,7 +971,7 @@ packages:
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
gal:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: gal
|
||||
sha256: "54c9b72528efce7c66234f3b6dd01cb0304fd8af8196de15571d7bdddb940977"
|
||||
@@ -1965,11 +1965,12 @@ packages:
|
||||
rive_animated_icon:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: rive_animated_icon
|
||||
sha256: f789e9f026c3e5a3bcb369db9baef0637ddd15d9a7c4fe4749505202cdc11942
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
path: "."
|
||||
ref: HEAD
|
||||
resolved-ref: "7ab479ea6747480038fc24914d6b642984fd5bc9"
|
||||
url: "https://github.com/ZhuJHua/rive_animated_icons.git"
|
||||
source: git
|
||||
version: "2.0.3"
|
||||
rive_common:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: mood_diary
|
||||
description: "A new Flutter project."
|
||||
publish_to: 'none'
|
||||
version: 2.6.2+62
|
||||
version: 2.6.3+63
|
||||
|
||||
environment:
|
||||
sdk: '>=3.4.0 <4.0.0'
|
||||
@@ -92,7 +92,10 @@ dependencies:
|
||||
webdav_client: 1.2.2
|
||||
confetti: 0.8.0
|
||||
flutter_native_splash: 2.4.3
|
||||
rive_animated_icon: 2.0.2
|
||||
gal: 2.3.0
|
||||
rive_animated_icon:
|
||||
git:
|
||||
url: https://github.com/ZhuJHua/rive_animated_icons.git
|
||||
network_info_plus: 6.1.2
|
||||
scrollable_positioned_list: 0.3.8
|
||||
flutter_localizations:
|
||||
|
||||
Reference in New Issue
Block a user