import 'dart:async'; import 'dart:io'; import 'package:aves/services/common/services.dart'; import 'package:aves/utils/android_file_utils.dart'; import 'package:collection/collection.dart'; final entryDirRepo = EntryDirRepo._private(); class EntryDirRepo { EntryDirRepo._private(); // mapping between the raw entry directory path to a resolvable directory final Map _dirs = {}; final StreamController _ambiguousDirStreamController = StreamController.broadcast(); Stream get ambiguousDirStream => _ambiguousDirStreamController.stream; // get a resolvable directory for a raw entry directory path EntryDir getOrCreate(String? asIs) { var entryDir = _dirs[asIs]; if (entryDir != null) return entryDir; final asIsLower = asIs?.toLowerCase(); entryDir = _dirs.values.firstWhereOrNull((dir) => dir.asIsLower == asIsLower); if (entryDir != null && !entryDir.ambiguous) { entryDir.ambiguous = true; _ambiguousDirStreamController.add(entryDir); } return _dirs.putIfAbsent(asIs, () => entryDir ?? EntryDir(asIs)); } } // Some directories are ambiguous because they use different cases, // but the OS merge and present them as one directory. // This class resolves ambiguous directories to get the directory path // with the right case, as presented by the OS. class EntryDir { final String? asIs, asIsLower; bool ambiguous = false; String? _resolved; EntryDir(this.asIs) : asIsLower = asIs?.toLowerCase(); String? get resolved { if (!ambiguous) return asIs; if (asIs == null) return null; _resolved ??= _resolve(); return _resolved; } String? _resolve() { final vrl = androidFileUtils.relativeDirectoryFromPath(asIs!); if (vrl == null || vrl.relativeDir.isEmpty) return asIs; var resolved = vrl.volumePath; final parts = pContext.split(vrl.relativeDir); for (final part in parts) { FileSystemEntity? found; final dir = Directory(resolved); if (dir.existsSync()) { final partLower = part.toLowerCase(); try { final childrenDirs = dir.listSync().where((v) => v.absolute is Directory).toSet(); found = childrenDirs.firstWhereOrNull((v) => pContext.basename(v.path).toLowerCase() == partLower); } catch (error) { // ignore, could be IO issue when listing directory } } resolved = found?.path ?? '$resolved${pContext.separator}$part'; } return resolved; } }