2020-07-21 22:01:44 +00:00
|
|
|
import 'package:flutter/foundation.dart';
|
2020-07-20 12:29:07 +00:00
|
|
|
import 'package:logging/logging.dart';
|
2021-05-20 23:18:45 +00:00
|
|
|
import 'package:photos/core/constants.dart';
|
2023-02-22 09:59:33 +00:00
|
|
|
import "package:photos/core/event_bus.dart";
|
2020-07-20 12:29:07 +00:00
|
|
|
import 'package:photos/db/files_db.dart';
|
|
|
|
import 'package:photos/db/memories_db.dart';
|
2023-02-22 09:59:33 +00:00
|
|
|
import "package:photos/events/files_updated_event.dart";
|
2023-08-24 15:35:28 +00:00
|
|
|
import "package:photos/events/memories_setting_changed.dart";
|
2020-07-21 08:06:15 +00:00
|
|
|
import 'package:photos/models/filters/important_items_filter.dart';
|
2020-07-20 12:29:07 +00:00
|
|
|
import 'package:photos/models/memory.dart';
|
2023-11-20 08:57:01 +00:00
|
|
|
import "package:photos/models/metadata/common_keys.dart";
|
2022-06-21 12:55:39 +00:00
|
|
|
import 'package:photos/services/collections_service.dart';
|
2023-08-24 15:35:28 +00:00
|
|
|
import "package:shared_preferences/shared_preferences.dart";
|
2020-07-20 12:29:07 +00:00
|
|
|
|
2020-07-21 22:01:44 +00:00
|
|
|
class MemoriesService extends ChangeNotifier {
|
2020-07-20 12:29:07 +00:00
|
|
|
final _logger = Logger("MemoryService");
|
|
|
|
final _memoriesDB = MemoriesDB.instance;
|
|
|
|
final _filesDB = FilesDB.instance;
|
2023-08-24 15:35:28 +00:00
|
|
|
late SharedPreferences _prefs;
|
|
|
|
|
2022-07-04 06:02:17 +00:00
|
|
|
static const daysInAYear = 365;
|
|
|
|
static const yearsBefore = 30;
|
|
|
|
static const daysBefore = 7;
|
|
|
|
static const daysAfter = 1;
|
2023-08-24 15:35:28 +00:00
|
|
|
static const _showMemoryKey = "memories.enabled";
|
2020-07-20 12:29:07 +00:00
|
|
|
|
2022-12-30 08:23:06 +00:00
|
|
|
List<Memory>? _cachedMemories;
|
|
|
|
Future<List<Memory>>? _future;
|
2021-02-14 09:42:02 +00:00
|
|
|
|
2020-07-21 12:19:55 +00:00
|
|
|
MemoriesService._privateConstructor();
|
2020-07-20 12:29:07 +00:00
|
|
|
|
2020-07-21 12:19:55 +00:00
|
|
|
static final MemoriesService instance = MemoriesService._privateConstructor();
|
2020-07-20 12:29:07 +00:00
|
|
|
|
2023-08-24 15:35:28 +00:00
|
|
|
void init(SharedPreferences prefs) {
|
2021-02-14 09:42:02 +00:00
|
|
|
addListener(() {
|
|
|
|
_cachedMemories = null;
|
|
|
|
});
|
2023-08-24 15:35:28 +00:00
|
|
|
_prefs = prefs;
|
2022-06-03 03:10:04 +00:00
|
|
|
// Clear memory after a delay, in async manner.
|
|
|
|
// Intention of delay is to give more CPU cycles to other tasks
|
2022-06-03 03:07:46 +00:00
|
|
|
Future.delayed(const Duration(seconds: 5), () {
|
|
|
|
_memoriesDB.clearMemoriesSeenBeforeTime(
|
2022-09-20 11:53:32 +00:00
|
|
|
DateTime.now().microsecondsSinceEpoch - (7 * microSecondsInDay),
|
2022-06-11 08:23:52 +00:00
|
|
|
);
|
2022-06-03 03:07:46 +00:00
|
|
|
});
|
2023-02-22 09:59:33 +00:00
|
|
|
Bus.instance.on<FilesUpdatedEvent>().where((event) {
|
|
|
|
return event.type == EventType.deletedFromEverywhere;
|
|
|
|
}).listen((event) {
|
|
|
|
final generatedIDs = event.updatedFiles
|
|
|
|
.where((element) => element.generatedID != null)
|
|
|
|
.map((e) => e.generatedID!)
|
|
|
|
.toSet();
|
|
|
|
_cachedMemories?.removeWhere((element) {
|
|
|
|
return generatedIDs.contains(element.file.generatedID);
|
|
|
|
});
|
|
|
|
});
|
2020-07-20 12:29:07 +00:00
|
|
|
}
|
|
|
|
|
2021-03-17 21:11:31 +00:00
|
|
|
void clearCache() {
|
|
|
|
_cachedMemories = null;
|
2021-04-27 16:06:24 +00:00
|
|
|
_future = null;
|
2021-03-17 21:11:31 +00:00
|
|
|
}
|
|
|
|
|
2023-08-24 15:35:28 +00:00
|
|
|
bool get showMemories {
|
|
|
|
return _prefs.getBool(_showMemoryKey) ?? true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> setShowMemories(bool value) async {
|
|
|
|
await _prefs.setBool(_showMemoryKey, value);
|
|
|
|
Bus.instance.fire(MemoriesSettingChanged());
|
|
|
|
}
|
|
|
|
|
2020-07-20 12:29:07 +00:00
|
|
|
Future<List<Memory>> getMemories() async {
|
2023-08-24 15:35:28 +00:00
|
|
|
if (!showMemories) {
|
|
|
|
return [];
|
|
|
|
}
|
2021-02-14 09:42:02 +00:00
|
|
|
if (_cachedMemories != null) {
|
2022-12-30 08:23:06 +00:00
|
|
|
return _cachedMemories!;
|
2021-02-05 17:01:55 +00:00
|
|
|
}
|
2021-04-27 16:06:24 +00:00
|
|
|
if (_future != null) {
|
2022-12-30 08:23:06 +00:00
|
|
|
return _future!;
|
2021-04-27 16:06:24 +00:00
|
|
|
}
|
|
|
|
_future = _fetchMemories();
|
2022-12-30 08:23:06 +00:00
|
|
|
return _future!;
|
2021-04-27 16:06:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<List<Memory>> _fetchMemories() async {
|
2024-01-15 07:10:05 +00:00
|
|
|
final stopWatch = Stopwatch()..start();
|
2021-04-27 16:06:24 +00:00
|
|
|
_logger.info("Fetching memories");
|
2020-07-21 08:01:37 +00:00
|
|
|
final presentTime = DateTime.now();
|
2022-06-11 08:23:52 +00:00
|
|
|
final present = presentTime.subtract(
|
|
|
|
Duration(
|
2020-07-21 08:01:37 +00:00
|
|
|
hours: presentTime.hour,
|
|
|
|
minutes: presentTime.minute,
|
2022-06-11 08:23:52 +00:00
|
|
|
seconds: presentTime.second,
|
|
|
|
),
|
|
|
|
);
|
2021-04-27 16:00:29 +00:00
|
|
|
final List<List<int>> durations = [];
|
2020-07-21 20:30:15 +00:00
|
|
|
for (var yearAgo = 1; yearAgo <= yearsBefore; yearAgo++) {
|
2020-07-21 08:06:15 +00:00
|
|
|
final date = _getDate(present, yearAgo);
|
2022-07-04 06:02:17 +00:00
|
|
|
final startCreationTime = date
|
|
|
|
.subtract(const Duration(days: daysBefore))
|
|
|
|
.microsecondsSinceEpoch;
|
2022-07-03 09:49:33 +00:00
|
|
|
final endCreationTime =
|
2022-07-04 06:02:17 +00:00
|
|
|
date.add(const Duration(days: daysAfter)).microsecondsSinceEpoch;
|
2021-04-27 16:00:29 +00:00
|
|
|
durations.add([startCreationTime, endCreationTime]);
|
2020-07-20 12:29:07 +00:00
|
|
|
}
|
2022-10-14 08:35:20 +00:00
|
|
|
final ignoredCollections =
|
2023-08-14 05:54:28 +00:00
|
|
|
CollectionsService.instance.archivedOrHiddenCollectionIds();
|
2024-04-19 11:26:40 +00:00
|
|
|
final files = await _filesDB.getFilesCreatedWithinDurations(
|
2022-07-03 10:09:01 +00:00
|
|
|
durations,
|
2022-10-14 08:35:20 +00:00
|
|
|
ignoredCollections,
|
2023-11-20 08:57:01 +00:00
|
|
|
visibility: visibleVisibility,
|
2022-07-03 10:09:01 +00:00
|
|
|
);
|
2020-07-29 15:38:13 +00:00
|
|
|
final seenTimes = await _memoriesDB.getSeenTimes();
|
2021-04-27 16:00:29 +00:00
|
|
|
final List<Memory> memories = [];
|
|
|
|
final filter = ImportantItemsFilter();
|
2020-07-20 12:29:07 +00:00
|
|
|
for (final file in files) {
|
2020-07-21 08:06:15 +00:00
|
|
|
if (filter.shouldInclude(file)) {
|
2020-08-09 22:34:59 +00:00
|
|
|
final seenTime = seenTimes[file.generatedID] ?? -1;
|
2020-07-29 15:38:13 +00:00
|
|
|
memories.add(Memory(file, seenTime));
|
2020-07-21 08:06:15 +00:00
|
|
|
}
|
2020-07-20 12:29:07 +00:00
|
|
|
}
|
2021-02-14 09:42:02 +00:00
|
|
|
_cachedMemories = memories;
|
2024-01-15 07:10:05 +00:00
|
|
|
stopWatch.stop();
|
|
|
|
_logger.info("Fetched memories, duration: ${stopWatch.elapsed}");
|
2022-12-30 08:23:06 +00:00
|
|
|
return _cachedMemories!;
|
2020-07-20 12:29:07 +00:00
|
|
|
}
|
|
|
|
|
2020-07-21 08:06:15 +00:00
|
|
|
DateTime _getDate(DateTime present, int yearAgo) {
|
|
|
|
final year = (present.year - yearAgo).toString();
|
2022-07-03 09:49:33 +00:00
|
|
|
final month = present.month > 9
|
|
|
|
? present.month.toString()
|
|
|
|
: "0" + present.month.toString();
|
|
|
|
final day =
|
|
|
|
present.day > 9 ? present.day.toString() : "0" + present.day.toString();
|
2020-07-21 08:06:15 +00:00
|
|
|
final date = DateTime.parse(year + "-" + month + "-" + day);
|
|
|
|
return date;
|
|
|
|
}
|
|
|
|
|
2020-07-20 12:29:07 +00:00
|
|
|
Future markMemoryAsSeen(Memory memory) async {
|
2021-04-27 16:23:01 +00:00
|
|
|
memory.markSeen();
|
2020-07-21 22:01:44 +00:00
|
|
|
await _memoriesDB.markMemoryAsSeen(
|
2022-06-11 08:23:52 +00:00
|
|
|
memory,
|
|
|
|
DateTime.now().microsecondsSinceEpoch,
|
|
|
|
);
|
2020-07-21 22:01:44 +00:00
|
|
|
notifyListeners();
|
2020-07-20 12:29:07 +00:00
|
|
|
}
|
|
|
|
}
|