source: disable analysis for widget, screen saver; disabling analysis also disables entry discovery

This commit is contained in:
Thibault Deckers 2024-10-07 22:24:58 +02:00
parent 618b63bfc0
commit 211f803afe
17 changed files with 64 additions and 86 deletions

View file

@ -296,14 +296,11 @@ open class MainActivity : FlutterFragmentActivity() {
open fun extractIntentData(intent: Intent?): FieldMap { open fun extractIntentData(intent: Intent?): FieldMap {
when (val action = intent?.action) { when (val action = intent?.action) {
Intent.ACTION_MAIN -> { Intent.ACTION_MAIN -> {
val fields = HashMap<String, Any?>() return hashMapOf(
if (intent.getBooleanExtra(EXTRA_KEY_SAFE_MODE, false)) { INTENT_DATA_KEY_PAGE to intent.getStringExtra(EXTRA_KEY_PAGE),
fields[INTENT_DATA_KEY_SAFE_MODE] = true INTENT_DATA_KEY_FILTERS to extractFiltersFromIntent(intent),
} INTENT_DATA_KEY_EXPLORER_PATH to intent.getStringExtra(EXTRA_KEY_EXPLORER_PATH),
fields[INTENT_DATA_KEY_PAGE] = intent.getStringExtra(EXTRA_KEY_PAGE) )
fields[INTENT_DATA_KEY_FILTERS] = extractFiltersFromIntent(intent)
fields[INTENT_DATA_KEY_EXPLORER_PATH] = intent.getStringExtra(EXTRA_KEY_EXPLORER_PATH)
return fields
} }
Intent.ACTION_VIEW, Intent.ACTION_VIEW,
@ -557,7 +554,6 @@ open class MainActivity : FlutterFragmentActivity() {
const val INTENT_DATA_KEY_MIME_TYPE = "mimeType" const val INTENT_DATA_KEY_MIME_TYPE = "mimeType"
const val INTENT_DATA_KEY_PAGE = "page" const val INTENT_DATA_KEY_PAGE = "page"
const val INTENT_DATA_KEY_QUERY = "query" const val INTENT_DATA_KEY_QUERY = "query"
const val INTENT_DATA_KEY_SAFE_MODE = "safeMode"
const val INTENT_DATA_KEY_SECURE_URIS = "secureUris" const val INTENT_DATA_KEY_SECURE_URIS = "secureUris"
const val INTENT_DATA_KEY_URI = "uri" const val INTENT_DATA_KEY_URI = "uri"
const val INTENT_DATA_KEY_WIDGET_ID = "widgetId" const val INTENT_DATA_KEY_WIDGET_ID = "widgetId"
@ -566,7 +562,6 @@ open class MainActivity : FlutterFragmentActivity() {
const val EXTRA_KEY_EXPLORER_PATH = "explorerPath" const val EXTRA_KEY_EXPLORER_PATH = "explorerPath"
const val EXTRA_KEY_FILTERS_ARRAY = "filters" const val EXTRA_KEY_FILTERS_ARRAY = "filters"
const val EXTRA_KEY_FILTERS_STRING = "filtersString" const val EXTRA_KEY_FILTERS_STRING = "filtersString"
const val EXTRA_KEY_SAFE_MODE = "safeMode"
const val EXTRA_KEY_WIDGET_ID = "widgetId" const val EXTRA_KEY_WIDGET_ID = "widgetId"
// dart page routes // dart page routes

View file

@ -21,15 +21,11 @@ class MediaStoreStreamHandler(private val context: Context, arguments: Any?) : E
private var knownEntries: Map<Long?, Int?>? = null private var knownEntries: Map<Long?, Int?>? = null
private var directory: String? = null private var directory: String? = null
private var safe: Boolean = false
init { init {
if (arguments is Map<*, *>) { if (arguments is Map<*, *>) {
knownEntries = (arguments["knownEntries"] as? Map<*, *>?)?.map { (it.key as Number?)?.toLong() to it.value as Int? }?.toMap() knownEntries = (arguments["knownEntries"] as? Map<*, *>?)?.map { (it.key as Number?)?.toLong() to it.value as Int? }?.toMap()
directory = arguments["directory"] as String? directory = arguments["directory"] as String?
// do not use kotlin.collections `getOrDefault` as it crashes on API <24
// and there is no warning from Android Studio
safe = arguments["safe"] as Boolean? ?: false
} }
} }
@ -63,7 +59,7 @@ class MediaStoreStreamHandler(private val context: Context, arguments: Any?) : E
} }
private fun fetchAll() { private fun fetchAll() {
MediaStoreImageProvider().fetchAll(context, knownEntries ?: emptyMap(), directory, safe) { success(it) } MediaStoreImageProvider().fetchAll(context, knownEntries ?: emptyMap(), directory) { success(it) }
endOfStream() endOfStream()
} }

View file

@ -5,7 +5,6 @@ import android.content.Context
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.media.MediaMetadataRetriever import android.media.MediaMetadataRetriever
import android.net.Uri import android.net.Uri
import androidx.exifinterface.media.ExifInterfaceFork as ExifInterface
import com.drew.metadata.avi.AviDirectory import com.drew.metadata.avi.AviDirectory
import com.drew.metadata.exif.ExifIFD0Directory import com.drew.metadata.exif.ExifIFD0Directory
import com.drew.metadata.jpeg.JpegDirectory import com.drew.metadata.jpeg.JpegDirectory
@ -29,6 +28,7 @@ import deckers.thibault.aves.utils.StorageUtils
import deckers.thibault.aves.utils.UriUtils.tryParseId import deckers.thibault.aves.utils.UriUtils.tryParseId
import org.beyka.tiffbitmapfactory.TiffBitmapFactory import org.beyka.tiffbitmapfactory.TiffBitmapFactory
import java.io.IOException import java.io.IOException
import androidx.exifinterface.media.ExifInterfaceFork as ExifInterface
class SourceEntry { class SourceEntry {
private val origin: Int private val origin: Int
@ -116,8 +116,8 @@ class SourceEntry {
// metadata retrieval // metadata retrieval
// expects entry with: uri, mimeType // expects entry with: uri, mimeType
// finds: width, height, orientation/rotation, date, title, duration // finds: width, height, orientation/rotation, date, title, duration
fun fillPreCatalogMetadata(context: Context, safe: Boolean): SourceEntry { fun fillPreCatalogMetadata(context: Context): SourceEntry {
if (isSvg || safe) return this if (isSvg) return this
if (isVideo) { if (isVideo) {
fillVideoByMediaMetadataRetriever(context) fillVideoByMediaMetadataRetriever(context)
if (isSized && hasDuration) return this if (isSized && hasDuration) return this

View file

@ -53,7 +53,7 @@ internal class FileImageProvider : ImageProvider() {
return return
} }
} }
entry.fillPreCatalogMetadata(context, safe = false) entry.fillPreCatalogMetadata(context)
if (allowUnsized || entry.isSized || entry.isSvg || entry.isVideo) { if (allowUnsized || entry.isSized || entry.isSvg || entry.isVideo) {
callback.onSuccess(entry.toMap()) callback.onSuccess(entry.toMap())

View file

@ -51,10 +51,9 @@ class MediaStoreImageProvider : ImageProvider() {
context: Context, context: Context,
knownEntries: Map<Long?, Int?>, knownEntries: Map<Long?, Int?>,
directory: String?, directory: String?,
safe: Boolean,
handleNewEntry: NewEntryHandler, handleNewEntry: NewEntryHandler,
) { ) {
Log.d(LOG_TAG, "fetching all media store items for ${knownEntries.size} known entries, directory=$directory safe=$safe") Log.d(LOG_TAG, "fetching all media store items for ${knownEntries.size} known entries, directory=$directory")
val isModified = fun(contentId: Long, dateModifiedSecs: Int): Boolean { val isModified = fun(contentId: Long, dateModifiedSecs: Int): Boolean {
val knownDate = knownEntries[contentId] val knownDate = knownEntries[contentId]
return knownDate == null || knownDate < dateModifiedSecs return knownDate == null || knownDate < dateModifiedSecs
@ -84,8 +83,8 @@ class MediaStoreImageProvider : ImageProvider() {
} else { } else {
handleNew = handleNewEntry handleNew = handleNewEntry
} }
fetchFrom(context, isModified, handleNew, IMAGE_CONTENT_URI, IMAGE_PROJECTION, selection, selectionArgs, safe = safe) fetchFrom(context, isModified, handleNew, IMAGE_CONTENT_URI, IMAGE_PROJECTION, selection, selectionArgs)
fetchFrom(context, isModified, handleNew, VIDEO_CONTENT_URI, VIDEO_PROJECTION, selection, selectionArgs, safe = safe) fetchFrom(context, isModified, handleNew, VIDEO_CONTENT_URI, VIDEO_PROJECTION, selection, selectionArgs)
} }
// the provided URI can point to the wrong media collection, // the provided URI can point to the wrong media collection,
@ -208,7 +207,6 @@ class MediaStoreImageProvider : ImageProvider() {
selection: String? = null, selection: String? = null,
selectionArgs: Array<String>? = null, selectionArgs: Array<String>? = null,
fileMimeType: String? = null, fileMimeType: String? = null,
safe: Boolean = false,
): Boolean { ): Boolean {
var found = false var found = false
val orderBy = "${MediaStore.MediaColumns.DATE_MODIFIED} DESC" val orderBy = "${MediaStore.MediaColumns.DATE_MODIFIED} DESC"
@ -302,7 +300,7 @@ class MediaStoreImageProvider : ImageProvider() {
// missing some attributes such as width, height, orientation. // missing some attributes such as width, height, orientation.
// Also, the reported size of raw images is inconsistent across devices // Also, the reported size of raw images is inconsistent across devices
// and Android versions (sometimes the raw size, sometimes the decoded size). // and Android versions (sometimes the raw size, sometimes the decoded size).
val entry = SourceEntry(entryMap).fillPreCatalogMetadata(context, safe) val entry = SourceEntry(entryMap).fillPreCatalogMetadata(context)
entryMap = entry.toMap() entryMap = entry.toMap()
} }

View file

@ -70,7 +70,7 @@ open class UnknownContentProvider : ImageProvider() {
return return
} }
val entry = SourceEntry(fields).fillPreCatalogMetadata(context, safe = false) val entry = SourceEntry(fields).fillPreCatalogMetadata(context)
if (allowUnsized || entry.isSized || entry.isSvg || entry.isVideo) { if (allowUnsized || entry.isSized || entry.isSvg || entry.isVideo) {
callback.onSuccess(entry.toMap()) callback.onSuccess(entry.toMap())
} else { } else {

View file

@ -21,7 +21,6 @@ class IntentDataKeys {
static const mimeType = 'mimeType'; static const mimeType = 'mimeType';
static const page = 'page'; static const page = 'page';
static const query = 'query'; static const query = 'query';
static const safeMode = 'safeMode';
static const secureUris = 'secureUris'; static const secureUris = 'secureUris';
static const uri = 'uri'; static const uri = 'uri';
static const widgetId = 'widgetId'; static const widgetId = 'widgetId';

View file

@ -31,7 +31,7 @@ import 'package:collection/collection.dart';
import 'package:event_bus/event_bus.dart'; import 'package:event_bus/event_bus.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
enum SourceInitializationState { none, directory, full } enum SourceScope { none, album, full }
mixin SourceBase { mixin SourceBase {
EventBus get eventBus; EventBus get eventBus;
@ -93,7 +93,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place
_rawEntries.forEach((v) => v.dispose()); _rawEntries.forEach((v) => v.dispose());
} }
set safeMode(bool enabled); set canAnalyze(bool enabled);
final EventBus _eventBus = EventBus(); final EventBus _eventBus = EventBus();
@ -427,13 +427,12 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place
eventBus.fire(EntryMovedEvent(MoveType.move, movedEntries)); eventBus.fire(EntryMovedEvent(MoveType.move, movedEntries));
} }
SourceInitializationState get initState => SourceInitializationState.none; SourceScope get scope => SourceScope.none;
Future<void> init({ Future<void> init({
AnalysisController? analysisController, AnalysisController? analysisController,
String? directory, AlbumFilter? albumFilter,
bool loadTopEntriesFirst = false, bool loadTopEntriesFirst = false,
bool canAnalyze = true,
}); });
Future<Set<String>> refreshUris(Set<String> changedUris, {AnalysisController? analysisController}); Future<Set<String>> refreshUris(Set<String> changedUris, {AnalysisController? analysisController});
@ -518,13 +517,13 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place
// monitoring // monitoring
bool _monitoring = true; bool _canRefresh = true;
void pauseMonitoring() => _monitoring = false; void pauseMonitoring() => _canRefresh = false;
void resumeMonitoring() => _monitoring = true; void resumeMonitoring() => _canRefresh = true;
bool get isMonitoring => _monitoring; bool get canRefresh => _canRefresh;
// filter summary // filter summary

View file

@ -21,36 +21,34 @@ class MediaStoreSource extends CollectionSource {
final Debouncer _changeDebouncer = Debouncer(delay: ADurations.mediaContentChangeDebounceDelay); final Debouncer _changeDebouncer = Debouncer(delay: ADurations.mediaContentChangeDebounceDelay);
final Set<String> _changedUris = {}; final Set<String> _changedUris = {};
int? _lastGeneration; int? _lastGeneration;
SourceInitializationState _initState = SourceInitializationState.none; SourceScope _scope = SourceScope.none;
bool _safeMode = false; bool _canAnalyze = true;
@override @override
set safeMode(bool enabled) => _safeMode = enabled; set canAnalyze(bool enabled) => _canAnalyze = enabled;
@override @override
SourceInitializationState get initState => _initState; SourceScope get scope => _scope;
@override @override
Future<void> init({ Future<void> init({
AnalysisController? analysisController, AnalysisController? analysisController,
String? directory, AlbumFilter? albumFilter,
bool loadTopEntriesFirst = false, bool loadTopEntriesFirst = false,
bool canAnalyze = true,
}) async { }) async {
await reportService.log('$runtimeType init directory=$directory'); await reportService.log('$runtimeType init album=${albumFilter?.album}');
if (_initState == SourceInitializationState.none) { if (_scope == SourceScope.none) {
await _loadEssentials(); await _loadEssentials();
} }
if (_initState != SourceInitializationState.full) { if (_scope != SourceScope.full) {
_initState = directory != null ? SourceInitializationState.directory : SourceInitializationState.full; _scope = albumFilter != null ? SourceScope.album : SourceScope.full;
} }
addDirectories(albums: settings.pinnedFilters.whereType<AlbumFilter>().map((v) => v.album).toSet()); addDirectories(albums: settings.pinnedFilters.whereType<AlbumFilter>().map((v) => v.album).toSet());
await updateGeneration(); await updateGeneration();
unawaited(_loadEntries( unawaited(_loadEntries(
analysisController: analysisController, analysisController: analysisController,
directory: directory, directory: albumFilter?.album,
loadTopEntriesFirst: loadTopEntriesFirst, loadTopEntriesFirst: loadTopEntriesFirst,
canAnalyze: canAnalyze && !_safeMode,
)); ));
} }
@ -80,7 +78,6 @@ class MediaStoreSource extends CollectionSource {
AnalysisController? analysisController, AnalysisController? analysisController,
String? directory, String? directory,
required bool loadTopEntriesFirst, required bool loadTopEntriesFirst,
required bool canAnalyze,
}) async { }) async {
unawaited(reportService.log('$runtimeType load start')); unawaited(reportService.log('$runtimeType load start'));
final stopwatch = Stopwatch()..start(); final stopwatch = Stopwatch()..start();
@ -158,6 +155,12 @@ class MediaStoreSource extends CollectionSource {
knownDateByContentId[contentId] = 0; knownDateByContentId[contentId] = 0;
}); });
if (!_canAnalyze) {
// it can discover new entries only if it can analyze them
state = SourceState.ready;
return;
}
// items to add to the collection // items to add to the collection
final newEntries = <AvesEntry>{}; final newEntries = <AvesEntry>{};
@ -169,7 +172,7 @@ class MediaStoreSource extends CollectionSource {
// fetch new & modified entries // fetch new & modified entries
debugPrint('$runtimeType load ${stopwatch.elapsed} fetch new entries'); debugPrint('$runtimeType load ${stopwatch.elapsed} fetch new entries');
mediaStoreService.getEntries(_safeMode, knownDateByContentId, directory: directory).listen( mediaStoreService.getEntries(knownDateByContentId, directory: directory).listen(
(entry) { (entry) {
// when discovering modified entry with known content ID, // when discovering modified entry with known content ID,
// reuse known entry ID to overwrite it while preserving favourites, etc. // reuse known entry ID to overwrite it while preserving favourites, etc.
@ -210,11 +213,7 @@ class MediaStoreSource extends CollectionSource {
if (analysisIds != null) { if (analysisIds != null) {
analysisEntries = visibleEntries.where((entry) => analysisIds.contains(entry.id)).toSet(); analysisEntries = visibleEntries.where((entry) => analysisIds.contains(entry.id)).toSet();
} }
if (canAnalyze) { await analyze(analysisController, entries: analysisEntries);
await analyze(analysisController, entries: analysisEntries);
} else {
state = SourceState.ready;
}
// the home page may not reflect the current derived filters // the home page may not reflect the current derived filters
// as the initial addition of entries is silent, // as the initial addition of entries is silent,
@ -234,7 +233,7 @@ class MediaStoreSource extends CollectionSource {
// sometimes yields an entry with its temporary path: `/data/sec/camera/!@#$%^..._temp.jpg` // sometimes yields an entry with its temporary path: `/data/sec/camera/!@#$%^..._temp.jpg`
@override @override
Future<Set<String>> refreshUris(Set<String> changedUris, {AnalysisController? analysisController}) async { Future<Set<String>> refreshUris(Set<String> changedUris, {AnalysisController? analysisController}) async {
if (_initState == SourceInitializationState.none || !isMonitoring || !isReady) return changedUris; if (_scope == SourceScope.none || !canRefresh || !isReady) return changedUris;
state = SourceState.loading; state = SourceState.loading;
@ -272,7 +271,8 @@ class MediaStoreSource extends CollectionSource {
if (volume != null) { if (volume != null) {
if (existingEntry != null) { if (existingEntry != null) {
entriesToRefresh.add(existingEntry); entriesToRefresh.add(existingEntry);
} else { } else if (_canAnalyze) {
// it can discover new entries only if it can analyze them
sourceEntry.id = localMediaDb.nextId; sourceEntry.id = localMediaDb.nextId;
newEntries.add(sourceEntry); newEntries.add(sourceEntry);
} }
@ -329,10 +329,6 @@ class MediaStoreSource extends CollectionSource {
} }
void onStoreChanged(String? uri) { void onStoreChanged(String? uri) {
// dismiss changes if the source is only loaded to view a specific directory
// to let the main instance handle the change in the database
if (_initState == SourceInitializationState.directory) return;
if (uri != null) _changedUris.add(uri); if (uri != null) _changedUris.add(uri);
if (_changedUris.isNotEmpty) { if (_changedUris.isNotEmpty) {
_changeDebouncer(() async { _changeDebouncer(() async {

View file

@ -15,7 +15,7 @@ abstract class MediaStoreService {
Future<int?> getGeneration(); Future<int?> getGeneration();
// knownEntries: map of contentId -> dateModifiedSecs // knownEntries: map of contentId -> dateModifiedSecs
Stream<AvesEntry> getEntries(bool safe, Map<int?, int?> knownEntries, {String? directory}); Stream<AvesEntry> getEntries(Map<int?, int?> knownEntries, {String? directory});
// returns media URI // returns media URI
Future<Uri?> scanFile(String path, String mimeType); Future<Uri?> scanFile(String path, String mimeType);
@ -77,13 +77,12 @@ class PlatformMediaStoreService implements MediaStoreService {
} }
@override @override
Stream<AvesEntry> getEntries(bool safe, Map<int?, int?> knownEntries, {String? directory}) { Stream<AvesEntry> getEntries(Map<int?, int?> knownEntries, {String? directory}) {
try { try {
return _stream return _stream
.receiveBroadcastStream(<String, dynamic>{ .receiveBroadcastStream(<String, dynamic>{
'knownEntries': knownEntries, 'knownEntries': knownEntries,
'directory': directory, 'directory': directory,
'safe': safe,
}) })
.where((event) => event is Map) .where((event) => event is Map)
.map((event) => AvesEntry.fromMap(event as Map)); .map((event) => AvesEntry.fromMap(event as Map));

View file

@ -96,7 +96,8 @@ Future<AvesEntry?> _getWidgetEntry(int widgetId, bool reuseEntry) async {
readyCompleter.complete(); readyCompleter.complete();
} }
}); });
await source.init(canAnalyze: false); source.canAnalyze = false;
await source.init();
await readyCompleter.future; await readyCompleter.future;
final entries = CollectionLens(source: source, filters: filters).sortedEntries; final entries = CollectionLens(source: source, filters: filters).sortedEntries;

View file

@ -683,7 +683,7 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
Future<void> _onAnalysisCompletion() async { Future<void> _onAnalysisCompletion() async {
debugPrint('Analysis completed'); debugPrint('Analysis completed');
if (_mediaStoreSource.initState != SourceInitializationState.none) { if (_mediaStoreSource.scope != SourceScope.none) {
await _mediaStoreSource.loadCatalogMetadata(); await _mediaStoreSource.loadCatalogMetadata();
await _mediaStoreSource.loadAddresses(); await _mediaStoreSource.loadAddresses();
_mediaStoreSource.updateDerivedFilters(); _mediaStoreSource.updateDerivedFilters();

View file

@ -35,9 +35,10 @@ Future<String?> pickAlbum({
required MoveType? moveType, required MoveType? moveType,
}) async { }) async {
final source = context.read<CollectionSource>(); final source = context.read<CollectionSource>();
if (source.initState != SourceInitializationState.full) { if (source.scope != SourceScope.full) {
await reportService.log('Complete source initialization to pick album'); await reportService.log('Complete source initialization to pick album');
// source may not be fully initialized in view mode // source may not be fully initialized in view mode
source.canAnalyze = true;
await source.init(); await source.init();
} }
final filter = await Navigator.maybeOf(context)?.push( final filter = await Navigator.maybeOf(context)?.push(

View file

@ -98,7 +98,6 @@ class _HomePageState extends State<HomePage> {
var appMode = AppMode.main; var appMode = AppMode.main;
var error = false; var error = false;
final intentData = widget.intentData ?? await IntentService.getIntentData(); final intentData = widget.intentData ?? await IntentService.getIntentData();
final safeMode = (intentData[IntentDataKeys.safeMode] as bool?) ?? false;
final intentAction = intentData[IntentDataKeys.action] as String?; final intentAction = intentData[IntentDataKeys.action] as String?;
_initialFilters = null; _initialFilters = null;
_initialExplorerPath = null; _initialExplorerPath = null;
@ -223,19 +222,16 @@ class _HomePageState extends State<HomePage> {
unawaited(GlobalSearch.registerCallback()); unawaited(GlobalSearch.registerCallback());
unawaited(AnalysisService.registerCallback()); unawaited(AnalysisService.registerCallback());
final source = context.read<CollectionSource>(); final source = context.read<CollectionSource>();
source.safeMode = safeMode; if (source.scope != SourceScope.full) {
if (source.initState != SourceInitializationState.full) { await reportService.log('Initialize source (init state=${source.scope.name}) to start app with mode=$appMode');
await reportService.log('Initialize source (init state=${source.initState.name}) to start app with mode=$appMode'); final loadTopEntriesFirst = settings.homePage == HomePageSetting.collection && settings.homeCustomCollection.isEmpty;
await source.init( await source.init(loadTopEntriesFirst: loadTopEntriesFirst);
loadTopEntriesFirst: settings.homePage == HomePageSetting.collection && settings.homeCustomCollection.isEmpty,
);
} }
case AppMode.screenSaver: case AppMode.screenSaver:
final source = context.read<CollectionSource>();
await reportService.log('Initialize source to start screen saver'); await reportService.log('Initialize source to start screen saver');
await source.init( final source = context.read<CollectionSource>();
canAnalyze: false, source.canAnalyze = false;
); await source.init();
case AppMode.view: case AppMode.view:
if (_isViewerSourceable(_viewerEntry) && _secureUris == null) { if (_isViewerSourceable(_viewerEntry) && _secureUris == null) {
final directory = _viewerEntry?.directory; final directory = _viewerEntry?.directory;
@ -243,10 +239,8 @@ class _HomePageState extends State<HomePage> {
unawaited(AnalysisService.registerCallback()); unawaited(AnalysisService.registerCallback());
await reportService.log('Initialize source to view item in directory $directory'); await reportService.log('Initialize source to view item in directory $directory');
final source = context.read<CollectionSource>(); final source = context.read<CollectionSource>();
await source.init( source.canAnalyze = false;
directory: directory, await source.init(albumFilter: AlbumFilter(directory, null));
canAnalyze: false,
);
} }
} else { } else {
await _initViewerEssentials(); await _initViewerEssentials();
@ -311,7 +305,7 @@ class _HomePageState extends State<HomePage> {
CollectionLens? collection; CollectionLens? collection;
final source = context.read<CollectionSource>(); final source = context.read<CollectionSource>();
if (source.initState != SourceInitializationState.none) { if (source.scope != SourceScope.none) {
final album = viewerEntry.directory; final album = viewerEntry.directory;
if (album != null) { if (album != null) {
// wait for collection to pass the `loading` state // wait for collection to pass the `loading` state

View file

@ -437,7 +437,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
showFeedback(context, FeedbackType.warn, l10n.genericFailureFeedback); showFeedback(context, FeedbackType.warn, l10n.genericFailureFeedback);
} else { } else {
final source = context.read<CollectionSource>(); final source = context.read<CollectionSource>();
if (source.initState != SourceInitializationState.none) { if (source.scope != SourceScope.none) {
await source.removeEntries({targetEntry.uri}, includeTrash: true); await source.removeEntries({targetEntry.uri}, includeTrash: true);
} }
EntryDeletedNotification({targetEntry}).dispatch(context); EntryDeletedNotification({targetEntry}).dispatch(context);

View file

@ -33,7 +33,7 @@ class FakeMediaStoreService extends Fake implements MediaStoreService {
} }
@override @override
Stream<AvesEntry> getEntries(bool safe, Map<int?, int?> knownEntries, {String? directory}) => Stream.fromIterable(entries); Stream<AvesEntry> getEntries(Map<int?, int?> knownEntries, {String? directory}) => Stream.fromIterable(entries);
static var _lastId = 1; static var _lastId = 1;

View file

@ -109,7 +109,7 @@ void main() {
final source = MediaStoreSource(); final source = MediaStoreSource();
unawaited(source.init()); unawaited(source.init());
await Future.delayed(const Duration(milliseconds: 10)); await Future.delayed(const Duration(milliseconds: 10));
expect(source.initState, SourceInitializationState.full); expect(source.scope, SourceScope.full);
await source.refreshUris({refreshEntry.uri}); await source.refreshUris({refreshEntry.uri});
await Future.delayed(const Duration(seconds: 1)); await Future.delayed(const Duration(seconds: 1));