diff --git a/CHANGELOG.md b/CHANGELOG.md index 706a7352f..66a4b977c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## [v1.8.4] - 2023-03-17 + +### Added + +- TV: improved support for Licenses + +### Fixed + +- Viewer: playing video from app content provider +- Search: using the query bar yields a black screen + ## [v1.8.3] - 2023-03-13 ### Added diff --git a/fastlane/metadata/android/en-US/changelogs/95.txt b/fastlane/metadata/android/en-US/changelogs/95.txt new file mode 100644 index 000000000..3ee89824b --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/95.txt @@ -0,0 +1,5 @@ +In v1.8.4: +- view items in full-screen when selecting them +- watch videos using picture-in-picture +- navigate with TalkBack +Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/9501.txt b/fastlane/metadata/android/en-US/changelogs/9501.txt new file mode 100644 index 000000000..3ee89824b --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/9501.txt @@ -0,0 +1,5 @@ +In v1.8.4: +- view items in full-screen when selecting them +- watch videos using picture-in-picture +- navigate with TalkBack +Full changelog available on GitHub \ No newline at end of file diff --git a/lib/model/covers.dart b/lib/model/covers.dart index 601a07bd5..0eb13eb55 100644 --- a/lib/model/covers.dart +++ b/lib/model/covers.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/vaults/vaults.dart'; diff --git a/lib/model/db/db_metadata.dart b/lib/model/db/db_metadata.dart index bf4f677ae..524efd299 100644 --- a/lib/model/db/db_metadata.dart +++ b/lib/model/db/db_metadata.dart @@ -1,5 +1,5 @@ import 'package:aves/model/covers.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/favourites.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/metadata/address.dart'; diff --git a/lib/model/db/db_metadata_sqflite.dart b/lib/model/db/db_metadata_sqflite.dart index 9bc6a8885..e8903a076 100644 --- a/lib/model/db/db_metadata_sqflite.dart +++ b/lib/model/db/db_metadata_sqflite.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:aves/model/covers.dart'; import 'package:aves/model/db/db_metadata.dart'; import 'package:aves/model/db/db_metadata_sqflite_upgrade.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/favourites.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/metadata/address.dart'; diff --git a/lib/model/entry_cache.dart b/lib/model/entry/cache.dart similarity index 100% rename from lib/model/entry_cache.dart rename to lib/model/entry/cache.dart diff --git a/lib/model/entry_dirs.dart b/lib/model/entry/dirs.dart similarity index 100% rename from lib/model/entry_dirs.dart rename to lib/model/entry/dirs.dart diff --git a/lib/model/entry.dart b/lib/model/entry/entry.dart similarity index 55% rename from lib/model/entry.dart rename to lib/model/entry/entry.dart index 794ed6f6e..268357e2a 100644 --- a/lib/model/entry.dart +++ b/lib/model/entry/entry.dart @@ -2,51 +2,42 @@ import 'dart:async'; import 'dart:io'; import 'dart:ui'; -import 'package:aves/geo/countries.dart'; -import 'package:aves/model/entry_cache.dart'; -import 'package:aves/model/entry_dirs.dart'; -import 'package:aves/model/favourites.dart'; -import 'package:aves/model/geotiff.dart'; +import 'package:aves/model/entry/cache.dart'; +import 'package:aves/model/entry/dirs.dart'; import 'package:aves/model/metadata/address.dart'; import 'package:aves/model/metadata/catalog.dart'; import 'package:aves/model/metadata/trash.dart'; -import 'package:aves/model/multipage.dart'; -import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/trash.dart'; -import 'package:aves/model/video/metadata.dart'; import 'package:aves/ref/mime_types.dart'; -import 'package:aves/services/common/service_policy.dart'; import 'package:aves/services/common/services.dart'; -import 'package:aves/services/geocoding_service.dart'; -import 'package:aves/services/metadata/svg_metadata_service.dart'; import 'package:aves/theme/format.dart'; -import 'package:aves/utils/android_file_utils.dart'; -import 'package:aves/utils/change_notifier.dart'; +import 'package:aves_utils/aves_utils.dart'; import 'package:aves/utils/time_utils.dart'; +import 'package:aves_model/aves_model.dart'; import 'package:collection/collection.dart'; -import 'package:country_code/country_code.dart'; import 'package:flutter/foundation.dart'; -import 'package:latlong2/latlong.dart'; enum EntryDataType { basic, aspectRatio, catalog, address, references } -class EntryOrigins { - static const int mediaStoreContent = 0; - static const int unknownContent = 1; - static const int file = 2; - static const int vault = 3; -} - -class AvesEntry { - // `sizeBytes`, `dateModifiedSecs` can be missing in viewer mode +class AvesEntry with AvesEntryBase { + @override int id; + + @override String uri; + + @override + int? pageId; + + @override + int? sizeBytes; + String? _path, _filename, _extension, _sourceTitle; EntryDir? _directory; - int? pageId, contentId; + int? contentId; final String sourceMimeType; int width, height, sourceRotationDegrees; - int? sizeBytes, dateAddedSecs, _dateModifiedSecs, sourceDateTakenMillis, _durationMillis; + int? dateAddedSecs, _dateModifiedSecs, sourceDateTakenMillis, _durationMillis; bool trashed; int origin; @@ -57,7 +48,11 @@ class AvesEntry { List? burstEntries; - final AChangeNotifier visualChangeNotifier = AChangeNotifier(), metadataChangeNotifier = AChangeNotifier(), addressChangeNotifier = AChangeNotifier(); + @override + final AChangeNotifier visualChangeNotifier = AChangeNotifier(); + + final AChangeNotifier metadataChangeNotifier = AChangeNotifier(); + final AChangeNotifier addressChangeNotifier = AChangeNotifier(); AvesEntry({ required int? id, @@ -243,140 +238,8 @@ class AvesEntry { // so we use the one found during cataloguing if possible String get mimeType => _catalogMetadata?.mimeType ?? sourceMimeType; - String get mimeTypeAnySubtype => mimeType.replaceAll(RegExp('/.*'), '/*'); - - bool get isFavourite => favourites.isFavourite(this); - - bool get isSvg => mimeType == MimeTypes.svg; - - // guess whether this is a photo, according to file type (used as a hint to e.g. display megapixels) - bool get isPhoto => [MimeTypes.heic, MimeTypes.heif, MimeTypes.jpeg, MimeTypes.tiff].contains(mimeType) || isRaw; - - // Android's `BitmapRegionDecoder` documentation states that "only the JPEG and PNG formats are supported" - // but in practice (tested on API 25, 27, 29), it successfully decodes the formats listed below, - // and it actually fails to decode GIF, DNG and animated WEBP. Other formats were not tested. - bool get _supportedByBitmapRegionDecoder => - [ - MimeTypes.heic, - MimeTypes.heif, - MimeTypes.jpeg, - MimeTypes.png, - MimeTypes.webp, - MimeTypes.arw, - MimeTypes.cr2, - MimeTypes.nef, - MimeTypes.nrw, - MimeTypes.orf, - MimeTypes.pef, - MimeTypes.raf, - MimeTypes.rw2, - MimeTypes.srw, - ].contains(mimeType) && - !isAnimated; - - bool get supportTiling => _supportedByBitmapRegionDecoder || mimeType == MimeTypes.tiff; - - bool get useTiles => supportTiling && (width > 4096 || height > 4096); - - bool get isRaw => MimeTypes.rawImages.contains(mimeType); - - bool get isImage => MimeTypes.isImage(mimeType); - - bool get isVideo => MimeTypes.isVideo(mimeType); - bool get isCatalogued => _catalogMetadata != null; - bool get isAnimated => _catalogMetadata?.isAnimated ?? false; - - bool get isGeotiff => _catalogMetadata?.isGeotiff ?? false; - - bool get is360 => _catalogMetadata?.is360 ?? false; - - bool get isMediaStoreContent => uri.startsWith('content://media/'); - - bool get isMediaStoreMediaContent => isMediaStoreContent && {'/external/images/', '/external/video/'}.any(uri.contains); - - bool get isVaultContent => path?.startsWith(androidFileUtils.vaultRoot) ?? false; - - bool get canEdit => !settings.isReadOnly && path != null && !trashed && (isMediaStoreContent || isVaultContent); - - bool get canEditDate => canEdit && (canEditExif || canEditXmp); - - bool get canEditLocation => canEdit && (canEditExif || mimeType == MimeTypes.mp4); - - bool get canEditTitleDescription => canEdit && canEditXmp; - - bool get canEditRating => canEdit && canEditXmp; - - bool get canEditTags => canEdit && canEditXmp; - - bool get canRotate => canEdit && (canEditExif || mimeType == MimeTypes.mp4); - - bool get canFlip => canEdit && canEditExif; - - bool get canEditExif => MimeTypes.canEditExif(mimeType); - - bool get canEditIptc => MimeTypes.canEditIptc(mimeType); - - bool get canEditXmp => MimeTypes.canEditXmp(mimeType); - - bool get canRemoveMetadata => MimeTypes.canRemoveMetadata(mimeType); - - // Media Store size/rotation is inaccurate, e.g. a portrait FHD video is rotated according to its metadata, - // so it should be registered as width=1920, height=1080, orientation=90, - // but is incorrectly registered as width=1080, height=1920, orientation=0. - // Double-checking the width/height during loading or cataloguing is the proper solution, but it would take space and time. - // Comparing width and height can help with the portrait FHD video example, - // but it fails for a portrait screenshot rotated, which is landscape with width=1080, height=1920, orientation=90 - bool get isRotated => rotationDegrees % 180 == 90; - - static const ratioSeparator = '\u2236'; - static const resolutionSeparator = ' \u00D7 '; - - bool get isSized => width > 0 && height > 0; - - String get resolutionText { - final ws = width; - final hs = height; - return isRotated ? '$hs$resolutionSeparator$ws' : '$ws$resolutionSeparator$hs'; - } - - String get aspectRatioText { - if (width > 0 && height > 0) { - final gcd = width.gcd(height); - final w = width ~/ gcd; - final h = height ~/ gcd; - return isRotated ? '$h$ratioSeparator$w' : '$w$ratioSeparator$h'; - } else { - return '?$ratioSeparator?'; - } - } - - double get displayAspectRatio { - if (width == 0 || height == 0) return 1; - return isRotated ? height / width : width / height; - } - - Size get displaySize { - final w = width.toDouble(); - final h = height.toDouble(); - return isRotated ? Size(h, w) : Size(w, h); - } - - Size videoDisplaySize(double sar) { - final size = displaySize; - if (sar != 1) { - final dar = displayAspectRatio * sar; - final w = size.width; - final h = size.height; - if (w >= h) return Size(w, w / dar); - if (h > w) return Size(h * dar, h); - } - return size; - } - - int get megaPixels => (width * height / 1000000).round(); - DateTime? _bestDate; DateTime? get bestDate { @@ -386,6 +249,7 @@ class AvesEntry { int get rating => _catalogMetadata?.rating ?? 0; + @override int get rotationDegrees => _catalogMetadata?.rotationDegrees ?? sourceRotationDegrees; set rotationDegrees(int rotationDegrees) { @@ -397,6 +261,27 @@ class AvesEntry { set isFlipped(bool isFlipped) => _catalogMetadata?.isFlipped = isFlipped; + // Media Store size/rotation is inaccurate, e.g. a portrait FHD video is rotated according to its metadata, + // so it should be registered as width=1920, height=1080, orientation=90, + // but is incorrectly registered as width=1080, height=1920, orientation=0. + // Double-checking the width/height during loading or cataloguing is the proper solution, but it would take space and time. + // Comparing width and height can help with the portrait FHD video example, + // but it fails for a portrait screenshot rotated, which is landscape with width=1080, height=1920, orientation=90 + bool get isRotated => rotationDegrees % 180 == 90; + + @override + double get displayAspectRatio { + if (width == 0 || height == 0) return 1; + return isRotated ? height / width : width / height; + } + + @override + Size get displaySize { + final w = width.toDouble(); + final h = height.toDouble(); + return isRotated ? Size(h, w) : Size(w, h); + } + String? get sourceTitle => _sourceTitle; set sourceTitle(String? sourceTitle) { @@ -423,6 +308,7 @@ class AvesEntry { return d == null ? null : DateTime(d.year, d.month, d.day); } + @override int? get durationMillis => _durationMillis; set durationMillis(int? durationMillis) { @@ -459,8 +345,6 @@ class AvesEntry { // derived from Google reverse geocoding addresses bool get hasFineAddress => _addressDetails?.place?.isNotEmpty == true || (_addressDetails?.countryName?.length ?? 0) > 3; - LatLng? get latLng => hasGps ? LatLng(_catalogMetadata!.latitude!, _catalogMetadata!.longitude!) : null; - Set? _tags; Set get tags { @@ -504,53 +388,6 @@ class AvesEntry { addressDetails = null; } - Future catalog({required bool background, required bool force, required bool persist}) async { - if (isCatalogued && !force) return; - if (isSvg) { - // vector image sizing is not essential, so we should not spend time for it during loading - // but it is useful anyway (for aspect ratios etc.) so we size them during cataloguing - final size = await SvgMetadataService.getSize(this); - if (size != null) { - final fields = { - 'width': size.width.ceil(), - 'height': size.height.ceil(), - }; - await applyNewFields(fields, persist: persist); - } - catalogMetadata = CatalogMetadata(id: id); - } else { - // pre-processing - if (isVideo && (!isSized || durationMillis == 0)) { - // exotic video that is not sized during loading - final fields = await VideoMetadataFormatter.getLoadingMetadata(this); - await applyNewFields(fields, persist: persist); - } - - // cataloguing on platform - catalogMetadata = await metadataFetchService.getCatalogMetadata(this, background: background); - - // post-processing - if (isVideo && (catalogMetadata?.dateMillis ?? 0) == 0) { - catalogMetadata = await VideoMetadataFormatter.getCatalogMetadata(this); - } - if (isGeotiff && !hasGps) { - final info = await metadataFetchService.getGeoTiffInfo(this); - if (info != null) { - final center = MappedGeoTiff( - info: info, - entry: this, - ).center; - if (center != null) { - catalogMetadata = catalogMetadata?.copyWith( - latitude: center.latitude, - longitude: center.longitude, - ); - } - } - } - } - } - AddressDetails? get addressDetails => _addressDetails; set addressDetails(AddressDetails? newAddress) { @@ -558,79 +395,6 @@ class AvesEntry { addressChangeNotifier.notify(); } - Future locate({required bool background, required bool force, required Locale geocoderLocale}) async { - if (hasGps) { - await _locateCountry(force: force); - if (await availability.canLocatePlaces) { - await locatePlace(background: background, force: force, geocoderLocale: geocoderLocale); - } - } else { - addressDetails = null; - } - } - - // quick reverse geocoding to find the country, using an offline asset - Future _locateCountry({required bool force}) async { - if (!hasGps || (hasAddress && !force)) return; - final countryCode = await countryTopology.countryCode(latLng!); - setCountry(countryCode); - } - - void setCountry(CountryCode? countryCode) { - if (hasFineAddress || countryCode == null) return; - addressDetails = AddressDetails( - id: id, - countryCode: countryCode.alpha2, - countryName: countryCode.alpha3, - ); - } - - // full reverse geocoding, requiring Play Services and some connectivity - Future locatePlace({required bool background, required bool force, required Locale geocoderLocale}) async { - if (!hasGps || (hasFineAddress && !force)) return; - try { - Future> call() => GeocodingService.getAddress(latLng!, geocoderLocale); - final addresses = await (background - ? servicePolicy.call( - call, - priority: ServiceCallPriority.getLocation, - ) - : call()); - if (addresses.isNotEmpty) { - final address = addresses.first; - final cc = address.countryCode?.toUpperCase(); - final cn = address.countryName; - final aa = address.adminArea; - addressDetails = AddressDetails( - id: id, - countryCode: cc, - countryName: cn, - adminArea: aa, - // if country & admin fields are null, it is likely the ocean, - // which is identified by `featureName` but we default to the address line anyway - locality: address.locality ?? (cc == null && cn == null && aa == null ? address.addressLine : null), - ); - } - } catch (error, stack) { - debugPrint('$runtimeType locate failed with path=$path coordinates=$latLng error=$error\n$stack'); - } - } - - Future findAddressLine({required Locale geocoderLocale}) async { - if (!hasGps) return null; - - try { - final addresses = await GeocodingService.getAddress(latLng!, geocoderLocale); - if (addresses.isNotEmpty) { - final address = addresses.first; - return address.addressLine; - } - } catch (error, stack) { - debugPrint('$runtimeType findAddressLine failed with path=$path coordinates=$latLng error=$error\n$stack'); - } - return null; - } - String get shortAddress { // `admin area` examples: Seoul, Geneva, null // `locality` examples: Mapo-gu, Geneva, Annecy @@ -732,107 +496,4 @@ class AvesEntry { visualChangeNotifier.notify(); } } - - // favourites - - Future toggleFavourite() async { - if (isFavourite) { - await removeFromFavourites(); - } else { - await addToFavourites(); - } - } - - Future addToFavourites() async { - if (!isFavourite) { - await favourites.add({this}); - } - } - - Future removeFromFavourites() async { - if (isFavourite) { - await favourites.removeEntries({this}); - } - } - - // multipage - - static final _burstFilenamePattern = RegExp(r'^(\d{8}_\d{6})_(\d+)$'); - - bool get isMultiPage => (_catalogMetadata?.isMultiPage ?? false) || isBurst; - - bool get isBurst => burstEntries?.isNotEmpty == true; - - // for backward compatibility - bool get _isMotionPhotoLegacy => isMultiPage && !isBurst && mimeType == MimeTypes.jpeg; - - bool get isMotionPhoto => (_catalogMetadata?.isMotionPhoto ?? false) || _isMotionPhotoLegacy; - - String? get burstKey { - if (filenameWithoutExtension != null) { - final match = _burstFilenamePattern.firstMatch(filenameWithoutExtension!); - if (match != null) { - return '$directory/${match.group(1)}'; - } - } - return null; - } - - Future getMultiPageInfo() async { - if (isBurst) { - return MultiPageInfo( - mainEntry: this, - pages: burstEntries! - .mapIndexed((index, entry) => SinglePageInfo( - index: index, - pageId: entry.id, - isDefault: index == 0, - uri: entry.uri, - mimeType: entry.mimeType, - width: entry.width, - height: entry.height, - rotationDegrees: entry.rotationDegrees, - durationMillis: entry.durationMillis, - )) - .toList(), - ); - } else { - return await metadataFetchService.getMultiPageInfo(this); - } - } - - // sort - - // compare by: - // 1) title ascending - // 2) extension ascending - static int compareByName(AvesEntry a, AvesEntry b) { - final c = compareAsciiUpperCaseNatural(a.bestTitle ?? '', b.bestTitle ?? ''); - return c != 0 ? c : compareAsciiUpperCase(a.extension ?? '', b.extension ?? ''); - } - - // compare by: - // 1) date descending - // 2) name descending - static int compareByDate(AvesEntry a, AvesEntry b) { - var c = (b.bestDate ?? epoch).compareTo(a.bestDate ?? epoch); - if (c != 0) return c; - return compareByName(b, a); - } - - // compare by: - // 1) rating descending - // 2) date descending - static int compareByRating(AvesEntry a, AvesEntry b) { - final c = b.rating.compareTo(a.rating); - return c != 0 ? c : compareByDate(a, b); - } - - // compare by: - // 1) size descending - // 2) date descending - static int compareBySize(AvesEntry a, AvesEntry b) { - final c = (b.sizeBytes ?? 0).compareTo(a.sizeBytes ?? 0); - return c != 0 ? c : compareByDate(a, b); - } } diff --git a/lib/model/entry/extensions/catalog.dart b/lib/model/entry/extensions/catalog.dart new file mode 100644 index 000000000..6a7aa331e --- /dev/null +++ b/lib/model/entry/extensions/catalog.dart @@ -0,0 +1,56 @@ +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/props.dart'; +import 'package:aves/model/geotiff.dart'; +import 'package:aves/model/metadata/catalog.dart'; +import 'package:aves/model/video/metadata.dart'; +import 'package:aves/services/common/services.dart'; +import 'package:aves/services/metadata/svg_metadata_service.dart'; + +extension ExtraAvesEntryCatalog on AvesEntry { + Future catalog({required bool background, required bool force, required bool persist}) async { + if (isCatalogued && !force) return; + if (isSvg) { + // vector image sizing is not essential, so we should not spend time for it during loading + // but it is useful anyway (for aspect ratios etc.) so we size them during cataloguing + final size = await SvgMetadataService.getSize(this); + if (size != null) { + final fields = { + 'width': size.width.ceil(), + 'height': size.height.ceil(), + }; + await applyNewFields(fields, persist: persist); + } + catalogMetadata = CatalogMetadata(id: id); + } else { + // pre-processing + if (isVideo && (!isSized || durationMillis == 0)) { + // exotic video that is not sized during loading + final fields = await VideoMetadataFormatter.getLoadingMetadata(this); + await applyNewFields(fields, persist: persist); + } + + // cataloguing on platform + catalogMetadata = await metadataFetchService.getCatalogMetadata(this, background: background); + + // post-processing + if (isVideo && (catalogMetadata?.dateMillis ?? 0) == 0) { + catalogMetadata = await VideoMetadataFormatter.getCatalogMetadata(this); + } + if (isGeotiff && !hasGps) { + final info = await metadataFetchService.getGeoTiffInfo(this); + if (info != null) { + final center = MappedGeoTiff( + info: info, + entry: this, + ).center; + if (center != null) { + catalogMetadata = catalogMetadata?.copyWith( + latitude: center.latitude, + longitude: center.longitude, + ); + } + } + } + } + } +} diff --git a/lib/model/entry/extensions/favourites.dart b/lib/model/entry/extensions/favourites.dart new file mode 100644 index 000000000..22ca36f0b --- /dev/null +++ b/lib/model/entry/extensions/favourites.dart @@ -0,0 +1,26 @@ +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/favourites.dart'; + +extension ExtraAvesEntryFav on AvesEntry { + bool get isFavourite => favourites.isFavourite(this); + + Future toggleFavourite() async { + if (isFavourite) { + await removeFromFavourites(); + } else { + await addToFavourites(); + } + } + + Future addToFavourites() async { + if (!isFavourite) { + await favourites.add({this}); + } + } + + Future removeFromFavourites() async { + if (isFavourite) { + await favourites.removeEntries({this}); + } + } +} diff --git a/lib/model/entry_images.dart b/lib/model/entry/extensions/images.dart similarity index 96% rename from lib/model/entry_images.dart rename to lib/model/entry/extensions/images.dart index 95a3e24a4..46e18a337 100644 --- a/lib/model/entry_images.dart +++ b/lib/model/entry/extensions/images.dart @@ -3,8 +3,8 @@ import 'dart:math'; import 'package:aves/image_providers/region_provider.dart'; import 'package:aves/image_providers/thumbnail_provider.dart'; import 'package:aves/image_providers/uri_image_provider.dart'; -import 'package:aves/model/entry.dart'; -import 'package:aves/model/entry_cache.dart'; +import 'package:aves/model/entry/cache.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/utils/math_utils.dart'; import 'package:collection/collection.dart'; import 'package:flutter/widgets.dart'; diff --git a/lib/model/entry_info.dart b/lib/model/entry/extensions/info.dart similarity index 90% rename from lib/model/entry_info.dart rename to lib/model/entry/extensions/info.dart index 4589b5f6f..46649a95f 100644 --- a/lib/model/entry_info.dart +++ b/lib/model/entry/extensions/info.dart @@ -1,8 +1,9 @@ import 'dart:async'; import 'dart:collection'; -import 'package:aves/model/entry.dart'; -import 'package:aves/model/video/keys.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/multipage.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/video/metadata.dart'; import 'package:aves/ref/mime_types.dart'; import 'package:aves/services/common/services.dart'; @@ -10,6 +11,7 @@ import 'package:aves/services/metadata/svg_metadata_service.dart'; import 'package:aves/theme/colors.dart'; import 'package:aves/utils/constants.dart'; import 'package:aves/widgets/viewer/info/metadata/metadata_dir.dart'; +import 'package:aves_model/aves_model.dart'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -79,27 +81,27 @@ extension ExtraAvesEntryInfo on AvesEntry { if (mediaInfo.containsKey(Keys.streams)) { String getTypeText(Map stream) { - final type = stream[Keys.streamType] ?? StreamTypes.unknown; + final type = stream[Keys.streamType] ?? MediaStreamTypes.unknown; switch (type) { - case StreamTypes.attachment: + case MediaStreamTypes.attachment: return 'Attachment'; - case StreamTypes.audio: + case MediaStreamTypes.audio: return 'Audio'; - case StreamTypes.metadata: + case MediaStreamTypes.metadata: return 'Metadata'; - case StreamTypes.subtitle: - case StreamTypes.timedText: + case MediaStreamTypes.subtitle: + case MediaStreamTypes.timedText: return 'Text'; - case StreamTypes.video: + case MediaStreamTypes.video: return stream.containsKey(Keys.fpsDen) ? 'Video' : 'Image'; - case StreamTypes.unknown: + case MediaStreamTypes.unknown: default: return 'Unknown'; } } final allStreams = (mediaInfo[Keys.streams] as List).cast(); - final attachmentStreams = allStreams.where((stream) => stream[Keys.streamType] == StreamTypes.attachment).toList(); + final attachmentStreams = allStreams.where((stream) => stream[Keys.streamType] == MediaStreamTypes.attachment).toList(); final knownStreams = allStreams.whereNot(attachmentStreams.contains); // display known streams as separate directories (e.g. video, audio, subs) diff --git a/lib/model/entry/extensions/location.dart b/lib/model/entry/extensions/location.dart new file mode 100644 index 000000000..97f477c15 --- /dev/null +++ b/lib/model/entry/extensions/location.dart @@ -0,0 +1,89 @@ +import 'dart:async'; +import 'dart:ui'; + +import 'package:aves/geo/countries.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/metadata/address.dart'; +import 'package:aves/services/common/service_policy.dart'; +import 'package:aves/services/common/services.dart'; +import 'package:aves/services/geocoding_service.dart'; +import 'package:country_code/country_code.dart'; +import 'package:flutter/foundation.dart'; +import 'package:latlong2/latlong.dart'; + +extension ExtraAvesEntryLocation on AvesEntry { + LatLng? get latLng => hasGps ? LatLng(catalogMetadata!.latitude!, catalogMetadata!.longitude!) : null; + + Future locate({required bool background, required bool force, required Locale geocoderLocale}) async { + if (hasGps) { + await _locateCountry(force: force); + if (await availability.canLocatePlaces) { + await locatePlace(background: background, force: force, geocoderLocale: geocoderLocale); + } + } else { + addressDetails = null; + } + } + + // quick reverse geocoding to find the country, using an offline asset + Future _locateCountry({required bool force}) async { + if (!hasGps || (hasAddress && !force)) return; + final countryCode = await countryTopology.countryCode(latLng!); + setCountry(countryCode); + } + + void setCountry(CountryCode? countryCode) { + if (hasFineAddress || countryCode == null) return; + addressDetails = AddressDetails( + id: id, + countryCode: countryCode.alpha2, + countryName: countryCode.alpha3, + ); + } + + // full reverse geocoding, requiring Play Services and some connectivity + Future locatePlace({required bool background, required bool force, required Locale geocoderLocale}) async { + if (!hasGps || (hasFineAddress && !force)) return; + try { + Future> call() => GeocodingService.getAddress(latLng!, geocoderLocale); + final addresses = await (background + ? servicePolicy.call( + call, + priority: ServiceCallPriority.getLocation, + ) + : call()); + if (addresses.isNotEmpty) { + final address = addresses.first; + final cc = address.countryCode?.toUpperCase(); + final cn = address.countryName; + final aa = address.adminArea; + addressDetails = AddressDetails( + id: id, + countryCode: cc, + countryName: cn, + adminArea: aa, + // if country & admin fields are null, it is likely the ocean, + // which is identified by `featureName` but we default to the address line anyway + locality: address.locality ?? (cc == null && cn == null && aa == null ? address.addressLine : null), + ); + } + } catch (error, stack) { + debugPrint('$runtimeType locate failed with path=$path coordinates=$latLng error=$error\n$stack'); + } + } + + Future findAddressLine({required Locale geocoderLocale}) async { + if (!hasGps) return null; + + try { + final addresses = await GeocodingService.getAddress(latLng!, geocoderLocale); + if (addresses.isNotEmpty) { + final address = addresses.first; + return address.addressLine; + } + } catch (error, stack) { + debugPrint('$runtimeType findAddressLine failed with path=$path coordinates=$latLng error=$error\n$stack'); + } + return null; + } +} diff --git a/lib/model/entry_metadata_edition.dart b/lib/model/entry/extensions/metadata_edition.dart similarity index 99% rename from lib/model/entry_metadata_edition.dart rename to lib/model/entry/extensions/metadata_edition.dart index 6a0eb8ff3..eaa156585 100644 --- a/lib/model/entry_metadata_edition.dart +++ b/lib/model/entry/extensions/metadata_edition.dart @@ -1,7 +1,9 @@ import 'dart:convert'; import 'dart:io'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/catalog.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/metadata/date_modifier.dart'; import 'package:aves/model/metadata/enums/date_field_source.dart'; import 'package:aves/model/metadata/enums/enums.dart'; diff --git a/lib/model/entry/extensions/multipage.dart b/lib/model/entry/extensions/multipage.dart new file mode 100644 index 000000000..f77f252b1 --- /dev/null +++ b/lib/model/entry/extensions/multipage.dart @@ -0,0 +1,53 @@ +import 'dart:async'; + +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/multipage.dart'; +import 'package:aves/ref/mime_types.dart'; +import 'package:aves/services/common/services.dart'; +import 'package:collection/collection.dart'; + +extension ExtraAvesEntryMultipage on AvesEntry { + static final _burstFilenamePattern = RegExp(r'^(\d{8}_\d{6})_(\d+)$'); + + bool get isMultiPage => (catalogMetadata?.isMultiPage ?? false) || isBurst; + + bool get isBurst => burstEntries?.isNotEmpty == true; + + // for backward compatibility + bool get _isMotionPhotoLegacy => isMultiPage && !isBurst && mimeType == MimeTypes.jpeg; + + bool get isMotionPhoto => (catalogMetadata?.isMotionPhoto ?? false) || _isMotionPhotoLegacy; + + String? get burstKey { + if (filenameWithoutExtension != null) { + final match = _burstFilenamePattern.firstMatch(filenameWithoutExtension!); + if (match != null) { + return '$directory/${match.group(1)}'; + } + } + return null; + } + + Future getMultiPageInfo() async { + if (isBurst) { + return MultiPageInfo( + mainEntry: this, + pages: burstEntries! + .mapIndexed((index, entry) => SinglePageInfo( + index: index, + pageId: entry.id, + isDefault: index == 0, + uri: entry.uri, + mimeType: entry.mimeType, + width: entry.width, + height: entry.height, + rotationDegrees: entry.rotationDegrees, + durationMillis: entry.durationMillis, + )) + .toList(), + ); + } else { + return await metadataFetchService.getMultiPageInfo(this); + } + } +} diff --git a/lib/model/entry/extensions/props.dart b/lib/model/entry/extensions/props.dart new file mode 100644 index 000000000..ed1c3ef1c --- /dev/null +++ b/lib/model/entry/extensions/props.dart @@ -0,0 +1,119 @@ +import 'dart:ui'; + +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/settings/settings.dart'; +import 'package:aves/ref/mime_types.dart'; +import 'package:aves/utils/android_file_utils.dart'; + +extension ExtraAvesEntryProps on AvesEntry { + String get mimeTypeAnySubtype => mimeType.replaceAll(RegExp('/.*'), '/*'); + + bool get isSvg => mimeType == MimeTypes.svg; + + // guess whether this is a photo, according to file type (used as a hint to e.g. display megapixels) + bool get isPhoto => [MimeTypes.heic, MimeTypes.heif, MimeTypes.jpeg, MimeTypes.tiff].contains(mimeType) || isRaw; + + // Android's `BitmapRegionDecoder` documentation states that "only the JPEG and PNG formats are supported" + // but in practice (tested on API 25, 27, 29), it successfully decodes the formats listed below, + // and it actually fails to decode GIF, DNG and animated WEBP. Other formats were not tested. + bool get _supportedByBitmapRegionDecoder => + [ + MimeTypes.heic, + MimeTypes.heif, + MimeTypes.jpeg, + MimeTypes.png, + MimeTypes.webp, + MimeTypes.arw, + MimeTypes.cr2, + MimeTypes.nef, + MimeTypes.nrw, + MimeTypes.orf, + MimeTypes.pef, + MimeTypes.raf, + MimeTypes.rw2, + MimeTypes.srw, + ].contains(mimeType) && + !isAnimated; + + bool get supportTiling => _supportedByBitmapRegionDecoder || mimeType == MimeTypes.tiff; + + bool get useTiles => supportTiling && (width > 4096 || height > 4096); + + bool get isRaw => MimeTypes.rawImages.contains(mimeType); + + bool get isImage => MimeTypes.isImage(mimeType); + + bool get isVideo => MimeTypes.isVideo(mimeType); + + bool get isAnimated => catalogMetadata?.isAnimated ?? false; + + bool get isGeotiff => catalogMetadata?.isGeotiff ?? false; + + bool get is360 => catalogMetadata?.is360 ?? false; + + bool get isMediaStoreContent => uri.startsWith('content://media/'); + + bool get isMediaStoreMediaContent => isMediaStoreContent && {'/external/images/', '/external/video/'}.any(uri.contains); + + bool get isVaultContent => path?.startsWith(androidFileUtils.vaultRoot) ?? false; + + bool get canEdit => !settings.isReadOnly && path != null && !trashed && (isMediaStoreContent || isVaultContent); + + bool get canEditDate => canEdit && (canEditExif || canEditXmp); + + bool get canEditLocation => canEdit && (canEditExif || mimeType == MimeTypes.mp4); + + bool get canEditTitleDescription => canEdit && canEditXmp; + + bool get canEditRating => canEdit && canEditXmp; + + bool get canEditTags => canEdit && canEditXmp; + + bool get canRotate => canEdit && (canEditExif || mimeType == MimeTypes.mp4); + + bool get canFlip => canEdit && canEditExif; + + bool get canEditExif => MimeTypes.canEditExif(mimeType); + + bool get canEditIptc => MimeTypes.canEditIptc(mimeType); + + bool get canEditXmp => MimeTypes.canEditXmp(mimeType); + + bool get canRemoveMetadata => MimeTypes.canRemoveMetadata(mimeType); + + static const ratioSeparator = '\u2236'; + static const resolutionSeparator = ' \u00D7 '; + + bool get isSized => width > 0 && height > 0; + + String get resolutionText { + final ws = width; + final hs = height; + return isRotated ? '$hs$resolutionSeparator$ws' : '$ws$resolutionSeparator$hs'; + } + + String get aspectRatioText { + if (width > 0 && height > 0) { + final gcd = width.gcd(height); + final w = width ~/ gcd; + final h = height ~/ gcd; + return isRotated ? '$h$ratioSeparator$w' : '$w$ratioSeparator$h'; + } else { + return '?$ratioSeparator?'; + } + } + + Size videoDisplaySize(double sar) { + final size = displaySize; + if (sar != 1) { + final dar = displayAspectRatio * sar; + final w = size.width; + final h = size.height; + if (w >= h) return Size(w, w / dar); + if (h > w) return Size(h * dar, h); + } + return size; + } + + int get megaPixels => (width * height / 1000000).round(); +} diff --git a/lib/model/entry/origins.dart b/lib/model/entry/origins.dart new file mode 100644 index 000000000..efc35496a --- /dev/null +++ b/lib/model/entry/origins.dart @@ -0,0 +1,6 @@ +class EntryOrigins { + static const int mediaStoreContent = 0; + static const int unknownContent = 1; + static const int file = 2; + static const int vault = 3; +} diff --git a/lib/model/entry/sort.dart b/lib/model/entry/sort.dart new file mode 100644 index 000000000..f15521bc0 --- /dev/null +++ b/lib/model/entry/sort.dart @@ -0,0 +1,38 @@ +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/utils/time_utils.dart'; +import 'package:collection/collection.dart'; + +class AvesEntrySort { + // compare by: + // 1) title ascending + // 2) extension ascending + static int compareByName(AvesEntry a, AvesEntry b) { + final c = compareAsciiUpperCaseNatural(a.bestTitle ?? '', b.bestTitle ?? ''); + return c != 0 ? c : compareAsciiUpperCase(a.extension ?? '', b.extension ?? ''); + } + + // compare by: + // 1) date descending + // 2) name descending + static int compareByDate(AvesEntry a, AvesEntry b) { + var c = (b.bestDate ?? epoch).compareTo(a.bestDate ?? epoch); + if (c != 0) return c; + return compareByName(b, a); + } + + // compare by: + // 1) rating descending + // 2) date descending + static int compareByRating(AvesEntry a, AvesEntry b) { + final c = b.rating.compareTo(a.rating); + return c != 0 ? c : compareByDate(a, b); + } + + // compare by: + // 1) size descending + // 2) date descending + static int compareBySize(AvesEntry a, AvesEntry b) { + final c = (b.sizeBytes ?? 0).compareTo(a.sizeBytes ?? 0); + return c != 0 ? c : compareByDate(a, b); + } +} diff --git a/lib/model/favourites.dart b/lib/model/favourites.dart index d994e31cc..8c5eeea57 100644 --- a/lib/model/favourites.dart +++ b/lib/model/favourites.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/utils/android_file_utils.dart'; diff --git a/lib/model/filters/coordinate.dart b/lib/model/filters/coordinate.dart index 704d6bac3..2c822fecf 100644 --- a/lib/model/filters/coordinate.dart +++ b/lib/model/filters/coordinate.dart @@ -1,4 +1,5 @@ import 'package:aves/l10n/l10n.dart'; +import 'package:aves/model/entry/extensions/location.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/settings/enums/coordinate_format.dart'; import 'package:aves/model/settings/enums/enums.dart'; diff --git a/lib/model/filters/favourite.dart b/lib/model/filters/favourite.dart index 7a028411c..413da4c86 100644 --- a/lib/model/filters/favourite.dart +++ b/lib/model/filters/favourite.dart @@ -1,4 +1,5 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/favourites.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; diff --git a/lib/model/filters/filters.dart b/lib/model/filters/filters.dart index a043214b8..212b6c92d 100644 --- a/lib/model/filters/filters.dart +++ b/lib/model/filters/filters.dart @@ -1,7 +1,7 @@ import 'dart:convert'; import 'package:aves/model/covers.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/aspect_ratio.dart'; import 'package:aves/model/filters/coordinate.dart'; diff --git a/lib/model/filters/placeholder.dart b/lib/model/filters/placeholder.dart index e0be8e084..d2438e762 100644 --- a/lib/model/filters/placeholder.dart +++ b/lib/model/filters/placeholder.dart @@ -1,4 +1,6 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/catalog.dart'; +import 'package:aves/model/entry/extensions/location.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/icons.dart'; diff --git a/lib/model/filters/query.dart b/lib/model/filters/query.dart index b82a2ab1c..9df1f55f6 100644 --- a/lib/model/filters/query.dart +++ b/lib/model/filters/query.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; diff --git a/lib/model/filters/trash.dart b/lib/model/filters/trash.dart index a378a8668..c995f95b2 100644 --- a/lib/model/filters/trash.dart +++ b/lib/model/filters/trash.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; diff --git a/lib/model/filters/type.dart b/lib/model/filters/type.dart index 3ecb278f7..9eafa93b3 100644 --- a/lib/model/filters/type.dart +++ b/lib/model/filters/type.dart @@ -1,3 +1,5 @@ +import 'package:aves/model/entry/extensions/multipage.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; diff --git a/lib/model/geotiff.dart b/lib/model/geotiff.dart index dc3ab8deb..a557039ae 100644 --- a/lib/model/geotiff.dart +++ b/lib/model/geotiff.dart @@ -2,8 +2,8 @@ import 'dart:async'; import 'dart:math'; import 'dart:ui' as ui; -import 'package:aves/model/entry.dart'; -import 'package:aves/model/entry_images.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/images.dart'; import 'package:aves/ref/geotiff.dart'; import 'package:aves/utils/math_utils.dart'; import 'package:aves_map/aves_map.dart'; diff --git a/lib/model/multipage.dart b/lib/model/multipage.dart index b28945784..278a62e6a 100644 --- a/lib/model/multipage.dart +++ b/lib/model/multipage.dart @@ -1,4 +1,5 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/multipage.dart'; import 'package:aves/ref/mime_types.dart'; import 'package:aves/services/common/services.dart'; import 'package:collection/collection.dart'; diff --git a/lib/model/naming_pattern.dart b/lib/model/naming_pattern.dart index 2cbe98dfc..e24e5221f 100644 --- a/lib/model/naming_pattern.dart +++ b/lib/model/naming_pattern.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; diff --git a/lib/model/query.dart b/lib/model/query.dart index df48d2206..82be471d9 100644 --- a/lib/model/query.dart +++ b/lib/model/query.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:aves/utils/change_notifier.dart'; +import 'package:aves_utils/aves_utils.dart'; import 'package:flutter/foundation.dart'; class Query extends ChangeNotifier { diff --git a/lib/model/settings/enums/accessibility_animations.dart b/lib/model/settings/enums/accessibility_animations.dart index d301f098e..e9b6b1184 100644 --- a/lib/model/settings/enums/accessibility_animations.dart +++ b/lib/model/settings/enums/accessibility_animations.dart @@ -1,21 +1,7 @@ +import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/model/settings/settings.dart'; -import 'package:aves/widgets/common/extensions/build_context.dart'; -import 'package:flutter/widgets.dart'; - -import 'enums.dart'; extension ExtraAccessibilityAnimations on AccessibilityAnimations { - String getName(BuildContext context) { - switch (this) { - case AccessibilityAnimations.system: - return context.l10n.settingsSystemDefault; - case AccessibilityAnimations.disabled: - return context.l10n.accessibilityAnimationsRemove; - case AccessibilityAnimations.enabled: - return context.l10n.accessibilityAnimationsKeep; - } - } - bool get animate { switch (this) { case AccessibilityAnimations.system: diff --git a/lib/model/settings/enums/accessibility_timeout.dart b/lib/model/settings/enums/accessibility_timeout.dart deleted file mode 100644 index 5c7529f77..000000000 --- a/lib/model/settings/enums/accessibility_timeout.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:aves/widgets/common/extensions/build_context.dart'; -import 'package:flutter/widgets.dart'; - -import 'enums.dart'; - -extension ExtraAccessibilityTimeout on AccessibilityTimeout { - String getName(BuildContext context) { - switch (this) { - case AccessibilityTimeout.system: - return context.l10n.settingsSystemDefault; - case AccessibilityTimeout.s1: - return context.l10n.timeSeconds(1); - case AccessibilityTimeout.s3: - return context.l10n.timeSeconds(3); - case AccessibilityTimeout.s5: - return context.l10n.timeSeconds(5); - case AccessibilityTimeout.s10: - return context.l10n.timeSeconds(10); - case AccessibilityTimeout.s30: - return context.l10n.timeSeconds(30); - } - } -} diff --git a/lib/model/settings/enums/coordinate_format.dart b/lib/model/settings/enums/coordinate_format.dart index 4b3b2ffd9..c03fe3727 100644 --- a/lib/model/settings/enums/coordinate_format.dart +++ b/lib/model/settings/enums/coordinate_format.dart @@ -1,21 +1,9 @@ import 'package:aves/l10n/l10n.dart'; -import 'package:aves/widgets/common/extensions/build_context.dart'; -import 'package:flutter/widgets.dart'; +import 'package:aves/model/settings/enums/enums.dart'; import 'package:intl/intl.dart'; import 'package:latlong2/latlong.dart'; -import 'enums.dart'; - extension ExtraCoordinateFormat on CoordinateFormat { - String getName(BuildContext context) { - switch (this) { - case CoordinateFormat.dms: - return context.l10n.coordinateFormatDms; - case CoordinateFormat.decimal: - return context.l10n.coordinateFormatDecimal; - } - } - static const _separator = ', '; String format(AppLocalizations l10n, LatLng latLng, {bool minuteSecondPadding = false, int dmsSecondDecimals = 2}) { diff --git a/lib/model/settings/enums/display_refresh_rate_mode.dart b/lib/model/settings/enums/display_refresh_rate_mode.dart index 37c1d76f2..79bf93508 100644 --- a/lib/model/settings/enums/display_refresh_rate_mode.dart +++ b/lib/model/settings/enums/display_refresh_rate_mode.dart @@ -1,23 +1,10 @@ +import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/services/common/services.dart'; -import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:device_info_plus/device_info_plus.dart'; -import 'package:flutter/material.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter_displaymode/flutter_displaymode.dart'; -import 'enums.dart'; - extension ExtraDisplayRefreshRateMode on DisplayRefreshRateMode { - String getName(BuildContext context) { - switch (this) { - case DisplayRefreshRateMode.auto: - return context.l10n.settingsSystemDefault; - case DisplayRefreshRateMode.highest: - return context.l10n.displayRefreshRatePreferHighest; - case DisplayRefreshRateMode.lowest: - return context.l10n.displayRefreshRatePreferLowest; - } - } - Future apply() async { if (!await windowService.isActivity()) return; diff --git a/lib/model/settings/enums/entry_background.dart b/lib/model/settings/enums/entry_background.dart index c9935b6d1..b4fc3a636 100644 --- a/lib/model/settings/enums/entry_background.dart +++ b/lib/model/settings/enums/entry_background.dart @@ -1,7 +1,6 @@ +import 'package:aves/model/settings/enums/enums.dart'; import 'package:flutter/material.dart'; -import 'enums.dart'; - extension ExtraEntryBackground on EntryBackground { bool get isColor { switch (this) { diff --git a/lib/model/settings/enums/home_page.dart b/lib/model/settings/enums/home_page.dart index c52228d8a..4cdf8b057 100644 --- a/lib/model/settings/enums/home_page.dart +++ b/lib/model/settings/enums/home_page.dart @@ -1,20 +1,8 @@ +import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/widgets/collection/collection_page.dart'; -import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/filter_grids/albums_page.dart'; -import 'package:flutter/widgets.dart'; - -import 'enums.dart'; extension ExtraHomePageSetting on HomePageSetting { - String getName(BuildContext context) { - switch (this) { - case HomePageSetting.collection: - return context.l10n.drawerCollectionAll; - case HomePageSetting.albums: - return context.l10n.drawerAlbumPage; - } - } - String get routeName { switch (this) { case HomePageSetting.collection: diff --git a/lib/model/settings/enums/l10n.dart b/lib/model/settings/enums/l10n.dart new file mode 100644 index 000000000..88ce874bf --- /dev/null +++ b/lib/model/settings/enums/l10n.dart @@ -0,0 +1,278 @@ +import 'package:aves/model/settings/enums/enums.dart'; +import 'package:aves/widgets/common/extensions/build_context.dart'; +import 'package:aves_map/aves_map.dart'; +import 'package:flutter/widgets.dart'; + +extension ExtraAccessibilityAnimationsName on AccessibilityAnimations { + String getName(BuildContext context) { + switch (this) { + case AccessibilityAnimations.system: + return context.l10n.settingsSystemDefault; + case AccessibilityAnimations.disabled: + return context.l10n.accessibilityAnimationsRemove; + case AccessibilityAnimations.enabled: + return context.l10n.accessibilityAnimationsKeep; + } + } +} + +extension ExtraAccessibilityTimeoutName on AccessibilityTimeout { + String getName(BuildContext context) { + switch (this) { + case AccessibilityTimeout.system: + return context.l10n.settingsSystemDefault; + case AccessibilityTimeout.s1: + return context.l10n.timeSeconds(1); + case AccessibilityTimeout.s3: + return context.l10n.timeSeconds(3); + case AccessibilityTimeout.s5: + return context.l10n.timeSeconds(5); + case AccessibilityTimeout.s10: + return context.l10n.timeSeconds(10); + case AccessibilityTimeout.s30: + return context.l10n.timeSeconds(30); + } + } +} + +extension ExtraAvesThemeBrightnessName on AvesThemeBrightness { + String getName(BuildContext context) { + switch (this) { + case AvesThemeBrightness.system: + return context.l10n.settingsSystemDefault; + case AvesThemeBrightness.light: + return context.l10n.themeBrightnessLight; + case AvesThemeBrightness.dark: + return context.l10n.themeBrightnessDark; + case AvesThemeBrightness.black: + return context.l10n.themeBrightnessBlack; + } + } +} + +extension ExtraCoordinateFormatName on CoordinateFormat { + String getName(BuildContext context) { + switch (this) { + case CoordinateFormat.dms: + return context.l10n.coordinateFormatDms; + case CoordinateFormat.decimal: + return context.l10n.coordinateFormatDecimal; + } + } +} + +extension ExtraDisplayRefreshRateModeName on DisplayRefreshRateMode { + String getName(BuildContext context) { + switch (this) { + case DisplayRefreshRateMode.auto: + return context.l10n.settingsSystemDefault; + case DisplayRefreshRateMode.highest: + return context.l10n.displayRefreshRatePreferHighest; + case DisplayRefreshRateMode.lowest: + return context.l10n.displayRefreshRatePreferLowest; + } + } +} + +extension ExtraEntryMapStyleName on EntryMapStyle { + String getName(BuildContext context) { + switch (this) { + case EntryMapStyle.googleNormal: + return context.l10n.mapStyleGoogleNormal; + case EntryMapStyle.googleHybrid: + return context.l10n.mapStyleGoogleHybrid; + case EntryMapStyle.googleTerrain: + return context.l10n.mapStyleGoogleTerrain; + case EntryMapStyle.hmsNormal: + return context.l10n.mapStyleHuaweiNormal; + case EntryMapStyle.hmsTerrain: + return context.l10n.mapStyleHuaweiTerrain; + case EntryMapStyle.osmHot: + return context.l10n.mapStyleOsmHot; + case EntryMapStyle.stamenToner: + return context.l10n.mapStyleStamenToner; + case EntryMapStyle.stamenWatercolor: + return context.l10n.mapStyleStamenWatercolor; + } + } +} + +extension ExtraHomePageSettingName on HomePageSetting { + String getName(BuildContext context) { + switch (this) { + case HomePageSetting.collection: + return context.l10n.drawerCollectionAll; + case HomePageSetting.albums: + return context.l10n.drawerAlbumPage; + } + } +} + +extension ExtraKeepScreenOnName on KeepScreenOn { + String getName(BuildContext context) { + switch (this) { + case KeepScreenOn.never: + return context.l10n.keepScreenOnNever; + case KeepScreenOn.videoPlayback: + return context.l10n.keepScreenOnVideoPlayback; + case KeepScreenOn.viewerOnly: + return context.l10n.keepScreenOnViewerOnly; + case KeepScreenOn.always: + return context.l10n.keepScreenOnAlways; + } + } +} + +extension ExtraSlideshowVideoPlaybackName on SlideshowVideoPlayback { + String getName(BuildContext context) { + switch (this) { + case SlideshowVideoPlayback.skip: + return context.l10n.videoPlaybackSkip; + case SlideshowVideoPlayback.playMuted: + return context.l10n.videoPlaybackMuted; + case SlideshowVideoPlayback.playWithSound: + return context.l10n.videoPlaybackWithSound; + } + } +} + +extension ExtraSubtitlePositionName on SubtitlePosition { + String getName(BuildContext context) { + switch (this) { + case SubtitlePosition.top: + return context.l10n.subtitlePositionTop; + case SubtitlePosition.bottom: + return context.l10n.subtitlePositionBottom; + } + } +} + +extension ExtraThumbnailOverlayLocationIconName on ThumbnailOverlayLocationIcon { + String getName(BuildContext context) { + switch (this) { + case ThumbnailOverlayLocationIcon.located: + return context.l10n.filterLocatedLabel; + case ThumbnailOverlayLocationIcon.unlocated: + return context.l10n.filterNoLocationLabel; + case ThumbnailOverlayLocationIcon.none: + return context.l10n.settingsDisabled; + } + } +} + +extension ExtraThumbnailOverlayTagIconName on ThumbnailOverlayTagIcon { + String getName(BuildContext context) { + switch (this) { + case ThumbnailOverlayTagIcon.tagged: + return context.l10n.filterTaggedLabel; + case ThumbnailOverlayTagIcon.untagged: + return context.l10n.filterNoTagLabel; + case ThumbnailOverlayTagIcon.none: + return context.l10n.settingsDisabled; + } + } +} + +extension ExtraUnitSystemName on UnitSystem { + String getName(BuildContext context) { + switch (this) { + case UnitSystem.metric: + return context.l10n.unitSystemMetric; + case UnitSystem.imperial: + return context.l10n.unitSystemImperial; + } + } +} + +extension ExtraVideoAutoPlayModeName on VideoAutoPlayMode { + String getName(BuildContext context) { + switch (this) { + case VideoAutoPlayMode.disabled: + return context.l10n.settingsDisabled; + case VideoAutoPlayMode.playMuted: + return context.l10n.videoPlaybackMuted; + case VideoAutoPlayMode.playWithSound: + return context.l10n.videoPlaybackWithSound; + } + } +} + +extension ExtraVideoBackgroundModeName on VideoBackgroundMode { + String getName(BuildContext context) { + switch (this) { + case VideoBackgroundMode.disabled: + return context.l10n.settingsDisabled; + case VideoBackgroundMode.pip: + return context.l10n.settingsVideoEnablePip; + } + } +} + +extension ExtraVideoControlsName on VideoControls { + String getName(BuildContext context) { + switch (this) { + case VideoControls.play: + return context.l10n.videoControlsPlay; + case VideoControls.playSeek: + return context.l10n.videoControlsPlaySeek; + case VideoControls.playOutside: + return context.l10n.videoControlsPlayOutside; + case VideoControls.none: + return context.l10n.videoControlsNone; + } + } +} + +extension ExtraVideoLoopModeName on VideoLoopMode { + String getName(BuildContext context) { + switch (this) { + case VideoLoopMode.never: + return context.l10n.videoLoopModeNever; + case VideoLoopMode.shortOnly: + return context.l10n.videoLoopModeShortOnly; + case VideoLoopMode.always: + return context.l10n.videoLoopModeAlways; + } + } +} + +extension ExtraViewerTransitionName on ViewerTransition { + String getName(BuildContext context) { + switch (this) { + case ViewerTransition.slide: + return context.l10n.viewerTransitionSlide; + case ViewerTransition.parallax: + return context.l10n.viewerTransitionParallax; + case ViewerTransition.fade: + return context.l10n.viewerTransitionFade; + case ViewerTransition.zoomIn: + return context.l10n.viewerTransitionZoomIn; + case ViewerTransition.none: + return context.l10n.viewerTransitionNone; + } + } +} + +extension ExtraWidgetDisplayedItemName on WidgetDisplayedItem { + String getName(BuildContext context) { + switch (this) { + case WidgetDisplayedItem.random: + return context.l10n.widgetDisplayedItemRandom; + case WidgetDisplayedItem.mostRecent: + return context.l10n.widgetDisplayedItemMostRecent; + } + } +} + +extension ExtraWidgetOpenPageName on WidgetOpenPage { + String getName(BuildContext context) { + switch (this) { + case WidgetOpenPage.home: + return context.l10n.widgetOpenPageHome; + case WidgetOpenPage.collection: + return context.l10n.widgetOpenPageCollection; + case WidgetOpenPage.viewer: + return context.l10n.widgetOpenPageViewer; + } + } +} diff --git a/lib/model/settings/enums/map_style.dart b/lib/model/settings/enums/map_style.dart index cedc7c71f..89e0e6e26 100644 --- a/lib/model/settings/enums/map_style.dart +++ b/lib/model/settings/enums/map_style.dart @@ -1,6 +1,4 @@ -import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves_map/aves_map.dart'; -import 'package:flutter/widgets.dart'; extension ExtraEntryMapStyle on EntryMapStyle { static bool isHeavy(EntryMapStyle? style) { @@ -16,27 +14,6 @@ extension ExtraEntryMapStyle on EntryMapStyle { } } - String getName(BuildContext context) { - switch (this) { - case EntryMapStyle.googleNormal: - return context.l10n.mapStyleGoogleNormal; - case EntryMapStyle.googleHybrid: - return context.l10n.mapStyleGoogleHybrid; - case EntryMapStyle.googleTerrain: - return context.l10n.mapStyleGoogleTerrain; - case EntryMapStyle.hmsNormal: - return context.l10n.mapStyleHuaweiNormal; - case EntryMapStyle.hmsTerrain: - return context.l10n.mapStyleHuaweiTerrain; - case EntryMapStyle.osmHot: - return context.l10n.mapStyleOsmHot; - case EntryMapStyle.stamenToner: - return context.l10n.mapStyleStamenToner; - case EntryMapStyle.stamenWatercolor: - return context.l10n.mapStyleStamenWatercolor; - } - } - bool get needMobileService { switch (this) { case EntryMapStyle.osmHot: diff --git a/lib/model/settings/enums/screen_on.dart b/lib/model/settings/enums/screen_on.dart index 5f21d4348..a2f168c64 100644 --- a/lib/model/settings/enums/screen_on.dart +++ b/lib/model/settings/enums/screen_on.dart @@ -1,23 +1,7 @@ +import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/services/common/services.dart'; -import 'package:aves/widgets/common/extensions/build_context.dart'; -import 'package:flutter/widgets.dart'; - -import 'enums.dart'; extension ExtraKeepScreenOn on KeepScreenOn { - String getName(BuildContext context) { - switch (this) { - case KeepScreenOn.never: - return context.l10n.keepScreenOnNever; - case KeepScreenOn.videoPlayback: - return context.l10n.keepScreenOnVideoPlayback; - case KeepScreenOn.viewerOnly: - return context.l10n.keepScreenOnViewerOnly; - case KeepScreenOn.always: - return context.l10n.keepScreenOnAlways; - } - } - void apply() { windowService.keepScreenOn(this == KeepScreenOn.always); } diff --git a/lib/model/settings/enums/slideshow_video_playback.dart b/lib/model/settings/enums/slideshow_video_playback.dart deleted file mode 100644 index 453ddf29b..000000000 --- a/lib/model/settings/enums/slideshow_video_playback.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:aves/widgets/common/extensions/build_context.dart'; -import 'package:flutter/widgets.dart'; - -import 'enums.dart'; - -extension ExtraSlideshowVideoPlayback on SlideshowVideoPlayback { - String getName(BuildContext context) { - switch (this) { - case SlideshowVideoPlayback.skip: - return context.l10n.videoPlaybackSkip; - case SlideshowVideoPlayback.playMuted: - return context.l10n.videoPlaybackMuted; - case SlideshowVideoPlayback.playWithSound: - return context.l10n.videoPlaybackWithSound; - } - } -} diff --git a/lib/model/settings/enums/subtitle_position.dart b/lib/model/settings/enums/subtitle_position.dart index 88d49d6f1..988545095 100644 --- a/lib/model/settings/enums/subtitle_position.dart +++ b/lib/model/settings/enums/subtitle_position.dart @@ -1,18 +1,7 @@ -import 'package:aves/widgets/common/extensions/build_context.dart'; +import 'package:aves/model/settings/enums/enums.dart'; import 'package:flutter/widgets.dart'; -import 'enums.dart'; - extension ExtraSubtitlePosition on SubtitlePosition { - String getName(BuildContext context) { - switch (this) { - case SubtitlePosition.top: - return context.l10n.subtitlePositionTop; - case SubtitlePosition.bottom: - return context.l10n.subtitlePositionBottom; - } - } - TextAlignVertical toTextAlignVertical() { switch (this) { case SubtitlePosition.top: diff --git a/lib/model/settings/enums/theme_brightness.dart b/lib/model/settings/enums/theme_brightness.dart index 5edf515ae..af38e11c6 100644 --- a/lib/model/settings/enums/theme_brightness.dart +++ b/lib/model/settings/enums/theme_brightness.dart @@ -1,22 +1,7 @@ -import 'package:aves/widgets/common/extensions/build_context.dart'; +import 'package:aves/model/settings/enums/enums.dart'; import 'package:flutter/material.dart'; -import 'enums.dart'; - extension ExtraAvesThemeBrightness on AvesThemeBrightness { - String getName(BuildContext context) { - switch (this) { - case AvesThemeBrightness.system: - return context.l10n.settingsSystemDefault; - case AvesThemeBrightness.light: - return context.l10n.themeBrightnessLight; - case AvesThemeBrightness.dark: - return context.l10n.themeBrightnessDark; - case AvesThemeBrightness.black: - return context.l10n.themeBrightnessBlack; - } - } - ThemeMode get appThemeMode { switch (this) { case AvesThemeBrightness.system: diff --git a/lib/model/settings/enums/thumbnail_overlay_location_icon.dart b/lib/model/settings/enums/thumbnail_overlay_location_icon.dart index e61a0fd37..c241fe853 100644 --- a/lib/model/settings/enums/thumbnail_overlay_location_icon.dart +++ b/lib/model/settings/enums/thumbnail_overlay_location_icon.dart @@ -1,21 +1,8 @@ +import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/theme/icons.dart'; -import 'package:aves/widgets/common/extensions/build_context.dart'; -import 'package:flutter/material.dart'; - -import 'enums.dart'; +import 'package:flutter/widgets.dart'; extension ExtraThumbnailOverlayLocationIcon on ThumbnailOverlayLocationIcon { - String getName(BuildContext context) { - switch (this) { - case ThumbnailOverlayLocationIcon.located: - return context.l10n.filterLocatedLabel; - case ThumbnailOverlayLocationIcon.unlocated: - return context.l10n.filterNoLocationLabel; - case ThumbnailOverlayLocationIcon.none: - return context.l10n.settingsDisabled; - } - } - IconData getIcon(BuildContext context) { switch (this) { case ThumbnailOverlayLocationIcon.unlocated: diff --git a/lib/model/settings/enums/thumbnail_overlay_tag_icon.dart b/lib/model/settings/enums/thumbnail_overlay_tag_icon.dart index 00f006ec9..969900e82 100644 --- a/lib/model/settings/enums/thumbnail_overlay_tag_icon.dart +++ b/lib/model/settings/enums/thumbnail_overlay_tag_icon.dart @@ -1,21 +1,8 @@ +import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/theme/icons.dart'; -import 'package:aves/widgets/common/extensions/build_context.dart'; -import 'package:flutter/material.dart'; - -import 'enums.dart'; +import 'package:flutter/widgets.dart'; extension ExtraThumbnailOverlayTagIcon on ThumbnailOverlayTagIcon { - String getName(BuildContext context) { - switch (this) { - case ThumbnailOverlayTagIcon.tagged: - return context.l10n.filterTaggedLabel; - case ThumbnailOverlayTagIcon.untagged: - return context.l10n.filterNoTagLabel; - case ThumbnailOverlayTagIcon.none: - return context.l10n.settingsDisabled; - } - } - IconData getIcon(BuildContext context) { switch (this) { case ThumbnailOverlayTagIcon.tagged: diff --git a/lib/model/settings/enums/unit_system.dart b/lib/model/settings/enums/unit_system.dart deleted file mode 100644 index cde99328b..000000000 --- a/lib/model/settings/enums/unit_system.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:aves/widgets/common/extensions/build_context.dart'; -import 'package:flutter/widgets.dart'; - -import 'enums.dart'; - -extension ExtraUnitSystem on UnitSystem { - String getName(BuildContext context) { - switch (this) { - case UnitSystem.metric: - return context.l10n.unitSystemMetric; - case UnitSystem.imperial: - return context.l10n.unitSystemImperial; - } - } -} diff --git a/lib/model/settings/enums/video_auto_play_mode.dart b/lib/model/settings/enums/video_auto_play_mode.dart deleted file mode 100644 index b06c765dd..000000000 --- a/lib/model/settings/enums/video_auto_play_mode.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:aves/widgets/common/extensions/build_context.dart'; -import 'package:flutter/widgets.dart'; - -import 'enums.dart'; - -extension ExtraVideoAutoPlayMode on VideoAutoPlayMode { - String getName(BuildContext context) { - switch (this) { - case VideoAutoPlayMode.disabled: - return context.l10n.settingsDisabled; - case VideoAutoPlayMode.playMuted: - return context.l10n.videoPlaybackMuted; - case VideoAutoPlayMode.playWithSound: - return context.l10n.videoPlaybackWithSound; - } - } -} diff --git a/lib/model/settings/enums/video_background_mode.dart b/lib/model/settings/enums/video_background_mode.dart deleted file mode 100644 index 0a7e040bb..000000000 --- a/lib/model/settings/enums/video_background_mode.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:aves/widgets/common/extensions/build_context.dart'; -import 'package:flutter/widgets.dart'; - -import 'enums.dart'; - -extension ExtraVideoBackgroundMode on VideoBackgroundMode { - String getName(BuildContext context) { - switch (this) { - case VideoBackgroundMode.disabled: - return context.l10n.settingsDisabled; - case VideoBackgroundMode.pip: - return context.l10n.settingsVideoEnablePip; - } - } -} diff --git a/lib/model/settings/enums/video_controls.dart b/lib/model/settings/enums/video_controls.dart deleted file mode 100644 index 6f9979f71..000000000 --- a/lib/model/settings/enums/video_controls.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:aves/widgets/common/extensions/build_context.dart'; -import 'package:flutter/widgets.dart'; - -import 'enums.dart'; - -extension ExtraVideoControls on VideoControls { - String getName(BuildContext context) { - switch (this) { - case VideoControls.play: - return context.l10n.videoControlsPlay; - case VideoControls.playSeek: - return context.l10n.videoControlsPlaySeek; - case VideoControls.playOutside: - return context.l10n.videoControlsPlayOutside; - case VideoControls.none: - return context.l10n.videoControlsNone; - } - } -} diff --git a/lib/model/settings/enums/video_loop_mode.dart b/lib/model/settings/enums/video_loop_mode.dart index 0a6cba0a9..0799fcfe8 100644 --- a/lib/model/settings/enums/video_loop_mode.dart +++ b/lib/model/settings/enums/video_loop_mode.dart @@ -1,29 +1,13 @@ -import 'package:aves/model/entry.dart'; -import 'package:aves/widgets/common/extensions/build_context.dart'; -import 'package:flutter/widgets.dart'; - -import 'enums.dart'; +import 'package:aves/model/settings/enums/enums.dart'; extension ExtraVideoLoopMode on VideoLoopMode { - String getName(BuildContext context) { - switch (this) { - case VideoLoopMode.never: - return context.l10n.videoLoopModeNever; - case VideoLoopMode.shortOnly: - return context.l10n.videoLoopModeShortOnly; - case VideoLoopMode.always: - return context.l10n.videoLoopModeAlways; - } - } - static const shortVideoThreshold = Duration(seconds: 30); - bool shouldLoop(AvesEntry entry) { + bool shouldLoop(int? durationMillis) { switch (this) { case VideoLoopMode.never: return false; case VideoLoopMode.shortOnly: - final durationMillis = entry.durationMillis; return durationMillis != null ? durationMillis < shortVideoThreshold.inMilliseconds : false; case VideoLoopMode.always: return true; diff --git a/lib/model/settings/enums/viewer_transition.dart b/lib/model/settings/enums/viewer_transition.dart index 739e067c3..1f4ff4352 100644 --- a/lib/model/settings/enums/viewer_transition.dart +++ b/lib/model/settings/enums/viewer_transition.dart @@ -1,25 +1,8 @@ -import 'package:aves/widgets/common/extensions/build_context.dart'; +import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/widgets/viewer/controls/controller.dart'; import 'package:flutter/widgets.dart'; -import 'enums.dart'; - extension ExtraViewerTransition on ViewerTransition { - String getName(BuildContext context) { - switch (this) { - case ViewerTransition.slide: - return context.l10n.viewerTransitionSlide; - case ViewerTransition.parallax: - return context.l10n.viewerTransitionParallax; - case ViewerTransition.fade: - return context.l10n.viewerTransitionFade; - case ViewerTransition.zoomIn: - return context.l10n.viewerTransitionZoomIn; - case ViewerTransition.none: - return context.l10n.viewerTransitionNone; - } - } - TransitionBuilder builder(PageController pageController, int index) { switch (this) { case ViewerTransition.slide: diff --git a/lib/model/settings/enums/widget_displayed_item.dart b/lib/model/settings/enums/widget_displayed_item.dart deleted file mode 100644 index 0914d4a6a..000000000 --- a/lib/model/settings/enums/widget_displayed_item.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:aves/model/settings/enums/enums.dart'; -import 'package:aves/widgets/common/extensions/build_context.dart'; -import 'package:flutter/widgets.dart'; - -extension ExtraWidgetDisplayedItem on WidgetDisplayedItem { - String getName(BuildContext context) { - switch (this) { - case WidgetDisplayedItem.random: - return context.l10n.widgetDisplayedItemRandom; - case WidgetDisplayedItem.mostRecent: - return context.l10n.widgetDisplayedItemMostRecent; - } - } -} diff --git a/lib/model/settings/enums/widget_open_action.dart b/lib/model/settings/enums/widget_open_action.dart deleted file mode 100644 index 333be5fca..000000000 --- a/lib/model/settings/enums/widget_open_action.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:aves/model/settings/enums/enums.dart'; -import 'package:aves/widgets/common/extensions/build_context.dart'; -import 'package:flutter/widgets.dart'; - -extension ExtraWidgetOpenPage on WidgetOpenPage { - String getName(BuildContext context) { - switch (this) { - case WidgetOpenPage.home: - return context.l10n.widgetOpenPageHome; - case WidgetOpenPage.collection: - return context.l10n.widgetOpenPageCollection; - case WidgetOpenPage.viewer: - return context.l10n.widgetOpenPageViewer; - } - } -} diff --git a/lib/model/settings/enums/widget_shape.dart b/lib/model/settings/enums/widget_shape.dart index 19374bfed..deeed752a 100644 --- a/lib/model/settings/enums/widget_shape.dart +++ b/lib/model/settings/enums/widget_shape.dart @@ -1,6 +1,6 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/settings/enums/enums.dart'; -import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; extension ExtraWidgetShape on WidgetShape { Path path(Size widgetSize, double devicePixelRatio) { diff --git a/lib/model/settings/settings.dart b/lib/model/settings/settings.dart index 0856b3c3e..ea63e38e3 100644 --- a/lib/model/settings/settings.dart +++ b/lib/model/settings/settings.dart @@ -14,7 +14,7 @@ import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/model/settings/enums/map_style.dart'; import 'package:aves/model/source/enums/enums.dart'; import 'package:aves/services/accessibility_service.dart'; -import 'package:aves/services/common/optional_event_channel.dart'; +import 'package:aves_utils/aves_utils.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/widgets/aves_app.dart'; import 'package:aves/widgets/common/search/page.dart'; diff --git a/lib/model/source/album.dart b/lib/model/source/album.dart index fae997994..7b4e9c48c 100644 --- a/lib/model/source/album.dart +++ b/lib/model/source/album.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/filters/album.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_source.dart'; diff --git a/lib/model/source/collection_lens.dart b/lib/model/source/collection_lens.dart index 5c9730444..a117a8fdc 100644 --- a/lib/model/source/collection_lens.dart +++ b/lib/model/source/collection_lens.dart @@ -2,7 +2,9 @@ import 'dart:async'; import 'dart:collection'; import 'package:aves/model/actions/move_type.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/multipage.dart'; +import 'package:aves/model/entry/sort.dart'; import 'package:aves/model/favourites.dart'; import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/favourite.dart'; @@ -13,17 +15,16 @@ import 'package:aves/model/filters/rating.dart'; import 'package:aves/model/filters/trash.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_source.dart'; +import 'package:aves/model/source/enums/enums.dart'; import 'package:aves/model/source/events.dart'; import 'package:aves/model/source/location/location.dart'; import 'package:aves/model/source/section_keys.dart'; import 'package:aves/model/source/tag.dart'; -import 'package:aves/utils/change_notifier.dart'; import 'package:aves/utils/collection_utils.dart'; +import 'package:aves_utils/aves_utils.dart'; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; -import 'enums/enums.dart'; - class CollectionLens with ChangeNotifier { final CollectionSource source; final Set filters; @@ -190,7 +191,7 @@ class CollectionLens with ChangeNotifier { final byBurstKey = groupBy(_filteredSortedEntries, (entry) => entry.burstKey).whereNotNullKey(); byBurstKey.forEach((burstKey, entries) { if (entries.length > 1) { - entries.sort(AvesEntry.compareByName); + entries.sort(AvesEntrySort.compareByName); final mainEntry = entries.first; final burstEntry = mainEntry.copyWith(burstEntries: entries); @@ -209,16 +210,16 @@ class CollectionLens with ChangeNotifier { switch (sortFactor) { case EntrySortFactor.date: - _filteredSortedEntries.sort(AvesEntry.compareByDate); + _filteredSortedEntries.sort(AvesEntrySort.compareByDate); break; case EntrySortFactor.name: - _filteredSortedEntries.sort(AvesEntry.compareByName); + _filteredSortedEntries.sort(AvesEntrySort.compareByName); break; case EntrySortFactor.rating: - _filteredSortedEntries.sort(AvesEntry.compareByRating); + _filteredSortedEntries.sort(AvesEntrySort.compareByRating); break; case EntrySortFactor.size: - _filteredSortedEntries.sort(AvesEntry.compareBySize); + _filteredSortedEntries.sort(AvesEntrySort.compareBySize); break; } if (sortReverse) { diff --git a/lib/model/source/collection_source.dart b/lib/model/source/collection_source.dart index e31296c46..e2925ac74 100644 --- a/lib/model/source/collection_source.dart +++ b/lib/model/source/collection_source.dart @@ -2,7 +2,10 @@ import 'dart:async'; import 'package:aves/model/actions/move_type.dart'; import 'package:aves/model/covers.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/catalog.dart'; +import 'package:aves/model/entry/extensions/location.dart'; +import 'package:aves/model/entry/sort.dart'; import 'package:aves/model/favourites.dart'; import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/filters.dart'; @@ -105,7 +108,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place @override List get sortedEntriesByDate { - _sortedEntriesByDate ??= List.unmodifiable(visibleEntries.toList()..sort(AvesEntry.compareByDate)); + _sortedEntriesByDate ??= List.unmodifiable(visibleEntries.toList()..sort(AvesEntrySort.compareByDate)); return _sortedEntriesByDate!; } diff --git a/lib/model/source/enums/view.dart b/lib/model/source/enums/view.dart index aab4397f7..7f6c50ffd 100644 --- a/lib/model/source/enums/view.dart +++ b/lib/model/source/enums/view.dart @@ -1,9 +1,8 @@ +import 'package:aves/model/source/enums/enums.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:flutter/widgets.dart'; -import 'enums.dart'; - extension ExtraEntrySortFactor on EntrySortFactor { String getName(BuildContext context) { final l10n = context.l10n; diff --git a/lib/model/source/events.dart b/lib/model/source/events.dart index eed59df63..2cd779f31 100644 --- a/lib/model/source/events.dart +++ b/lib/model/source/events.dart @@ -1,5 +1,5 @@ import 'package:aves/model/actions/move_type.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:flutter/foundation.dart'; @immutable diff --git a/lib/model/source/location/country.dart b/lib/model/source/location/country.dart index a6de50f6a..2fcce4db0 100644 --- a/lib/model/source/location/country.dart +++ b/lib/model/source/location/country.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/filters/location.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/utils/collection_utils.dart'; diff --git a/lib/model/source/location/location.dart b/lib/model/source/location/location.dart index 80703a5b6..75fec0ede 100644 --- a/lib/model/source/location/location.dart +++ b/lib/model/source/location/location.dart @@ -1,7 +1,8 @@ import 'dart:math'; import 'package:aves/geo/countries.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/location.dart'; import 'package:aves/model/filters/location.dart'; import 'package:aves/model/metadata/address.dart'; import 'package:aves/model/settings/settings.dart'; diff --git a/lib/model/source/location/place.dart b/lib/model/source/location/place.dart index 342202990..a0b1e3ab6 100644 --- a/lib/model/source/location/place.dart +++ b/lib/model/source/location/place.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/filters/location.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/utils/collection_utils.dart'; diff --git a/lib/model/source/media_store_source.dart b/lib/model/source/media_store_source.dart index 22595cd36..4e1c17f2d 100644 --- a/lib/model/source/media_store_source.dart +++ b/lib/model/source/media_store_source.dart @@ -2,7 +2,8 @@ import 'dart:async'; import 'dart:math'; import 'package:aves/model/covers.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/origins.dart'; import 'package:aves/model/favourites.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/analysis_controller.dart'; diff --git a/lib/model/source/tag.dart b/lib/model/source/tag.dart index e1ebd33e6..6b8dcd682 100644 --- a/lib/model/source/tag.dart +++ b/lib/model/source/tag.dart @@ -1,4 +1,5 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/catalog.dart'; import 'package:aves/model/filters/tag.dart'; import 'package:aves/model/metadata/catalog.dart'; import 'package:aves/model/source/analysis_controller.dart'; diff --git a/lib/model/video/metadata.dart b/lib/model/video/metadata.dart index 7245ca79b..2691bb6fe 100644 --- a/lib/model/video/metadata.dart +++ b/lib/model/video/metadata.dart @@ -1,10 +1,9 @@ import 'dart:async'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/metadata/catalog.dart'; import 'package:aves/model/video/channel_layouts.dart'; import 'package:aves/model/video/codecs.dart'; -import 'package:aves/model/video/keys.dart'; import 'package:aves/model/video/profiles/aac.dart'; import 'package:aves/model/video/profiles/h264.dart'; import 'package:aves/model/video/profiles/hevc.dart'; @@ -17,6 +16,7 @@ import 'package:aves/utils/math_utils.dart'; import 'package:aves/utils/string_utils.dart'; import 'package:aves/utils/time_utils.dart'; import 'package:aves/widgets/viewer/video/fijkplayer.dart'; +import 'package:aves_model/aves_model.dart'; import 'package:collection/collection.dart'; import 'package:fijkplayer/fijkplayer.dart'; import 'package:flutter/foundation.dart'; @@ -230,7 +230,7 @@ class VideoMetadataFormatter { } break; case Keys.codecPixelFormat: - if (streamType == StreamTypes.video) { + if (streamType == MediaStreamTypes.video) { // this is just a short name used by FFmpeg // user-friendly descriptions for related enums are defined in libavutil/pixfmt.h save('Pixel Format', (value as String).toUpperCase()); @@ -425,13 +425,3 @@ class VideoMetadataFormatter { return '${(size / divider / divider).toStringAsFixed(round)} M$unit'; } } - -class StreamTypes { - static const attachment = 'attachment'; - static const audio = 'audio'; - static const metadata = 'metadata'; - static const subtitle = 'subtitle'; - static const timedText = 'timedtext'; - static const unknown = 'unknown'; - static const video = 'video'; -} diff --git a/lib/services/android_app_service.dart b/lib/services/android_app_service.dart index 1db5487ad..cf181a68b 100644 --- a/lib/services/android_app_service.dart +++ b/lib/services/android_app_service.dart @@ -1,4 +1,5 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/utils/android_file_utils.dart'; diff --git a/lib/services/android_debug_service.dart b/lib/services/android_debug_service.dart index 29d2b8627..9acb772da 100644 --- a/lib/services/android_debug_service.dart +++ b/lib/services/android_debug_service.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/ref/mime_types.dart'; import 'package:aves/services/common/services.dart'; import 'package:flutter/services.dart'; diff --git a/lib/services/media/embedded_data_service.dart b/lib/services/media/embedded_data_service.dart index 33f024ba2..d4a668176 100644 --- a/lib/services/media/embedded_data_service.dart +++ b/lib/services/media/embedded_data_service.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/utils/constants.dart'; import 'package:flutter/services.dart'; diff --git a/lib/services/media/media_edit_service.dart b/lib/services/media/media_edit_service.dart index 6374a1941..0ef3ba684 100644 --- a/lib/services/media/media_edit_service.dart +++ b/lib/services/media/media_edit_service.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/metadata/enums/enums.dart'; import 'package:aves/services/common/image_op_events.dart'; import 'package:aves/services/common/services.dart'; diff --git a/lib/services/media/media_fetch_service.dart b/lib/services/media/media_fetch_service.dart index e50f161d8..4d6a9c4cd 100644 --- a/lib/services/media/media_fetch_service.dart +++ b/lib/services/media/media_fetch_service.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'dart:math'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/ref/mime_types.dart'; import 'package:aves/services/common/output_buffer.dart'; import 'package:aves/services/common/service_policy.dart'; diff --git a/lib/services/media/media_session_service.dart b/lib/services/media/media_session_service.dart index 643c19297..8280965c7 100644 --- a/lib/services/media/media_session_service.dart +++ b/lib/services/media/media_session_service.dart @@ -1,8 +1,9 @@ import 'dart:async'; -import 'package:aves/services/common/optional_event_channel.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves_utils/aves_utils.dart'; import 'package:aves/services/common/services.dart'; -import 'package:aves/widgets/viewer/video/controller.dart'; +import 'package:aves_video/aves_video.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; @@ -12,6 +13,7 @@ abstract class MediaSessionService { Stream get mediaCommands; Future update({ + required AvesEntry entry, required AvesVideoController controller, required bool canSkipToNext, required bool canSkipToPrevious, @@ -43,11 +45,11 @@ class PlatformMediaSessionService implements MediaSessionService, Disposable { @override Future update({ + required AvesEntry entry, required AvesVideoController controller, required bool canSkipToNext, required bool canSkipToPrevious, }) async { - final entry = controller.entry; try { await _platformObject.invokeMethod('update', { 'uri': entry.uri, diff --git a/lib/services/media/media_store_service.dart b/lib/services/media/media_store_service.dart index 203fd2944..ab762d13a 100644 --- a/lib/services/media/media_store_service.dart +++ b/lib/services/media/media_store_service.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/services/common/services.dart'; import 'package:flutter/services.dart'; import 'package:streams_channel/streams_channel.dart'; diff --git a/lib/services/metadata/metadata_edit_service.dart b/lib/services/metadata/metadata_edit_service.dart index 9816fd9a2..53eb204a4 100644 --- a/lib/services/metadata/metadata_edit_service.dart +++ b/lib/services/metadata/metadata_edit_service.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/metadata/date_modifier.dart'; import 'package:aves/model/metadata/enums/enums.dart'; import 'package:aves/model/metadata/enums/metadata_type.dart'; diff --git a/lib/services/metadata/metadata_fetch_service.dart b/lib/services/metadata/metadata_fetch_service.dart index 16c52177f..4998a1b6c 100644 --- a/lib/services/metadata/metadata_fetch_service.dart +++ b/lib/services/metadata/metadata_fetch_service.dart @@ -1,4 +1,6 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/multipage.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/geotiff.dart'; import 'package:aves/model/metadata/catalog.dart'; import 'package:aves/model/metadata/fields.dart'; diff --git a/lib/services/metadata/svg_metadata_service.dart b/lib/services/metadata/svg_metadata_service.dart index a792ef028..882b3527b 100644 --- a/lib/services/metadata/svg_metadata_service.dart +++ b/lib/services/metadata/svg_metadata_service.dart @@ -1,6 +1,6 @@ import 'dart:convert'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/utils/string_utils.dart'; import 'package:collection/collection.dart'; diff --git a/lib/widget_common.dart b/lib/widget_common.dart index e2316fb3f..c111a54b5 100644 --- a/lib/widget_common.dart +++ b/lib/widget_common.dart @@ -1,7 +1,8 @@ import 'dart:async'; import 'package:aves/app_flavor.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/sort.dart'; import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_lens.dart'; @@ -80,7 +81,7 @@ Future _getWidgetEntry(int widgetId, bool reuseEntry) async { entries.shuffle(); break; case WidgetDisplayedItem.mostRecent: - entries.sort(AvesEntry.compareByDate); + entries.sort(AvesEntrySort.compareByDate); break; } final entry = entries.firstOrNull; diff --git a/lib/widgets/about/licenses.dart b/lib/widgets/about/licenses.dart index ac1f1b1f1..e3d476d58 100644 --- a/lib/widgets/about/licenses.dart +++ b/lib/widgets/about/licenses.dart @@ -4,6 +4,7 @@ import 'package:aves/ref/brand_colors.dart'; import 'package:aves/theme/colors.dart'; import 'package:aves/utils/dependencies.dart'; import 'package:aves/widgets/about/title.dart'; +import 'package:aves/widgets/about/tv_license_page.dart'; import 'package:aves/widgets/common/basic/link_chip.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/aves_expansion_tile.dart'; @@ -87,7 +88,7 @@ class _LicensesState extends State { // as of Flutter v1.22.4, `cardColor` is used as a background color by `LicensePage` cardColor: Theme.of(context).scaffoldBackgroundColor, ), - child: const LicensePage(), + child: settings.useTvLayout ? const TvLicensePage() : const LicensePage(), ), ), ), diff --git a/lib/widgets/about/tv_license_page.dart b/lib/widgets/about/tv_license_page.dart new file mode 100644 index 000000000..abd54b1c8 --- /dev/null +++ b/lib/widgets/about/tv_license_page.dart @@ -0,0 +1,357 @@ +import 'package:aves/widgets/common/basic/scaffold.dart'; +import 'package:aves/widgets/common/behaviour/intents.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:flutter/services.dart'; + +// as of Flutter v3.7.7, `LicensePage` is not designed for Android TV +// and gets rejected from Google Play review: +// ``` +// Your app’s text is cut off at the edge of the screen. +// Apps should not display any text or functionality that is partially cut off by the edges of the screen. +// For example, your app (version code 94) in the "Show All Licenses" section text is cut off from the bottom of the screen. +// ``` +class TvLicensePage extends StatefulWidget { + const TvLicensePage({super.key}); + + @override + State createState() => _TvLicensePageState(); +} + +class _TvLicensePageState extends State { + final FocusNode _railFocusNode = FocusNode(); + final ScrollController _detailsScrollController = ScrollController(); + final ValueNotifier _railIndexNotifier = ValueNotifier(0); + + final Future<_LicenseData> licenses = LicenseRegistry.licenses + .fold<_LicenseData>( + _LicenseData(), + (prev, license) => prev..addLicense(license), + ) + .then((licenseData) => licenseData..sortPackages()); + + @override + void dispose() { + _railIndexNotifier.dispose(); + _railFocusNode.dispose(); + _detailsScrollController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AvesScaffold( + appBar: AppBar( + automaticallyImplyLeading: false, + title: Text(MaterialLocalizations.of(context).licensesPageTitle), + ), + body: ValueListenableBuilder( + valueListenable: _railIndexNotifier, + builder: (context, selectedIndex, child) { + return FutureBuilder<_LicenseData>( + future: licenses, + builder: (context, snapshot) { + final data = snapshot.data; + if (data == null) { + return const Center( + child: CircularProgressIndicator(), + ); + } + + final packages = data.packages; + final rail = Focus( + focusNode: _railFocusNode, + skipTraversal: true, + canRequestFocus: false, + child: ConstrainedBox( + constraints: BoxConstraints.loose(const Size.fromWidth(300)), + child: ListView.builder( + itemBuilder: (context, index) { + final packageName = packages[index]; + final bindings = data.packageLicenseBindings[packageName]!; + final isSelected = index == selectedIndex; + return Ink( + color: isSelected ? Theme.of(context).highlightColor : Theme.of(context).cardColor, + child: ListTile( + title: Text(packageName), + subtitle: Text(MaterialLocalizations.of(context).licensesPackageDetailText(bindings.length)), + selected: isSelected, + onTap: () => _railIndexNotifier.value = index, + ), + ); + }, + itemCount: packages.length, + ), + ), + ); + + final packageName = packages[selectedIndex]; + final bindings = data.packageLicenseBindings[packageName]!; + + return SafeArea( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(width: 16), + rail, + Expanded( + child: FocusableActionDetector( + shortcuts: const { + SingleActivator(LogicalKeyboardKey.arrowUp): ScrollIntent(direction: AxisDirection.up, type: ScrollIncrementType.page), + SingleActivator(LogicalKeyboardKey.arrowDown): ScrollIntent(direction: AxisDirection.down, type: ScrollIncrementType.page), + }, + actions: { + ScrollIntent: ScrollControllerAction(scrollController: _detailsScrollController), + }, + child: KeyedSubtree( + key: Key(packageName), + child: _PackageLicensePage( + packageName: packageName, + licenseEntries: bindings.map((i) => data.licenses[i]).toList(growable: false), + scrollController: _detailsScrollController, + ), + ), + ), + ), + ], + ), + ); + }, + ); + }, + ), + ); + } +} + +// adapted from Flutter `_LicenseData` in `/material/about.dart` +class _LicenseData { + final List licenses = []; + final Map> packageLicenseBindings = >{}; + final List packages = []; + + // Special treatment for the first package since it should be the package + // for delivered application. + String? firstPackage; + + void addLicense(LicenseEntry entry) { + // Before the license can be added, we must first record the packages to + // which it belongs. + for (final String package in entry.packages) { + _addPackage(package); + // Bind this license to the package using the next index value. This + // creates a contract that this license must be inserted at this same + // index value. + packageLicenseBindings[package]!.add(licenses.length); + } + licenses.add(entry); // Completion of the contract above. + } + + /// Add a package and initialize package license binding. This is a no-op if + /// the package has been seen before. + void _addPackage(String package) { + if (!packageLicenseBindings.containsKey(package)) { + packageLicenseBindings[package] = []; + firstPackage ??= package; + packages.add(package); + } + } + + /// Sort the packages using some comparison method, or by the default manner, + /// which is to put the application package first, followed by every other + /// package in case-insensitive alphabetical order. + void sortPackages([int Function(String a, String b)? compare]) { + packages.sort(compare ?? + (a, b) { + // Based on how LicenseRegistry currently behaves, the first package + // returned is the end user application license. This should be + // presented first in the list. So here we make sure that first package + // remains at the front regardless of alphabetical sorting. + if (a == firstPackage) { + return -1; + } + if (b == firstPackage) { + return 1; + } + return a.toLowerCase().compareTo(b.toLowerCase()); + }); + } +} + +// adapted from Flutter `_PackageLicensePage` in `/material/about.dart` +class _PackageLicensePage extends StatefulWidget { + const _PackageLicensePage({ + required this.packageName, + required this.licenseEntries, + required this.scrollController, + }); + + final String packageName; + final List licenseEntries; + final ScrollController? scrollController; + + @override + _PackageLicensePageState createState() => _PackageLicensePageState(); +} + +class _PackageLicensePageState extends State<_PackageLicensePage> { + @override + void initState() { + super.initState(); + _initLicenses(); + } + + final List _licenses = []; + bool _loaded = false; + + Future _initLicenses() async { + for (final LicenseEntry license in widget.licenseEntries) { + if (!mounted) { + return; + } + final List paragraphs = await SchedulerBinding.instance.scheduleTask>( + license.paragraphs.toList, + Priority.animation, + debugLabel: 'License', + ); + if (!mounted) { + return; + } + setState(() { + _licenses.add(const Padding( + padding: EdgeInsets.all(18.0), + child: Divider(), + )); + for (final LicenseParagraph paragraph in paragraphs) { + if (paragraph.indent == LicenseParagraph.centeredIndent) { + _licenses.add(Padding( + padding: const EdgeInsets.only(top: 16.0), + child: Text( + paragraph.text, + style: const TextStyle(fontWeight: FontWeight.bold), + textAlign: TextAlign.center, + ), + )); + } else { + _licenses.add(Padding( + padding: EdgeInsetsDirectional.only(top: 8.0, start: 16.0 * paragraph.indent), + child: Text(paragraph.text), + )); + } + } + }); + } + setState(() { + _loaded = true; + }); + } + + @override + Widget build(BuildContext context) { + final MaterialLocalizations localizations = MaterialLocalizations.of(context); + final ThemeData theme = Theme.of(context); + final String title = widget.packageName; + final String subtitle = localizations.licensesPackageDetailText(widget.licenseEntries.length); + const double pad = 24; + const EdgeInsets padding = EdgeInsets.only(left: pad, right: pad, bottom: pad); + final List listWidgets = [ + ..._licenses, + if (!_loaded) + const Padding( + padding: EdgeInsets.symmetric(vertical: 24.0), + child: Center( + child: CircularProgressIndicator(), + ), + ), + ]; + + final Widget page; + if (widget.scrollController == null) { + page = Scaffold( + appBar: AppBar( + title: _PackageLicensePageTitle( + title, + subtitle, + theme.primaryTextTheme, + ), + ), + body: Center( + child: Material( + color: theme.cardColor, + elevation: 4.0, + child: Container( + constraints: BoxConstraints.loose(const Size.fromWidth(600.0)), + child: Localizations.override( + locale: const Locale('en', 'US'), + context: context, + child: ScrollConfiguration( + // A Scrollbar is built-in below. + behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false), + child: Scrollbar( + child: ListView(padding: padding, children: listWidgets), + ), + ), + ), + ), + ), + ), + ); + } else { + page = CustomScrollView( + controller: widget.scrollController, + slivers: [ + SliverAppBar( + automaticallyImplyLeading: false, + pinned: true, + backgroundColor: theme.cardColor, + title: _PackageLicensePageTitle(title, subtitle, theme.textTheme), + ), + SliverPadding( + padding: padding, + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) => Localizations.override( + locale: const Locale('en', 'US'), + context: context, + child: listWidgets[index], + ), + childCount: listWidgets.length, + ), + ), + ), + ], + ); + } + return DefaultTextStyle( + style: theme.textTheme.bodySmall!, + child: page, + ); + } +} + +class _PackageLicensePageTitle extends StatelessWidget { + const _PackageLicensePageTitle( + this.title, + this.subtitle, + this.theme, + ); + + final String title; + final String subtitle; + final TextTheme theme; + + @override + Widget build(BuildContext context) { + final Color? color = Theme.of(context).appBarTheme.foregroundColor; + + return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(title, style: theme.titleLarge?.copyWith(color: color)), + Text(subtitle, style: theme.titleSmall?.copyWith(color: color)), + ], + ); + } +} diff --git a/lib/widgets/aves_app.dart b/lib/widgets/aves_app.dart index 17f89b8d4..85fd93fae 100644 --- a/lib/widgets/aves_app.dart +++ b/lib/widgets/aves_app.dart @@ -18,7 +18,7 @@ import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/media_store_source.dart'; import 'package:aves/services/accessibility_service.dart'; -import 'package:aves/services/common/optional_event_channel.dart'; +import 'package:aves_utils/aves_utils.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/theme/colors.dart'; import 'package:aves/theme/durations.dart'; diff --git a/lib/widgets/collection/app_bar.dart b/lib/widgets/collection/app_bar.dart index d9206938a..de01c21f4 100644 --- a/lib/widgets/collection/app_bar.dart +++ b/lib/widgets/collection/app_bar.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:aves/app_mode.dart'; import 'package:aves/model/actions/entry_set_actions.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/query.dart'; import 'package:aves/model/filters/trash.dart'; diff --git a/lib/widgets/collection/collection_grid.dart b/lib/widgets/collection/collection_grid.dart index 15d424e05..f1d78f0b5 100644 --- a/lib/widgets/collection/collection_grid.dart +++ b/lib/widgets/collection/collection_grid.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'package:aves/app_mode.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/favourites.dart'; import 'package:aves/model/filters/favourite.dart'; import 'package:aves/model/filters/mime.dart'; diff --git a/lib/widgets/collection/collection_page.dart b/lib/widgets/collection/collection_page.dart index 0251638a3..0c3a1ac0e 100644 --- a/lib/widgets/collection/collection_page.dart +++ b/lib/widgets/collection/collection_page.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'package:aves/app_mode.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/query.dart'; import 'package:aves/model/filters/trash.dart'; diff --git a/lib/widgets/collection/draggable_thumb_label.dart b/lib/widgets/collection/draggable_thumb_label.dart index d04670818..6c5bf4bb6 100644 --- a/lib/widgets/collection/draggable_thumb_label.dart +++ b/lib/widgets/collection/draggable_thumb_label.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/filters/rating.dart'; import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_source.dart'; diff --git a/lib/widgets/collection/entry_set_action_delegate.dart b/lib/widgets/collection/entry_set_action_delegate.dart index f6f0a6545..3f690f478 100644 --- a/lib/widgets/collection/entry_set_action_delegate.dart +++ b/lib/widgets/collection/entry_set_action_delegate.dart @@ -4,8 +4,10 @@ import 'package:aves/app_mode.dart'; import 'package:aves/model/actions/entry_set_actions.dart'; import 'package:aves/model/actions/move_type.dart'; import 'package:aves/model/device.dart'; -import 'package:aves/model/entry.dart'; -import 'package:aves/model/entry_metadata_edition.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/favourites.dart'; +import 'package:aves/model/entry/extensions/metadata_edition.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/favourites.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/metadata/date_modifier.dart'; diff --git a/lib/widgets/collection/grid/headers/album.dart b/lib/widgets/collection/grid/headers/album.dart index 85fb41b32..dc15e9bfc 100644 --- a/lib/widgets/collection/grid/headers/album.dart +++ b/lib/widgets/collection/grid/headers/album.dart @@ -1,5 +1,5 @@ import 'package:aves/model/covers.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/section_keys.dart'; import 'package:aves/theme/icons.dart'; diff --git a/lib/widgets/collection/grid/headers/any.dart b/lib/widgets/collection/grid/headers/any.dart index c3949add9..f3333bda7 100644 --- a/lib/widgets/collection/grid/headers/any.dart +++ b/lib/widgets/collection/grid/headers/any.dart @@ -1,6 +1,6 @@ import 'dart:math'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/enums/enums.dart'; diff --git a/lib/widgets/collection/grid/list_details.dart b/lib/widgets/collection/grid/list_details.dart index f71ad7d0b..456ffecc1 100644 --- a/lib/widgets/collection/grid/list_details.dart +++ b/lib/widgets/collection/grid/list_details.dart @@ -1,4 +1,5 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/location.dart'; import 'package:aves/model/settings/enums/coordinate_format.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/format.dart'; diff --git a/lib/widgets/collection/grid/section_layout.dart b/lib/widgets/collection/grid/section_layout.dart index 276861e32..1156b6123 100644 --- a/lib/widgets/collection/grid/section_layout.dart +++ b/lib/widgets/collection/grid/section_layout.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/section_keys.dart'; import 'package:aves/widgets/collection/grid/headers/any.dart'; diff --git a/lib/widgets/collection/grid/tile.dart b/lib/widgets/collection/grid/tile.dart index 64eddc2c0..38d5bc5b8 100644 --- a/lib/widgets/collection/grid/tile.dart +++ b/lib/widgets/collection/grid/tile.dart @@ -1,5 +1,5 @@ import 'package:aves/app_mode.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/selection.dart'; import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/enums/enums.dart'; diff --git a/lib/widgets/collection/query_bar.dart b/lib/widgets/collection/query_bar.dart index d1442b4c3..e9bc1cc68 100644 --- a/lib/widgets/collection/query_bar.dart +++ b/lib/widgets/collection/query_bar.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/selection.dart'; import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/widgets/common/basic/query_bar.dart'; diff --git a/lib/widgets/common/action_controls/quick_choosers/share_button.dart b/lib/widgets/common/action_controls/quick_choosers/share_button.dart index 379c6b9a2..7e0164400 100644 --- a/lib/widgets/common/action_controls/quick_choosers/share_button.dart +++ b/lib/widgets/common/action_controls/quick_choosers/share_button.dart @@ -1,6 +1,7 @@ import 'package:aves/model/actions/entry_actions.dart'; import 'package:aves/model/actions/share_actions.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/multipage.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/button.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/share_chooser.dart'; import 'package:flutter/material.dart'; diff --git a/lib/widgets/common/action_controls/togglers/favourite.dart b/lib/widgets/common/action_controls/togglers/favourite.dart index 510853037..4589b528e 100644 --- a/lib/widgets/common/action_controls/togglers/favourite.dart +++ b/lib/widgets/common/action_controls/togglers/favourite.dart @@ -1,4 +1,5 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/favourites.dart'; import 'package:aves/model/favourites.dart'; import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; diff --git a/lib/widgets/common/action_controls/togglers/mute.dart b/lib/widgets/common/action_controls/togglers/mute.dart index 7eec6faac..fa9a2a693 100644 --- a/lib/widgets/common/action_controls/togglers/mute.dart +++ b/lib/widgets/common/action_controls/togglers/mute.dart @@ -4,7 +4,7 @@ import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/common/basic/popup/menu_row.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/buttons/captioned_button.dart'; -import 'package:aves/widgets/viewer/video/controller.dart'; +import 'package:aves_video/aves_video.dart'; import 'package:flutter/material.dart'; class MuteToggler extends StatelessWidget { diff --git a/lib/widgets/common/action_controls/togglers/play.dart b/lib/widgets/common/action_controls/togglers/play.dart index e2f5bbe6b..7ca45ffe3 100644 --- a/lib/widgets/common/action_controls/togglers/play.dart +++ b/lib/widgets/common/action_controls/togglers/play.dart @@ -5,7 +5,7 @@ import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/common/basic/popup/menu_row.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/buttons/captioned_button.dart'; -import 'package:aves/widgets/viewer/video/controller.dart'; +import 'package:aves_video/aves_video.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; diff --git a/lib/widgets/common/action_mixins/entry_editor.dart b/lib/widgets/common/action_mixins/entry_editor.dart index 40471bbde..ff1ae2d70 100644 --- a/lib/widgets/common/action_mixins/entry_editor.dart +++ b/lib/widgets/common/action_mixins/entry_editor.dart @@ -1,5 +1,6 @@ -import 'package:aves/model/entry.dart'; -import 'package:aves/model/entry_metadata_edition.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/metadata_edition.dart'; +import 'package:aves/model/entry/extensions/multipage.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/placeholder.dart'; import 'package:aves/model/filters/tag.dart'; diff --git a/lib/widgets/common/action_mixins/entry_storage.dart b/lib/widgets/common/action_mixins/entry_storage.dart index e3c1e5a12..768a2d085 100644 --- a/lib/widgets/common/action_mixins/entry_storage.dart +++ b/lib/widgets/common/action_mixins/entry_storage.dart @@ -3,7 +3,9 @@ import 'dart:io'; import 'package:aves/app_mode.dart'; import 'package:aves/model/actions/move_type.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/multipage.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/trash.dart'; import 'package:aves/model/highlight.dart'; diff --git a/lib/widgets/common/action_mixins/permission_aware.dart b/lib/widgets/common/action_mixins/permission_aware.dart index db158802b..5d8fa13f9 100644 --- a/lib/widgets/common/action_mixins/permission_aware.dart +++ b/lib/widgets/common/action_mixins/permission_aware.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; diff --git a/lib/widgets/common/action_mixins/size_aware.dart b/lib/widgets/common/action_mixins/size_aware.dart index cfcb39df0..d1a93321b 100644 --- a/lib/widgets/common/action_mixins/size_aware.dart +++ b/lib/widgets/common/action_mixins/size_aware.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'dart:math'; import 'package:aves/model/actions/move_type.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/utils/collection_utils.dart'; diff --git a/lib/widgets/common/grid/theme.dart b/lib/widgets/common/grid/theme.dart index 2e7bdd1ef..72aabace6 100644 --- a/lib/widgets/common/grid/theme.dart +++ b/lib/widgets/common/grid/theme.dart @@ -1,6 +1,9 @@ import 'dart:math'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/favourites.dart'; +import 'package:aves/model/entry/extensions/multipage.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/widgets/common/identity/aves_icons.dart'; diff --git a/lib/widgets/common/identity/aves_icons.dart b/lib/widgets/common/identity/aves_icons.dart index 20a3443d9..0428f0ca0 100644 --- a/lib/widgets/common/identity/aves_icons.dart +++ b/lib/widgets/common/identity/aves_icons.dart @@ -1,6 +1,8 @@ import 'package:aves/image_providers/app_icon_image_provider.dart'; import 'package:aves/model/covers.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/multipage.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/vaults/vaults.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/utils/android_file_utils.dart'; diff --git a/lib/widgets/common/map/geo_map.dart b/lib/widgets/common/map/geo_map.dart index 0643b2ce4..0a2e8691e 100644 --- a/lib/widgets/common/map/geo_map.dart +++ b/lib/widgets/common/map/geo_map.dart @@ -2,14 +2,16 @@ import 'dart:async'; import 'dart:math'; import 'dart:ui'; -import 'package:aves/model/entry.dart'; -import 'package:aves/model/entry_images.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/images.dart'; +import 'package:aves/model/entry/extensions/location.dart'; +import 'package:aves/model/entry/sort.dart'; +import 'package:aves/model/settings/enums/l10n.dart'; import 'package:aves/model/settings/enums/map_style.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/theme/icons.dart'; -import 'package:aves/utils/change_notifier.dart'; import 'package:aves/utils/constants.dart'; import 'package:aves/utils/math_utils.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; @@ -21,6 +23,7 @@ import 'package:aves/widgets/common/map/leaflet/map.dart'; import 'package:aves/widgets/common/thumbnail/image.dart'; import 'package:aves/widgets/dialogs/aves_selection_dialog.dart'; import 'package:aves_map/aves_map.dart'; +import 'package:aves_utils/aves_utils.dart'; import 'package:collection/collection.dart'; import 'package:fluster/fluster.dart'; import 'package:flutter/material.dart'; @@ -343,7 +346,7 @@ class _GeoMapState extends State { ZoomedBounds? _initBoundsForEntries({required List entries, int? recentCount}) { if (recentCount != null) { - entries = List.of(entries)..sort(AvesEntry.compareByDate); + entries = List.of(entries)..sort(AvesEntrySort.compareByDate); entries = entries.take(recentCount).toList(); } diff --git a/lib/widgets/common/map/map_action_delegate.dart b/lib/widgets/common/map/map_action_delegate.dart index 06a984005..a41c840d5 100644 --- a/lib/widgets/common/map/map_action_delegate.dart +++ b/lib/widgets/common/map/map_action_delegate.dart @@ -1,5 +1,5 @@ import 'package:aves/model/actions/map_actions.dart'; -import 'package:aves/model/settings/enums/map_style.dart'; +import 'package:aves/model/settings/enums/l10n.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; diff --git a/lib/widgets/common/thumbnail/decorated.dart b/lib/widgets/common/thumbnail/decorated.dart index 93ccf66ac..b4176b264 100644 --- a/lib/widgets/common/thumbnail/decorated.dart +++ b/lib/widgets/common/thumbnail/decorated.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/widgets/common/fx/borders.dart'; import 'package:aves/widgets/common/grid/overlay.dart'; import 'package:aves/widgets/common/thumbnail/image.dart'; diff --git a/lib/widgets/common/thumbnail/error.dart b/lib/widgets/common/thumbnail/error.dart index bb6fa6000..0ad1eb85f 100644 --- a/lib/widgets/common/thumbnail/error.dart +++ b/lib/widgets/common/thumbnail/error.dart @@ -1,7 +1,7 @@ import 'dart:io'; import 'dart:math'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/utils/mime_utils.dart'; import 'package:flutter/foundation.dart'; diff --git a/lib/widgets/common/thumbnail/image.dart b/lib/widgets/common/thumbnail/image.dart index 9d7dd5c55..a639f475f 100644 --- a/lib/widgets/common/thumbnail/image.dart +++ b/lib/widgets/common/thumbnail/image.dart @@ -2,8 +2,9 @@ import 'dart:math'; import 'dart:ui'; import 'package:aves/image_providers/thumbnail_provider.dart'; -import 'package:aves/model/entry.dart'; -import 'package:aves/model/entry_images.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/images.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/settings/enums/accessibility_animations.dart'; import 'package:aves/model/settings/enums/entry_background.dart'; import 'package:aves/model/settings/enums/enums.dart'; diff --git a/lib/widgets/common/thumbnail/notifications.dart b/lib/widgets/common/thumbnail/notifications.dart index b52226070..d5edca496 100644 --- a/lib/widgets/common/thumbnail/notifications.dart +++ b/lib/widgets/common/thumbnail/notifications.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:flutter/widgets.dart'; @immutable diff --git a/lib/widgets/common/thumbnail/overlay.dart b/lib/widgets/common/thumbnail/overlay.dart index ed64484d5..6326be3d4 100644 --- a/lib/widgets/common/thumbnail/overlay.dart +++ b/lib/widgets/common/thumbnail/overlay.dart @@ -1,6 +1,6 @@ import 'dart:math'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/highlight.dart'; import 'package:aves/model/selection.dart'; import 'package:aves/theme/durations.dart'; diff --git a/lib/widgets/common/thumbnail/scroller.dart b/lib/widgets/common/thumbnail/scroller.dart index 25cf3d955..ab5680dd6 100644 --- a/lib/widgets/common/thumbnail/scroller.dart +++ b/lib/widgets/common/thumbnail/scroller.dart @@ -1,6 +1,6 @@ import 'dart:math'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/widgets/common/behaviour/known_extent_scroll_physics.dart'; import 'package:aves/widgets/common/grid/theme.dart'; diff --git a/lib/widgets/debug/database.dart b/lib/widgets/debug/database.dart index dca8795fa..d5d2eb5cf 100644 --- a/lib/widgets/debug/database.dart +++ b/lib/widgets/debug/database.dart @@ -1,5 +1,5 @@ import 'package:aves/model/covers.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/favourites.dart'; import 'package:aves/model/metadata/address.dart'; import 'package:aves/model/metadata/catalog.dart'; diff --git a/lib/widgets/dialogs/add_shortcut_dialog.dart b/lib/widgets/dialogs/add_shortcut_dialog.dart index f25fc4108..f445e3df6 100644 --- a/lib/widgets/dialogs/add_shortcut_dialog.dart +++ b/lib/widgets/dialogs/add_shortcut_dialog.dart @@ -1,5 +1,5 @@ import 'package:aves/model/covers.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/filters/query.dart'; import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; diff --git a/lib/widgets/dialogs/convert_entry_dialog.dart b/lib/widgets/dialogs/convert_entry_dialog.dart index 6fe70a92e..090c1b0f4 100644 --- a/lib/widgets/dialogs/convert_entry_dialog.dart +++ b/lib/widgets/dialogs/convert_entry_dialog.dart @@ -1,4 +1,5 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/metadata/enums/enums.dart'; import 'package:aves/model/metadata/enums/length_unit.dart'; import 'package:aves/model/settings/settings.dart'; @@ -148,7 +149,7 @@ class _ConvertEntryDialogState extends State { ), ), const SizedBox(width: 8), - const Text(AvesEntry.resolutionSeparator), + const Text(ExtraAvesEntryProps.resolutionSeparator), const SizedBox(width: 8), Expanded( child: TextField( diff --git a/lib/widgets/dialogs/entry_editors/edit_date_dialog.dart b/lib/widgets/dialogs/entry_editors/edit_date_dialog.dart index ea1f51447..de368dc55 100644 --- a/lib/widgets/dialogs/entry_editors/edit_date_dialog.dart +++ b/lib/widgets/dialogs/entry_editors/edit_date_dialog.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/metadata/date_modifier.dart'; import 'package:aves/model/metadata/enums/date_edit_action.dart'; import 'package:aves/model/metadata/enums/date_field_source.dart'; diff --git a/lib/widgets/dialogs/entry_editors/edit_description_dialog.dart b/lib/widgets/dialogs/entry_editors/edit_description_dialog.dart index 4478de495..e60e49cae 100644 --- a/lib/widgets/dialogs/entry_editors/edit_description_dialog.dart +++ b/lib/widgets/dialogs/entry_editors/edit_description_dialog.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry_metadata_edition.dart'; +import 'package:aves/model/entry/extensions/metadata_edition.dart'; import 'package:aves/widgets/common/basic/labeled_checkbox.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/providers/media_query_data_provider.dart'; diff --git a/lib/widgets/dialogs/entry_editors/edit_location_dialog.dart b/lib/widgets/dialogs/entry_editors/edit_location_dialog.dart index 1b81adf64..f5e6a8e34 100644 --- a/lib/widgets/dialogs/entry_editors/edit_location_dialog.dart +++ b/lib/widgets/dialogs/entry_editors/edit_location_dialog.dart @@ -1,5 +1,6 @@ -import 'package:aves/model/entry.dart'; -import 'package:aves/model/entry_metadata_edition.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/location.dart'; +import 'package:aves/model/entry/extensions/metadata_edition.dart'; import 'package:aves/model/metadata/enums/enums.dart'; import 'package:aves/model/metadata/enums/location_edit_action.dart'; import 'package:aves/model/settings/enums/coordinate_format.dart'; diff --git a/lib/widgets/dialogs/entry_editors/edit_rating_dialog.dart b/lib/widgets/dialogs/entry_editors/edit_rating_dialog.dart index e1dab151a..c118cbb8f 100644 --- a/lib/widgets/dialogs/entry_editors/edit_rating_dialog.dart +++ b/lib/widgets/dialogs/entry_editors/edit_rating_dialog.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/providers/media_query_data_provider.dart'; diff --git a/lib/widgets/dialogs/entry_editors/rename_entry_dialog.dart b/lib/widgets/dialogs/entry_editors/rename_entry_dialog.dart index 262b60474..ff1a175a8 100644 --- a/lib/widgets/dialogs/entry_editors/rename_entry_dialog.dart +++ b/lib/widgets/dialogs/entry_editors/rename_entry_dialog.dart @@ -1,6 +1,6 @@ import 'dart:io'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/utils/constants.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; diff --git a/lib/widgets/dialogs/entry_editors/rename_entry_set_page.dart b/lib/widgets/dialogs/entry_editors/rename_entry_set_page.dart index 5980f0993..55d3f68d7 100644 --- a/lib/widgets/dialogs/entry_editors/rename_entry_set_page.dart +++ b/lib/widgets/dialogs/entry_editors/rename_entry_set_page.dart @@ -1,6 +1,6 @@ import 'dart:math'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/naming_pattern.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/durations.dart'; diff --git a/lib/widgets/dialogs/entry_editors/tag_editor_page.dart b/lib/widgets/dialogs/entry_editors/tag_editor_page.dart index fe4a06c52..87d5e66a1 100644 --- a/lib/widgets/dialogs/entry_editors/tag_editor_page.dart +++ b/lib/widgets/dialogs/entry_editors/tag_editor_page.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/placeholder.dart'; import 'package:aves/model/filters/tag.dart'; diff --git a/lib/widgets/dialogs/filter_editors/cover_selection_dialog.dart b/lib/widgets/dialogs/filter_editors/cover_selection_dialog.dart index afbeb28f2..30aee7b04 100644 --- a/lib/widgets/dialogs/filter_editors/cover_selection_dialog.dart +++ b/lib/widgets/dialogs/filter_editors/cover_selection_dialog.dart @@ -1,7 +1,7 @@ import 'dart:math'; import 'package:aves/image_providers/app_icon_image_provider.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/settings/enums/enums.dart'; diff --git a/lib/widgets/dialogs/item_picker.dart b/lib/widgets/dialogs/item_picker.dart index 2c5c2631d..1b908c1f0 100644 --- a/lib/widgets/dialogs/item_picker.dart +++ b/lib/widgets/dialogs/item_picker.dart @@ -1,6 +1,6 @@ import 'dart:math'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/fx/borders.dart'; diff --git a/lib/widgets/dialogs/pick_dialogs/item_pick_page.dart b/lib/widgets/dialogs/pick_dialogs/item_pick_page.dart index 72ad90cc0..4631a2b94 100644 --- a/lib/widgets/dialogs/pick_dialogs/item_pick_page.dart +++ b/lib/widgets/dialogs/pick_dialogs/item_pick_page.dart @@ -1,5 +1,5 @@ import 'package:aves/app_mode.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/filters/query.dart'; import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/widgets/collection/collection_grid.dart'; diff --git a/lib/widgets/dialogs/video_stream_selection_dialog.dart b/lib/widgets/dialogs/video_stream_selection_dialog.dart index eb0e65350..fe14da528 100644 --- a/lib/widgets/dialogs/video_stream_selection_dialog.dart +++ b/lib/widgets/dialogs/video_stream_selection_dialog.dart @@ -1,10 +1,10 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/ref/languages.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/theme/themes.dart'; import 'package:aves/widgets/common/basic/text_dropdown_button.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; -import 'package:aves/widgets/viewer/video/controller.dart'; +import 'package:aves_video/aves_video.dart'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; @@ -13,7 +13,7 @@ import 'aves_dialog.dart'; class VideoStreamSelectionDialog extends StatefulWidget { static const routeName = '/dialog/select_video_stream'; - final Map streams; + final Map streams; const VideoStreamSelectionDialog({ super.key, @@ -25,23 +25,23 @@ class VideoStreamSelectionDialog extends StatefulWidget { } class _VideoStreamSelectionDialogState extends State { - late List _videoStreams, _audioStreams, _textStreams; - StreamSummary? _currentVideo, _currentAudio, _currentText; + late List _videoStreams, _audioStreams, _textStreams; + MediaStreamSummary? _currentVideo, _currentAudio, _currentText; @override void initState() { super.initState(); - final byType = groupBy(widget.streams.keys, (v) => v.type); + final byType = groupBy(widget.streams.keys, (v) => v.type); // check width/height to exclude image streams (that are included among video streams) - _videoStreams = (byType[StreamType.video] ?? []).where((v) => v.width != null && v.height != null).toList(); - _audioStreams = (byType[StreamType.audio] ?? []); - _textStreams = [null, ...byType[StreamType.text] ?? []]; + _videoStreams = (byType[MediaStreamType.video] ?? []).where((v) => v.width != null && v.height != null).toList(); + _audioStreams = (byType[MediaStreamType.audio] ?? []); + _textStreams = [null, ...byType[MediaStreamType.text] ?? []]; final streamEntries = widget.streams.entries; - _currentVideo = streamEntries.firstWhereOrNull((kv) => kv.key.type == StreamType.video && kv.value)?.key; - _currentAudio = streamEntries.firstWhereOrNull((kv) => kv.key.type == StreamType.audio && kv.value)?.key; - _currentText = streamEntries.firstWhereOrNull((kv) => kv.key.type == StreamType.text && kv.value)?.key; + _currentVideo = streamEntries.firstWhereOrNull((kv) => kv.key.type == MediaStreamType.video && kv.value)?.key; + _currentAudio = streamEntries.firstWhereOrNull((kv) => kv.key.type == MediaStreamType.audio && kv.value)?.key; + _currentText = streamEntries.firstWhereOrNull((kv) => kv.key.type == MediaStreamType.text && kv.value)?.key; } @override @@ -97,7 +97,7 @@ class _VideoStreamSelectionDialogState extends State return language?.native ?? value; } - String _commonStreamName(StreamSummary? stream) { + String _commonStreamName(MediaStreamSummary? stream) { if (stream == null) return context.l10n.videoStreamSelectionDialogOff; final title = stream.title; final language = stream.language; @@ -111,13 +111,13 @@ class _VideoStreamSelectionDialogState extends State } } - String _streamName(StreamSummary? stream) { + String _streamName(MediaStreamSummary? stream) { final common = _commonStreamName(stream); - if (stream != null && stream.type == StreamType.video) { + if (stream != null && stream.type == MediaStreamType.video) { final w = stream.width; final h = stream.height; if (w != null && h != null) { - return '$common • $w${AvesEntry.resolutionSeparator}$h'; + return '$common • $w${ExtraAvesEntryProps.resolutionSeparator}$h'; } } return common; @@ -126,9 +126,9 @@ class _VideoStreamSelectionDialogState extends State List _buildSection({ required IconData icon, required String title, - required List streams, - required StreamSummary? current, - required ValueSetter setter, + required List streams, + required MediaStreamSummary? current, + required ValueSetter setter, }) { return [ Padding( @@ -143,7 +143,7 @@ class _VideoStreamSelectionDialogState extends State ), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), - child: TextDropdownButton( + child: TextDropdownButton( values: streams.whereNotNull().toList(), valueText: _streamName, value: current, @@ -156,8 +156,8 @@ class _VideoStreamSelectionDialogState extends State } void _submit(BuildContext context) => Navigator.maybeOf(context)?.pop({ - StreamType.video: _currentVideo, - StreamType.audio: _currentAudio, - StreamType.text: _currentText, + MediaStreamType.video: _currentVideo, + MediaStreamType.audio: _currentAudio, + MediaStreamType.text: _currentText, }); } diff --git a/lib/widgets/filter_grids/albums_page.dart b/lib/widgets/filter_grids/albums_page.dart index b39500edd..5cd758ffb 100644 --- a/lib/widgets/filter_grids/albums_page.dart +++ b/lib/widgets/filter_grids/albums_page.dart @@ -1,4 +1,5 @@ import 'package:aves/model/covers.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/settings/settings.dart'; diff --git a/lib/widgets/filter_grids/common/action_delegates/album_set.dart b/lib/widgets/filter_grids/common/action_delegates/album_set.dart index 1565080f7..207802309 100644 --- a/lib/widgets/filter_grids/common/action_delegates/album_set.dart +++ b/lib/widgets/filter_grids/common/action_delegates/album_set.dart @@ -4,7 +4,7 @@ import 'package:aves/app_mode.dart'; import 'package:aves/model/actions/chip_set_actions.dart'; import 'package:aves/model/actions/move_type.dart'; import 'package:aves/model/device.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/highlight.dart'; diff --git a/lib/widgets/filter_grids/common/action_delegates/chip_set.dart b/lib/widgets/filter_grids/common/action_delegates/chip_set.dart index 50e7dde04..a9b9c20a2 100644 --- a/lib/widgets/filter_grids/common/action_delegates/chip_set.dart +++ b/lib/widgets/filter_grids/common/action_delegates/chip_set.dart @@ -1,7 +1,7 @@ import 'package:aves/app_mode.dart'; import 'package:aves/model/actions/chip_set_actions.dart'; import 'package:aves/model/covers.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/query.dart'; diff --git a/lib/widgets/filter_grids/common/filter_grid_page.dart b/lib/widgets/filter_grids/common/filter_grid_page.dart index 38d773180..26d2c0b65 100644 --- a/lib/widgets/filter_grids/common/filter_grid_page.dart +++ b/lib/widgets/filter_grids/common/filter_grid_page.dart @@ -12,8 +12,8 @@ import 'package:aves/model/source/enums/enums.dart'; import 'package:aves/model/vaults/vaults.dart'; import 'package:aves/theme/colors.dart'; import 'package:aves/theme/durations.dart'; -import 'package:aves/widgets/common/basic/draggable_scrollbar/scrollbar.dart'; import 'package:aves/widgets/common/basic/draggable_scrollbar/notifications.dart'; +import 'package:aves/widgets/common/basic/draggable_scrollbar/scrollbar.dart'; import 'package:aves/widgets/common/basic/insets.dart'; import 'package:aves/widgets/common/basic/scaffold.dart'; import 'package:aves/widgets/common/behaviour/pop/double_back.dart'; diff --git a/lib/widgets/filter_grids/common/list_details.dart b/lib/widgets/filter_grids/common/list_details.dart index 84c11a2e3..d7cacb7d7 100644 --- a/lib/widgets/filter_grids/common/list_details.dart +++ b/lib/widgets/filter_grids/common/list_details.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/source/collection_source.dart'; diff --git a/lib/widgets/home_page.dart b/lib/widgets/home_page.dart index e03454aaf..52ba45a4e 100644 --- a/lib/widgets/home_page.dart +++ b/lib/widgets/home_page.dart @@ -1,7 +1,8 @@ import 'dart:async'; import 'package:aves/app_mode.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/catalog.dart'; import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/settings/enums/enums.dart'; @@ -227,11 +228,12 @@ class _HomePageState extends State { canAnalyze: false, ); } + } else { + await _initViewerEssentials(); } break; case AppMode.setWallpaper: - // for video playback storage - await metadataDb.init(); + await _initViewerEssentials(); break; case AppMode.pickMediaInternal: case AppMode.pickFilterInternal: @@ -247,6 +249,11 @@ class _HomePageState extends State { )); } + Future _initViewerEssentials() async { + // for video playback storage + await metadataDb.init(); + } + bool _isViewerSourceable(AvesEntry? viewerEntry) { return viewerEntry != null && viewerEntry.directory != null && !settings.hiddenFilters.any((filter) => filter.test(viewerEntry)); } diff --git a/lib/widgets/home_widget.dart b/lib/widgets/home_widget.dart index 95256f867..69f8c61b1 100644 --- a/lib/widgets/home_widget.dart +++ b/lib/widgets/home_widget.dart @@ -2,8 +2,8 @@ import 'dart:async'; import 'dart:typed_data'; import 'dart:ui' as ui; -import 'package:aves/model/entry.dart'; -import 'package:aves/model/entry_images.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/images.dart'; import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/model/settings/enums/widget_shape.dart'; import 'package:aves/utils/constants.dart'; diff --git a/lib/widgets/map/map_info_row.dart b/lib/widgets/map/map_info_row.dart index 1bef21f85..67a600cbf 100644 --- a/lib/widgets/map/map_info_row.dart +++ b/lib/widgets/map/map_info_row.dart @@ -1,4 +1,5 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/location.dart'; import 'package:aves/model/settings/enums/coordinate_format.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/services/common/services.dart'; diff --git a/lib/widgets/map/map_page.dart b/lib/widgets/map/map_page.dart index b3629257b..5cf00c41b 100644 --- a/lib/widgets/map/map_page.dart +++ b/lib/widgets/map/map_page.dart @@ -3,7 +3,8 @@ import 'dart:async'; import 'package:aves/app_mode.dart'; import 'package:aves/model/actions/map_actions.dart'; import 'package:aves/model/actions/map_cluster_actions.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/location.dart'; import 'package:aves/model/filters/coordinate.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/geotiff.dart'; @@ -31,8 +32,8 @@ import 'package:aves/widgets/common/providers/map_theme_provider.dart'; import 'package:aves/widgets/common/thumbnail/scroller.dart'; import 'package:aves/widgets/filter_grids/common/action_delegates/chip.dart'; import 'package:aves/widgets/map/map_info_row.dart'; -import 'package:aves/widgets/viewer/entry_viewer_page.dart'; import 'package:aves/widgets/viewer/controls/notifications.dart'; +import 'package:aves/widgets/viewer/entry_viewer_page.dart'; import 'package:aves_map/aves_map.dart'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; diff --git a/lib/widgets/search/search_delegate.dart b/lib/widgets/search/search_delegate.dart index 7dba129a5..d982aeeaf 100644 --- a/lib/widgets/search/search_delegate.dart +++ b/lib/widgets/search/search_delegate.dart @@ -272,14 +272,20 @@ class CollectionSearchDelegate extends AvesSearchDelegate with FeedbackMixin, Va ); } + var _selectingFromQuery = false; + @override Widget buildResults(BuildContext context) { - WidgetsBinding.instance.addPostFrameCallback((_) { - // `buildResults` is called in the build phase, - // so we post the call that will filter the collection - // and possibly trigger a rebuild here - _select(context, _buildQueryFilter(true)); - }); + // guard against multiple build calls + if (!_selectingFromQuery) { + _selectingFromQuery = true; + WidgetsBinding.instance.addPostFrameCallback((_) { + // `buildResults` is called in the build phase, + // so we post the call that will filter the collection + // and possibly trigger a rebuild here + _select(context, _buildQueryFilter(true)); + }); + } return const SizedBox(); } diff --git a/lib/widgets/settings/accessibility/accessibility.dart b/lib/widgets/settings/accessibility/accessibility.dart index 484414f3a..17d963d7f 100644 --- a/lib/widgets/settings/accessibility/accessibility.dart +++ b/lib/widgets/settings/accessibility/accessibility.dart @@ -1,7 +1,7 @@ import 'dart:async'; -import 'package:aves/model/settings/enums/accessibility_animations.dart'; import 'package:aves/model/settings/enums/enums.dart'; +import 'package:aves/model/settings/enums/l10n.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; diff --git a/lib/widgets/settings/accessibility/time_to_take_action.dart b/lib/widgets/settings/accessibility/time_to_take_action.dart index 950eb76ec..137867c57 100644 --- a/lib/widgets/settings/accessibility/time_to_take_action.dart +++ b/lib/widgets/settings/accessibility/time_to_take_action.dart @@ -1,5 +1,5 @@ -import 'package:aves/model/settings/enums/accessibility_timeout.dart'; import 'package:aves/model/settings/enums/enums.dart'; +import 'package:aves/model/settings/enums/l10n.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/services/accessibility_service.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; diff --git a/lib/widgets/settings/common/quick_actions/editor_page.dart b/lib/widgets/settings/common/quick_actions/editor_page.dart index 4736dd4d4..120e2e55d 100644 --- a/lib/widgets/settings/common/quick_actions/editor_page.dart +++ b/lib/widgets/settings/common/quick_actions/editor_page.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:aves/theme/durations.dart'; import 'package:aves/theme/icons.dart'; -import 'package:aves/utils/change_notifier.dart'; +import 'package:aves_utils/aves_utils.dart'; import 'package:aves/utils/constants.dart'; import 'package:aves/widgets/common/basic/scaffold.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; diff --git a/lib/widgets/settings/display/display.dart b/lib/widgets/settings/display/display.dart index 959249993..e5f9924dc 100644 --- a/lib/widgets/settings/display/display.dart +++ b/lib/widgets/settings/display/display.dart @@ -1,9 +1,8 @@ import 'dart:async'; import 'package:aves/model/device.dart'; -import 'package:aves/model/settings/enums/display_refresh_rate_mode.dart'; import 'package:aves/model/settings/enums/enums.dart'; -import 'package:aves/model/settings/enums/theme_brightness.dart'; +import 'package:aves/model/settings/enums/l10n.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; diff --git a/lib/widgets/settings/home_widget_settings_page.dart b/lib/widgets/settings/home_widget_settings_page.dart index a0c1341c6..8be4d42c8 100644 --- a/lib/widgets/settings/home_widget_settings_page.dart +++ b/lib/widgets/settings/home_widget_settings_page.dart @@ -1,7 +1,6 @@ import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/settings/enums/enums.dart'; -import 'package:aves/model/settings/enums/widget_displayed_item.dart'; -import 'package:aves/model/settings/enums/widget_open_action.dart'; +import 'package:aves/model/settings/enums/l10n.dart'; import 'package:aves/model/settings/enums/widget_shape.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/services/widget_service.dart'; diff --git a/lib/widgets/settings/language/language.dart b/lib/widgets/settings/language/language.dart index ff33641f5..472c2b990 100644 --- a/lib/widgets/settings/language/language.dart +++ b/lib/widgets/settings/language/language.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:aves/model/settings/enums/coordinate_format.dart'; import 'package:aves/model/settings/enums/enums.dart'; -import 'package:aves/model/settings/enums/unit_system.dart'; +import 'package:aves/model/settings/enums/l10n.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; diff --git a/lib/widgets/settings/navigation/navigation.dart b/lib/widgets/settings/navigation/navigation.dart index 2c738b136..93ea8a347 100644 --- a/lib/widgets/settings/navigation/navigation.dart +++ b/lib/widgets/settings/navigation/navigation.dart @@ -1,8 +1,7 @@ import 'dart:async'; import 'package:aves/model/settings/enums/enums.dart'; -import 'package:aves/model/settings/enums/home_page.dart'; -import 'package:aves/model/settings/enums/screen_on.dart'; +import 'package:aves/model/settings/enums/l10n.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; diff --git a/lib/widgets/settings/screen_saver_settings_page.dart b/lib/widgets/settings/screen_saver_settings_page.dart index 8d7902244..aed3abf0f 100644 --- a/lib/widgets/settings/screen_saver_settings_page.dart +++ b/lib/widgets/settings/screen_saver_settings_page.dart @@ -1,7 +1,6 @@ import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/settings/enums/enums.dart'; -import 'package:aves/model/settings/enums/slideshow_video_playback.dart'; -import 'package:aves/model/settings/enums/viewer_transition.dart'; +import 'package:aves/model/settings/enums/l10n.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/widgets/common/basic/scaffold.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; diff --git a/lib/widgets/settings/settings_page.dart b/lib/widgets/settings/settings_page.dart index cdb23c642..c917ffcde 100644 --- a/lib/widgets/settings/settings_page.dart +++ b/lib/widgets/settings/settings_page.dart @@ -97,8 +97,15 @@ class _SettingsPageState extends State with FeedbackMixin { primary: false, ), ), - const Expanded( - child: _TvRail(), + Expanded( + child: MediaQuery.removePadding( + context: context, + removeLeft: true, + removeTop: true, + removeRight: true, + removeBottom: true, + child: const _TvRail(), + ), ), ], ), diff --git a/lib/widgets/settings/thumbnails/overlay.dart b/lib/widgets/settings/thumbnails/overlay.dart index b8c47950e..aa1bef264 100644 --- a/lib/widgets/settings/thumbnails/overlay.dart +++ b/lib/widgets/settings/thumbnails/overlay.dart @@ -1,4 +1,5 @@ import 'package:aves/model/settings/enums/enums.dart'; +import 'package:aves/model/settings/enums/l10n.dart'; import 'package:aves/model/settings/enums/thumbnail_overlay_location_icon.dart'; import 'package:aves/model/settings/enums/thumbnail_overlay_tag_icon.dart'; import 'package:aves/model/settings/settings.dart'; diff --git a/lib/widgets/settings/video/controls.dart b/lib/widgets/settings/video/controls.dart index cbe4423b9..e8ee34c9c 100644 --- a/lib/widgets/settings/video/controls.dart +++ b/lib/widgets/settings/video/controls.dart @@ -1,5 +1,5 @@ import 'package:aves/model/settings/enums/enums.dart'; -import 'package:aves/model/settings/enums/video_controls.dart'; +import 'package:aves/model/settings/enums/l10n.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/widgets/common/basic/scaffold.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; diff --git a/lib/widgets/settings/video/subtitle_theme.dart b/lib/widgets/settings/video/subtitle_theme.dart index 5121d0377..6fc3fd7e2 100644 --- a/lib/widgets/settings/video/subtitle_theme.dart +++ b/lib/widgets/settings/video/subtitle_theme.dart @@ -1,5 +1,5 @@ import 'package:aves/model/settings/enums/enums.dart'; -import 'package:aves/model/settings/enums/subtitle_position.dart'; +import 'package:aves/model/settings/enums/l10n.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/widgets/common/basic/list_tiles/color.dart'; import 'package:aves/widgets/common/basic/list_tiles/slider.dart'; diff --git a/lib/widgets/settings/video/video.dart b/lib/widgets/settings/video/video.dart index c0e2e4acc..ba68e3d6b 100644 --- a/lib/widgets/settings/video/video.dart +++ b/lib/widgets/settings/video/video.dart @@ -3,9 +3,7 @@ import 'dart:async'; import 'package:aves/model/device.dart'; import 'package:aves/model/filters/mime.dart'; import 'package:aves/model/settings/enums/enums.dart'; -import 'package:aves/model/settings/enums/video_auto_play_mode.dart'; -import 'package:aves/model/settings/enums/video_background_mode.dart'; -import 'package:aves/model/settings/enums/video_loop_mode.dart'; +import 'package:aves/model/settings/enums/l10n.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; diff --git a/lib/widgets/settings/viewer/slideshow.dart b/lib/widgets/settings/viewer/slideshow.dart index b2f954c23..f84cab854 100644 --- a/lib/widgets/settings/viewer/slideshow.dart +++ b/lib/widgets/settings/viewer/slideshow.dart @@ -1,6 +1,5 @@ import 'package:aves/model/settings/enums/enums.dart'; -import 'package:aves/model/settings/enums/slideshow_video_playback.dart'; -import 'package:aves/model/settings/enums/viewer_transition.dart'; +import 'package:aves/model/settings/enums/l10n.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/widgets/common/basic/scaffold.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; diff --git a/lib/widgets/stats/date/histogram.dart b/lib/widgets/stats/date/histogram.dart index bf268a171..772c07310 100644 --- a/lib/widgets/stats/date/histogram.dart +++ b/lib/widgets/stats/date/histogram.dart @@ -1,7 +1,8 @@ import 'dart:async'; import 'dart:math'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/sort.dart'; import 'package:aves/model/filters/date.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; @@ -50,7 +51,7 @@ class _HistogramState extends State with AutomaticKeepAliveClientMixi void initState() { super.initState(); - final entriesByDateDescending = List.of(widget.entries)..sort(AvesEntry.compareByDate); + final entriesByDateDescending = List.of(widget.entries)..sort(AvesEntrySort.compareByDate); var lastDate = entriesByDateDescending.firstWhereOrNull((entry) => entry.bestDate != null)?.bestDate; var firstDate = entriesByDateDescending.lastWhereOrNull((entry) => entry.bestDate != null)?.bestDate; diff --git a/lib/widgets/stats/stats_page.dart b/lib/widgets/stats/stats_page.dart index ac28daf8d..1a6ff3ff6 100644 --- a/lib/widgets/stats/stats_page.dart +++ b/lib/widgets/stats/stats_page.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/location.dart'; diff --git a/lib/widgets/viewer/action/entry_action_delegate.dart b/lib/widgets/viewer/action/entry_action_delegate.dart index 094035435..ee5ed6e72 100644 --- a/lib/widgets/viewer/action/entry_action_delegate.dart +++ b/lib/widgets/viewer/action/entry_action_delegate.dart @@ -6,8 +6,12 @@ import 'package:aves/model/actions/entry_actions.dart'; import 'package:aves/model/actions/move_type.dart'; import 'package:aves/model/actions/share_actions.dart'; import 'package:aves/model/device.dart'; -import 'package:aves/model/entry.dart'; -import 'package:aves/model/entry_metadata_edition.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/favourites.dart'; +import 'package:aves/model/entry/extensions/location.dart'; +import 'package:aves/model/entry/extensions/metadata_edition.dart'; +import 'package:aves/model/entry/extensions/multipage.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/model/settings/settings.dart'; @@ -243,7 +247,11 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix case EntryAction.openVideo: final controller = context.read().getController(targetEntry); if (controller != null) { - VideoActionNotification(controller: controller, action: action).dispatch(context); + VideoActionNotification( + controller: controller, + entry: targetEntry, + action: action, + ).dispatch(context); } break; case EntryAction.edit: diff --git a/lib/widgets/viewer/action/entry_info_action_delegate.dart b/lib/widgets/viewer/action/entry_info_action_delegate.dart index 469252264..62eeffbd0 100644 --- a/lib/widgets/viewer/action/entry_info_action_delegate.dart +++ b/lib/widgets/viewer/action/entry_info_action_delegate.dart @@ -4,9 +4,11 @@ import 'dart:convert'; import 'package:aves/app_mode.dart'; import 'package:aves/model/actions/entry_actions.dart'; import 'package:aves/model/actions/events.dart'; -import 'package:aves/model/entry.dart'; -import 'package:aves/model/entry_info.dart'; -import 'package:aves/model/entry_metadata_edition.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/info.dart'; +import 'package:aves/model/entry/extensions/metadata_edition.dart'; +import 'package:aves/model/entry/extensions/multipage.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/geotiff.dart'; import 'package:aves/model/settings/settings.dart'; diff --git a/lib/widgets/viewer/action/printer.dart b/lib/widgets/viewer/action/printer.dart index 27f8a80ac..d2969c953 100644 --- a/lib/widgets/viewer/action/printer.dart +++ b/lib/widgets/viewer/action/printer.dart @@ -1,8 +1,10 @@ import 'dart:async'; import 'dart:convert'; -import 'package:aves/model/entry.dart'; -import 'package:aves/model/entry_images.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/images.dart'; +import 'package:aves/model/entry/extensions/multipage.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/widgets/common/action_mixins/feedback.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; diff --git a/lib/widgets/viewer/action/single_entry_editor.dart b/lib/widgets/viewer/action/single_entry_editor.dart index 3bdf65348..bee999ece 100644 --- a/lib/widgets/viewer/action/single_entry_editor.dart +++ b/lib/widgets/viewer/action/single_entry_editor.dart @@ -1,7 +1,9 @@ import 'dart:async'; import 'package:aves/app_mode.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/catalog.dart'; +import 'package:aves/model/entry/extensions/location.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/services/common/services.dart'; diff --git a/lib/widgets/viewer/action/video_action_delegate.dart b/lib/widgets/viewer/action/video_action_delegate.dart index e9562998a..df67b5a91 100644 --- a/lib/widgets/viewer/action/video_action_delegate.dart +++ b/lib/widgets/viewer/action/video_action_delegate.dart @@ -1,6 +1,9 @@ import 'dart:async'; import 'package:aves/model/actions/entry_actions.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/location.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/filters/album.dart'; import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/services/common/services.dart'; @@ -18,7 +21,7 @@ import 'package:aves/widgets/dialogs/video_speed_dialog.dart'; import 'package:aves/widgets/dialogs/video_stream_selection_dialog.dart'; import 'package:aves/widgets/settings/video/video_settings_page.dart'; import 'package:aves/widgets/viewer/controls/notifications.dart'; -import 'package:aves/widgets/viewer/video/controller.dart'; +import 'package:aves_video/aves_video.dart'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -35,14 +38,14 @@ class VideoActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix stopOverlayHidingTimer(); } - Future onActionSelected(BuildContext context, AvesVideoController controller, EntryAction action) async { + Future onActionSelected(BuildContext context, AvesEntry entry, AvesVideoController controller, EntryAction action) async { // make sure overlay is not disappearing when selecting an action stopOverlayHidingTimer(); const ToggleOverlayNotification(visible: true).dispatch(context); switch (action) { case EntryAction.videoCaptureFrame: - await _captureFrame(context, controller); + await _captureFrame(context, entry, controller); break; case EntryAction.videoToggleMute: await controller.mute(!controller.isMuted); @@ -66,7 +69,6 @@ class VideoActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix await controller.seekTo(controller.currentPosition + 10000); break; case EntryAction.openVideo: - final entry = controller.entry; await androidAppService.open(entry.uri, entry.mimeTypeAnySubtype, forceChooser: false).then((success) { if (!success) showNoMatchingAppDialog(context); }); @@ -76,7 +78,7 @@ class VideoActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix } } - Future _captureFrame(BuildContext context, AvesVideoController controller) async { + Future _captureFrame(BuildContext context, AvesEntry entry, AvesVideoController controller) async { final positionMillis = controller.currentPosition; final bytes = await controller.captureFrame(); @@ -85,7 +87,6 @@ class VideoActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix if (!await checkFreeSpace(context, bytes.length, destinationAlbum)) return; - final entry = controller.entry; final rotationDegrees = entry.rotationDegrees; final dateTimeMillis = entry.catalogMetadata?.dateMillis; final latLng = entry.latLng; @@ -143,10 +144,10 @@ class VideoActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix Future _showStreamSelectionDialog(BuildContext context, AvesVideoController controller) async { final streams = controller.streams; - final currentSelectedStreams = await Future.wait(StreamType.values.map(controller.getSelectedStream)); + final currentSelectedStreams = await Future.wait(MediaStreamType.values.map(controller.getSelectedStream)); final currentSelectedIndices = currentSelectedStreams.whereNotNull().map((v) => v.index).toSet(); - final userSelectedStreams = await showDialog>( + final userSelectedStreams = await showDialog>( context: context, builder: (context) => VideoStreamSelectionDialog( streams: Map.fromEntries(streams.map((stream) => MapEntry(stream, currentSelectedIndices.contains(stream.index)))), @@ -155,7 +156,7 @@ class VideoActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix ); if (userSelectedStreams == null || userSelectedStreams.isEmpty) return; - await Future.forEach>( + await Future.forEach>( userSelectedStreams.entries, (kv) => controller.selectStream(kv.key, kv.value), ); diff --git a/lib/widgets/viewer/controls/controller.dart b/lib/widgets/viewer/controls/controller.dart index 3ee3f2a6e..5808a11ee 100644 --- a/lib/widgets/viewer/controls/controller.dart +++ b/lib/widgets/viewer/controls/controller.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'dart:math'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/widgets/viewer/controls/events.dart'; diff --git a/lib/widgets/viewer/controls/notifications.dart b/lib/widgets/viewer/controls/notifications.dart index 74850f6dd..5cb76faa7 100644 --- a/lib/widgets/viewer/controls/notifications.dart +++ b/lib/widgets/viewer/controls/notifications.dart @@ -1,8 +1,8 @@ import 'package:aves/model/actions/entry_actions.dart'; import 'package:aves/model/actions/move_type.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/filters/filters.dart'; -import 'package:aves/widgets/viewer/video/controller.dart'; +import 'package:aves_video/aves_video.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/widgets.dart'; @@ -62,10 +62,12 @@ class TvShowMoreInfoNotification extends Notification {} @immutable class VideoActionNotification extends Notification { final AvesVideoController controller; + final AvesEntry entry; final EntryAction action; const VideoActionNotification({ required this.controller, + required this.entry, required this.action, }); } diff --git a/lib/widgets/viewer/debug/db.dart b/lib/widgets/viewer/debug/db.dart index a7a039e8e..fded8c92d 100644 --- a/lib/widgets/viewer/debug/db.dart +++ b/lib/widgets/viewer/debug/db.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/metadata/address.dart'; import 'package:aves/model/metadata/catalog.dart'; import 'package:aves/model/metadata/trash.dart'; diff --git a/lib/widgets/viewer/debug/debug_page.dart b/lib/widgets/viewer/debug/debug_page.dart index 28b22af83..a45b213ed 100644 --- a/lib/widgets/viewer/debug/debug_page.dart +++ b/lib/widgets/viewer/debug/debug_page.dart @@ -1,6 +1,10 @@ import 'package:aves/app_mode.dart'; -import 'package:aves/model/entry.dart'; -import 'package:aves/model/entry_images.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/favourites.dart'; +import 'package:aves/model/entry/extensions/location.dart'; +import 'package:aves/model/entry/extensions/images.dart'; +import 'package:aves/model/entry/extensions/multipage.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/viewer/debug/db.dart'; import 'package:aves/widgets/viewer/debug/metadata.dart'; diff --git a/lib/widgets/viewer/debug/metadata.dart b/lib/widgets/viewer/debug/metadata.dart index 0d6e47327..99e403ccc 100644 --- a/lib/widgets/viewer/debug/metadata.dart +++ b/lib/widgets/viewer/debug/metadata.dart @@ -1,7 +1,7 @@ import 'dart:collection'; import 'dart:typed_data'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/ref/mime_types.dart'; import 'package:aves/services/android_debug_service.dart'; import 'package:aves/utils/constants.dart'; diff --git a/lib/widgets/viewer/entry_horizontal_pager.dart b/lib/widgets/viewer/entry_horizontal_pager.dart index 82bad368e..c491edad4 100644 --- a/lib/widgets/viewer/entry_horizontal_pager.dart +++ b/lib/widgets/viewer/entry_horizontal_pager.dart @@ -1,4 +1,6 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/multipage.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/settings/enums/accessibility_animations.dart'; import 'package:aves/model/settings/enums/viewer_transition.dart'; import 'package:aves/model/settings/settings.dart'; diff --git a/lib/widgets/viewer/entry_vertical_pager.dart b/lib/widgets/viewer/entry_vertical_pager.dart index 3f7c7a0d2..3446ca8b4 100644 --- a/lib/widgets/viewer/entry_vertical_pager.dart +++ b/lib/widgets/viewer/entry_vertical_pager.dart @@ -4,7 +4,10 @@ import 'dart:ui'; import 'package:aves/app_mode.dart'; import 'package:aves/model/actions/entry_actions.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/catalog.dart'; +import 'package:aves/model/entry/extensions/location.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/theme/durations.dart'; @@ -240,7 +243,7 @@ class _ViewerVerticalPageViewState extends State { ShowInfoIntent: CallbackAction(onInvoke: (intent) => ShowInfoPageNotification().dispatch(context)), TvShowLessInfoIntent: CallbackAction(onInvoke: (intent) => TvShowLessInfoNotification().dispatch(context)), TvShowMoreInfoIntent: CallbackAction(onInvoke: (intent) => TvShowMoreInfoNotification().dispatch(context)), - PlayPauseIntent: CallbackAction(onInvoke: (intent) => _onPlayPauseIntent(intent, entry)), + PlayPauseIntent: CallbackAction(onInvoke: _onPlayPauseIntent), EntryActionIntent: CallbackAction(onInvoke: (intent) => _onEntryActionIntent(intent.action)), ActivateIntent: CallbackAction(onInvoke: (intent) { if (useTvLayout) { @@ -249,7 +252,11 @@ class _ViewerVerticalPageViewState extends State { // address `TV-PC` requirement from https://developer.android.com/docs/quality-guidelines/tv-app-quality final controller = context.read().getController(_entry); if (controller != null) { - VideoActionNotification(controller: controller, action: EntryAction.videoTogglePlay).dispatch(context); + VideoActionNotification( + controller: controller, + entry: _entry, + action: EntryAction.videoTogglePlay, + ).dispatch(context); } } else { const ToggleOverlayNotification().dispatch(context); @@ -357,7 +364,7 @@ class _ViewerVerticalPageViewState extends State { } } - void _onPlayPauseIntent(PlayPauseIntent intent, entry) { + void _onPlayPauseIntent(PlayPauseIntent intent) { // address `TV-PP` requirement from https://developer.android.com/docs/quality-guidelines/tv-app-quality final _entry = entry; if (_entry != null && _entry.isVideo) { @@ -376,7 +383,11 @@ class _ViewerVerticalPageViewState extends State { break; } if (toggle) { - VideoActionNotification(controller: controller, action: EntryAction.videoTogglePlay).dispatch(context); + VideoActionNotification( + controller: controller, + entry: _entry, + action: EntryAction.videoTogglePlay, + ).dispatch(context); } } } diff --git a/lib/widgets/viewer/entry_viewer_page.dart b/lib/widgets/viewer/entry_viewer_page.dart index 527da25df..94ef5b5d7 100644 --- a/lib/widgets/viewer/entry_viewer_page.dart +++ b/lib/widgets/viewer/entry_viewer_page.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/widgets/common/basic/scaffold.dart'; import 'package:aves/widgets/viewer/controls/controller.dart'; diff --git a/lib/widgets/viewer/entry_viewer_stack.dart b/lib/widgets/viewer/entry_viewer_stack.dart index 45dda3132..900943741 100644 --- a/lib/widgets/viewer/entry_viewer_stack.dart +++ b/lib/widgets/viewer/entry_viewer_stack.dart @@ -4,7 +4,9 @@ import 'package:aves/app_mode.dart'; import 'package:aves/model/actions/entry_actions.dart'; import 'package:aves/model/actions/move_type.dart'; import 'package:aves/model/device.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/multipage.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/trash.dart'; import 'package:aves/model/highlight.dart'; @@ -13,7 +15,7 @@ import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/theme/durations.dart'; -import 'package:aves/utils/change_notifier.dart'; +import 'package:aves_utils/aves_utils.dart'; import 'package:aves/widgets/aves_app.dart'; import 'package:aves/widgets/collection/collection_page.dart'; import 'package:aves/widgets/common/action_mixins/feedback.dart'; @@ -31,7 +33,7 @@ import 'package:aves/widgets/viewer/overlay/top.dart'; import 'package:aves/widgets/viewer/overlay/video/video.dart'; import 'package:aves/widgets/viewer/page_entry_builder.dart'; import 'package:aves/widgets/viewer/video/conductor.dart'; -import 'package:aves/widgets/viewer/video/controller.dart'; +import 'package:aves_video/aves_video.dart'; import 'package:aves/widgets/viewer/visual/conductor.dart'; import 'package:aves/widgets/viewer/visual/controller_mixin.dart'; import 'package:collection/collection.dart'; @@ -373,7 +375,12 @@ class _EntryViewerStackState extends State with EntryViewContr scale: _overlayVideoControlScale, onActionSelected: (action) { if (videoController != null) { - _onVideoAction(context, videoController, action); + _onVideoAction( + context: context, + entry: targetEntry, + controller: videoController, + action: action, + ); } }, onActionMenuOpened: () { @@ -484,9 +491,12 @@ class _EntryViewerStackState extends State with EntryViewContr } else if (notification is ToggleOverlayNotification) { _overlayVisible.value = notification.visible ?? !_overlayVisible.value; } else if (notification is VideoActionNotification) { - final controller = notification.controller; - final action = notification.action; - _onVideoAction(context, controller, action); + _onVideoAction( + context: context, + entry: notification.entry, + controller: notification.controller, + action: notification.action, + ); } else if (notification is TvShowLessInfoNotification) { if (_overlayVisible.value) { _overlayVisible.value = false; @@ -513,8 +523,13 @@ class _EntryViewerStackState extends State with EntryViewContr return true; } - Future _onVideoAction(BuildContext context, AvesVideoController controller, EntryAction action) async { - await _videoActionDelegate.onActionSelected(context, controller, action); + Future _onVideoAction({ + required BuildContext context, + required AvesEntry entry, + required AvesVideoController controller, + required EntryAction action, + }) async { + await _videoActionDelegate.onActionSelected(context, entry, controller, action); if (action == EntryAction.videoToggleMute) { final override = controller.isMuted; videoMutedOverride = override; @@ -747,8 +762,7 @@ class _EntryViewerStackState extends State with EntryViewContr Future _enablePictureInPicture() async { final videoController = context.read().getPlayingController(); if (videoController != null) { - final targetEntry = videoController.entry; - final entrySize = targetEntry.displaySize; + final entrySize = videoController.entry.displaySize; final aspectRatio = Rational(entrySize.width.round(), entrySize.height.round()); final mq = context.read(); diff --git a/lib/widgets/viewer/hero.dart b/lib/widgets/viewer/hero.dart index 6c828d519..3e9c19f9f 100644 --- a/lib/widgets/viewer/hero.dart +++ b/lib/widgets/viewer/hero.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/widgets.dart'; diff --git a/lib/widgets/viewer/info/basic_section.dart b/lib/widgets/viewer/info/basic_section.dart index 4f081f3d2..b1da03c60 100644 --- a/lib/widgets/viewer/info/basic_section.dart +++ b/lib/widgets/viewer/info/basic_section.dart @@ -1,7 +1,10 @@ import 'package:aves/app_mode.dart'; import 'package:aves/image_providers/app_icon_image_provider.dart'; import 'package:aves/model/actions/entry_actions.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/favourites.dart'; +import 'package:aves/model/entry/extensions/multipage.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/favourites.dart'; import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/favourite.dart'; diff --git a/lib/widgets/viewer/info/embedded/embedded_data_opener.dart b/lib/widgets/viewer/info/embedded/embedded_data_opener.dart index d9f33d63d..ff96ebdcd 100644 --- a/lib/widgets/viewer/info/embedded/embedded_data_opener.dart +++ b/lib/widgets/viewer/info/embedded/embedded_data_opener.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/ref/mime_types.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/widgets/common/action_mixins/feedback.dart'; diff --git a/lib/widgets/viewer/info/info_app_bar.dart b/lib/widgets/viewer/info/info_app_bar.dart index 4e05c0c86..689152407 100644 --- a/lib/widgets/viewer/info/info_app_bar.dart +++ b/lib/widgets/viewer/info/info_app_bar.dart @@ -1,6 +1,7 @@ import 'package:aves/app_mode.dart'; import 'package:aves/model/actions/entry_actions.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/selection.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_lens.dart'; diff --git a/lib/widgets/viewer/info/info_page.dart b/lib/widgets/viewer/info/info_page.dart index 26c4dfeb7..3820af71c 100644 --- a/lib/widgets/viewer/info/info_page.dart +++ b/lib/widgets/viewer/info/info_page.dart @@ -2,7 +2,8 @@ import 'dart:async'; import 'package:aves/model/actions/entry_actions.dart'; import 'package:aves/model/actions/events.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/multipage.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/selection.dart'; import 'package:aves/model/source/collection_lens.dart'; diff --git a/lib/widgets/viewer/info/info_search.dart b/lib/widgets/viewer/info/info_search.dart index 008fa535e..5f5d48307 100644 --- a/lib/widgets/viewer/info/info_search.dart +++ b/lib/widgets/viewer/info/info_search.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/empty.dart'; diff --git a/lib/widgets/viewer/info/location_section.dart b/lib/widgets/viewer/info/location_section.dart index 208d270fe..158e44a6e 100644 --- a/lib/widgets/viewer/info/location_section.dart +++ b/lib/widgets/viewer/info/location_section.dart @@ -1,5 +1,6 @@ import 'package:aves/app_mode.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/location.dart'; import 'package:aves/model/filters/location.dart'; import 'package:aves/model/settings/enums/coordinate_format.dart'; import 'package:aves/model/settings/settings.dart'; diff --git a/lib/widgets/viewer/info/metadata/metadata_dir_tile.dart b/lib/widgets/viewer/info/metadata/metadata_dir_tile.dart index dc7a8f6f3..c353eb785 100644 --- a/lib/widgets/viewer/info/metadata/metadata_dir_tile.dart +++ b/lib/widgets/viewer/info/metadata/metadata_dir_tile.dart @@ -1,6 +1,6 @@ import 'dart:collection'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/ref/brand_colors.dart'; import 'package:aves/services/metadata/svg_metadata_service.dart'; import 'package:aves/theme/colors.dart'; diff --git a/lib/widgets/viewer/info/metadata/metadata_section.dart b/lib/widgets/viewer/info/metadata/metadata_section.dart index 80e76a7ce..8c4777cb2 100644 --- a/lib/widgets/viewer/info/metadata/metadata_section.dart +++ b/lib/widgets/viewer/info/metadata/metadata_section.dart @@ -1,7 +1,7 @@ import 'dart:async'; -import 'package:aves/model/entry.dart'; -import 'package:aves/model/entry_info.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/info.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/theme/icons.dart'; diff --git a/lib/widgets/viewer/info/metadata/metadata_thumbnail.dart b/lib/widgets/viewer/info/metadata/metadata_thumbnail.dart index 542b001eb..77d0407fc 100644 --- a/lib/widgets/viewer/info/metadata/metadata_thumbnail.dart +++ b/lib/widgets/viewer/info/metadata/metadata_thumbnail.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'dart:typed_data'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/services/common/services.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; diff --git a/lib/widgets/viewer/info/metadata/tv_page.dart b/lib/widgets/viewer/info/metadata/tv_page.dart index dbf3033f4..46af8c63b 100644 --- a/lib/widgets/viewer/info/metadata/tv_page.dart +++ b/lib/widgets/viewer/info/metadata/tv_page.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/common/basic/scaffold.dart'; import 'package:aves/widgets/common/behaviour/intents.dart'; diff --git a/lib/widgets/viewer/multipage/conductor.dart b/lib/widgets/viewer/multipage/conductor.dart index da6915149..bac45d9f2 100644 --- a/lib/widgets/viewer/multipage/conductor.dart +++ b/lib/widgets/viewer/multipage/conductor.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/widgets/viewer/multipage/controller.dart'; import 'package:collection/collection.dart'; diff --git a/lib/widgets/viewer/multipage/controller.dart b/lib/widgets/viewer/multipage/controller.dart index 5a7c68cd6..a2332915f 100644 --- a/lib/widgets/viewer/multipage/controller.dart +++ b/lib/widgets/viewer/multipage/controller.dart @@ -1,6 +1,7 @@ import 'dart:async'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/multipage.dart'; import 'package:aves/model/multipage.dart'; import 'package:flutter/foundation.dart'; diff --git a/lib/widgets/viewer/overlay/bottom.dart b/lib/widgets/viewer/overlay/bottom.dart index 2c3f15efb..d80348978 100644 --- a/lib/widgets/viewer/overlay/bottom.dart +++ b/lib/widgets/viewer/overlay/bottom.dart @@ -1,7 +1,8 @@ import 'dart:math'; import 'package:aves/app_mode.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/multipage.dart'; import 'package:aves/model/selection.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_lens.dart'; diff --git a/lib/widgets/viewer/overlay/details/date.dart b/lib/widgets/viewer/overlay/details/date.dart index da3bdb092..67e24fadd 100644 --- a/lib/widgets/viewer/overlay/details/date.dart +++ b/lib/widgets/viewer/overlay/details/date.dart @@ -1,4 +1,5 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/theme/format.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/utils/constants.dart'; diff --git a/lib/widgets/viewer/overlay/details/details.dart b/lib/widgets/viewer/overlay/details/details.dart index d24265ee7..3b10442c1 100644 --- a/lib/widgets/viewer/overlay/details/details.dart +++ b/lib/widgets/viewer/overlay/details/details.dart @@ -1,6 +1,6 @@ import 'dart:math'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/metadata/overlay.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/services/common/services.dart'; diff --git a/lib/widgets/viewer/overlay/details/location.dart b/lib/widgets/viewer/overlay/details/location.dart index aecf9dd2a..ad3ebd4af 100644 --- a/lib/widgets/viewer/overlay/details/location.dart +++ b/lib/widgets/viewer/overlay/details/location.dart @@ -1,4 +1,5 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/location.dart'; import 'package:aves/model/settings/enums/coordinate_format.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/icons.dart'; diff --git a/lib/widgets/viewer/overlay/details/position_title.dart b/lib/widgets/viewer/overlay/details/position_title.dart index 9d4767925..d13640e95 100644 --- a/lib/widgets/viewer/overlay/details/position_title.dart +++ b/lib/widgets/viewer/overlay/details/position_title.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/multipage.dart'; import 'package:aves/utils/constants.dart'; import 'package:aves/widgets/viewer/multipage/controller.dart'; diff --git a/lib/widgets/viewer/overlay/details/rating_tags.dart b/lib/widgets/viewer/overlay/details/rating_tags.dart index f99c222a1..9a837f478 100644 --- a/lib/widgets/viewer/overlay/details/rating_tags.dart +++ b/lib/widgets/viewer/overlay/details/rating_tags.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/utils/constants.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; diff --git a/lib/widgets/viewer/overlay/panorama.dart b/lib/widgets/viewer/overlay/panorama.dart index 5f853f583..2f58aae82 100644 --- a/lib/widgets/viewer/overlay/panorama.dart +++ b/lib/widgets/viewer/overlay/panorama.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/buttons/overlay_button.dart'; diff --git a/lib/widgets/viewer/overlay/selection_button.dart b/lib/widgets/viewer/overlay/selection_button.dart index ad2f1b899..874492854 100644 --- a/lib/widgets/viewer/overlay/selection_button.dart +++ b/lib/widgets/viewer/overlay/selection_button.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/selection.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/theme/icons.dart'; diff --git a/lib/widgets/viewer/overlay/thumbnail_preview.dart b/lib/widgets/viewer/overlay/thumbnail_preview.dart index a7fa1119a..934ad7518 100644 --- a/lib/widgets/viewer/overlay/thumbnail_preview.dart +++ b/lib/widgets/viewer/overlay/thumbnail_preview.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/utils/debouncer.dart'; import 'package:aves/widgets/common/thumbnail/scroller.dart'; diff --git a/lib/widgets/viewer/overlay/top.dart b/lib/widgets/viewer/overlay/top.dart index 7021e0149..67cd07c31 100644 --- a/lib/widgets/viewer/overlay/top.dart +++ b/lib/widgets/viewer/overlay/top.dart @@ -1,4 +1,5 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/multipage.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/themes.dart'; import 'package:aves/widgets/common/fx/blurred.dart'; diff --git a/lib/widgets/viewer/overlay/video/controls.dart b/lib/widgets/viewer/overlay/video/controls.dart index 71db0d998..d2522896f 100644 --- a/lib/widgets/viewer/overlay/video/controls.dart +++ b/lib/widgets/viewer/overlay/video/controls.dart @@ -1,13 +1,15 @@ import 'package:aves/model/actions/entry_actions.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/widgets/common/action_controls/togglers/play.dart'; import 'package:aves/widgets/common/identity/buttons/overlay_button.dart'; -import 'package:aves/widgets/viewer/video/controller.dart'; +import 'package:aves_video/aves_video.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class VideoControlRow extends StatelessWidget { + final AvesEntry entry; final AvesVideoController? controller; final Animation scale; final Function(EntryAction value) onActionSelected; @@ -17,6 +19,7 @@ class VideoControlRow extends StatelessWidget { const VideoControlRow({ super.key, + required this.entry, required this.controller, required this.scale, required this.onActionSelected, @@ -63,10 +66,9 @@ class VideoControlRow extends StatelessWidget { ], ); case VideoControls.playOutside: - final trashed = controller?.entry.trashed ?? false; return Padding( padding: const EdgeInsetsDirectional.only(start: padding), - child: _buildIconButton(context, EntryAction.openVideo, enabled: !trashed), + child: _buildIconButton(context, EntryAction.openVideo, enabled: !entry.trashed), ); case VideoControls.none: return const SizedBox(); diff --git a/lib/widgets/viewer/overlay/video/progress_bar.dart b/lib/widgets/viewer/overlay/video/progress_bar.dart index b96e26c10..aac65fe25 100644 --- a/lib/widgets/viewer/overlay/video/progress_bar.dart +++ b/lib/widgets/viewer/overlay/video/progress_bar.dart @@ -7,7 +7,7 @@ import 'package:aves/theme/themes.dart'; import 'package:aves/utils/constants.dart'; import 'package:aves/widgets/common/fx/blurred.dart'; import 'package:aves/widgets/common/fx/borders.dart'; -import 'package:aves/widgets/viewer/video/controller.dart'; +import 'package:aves_video/aves_video.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; diff --git a/lib/widgets/viewer/overlay/video/video.dart b/lib/widgets/viewer/overlay/video/video.dart index fa82c7fc1..409ea7bd2 100644 --- a/lib/widgets/viewer/overlay/video/video.dart +++ b/lib/widgets/viewer/overlay/video/video.dart @@ -1,11 +1,11 @@ import 'dart:async'; import 'package:aves/model/actions/entry_actions.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/widgets/common/identity/buttons/overlay_button.dart'; import 'package:aves/widgets/viewer/overlay/video/controls.dart'; import 'package:aves/widgets/viewer/overlay/video/progress_bar.dart'; -import 'package:aves/widgets/viewer/video/controller.dart'; +import 'package:aves_video/aves_video.dart'; import 'package:flutter/material.dart'; class VideoControlOverlay extends StatefulWidget { @@ -69,6 +69,7 @@ class _VideoControlOverlayState extends State with SingleTi ), ), VideoControlRow( + entry: entry, controller: controller, scale: scale, onActionSelected: widget.onActionSelected, diff --git a/lib/widgets/viewer/overlay/viewer_buttons.dart b/lib/widgets/viewer/overlay/viewer_buttons.dart index faf3196da..52a27e5db 100644 --- a/lib/widgets/viewer/overlay/viewer_buttons.dart +++ b/lib/widgets/viewer/overlay/viewer_buttons.dart @@ -2,7 +2,9 @@ import 'dart:math'; import 'package:aves/app_mode.dart'; import 'package:aves/model/actions/entry_actions.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/multipage.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/theme/durations.dart'; @@ -24,7 +26,7 @@ import 'package:aves/widgets/common/identity/buttons/overlay_button.dart'; import 'package:aves/widgets/viewer/action/entry_action_delegate.dart'; import 'package:aves/widgets/viewer/controls/notifications.dart'; import 'package:aves/widgets/viewer/video/conductor.dart'; -import 'package:aves/widgets/viewer/video/controller.dart'; +import 'package:aves_video/aves_video.dart'; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; diff --git a/lib/widgets/viewer/overlay/wallpaper_buttons.dart b/lib/widgets/viewer/overlay/wallpaper_buttons.dart index ba894d6e1..3d949f033 100644 --- a/lib/widgets/viewer/overlay/wallpaper_buttons.dart +++ b/lib/widgets/viewer/overlay/wallpaper_buttons.dart @@ -2,8 +2,9 @@ import 'dart:async'; import 'dart:math'; import 'dart:ui' as ui; -import 'package:aves/model/entry.dart'; -import 'package:aves/model/entry_images.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/images.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/wallpaper_target.dart'; import 'package:aves/services/wallpaper_service.dart'; import 'package:aves/widgets/common/action_mixins/feedback.dart'; diff --git a/lib/widgets/viewer/page_entry_builder.dart b/lib/widgets/viewer/page_entry_builder.dart index c3b25aca0..97fcb5db0 100644 --- a/lib/widgets/viewer/page_entry_builder.dart +++ b/lib/widgets/viewer/page_entry_builder.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/multipage.dart'; import 'package:aves/widgets/viewer/multipage/controller.dart'; import 'package:flutter/widgets.dart'; diff --git a/lib/widgets/viewer/panorama_page.dart b/lib/widgets/viewer/panorama_page.dart index ea1dbcd8f..00a97cb45 100644 --- a/lib/widgets/viewer/panorama_page.dart +++ b/lib/widgets/viewer/panorama_page.dart @@ -1,7 +1,7 @@ import 'dart:math'; -import 'package:aves/model/entry.dart'; -import 'package:aves/model/entry_images.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/images.dart'; import 'package:aves/model/panorama.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/icons.dart'; diff --git a/lib/widgets/viewer/slideshow_page.dart b/lib/widgets/viewer/slideshow_page.dart index c4762de49..48bcd0e2e 100644 --- a/lib/widgets/viewer/slideshow_page.dart +++ b/lib/widgets/viewer/slideshow_page.dart @@ -1,6 +1,6 @@ import 'package:aves/app_mode.dart'; import 'package:aves/model/actions/slideshow_actions.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/mime.dart'; import 'package:aves/model/settings/enums/enums.dart'; diff --git a/lib/widgets/viewer/video/conductor.dart b/lib/widgets/viewer/video/conductor.dart index 82abe32c3..7178949a4 100644 --- a/lib/widgets/viewer/video/conductor.dart +++ b/lib/widgets/viewer/video/conductor.dart @@ -1,11 +1,13 @@ import 'dart:async'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/services/common/services.dart'; -import 'package:aves/widgets/viewer/video/controller.dart'; +import 'package:aves_video/aves_video.dart'; +import 'package:aves/widgets/viewer/video/db_playback_state_handler.dart'; import 'package:aves/widgets/viewer/video/fijkplayer.dart'; import 'package:collection/collection.dart'; @@ -13,6 +15,7 @@ class VideoConductor { final CollectionLens? _collection; final List _controllers = []; final List _subscriptions = []; + final PlaybackStateHandler playbackStateHandler = DatabasePlaybackStateHandler(); static const _defaultMaxControllerCount = 3; @@ -34,8 +37,8 @@ class VideoConductor { if (controller != null) { _controllers.remove(controller); } else { - controller = IjkPlayerAvesVideoController(entry, persistPlayback: true); - _subscriptions.add(controller.statusStream.listen((event) => _onControllerStatusChanged(controller!, event))); + controller = IjkPlayerAvesVideoController(entry, playbackStateHandler: playbackStateHandler); + _subscriptions.add(controller.statusStream.listen((event) => _onControllerStatusChanged(entry, controller!, event))); } _controllers.insert(0, controller); while (_controllers.length > (maxControllerCount ?? _defaultMaxControllerCount)) { @@ -50,11 +53,11 @@ class VideoConductor { return _controllers.firstWhereOrNull((c) => c.entry.uri == entry.uri && c.entry.pageId == entry.pageId); } - Future _onControllerStatusChanged(AvesVideoController controller, VideoStatus status) async { + Future _onControllerStatusChanged(AvesEntry entry, AvesVideoController controller, VideoStatus status) async { bool canSkipToNext = false, canSkipToPrevious = false; final entries = _collection?.sortedEntries; if (entries != null) { - final currentIndex = entries.indexOf(controller.entry); + final currentIndex = entries.indexOf(entry); if (currentIndex != -1) { bool isVideo(AvesEntry entry) => entry.isVideo; canSkipToPrevious = entries.take(currentIndex).lastWhereOrNull(isVideo) != null; @@ -63,6 +66,7 @@ class VideoConductor { } await mediaSessionService.update( + entry: entry, controller: controller, canSkipToNext: canSkipToNext, canSkipToPrevious: canSkipToPrevious, diff --git a/lib/widgets/viewer/video/controller.dart b/lib/widgets/viewer/video/controller.dart deleted file mode 100644 index a41fb6d01..000000000 --- a/lib/widgets/viewer/video/controller.dart +++ /dev/null @@ -1,181 +0,0 @@ -import 'dart:async'; - -import 'package:aves/model/entry.dart'; -import 'package:aves/model/video_playback.dart'; -import 'package:aves/services/common/services.dart'; -import 'package:aves/theme/format.dart'; -import 'package:aves/widgets/common/extensions/build_context.dart'; -import 'package:aves/widgets/dialogs/aves_dialog.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -abstract class AvesVideoController { - final AvesEntry _entry; - final bool persistPlayback; - - AvesEntry get entry => _entry; - - static const resumeTimeSaveMinProgress = .05; - static const resumeTimeSaveMaxProgress = .95; - static const resumeTimeSaveMinDuration = Duration(minutes: 2); - - AvesVideoController(AvesEntry entry, {required this.persistPlayback}) : _entry = entry { - entry.visualChangeNotifier.addListener(onVisualChanged); - } - - @mustCallSuper - Future dispose() async { - entry.visualChangeNotifier.removeListener(onVisualChanged); - await _savePlaybackState(); - } - - Future _savePlaybackState() async { - final id = entry.id; - if (!isReady || duration < resumeTimeSaveMinDuration.inMilliseconds) return; - - if (persistPlayback) { - final _progress = progress; - if (resumeTimeSaveMinProgress < _progress && _progress < resumeTimeSaveMaxProgress) { - await metadataDb.addVideoPlayback({ - VideoPlaybackRow( - entryId: id, - resumeTimeMillis: currentPosition, - ) - }); - } else { - await metadataDb.removeVideoPlayback({id}); - } - } - } - - Future getResumeTime(BuildContext context) async { - if (!persistPlayback) return null; - - final id = entry.id; - final playback = await metadataDb.loadVideoPlayback(id); - final resumeTime = playback?.resumeTimeMillis ?? 0; - if (resumeTime == 0) return null; - - // clear on retrieval - await metadataDb.removeVideoPlayback({id}); - - final resume = await showDialog( - context: context, - builder: (context) => AvesDialog( - content: Text(context.l10n.videoResumeDialogMessage(formatFriendlyDuration(Duration(milliseconds: resumeTime)))), - actions: [ - TextButton( - onPressed: () => Navigator.maybeOf(context)?.pop(false), - child: Text(context.l10n.videoStartOverButtonLabel), - ), - TextButton( - onPressed: () => Navigator.maybeOf(context)?.pop(true), - child: Text(context.l10n.videoResumeButtonLabel), - ), - ], - ), - routeSettings: const RouteSettings(name: AvesDialog.confirmationRouteName), - ); - if (resume == null || !resume) return 0; - return resumeTime; - } - - void onVisualChanged(); - - Future play(); - - Future pause(); - - Future seekTo(int targetMillis); - - Future seekToProgress(double progress) => seekTo((duration * progress).toInt()); - - Listenable get playCompletedListenable; - - VideoStatus get status; - - Stream get statusStream; - - Stream get volumeStream; - - Stream get speedStream; - - bool get isReady; - - bool get isPlaying => status == VideoStatus.playing; - - int get duration; - - int get currentPosition; - - double get progress { - final _duration = duration; - return _duration != 0 ? currentPosition.toDouble() / _duration : 0; - } - - Stream get positionStream; - - Stream get timedTextStream; - - ValueNotifier get canCaptureFrameNotifier; - - ValueNotifier get canMuteNotifier; - - ValueNotifier get canSetSpeedNotifier; - - ValueNotifier get canSelectStreamNotifier; - - ValueNotifier get sarNotifier; - - bool get isMuted; - - double get speed; - - double get minSpeed; - - double get maxSpeed; - - set speed(double speed); - - Future selectStream(StreamType type, StreamSummary? selected); - - Future getSelectedStream(StreamType type); - - List get streams; - - Future captureFrame(); - - Future mute(bool muted); - - Widget buildPlayerWidget(BuildContext context); -} - -enum VideoStatus { - idle, - initialized, - paused, - playing, - completed, - error, -} - -enum StreamType { video, audio, text } - -class StreamSummary { - final StreamType type; - final int? index, width, height; - final String? codecName, language, title; - - const StreamSummary({ - required this.type, - required this.index, - required this.codecName, - required this.language, - required this.title, - required this.width, - required this.height, - }); - - @override - String toString() => '$runtimeType#${shortHash(this)}{type: type, index: $index, codecName: $codecName, language: $language, title: $title, width: $width, height: $height}'; -} diff --git a/lib/widgets/viewer/video/db_playback_state_handler.dart b/lib/widgets/viewer/video/db_playback_state_handler.dart new file mode 100644 index 000000000..675e25e1f --- /dev/null +++ b/lib/widgets/viewer/video/db_playback_state_handler.dart @@ -0,0 +1,58 @@ +import 'dart:async'; + +import 'package:aves/model/video_playback.dart'; +import 'package:aves/services/common/services.dart'; +import 'package:aves/theme/format.dart'; +import 'package:aves/widgets/common/extensions/build_context.dart'; +import 'package:aves/widgets/dialogs/aves_dialog.dart'; +import 'package:aves_video/aves_video.dart'; +import 'package:flutter/material.dart'; + +class DatabasePlaybackStateHandler extends PlaybackStateHandler { + static const resumeTimeSaveMinProgress = .05; + static const resumeTimeSaveMaxProgress = .95; + + @override + Future getResumeTime({required int entryId, required BuildContext context}) async { + final playback = await metadataDb.loadVideoPlayback(entryId); + final resumeTime = playback?.resumeTimeMillis ?? 0; + if (resumeTime == 0) return null; + + // clear on retrieval + await metadataDb.removeVideoPlayback({entryId}); + + final resume = await showDialog( + context: context, + builder: (context) => AvesDialog( + content: Text(context.l10n.videoResumeDialogMessage(formatFriendlyDuration(Duration(milliseconds: resumeTime)))), + actions: [ + TextButton( + onPressed: () => Navigator.maybeOf(context)?.pop(false), + child: Text(context.l10n.videoStartOverButtonLabel), + ), + TextButton( + onPressed: () => Navigator.maybeOf(context)?.pop(true), + child: Text(context.l10n.videoResumeButtonLabel), + ), + ], + ), + routeSettings: const RouteSettings(name: AvesDialog.confirmationRouteName), + ); + if (resume == null || !resume) return 0; + return resumeTime; + } + + @override + Future saveResumeTime({required int entryId, required int position, required double progress}) async { + if (resumeTimeSaveMinProgress < progress && progress < resumeTimeSaveMaxProgress) { + await metadataDb.addVideoPlayback({ + VideoPlaybackRow( + entryId: entryId, + resumeTimeMillis: position, + ) + }); + } else { + await metadataDb.removeVideoPlayback({entryId}); + } + } +} diff --git a/lib/widgets/viewer/video/fijkplayer.dart b/lib/widgets/viewer/video/fijkplayer.dart index 9b7d10a6f..bd373c087 100644 --- a/lib/widgets/viewer/video/fijkplayer.dart +++ b/lib/widgets/viewer/video/fijkplayer.dart @@ -1,13 +1,10 @@ import 'dart:async'; -import 'package:aves/model/entry.dart'; import 'package:aves/model/settings/enums/video_loop_mode.dart'; import 'package:aves/model/settings/settings.dart'; -import 'package:aves/model/video/keys.dart'; -import 'package:aves/model/video/metadata.dart'; -import 'package:aves/services/common/optional_event_channel.dart'; -import 'package:aves/utils/change_notifier.dart'; -import 'package:aves/widgets/viewer/video/controller.dart'; +import 'package:aves_model/aves_model.dart'; +import 'package:aves_utils/aves_utils.dart'; +import 'package:aves_video/aves_video.dart'; import 'package:collection/collection.dart'; import 'package:fijkplayer/fijkplayer.dart'; import 'package:flutter/material.dart'; @@ -24,7 +21,7 @@ class IjkPlayerAvesVideoController extends AvesVideoController { final StreamController _speedStreamController = StreamController.broadcast(); final AChangeNotifier _completedNotifier = AChangeNotifier(); Offset _macroBlockCrop = Offset.zero; - final List _streams = []; + final List _streams = []; Timer? _initialPlayTimer; double _speed = 1; double _volume = 1; @@ -62,12 +59,9 @@ class IjkPlayerAvesVideoController extends AvesVideoController { static const captureFrameEnabled = true; IjkPlayerAvesVideoController( - AvesEntry entry, { - required bool persistPlayback, - }) : super( - entry, - persistPlayback: persistPlayback, - ) { + super.entry, { + required super.playbackStateHandler, + }) { _instance = FijkPlayer(); _valueStream.map((value) => value.videoRenderStart).firstWhere((v) => v, orElse: () => false).then( (started) { @@ -168,7 +162,7 @@ class IjkPlayerAvesVideoController extends AvesVideoController { _macroBlockCrop = Offset(s.width, s.height); } - final loopEnabled = settings.videoLoopMode.shouldLoop(entry); + final loopEnabled = settings.videoLoopMode.shouldLoop(entry.durationMillis); // `fastseek`: enable fast, but inaccurate seeks for some formats // in practice the flag seems ineffective, but harmless too @@ -261,7 +255,7 @@ class IjkPlayerAvesVideoController extends AvesVideoController { if (type != null) { final width = stream[Keys.width] as int?; final height = stream[Keys.height] as int?; - _streams.add(StreamSummary( + _streams.add(MediaStreamSummary( type: type, index: stream[Keys.index], codecName: stream[Keys.codecName], @@ -271,16 +265,16 @@ class IjkPlayerAvesVideoController extends AvesVideoController { height: height, )); switch (type) { - case StreamType.video: + case MediaStreamType.video: // check width/height to exclude image streams (that are included among video streams) if (width != null && height != null) { videoStreamCount++; } break; - case StreamType.audio: + case MediaStreamType.audio: audioStreamCount++; break; - case StreamType.text: + case MediaStreamType.text: textStreamCount++; break; } @@ -289,7 +283,7 @@ class IjkPlayerAvesVideoController extends AvesVideoController { canSelectStreamNotifier.value = videoStreamCount > 1 || audioStreamCount > 1 || textStreamCount > 0; - final selectedVideo = await getSelectedStream(StreamType.video); + final selectedVideo = await getSelectedStream(MediaStreamType.video); if (selectedVideo != null) { final streamIndex = selectedVideo.index; final streamInfo = allStreams.firstWhereOrNull((stream) => stream[Keys.index] == streamIndex); @@ -439,7 +433,7 @@ class IjkPlayerAvesVideoController extends AvesVideoController { // 1) prevent video stream acceleration to catch up with audio // 2) apply timed text stream @override - Future selectStream(StreamType type, StreamSummary? selected) async { + Future selectStream(MediaStreamType type, MediaStreamSummary? selected) async { final current = await getSelectedStream(type); if (current != selected) { if (selected != null) { @@ -450,7 +444,7 @@ class IjkPlayerAvesVideoController extends AvesVideoController { } else if (current != null) { await _instance.deselectTrack(current.index!); } - if (type == StreamType.text) { + if (type == MediaStreamType.text) { _timedTextStreamController.add(null); } await seekTo(currentPosition); @@ -458,13 +452,13 @@ class IjkPlayerAvesVideoController extends AvesVideoController { } @override - Future getSelectedStream(StreamType type) async { + Future getSelectedStream(MediaStreamType type) async { final currentIndex = await _instance.getSelectedTrack(type.code); return currentIndex != -1 ? _streams.firstWhereOrNull((v) => v.index == currentIndex) : null; } @override - List get streams => _streams; + List get streams => _streams; @override Future captureFrame() { @@ -560,16 +554,16 @@ extension ExtraFijkPlayer on FijkPlayer { } } -extension ExtraStreamType on StreamType { - static StreamType? fromTypeString(String? type) { +extension ExtraStreamType on MediaStreamType { + static MediaStreamType? fromTypeString(String? type) { switch (type) { - case StreamTypes.video: - return StreamType.video; - case StreamTypes.audio: - return StreamType.audio; - case StreamTypes.subtitle: - case StreamTypes.timedText: - return StreamType.text; + case MediaStreamTypes.video: + return MediaStreamType.video; + case MediaStreamTypes.audio: + return MediaStreamType.audio; + case MediaStreamTypes.subtitle: + case MediaStreamTypes.timedText: + return MediaStreamType.text; default: return null; } @@ -578,11 +572,11 @@ extension ExtraStreamType on StreamType { int get code { // codes from ijkplayer ITrackInfo.java switch (this) { - case StreamType.video: + case MediaStreamType.video: return 1; - case StreamType.audio: + case MediaStreamType.audio: return 2; - case StreamType.text: + case MediaStreamType.text: // TIMEDTEXT = 3, SUBTITLE = 4 return 3; default: diff --git a/lib/widgets/viewer/video/flutter_vlc_player.dart b/lib/widgets/viewer/video/flutter_vlc_player.dart index d8f4dcaf4..f9de65fbe 100644 --- a/lib/widgets/viewer/video/flutter_vlc_player.dart +++ b/lib/widgets/viewer/video/flutter_vlc_player.dart @@ -2,8 +2,8 @@ // import 'dart:io'; // // import 'package:aves/model/entry.dart'; -// import 'package:aves/utils/change_notifier.dart'; -// import 'package:aves/widgets/viewer/video/controller.dart'; +// import 'package:aves_utils/aves_utils.dart'; +// import 'package:aves_video/aves_video.dart'; // import 'package:flutter/material.dart'; // import 'package:flutter/src/foundation/change_notifier.dart'; // import 'package:flutter/src/widgets/framework.dart'; diff --git a/lib/widgets/viewer/video/video_player.dart b/lib/widgets/viewer/video/video_player.dart index ee6d438c7..0aa9cf8c1 100644 --- a/lib/widgets/viewer/video/video_player.dart +++ b/lib/widgets/viewer/video/video_player.dart @@ -1,8 +1,8 @@ // import 'dart:async'; // // import 'package:aves/model/entry.dart'; -// import 'package:aves/utils/change_notifier.dart'; -// import 'package:aves/widgets/viewer/video/controller.dart'; +// import 'package:aves_utils/aves_utils.dart'; +// import 'package:aves_video/aves_video.dart'; // import 'package:flutter/src/foundation/change_notifier.dart'; // import 'package:flutter/src/widgets/framework.dart'; // import 'package:video_player/video_player.dart'; diff --git a/lib/widgets/viewer/visual/conductor.dart b/lib/widgets/viewer/visual/conductor.dart index d350c34ae..9f07652e2 100644 --- a/lib/widgets/viewer/visual/conductor.dart +++ b/lib/widgets/viewer/visual/conductor.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/widgets/viewer/visual/state.dart'; import 'package:aves_magnifier/aves_magnifier.dart'; import 'package:collection/collection.dart'; diff --git a/lib/widgets/viewer/visual/controller_mixin.dart b/lib/widgets/viewer/visual/controller_mixin.dart index f955fe8a2..6f39dd025 100644 --- a/lib/widgets/viewer/visual/controller_mixin.dart +++ b/lib/widgets/viewer/visual/controller_mixin.dart @@ -1,12 +1,14 @@ import 'package:aves/app_mode.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/multipage.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/widgets/viewer/multipage/conductor.dart'; import 'package:aves/widgets/viewer/multipage/controller.dart'; import 'package:aves/widgets/viewer/video/conductor.dart'; -import 'package:aves/widgets/viewer/video/controller.dart'; +import 'package:aves_video/aves_video.dart'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; diff --git a/lib/widgets/viewer/visual/entry_page_view.dart b/lib/widgets/viewer/visual/entry_page_view.dart index 91af37de0..fff2d55d1 100644 --- a/lib/widgets/viewer/visual/entry_page_view.dart +++ b/lib/widgets/viewer/visual/entry_page_view.dart @@ -2,7 +2,8 @@ import 'dart:async'; import 'package:aves/app_mode.dart'; import 'package:aves/model/actions/entry_actions.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/settings/enums/accessibility_animations.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/services/common/services.dart'; @@ -233,6 +234,7 @@ class _EntryPageViewState extends State with SingleTickerProvider ); VideoActionNotification( controller: videoController, + entry: entry, action: action, ).dispatch(context); } @@ -326,6 +328,7 @@ class _EntryPageViewState extends State with SingleTickerProvider ), ), VideoSubtitles( + entry: entry, controller: videoController, viewStateNotifier: _viewStateNotifier, ), diff --git a/lib/widgets/viewer/visual/error.dart b/lib/widgets/viewer/visual/error.dart index 85ce3d3d9..50d8167ee 100644 --- a/lib/widgets/viewer/visual/error.dart +++ b/lib/widgets/viewer/visual/error.dart @@ -1,6 +1,6 @@ import 'dart:io'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/empty.dart'; diff --git a/lib/widgets/viewer/visual/raster.dart b/lib/widgets/viewer/visual/raster.dart index a7770966e..1924c3fad 100644 --- a/lib/widgets/viewer/visual/raster.dart +++ b/lib/widgets/viewer/visual/raster.dart @@ -1,8 +1,9 @@ import 'dart:math'; import 'package:aves/image_providers/region_provider.dart'; -import 'package:aves/model/entry.dart'; -import 'package:aves/model/entry_images.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/images.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/settings/enums/entry_background.dart'; import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/model/settings/settings.dart'; diff --git a/lib/widgets/viewer/visual/vector.dart b/lib/widgets/viewer/visual/vector.dart index 02736fa30..5d583d31d 100644 --- a/lib/widgets/viewer/visual/vector.dart +++ b/lib/widgets/viewer/visual/vector.dart @@ -2,8 +2,8 @@ import 'dart:math'; import 'dart:ui'; import 'package:aves/image_providers/region_provider.dart'; -import 'package:aves/model/entry.dart'; -import 'package:aves/model/entry_images.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/images.dart'; import 'package:aves/model/settings/enums/entry_background.dart'; import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/model/settings/settings.dart'; diff --git a/lib/widgets/viewer/visual/video/cover.dart b/lib/widgets/viewer/visual/video/cover.dart index e9a3a81e1..6091cbd69 100644 --- a/lib/widgets/viewer/visual/video/cover.dart +++ b/lib/widgets/viewer/visual/video/cover.dart @@ -1,8 +1,9 @@ -import 'package:aves/model/entry.dart'; -import 'package:aves/model/entry_images.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/images.dart'; +import 'package:aves/model/entry/extensions/multipage.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/widgets/common/thumbnail/image.dart'; -import 'package:aves/widgets/viewer/video/controller.dart'; +import 'package:aves_video/aves_video.dart'; import 'package:aves_magnifier/aves_magnifier.dart'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; diff --git a/lib/widgets/viewer/visual/video/subtitle/subtitle.dart b/lib/widgets/viewer/visual/video/subtitle/subtitle.dart index c83f9db6f..48919273c 100644 --- a/lib/widgets/viewer/visual/video/subtitle/subtitle.dart +++ b/lib/widgets/viewer/visual/video/subtitle/subtitle.dart @@ -1,8 +1,10 @@ +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/settings/enums/subtitle_position.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/widgets/common/basic/text/background_painter.dart'; import 'package:aves/widgets/common/basic/text/outlined.dart'; -import 'package:aves/widgets/viewer/video/controller.dart'; +import 'package:aves_video/aves_video.dart'; import 'package:aves/widgets/viewer/visual/state.dart'; import 'package:aves/widgets/viewer/visual/video/subtitle/ass_parser.dart'; import 'package:aves/widgets/viewer/visual/video/subtitle/span.dart'; @@ -14,6 +16,7 @@ import 'package:latlong2/latlong.dart' as angles; import 'package:provider/provider.dart'; class VideoSubtitles extends StatelessWidget { + final AvesEntry entry; final AvesVideoController controller; final ValueNotifier viewStateNotifier; final bool debugMode; @@ -22,6 +25,7 @@ class VideoSubtitles extends StatelessWidget { const VideoSubtitles({ super.key, + required this.entry, required this.controller, required this.viewStateNotifier, this.debugMode = false, @@ -29,7 +33,7 @@ class VideoSubtitles extends StatelessWidget { @override Widget build(BuildContext context) { - final videoDisplaySize = controller.entry.videoDisplaySize(controller.sarNotifier.value); + final videoDisplaySize = entry.videoDisplaySize(controller.sarNotifier.value); return IgnorePointer( child: Consumer( builder: (context, settings, child) { diff --git a/lib/widgets/viewer/visual/video/video_view.dart b/lib/widgets/viewer/visual/video/video_view.dart index 6fadf9908..06c3d6819 100644 --- a/lib/widgets/viewer/visual/video/video_view.dart +++ b/lib/widgets/viewer/visual/video/video_view.dart @@ -1,5 +1,5 @@ -import 'package:aves/model/entry.dart'; -import 'package:aves/widgets/viewer/video/controller.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves_video/aves_video.dart'; import 'package:flutter/material.dart'; class VideoView extends StatefulWidget { diff --git a/lib/widgets/wallpaper_page.dart b/lib/widgets/wallpaper_page.dart index 5d6ad023a..7d398f5d3 100644 --- a/lib/widgets/wallpaper_page.dart +++ b/lib/widgets/wallpaper_page.dart @@ -1,5 +1,7 @@ import 'package:aves/model/actions/entry_actions.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/multipage.dart'; +import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/services/common/services.dart'; @@ -17,9 +19,9 @@ import 'package:aves/widgets/viewer/overlay/video/video.dart'; import 'package:aves/widgets/viewer/page_entry_builder.dart'; import 'package:aves/widgets/viewer/providers.dart'; import 'package:aves/widgets/viewer/video/conductor.dart'; -import 'package:aves/widgets/viewer/video/controller.dart'; import 'package:aves/widgets/viewer/visual/controller_mixin.dart'; import 'package:aves_magnifier/aves_magnifier.dart'; +import 'package:aves_video/aves_video.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:screen_brightness/screen_brightness.dart'; @@ -132,9 +134,12 @@ class _EntryEditorState extends State with EntryViewControllerMixin if (notification is ToggleOverlayNotification) { _overlayVisible.value = notification.visible ?? !_overlayVisible.value; } else if (notification is VideoActionNotification) { - final controller = notification.controller; - final action = notification.action; - _onVideoAction(context, controller, action); + _onVideoAction( + context: context, + entry: notification.entry, + controller: notification.controller, + action: notification.action, + ); } return true; }, @@ -176,7 +181,12 @@ class _EntryEditorState extends State with EntryViewControllerMixin entry: targetEntry, controller: videoController, scale: _overlayVideoControlScale, - onActionSelected: (action) => _onVideoAction(context, videoController, action), + onActionSelected: (action) => _onVideoAction( + context: context, + entry: targetEntry, + controller: videoController, + action: action, + ), onActionMenuOpened: () { // if the menu is opened while overlay is hiding, // the popup menu button is disposed and menu items are ineffective, @@ -236,9 +246,14 @@ class _EntryEditorState extends State with EntryViewControllerMixin ); } - void _onVideoAction(BuildContext context, AvesVideoController? videoController, EntryAction action) { - if (videoController != null) { - _videoActionDelegate.onActionSelected(context, videoController, action); + void _onVideoAction({ + required BuildContext context, + required AvesEntry entry, + required AvesVideoController? controller, + required EntryAction action, + }) { + if (controller != null) { + _videoActionDelegate.onActionSelected(context, entry, controller, action); } } diff --git a/plugins/aves_magnifier/pubspec.lock b/plugins/aves_magnifier/pubspec.lock index bb225830d..26dfee840 100644 --- a/plugins/aves_magnifier/pubspec.lock +++ b/plugins/aves_magnifier/pubspec.lock @@ -108,5 +108,5 @@ packages: source: hosted version: "2.1.4" sdks: - dart: ">=2.19.0 <3.0.0" + dart: ">=2.19.4 <3.0.0" flutter: ">=1.16.0" diff --git a/plugins/aves_magnifier/pubspec.yaml b/plugins/aves_magnifier/pubspec.yaml index 3ef32e0bd..083facfdd 100644 --- a/plugins/aves_magnifier/pubspec.yaml +++ b/plugins/aves_magnifier/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.0 <3.0.0" + sdk: ">=2.19.4 <3.0.0" dependencies: flutter: diff --git a/plugins/aves_map/pubspec.lock b/plugins/aves_map/pubspec.lock index bcfc9e221..f0229daf1 100644 --- a/plugins/aves_map/pubspec.lock +++ b/plugins/aves_map/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" aves_ui: dependency: "direct main" description: @@ -283,5 +283,5 @@ packages: source: hosted version: "2.0.0" sdks: - dart: ">=2.19.0 <3.0.0" + dart: ">=2.19.4 <3.0.0" flutter: ">=3.3.0" diff --git a/plugins/aves_map/pubspec.yaml b/plugins/aves_map/pubspec.yaml index 267d70a7f..15b1316cd 100644 --- a/plugins/aves_map/pubspec.yaml +++ b/plugins/aves_map/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.0 <3.0.0" + sdk: ">=2.19.4 <3.0.0" dependencies: flutter: diff --git a/plugins/aves_model/.gitignore b/plugins/aves_model/.gitignore new file mode 100644 index 000000000..28124a571 --- /dev/null +++ b/plugins/aves_model/.gitignore @@ -0,0 +1,30 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +#/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ diff --git a/plugins/aves_model/.metadata b/plugins/aves_model/.metadata new file mode 100644 index 000000000..ad910f56c --- /dev/null +++ b/plugins/aves_model/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 2ad6cd72c040113b47ee9055e722606a490ef0da + channel: stable + +project_type: package diff --git a/plugins/aves_model/analysis_options.yaml b/plugins/aves_model/analysis_options.yaml new file mode 100644 index 000000000..f04c6cf0f --- /dev/null +++ b/plugins/aves_model/analysis_options.yaml @@ -0,0 +1 @@ +include: ../../analysis_options.yaml diff --git a/plugins/aves_model/lib/aves_model.dart b/plugins/aves_model/lib/aves_model.dart new file mode 100644 index 000000000..d892a954c --- /dev/null +++ b/plugins/aves_model/lib/aves_model.dart @@ -0,0 +1,5 @@ +library aves_model; + +export 'src/entry/base.dart'; +export 'src/video/keys.dart'; +export 'src/video/stream_types.dart'; diff --git a/plugins/aves_model/lib/src/entry/base.dart b/plugins/aves_model/lib/src/entry/base.dart new file mode 100644 index 000000000..01966327d --- /dev/null +++ b/plugins/aves_model/lib/src/entry/base.dart @@ -0,0 +1,23 @@ +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; + +mixin AvesEntryBase { + int get id; + + String get uri; + + int? get pageId; + + int? get sizeBytes; + + int? get durationMillis; + + int get rotationDegrees; + + Size get displaySize; + + double get displayAspectRatio; + + Listenable get visualChangeNotifier; +} diff --git a/lib/model/video/keys.dart b/plugins/aves_model/lib/src/video/keys.dart similarity index 100% rename from lib/model/video/keys.dart rename to plugins/aves_model/lib/src/video/keys.dart diff --git a/plugins/aves_model/lib/src/video/stream_types.dart b/plugins/aves_model/lib/src/video/stream_types.dart new file mode 100644 index 000000000..d2be820f3 --- /dev/null +++ b/plugins/aves_model/lib/src/video/stream_types.dart @@ -0,0 +1,9 @@ +class MediaStreamTypes { + static const attachment = 'attachment'; + static const audio = 'audio'; + static const metadata = 'metadata'; + static const subtitle = 'subtitle'; + static const timedText = 'timedtext'; + static const unknown = 'unknown'; + static const video = 'video'; +} diff --git a/plugins/aves_model/pubspec.lock b/plugins/aves_model/pubspec.lock new file mode 100644 index 000000000..6e38c33fc --- /dev/null +++ b/plugins/aves_model/pubspec.lock @@ -0,0 +1,79 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + characters: + dependency: transitive + description: + name: characters + sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + url: "https://pub.dev" + source: hosted + version: "1.2.1" + collection: + dependency: transitive + description: + name: collection + sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + url: "https://pub.dev" + source: hosted + version: "1.17.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + url: "https://pub.dev" + source: hosted + version: "2.0.1" + js: + dependency: transitive + description: + name: js + sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + url: "https://pub.dev" + source: hosted + version: "0.6.5" + lints: + dependency: transitive + description: + name: lints + sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + url: "https://pub.dev" + source: hosted + version: "2.0.1" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + meta: + dependency: transitive + description: + name: meta + sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + url: "https://pub.dev" + source: hosted + version: "1.8.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" +sdks: + dart: ">=2.19.4 <3.0.0" diff --git a/plugins/aves_model/pubspec.yaml b/plugins/aves_model/pubspec.yaml new file mode 100644 index 000000000..440c3befc --- /dev/null +++ b/plugins/aves_model/pubspec.yaml @@ -0,0 +1,15 @@ +name: aves_model +version: 0.0.1 +publish_to: none + +environment: + sdk: '>=2.19.4 <3.0.0' + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + flutter_lints: + +flutter: diff --git a/plugins/aves_platform_meta/pubspec.lock b/plugins/aves_platform_meta/pubspec.lock index b0106103f..d4fa7deb8 100644 --- a/plugins/aves_platform_meta/pubspec.lock +++ b/plugins/aves_platform_meta/pubspec.lock @@ -84,4 +84,4 @@ packages: source: hosted version: "2.1.4" sdks: - dart: ">=2.19.0 <3.0.0" + dart: ">=2.19.4 <3.0.0" diff --git a/plugins/aves_platform_meta/pubspec.yaml b/plugins/aves_platform_meta/pubspec.yaml index 0cc9a2174..a33a46065 100644 --- a/plugins/aves_platform_meta/pubspec.yaml +++ b/plugins/aves_platform_meta/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.0 <3.0.0" + sdk: ">=2.19.4 <3.0.0" dependencies: flutter: diff --git a/plugins/aves_report/pubspec.lock b/plugins/aves_report/pubspec.lock index 8ac10f808..6e38c33fc 100644 --- a/plugins/aves_report/pubspec.lock +++ b/plugins/aves_report/pubspec.lock @@ -76,4 +76,4 @@ packages: source: hosted version: "2.1.4" sdks: - dart: ">=2.19.0 <3.0.0" + dart: ">=2.19.4 <3.0.0" diff --git a/plugins/aves_report/pubspec.yaml b/plugins/aves_report/pubspec.yaml index ce2029d14..25b1d5315 100644 --- a/plugins/aves_report/pubspec.yaml +++ b/plugins/aves_report/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.0 <3.0.0" + sdk: ">=2.19.4 <3.0.0" dependencies: flutter: diff --git a/plugins/aves_report_console/pubspec.lock b/plugins/aves_report_console/pubspec.lock index 9e4ee23d3..e92d2d71c 100644 --- a/plugins/aves_report_console/pubspec.lock +++ b/plugins/aves_report_console/pubspec.lock @@ -83,4 +83,4 @@ packages: source: hosted version: "2.1.4" sdks: - dart: ">=2.19.0 <3.0.0" + dart: ">=2.19.4 <3.0.0" diff --git a/plugins/aves_report_console/pubspec.yaml b/plugins/aves_report_console/pubspec.yaml index 2face2183..ee9350738 100644 --- a/plugins/aves_report_console/pubspec.yaml +++ b/plugins/aves_report_console/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.0 <3.0.0" + sdk: ">=2.19.4 <3.0.0" dependencies: flutter: diff --git a/plugins/aves_report_crashlytics/pubspec.lock b/plugins/aves_report_crashlytics/pubspec.lock index ec6cd74a7..e3180cc2c 100644 --- a/plugins/aves_report_crashlytics/pubspec.lock +++ b/plugins/aves_report_crashlytics/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: cb3a948a1eebbf8efd987c43f95418269930e912a88bc7b6a5a7658423133635 + sha256: "330d7fcbb72624f5b6d374af8b059b0ef4ba96ba5b8987f874964a1287eb617d" url: "https://pub.dev" source: hosted - version: "1.0.17" + version: "1.0.18" async: dependency: transitive description: @@ -68,10 +68,10 @@ packages: dependency: "direct main" description: name: firebase_core - sha256: "1c121a478af23755b0b93fd4aa70d3bd32a587dd51ef0a3979091ac0d2317d32" + sha256: "75f747cafd7cbd6c00b908e3a7aa59fc31593d46ba8165d9ee8a79e69464a394" url: "https://pub.dev" source: hosted - version: "2.7.1" + version: "2.8.0" firebase_core_platform_interface: dependency: transitive description: @@ -92,18 +92,18 @@ packages: dependency: "direct main" description: name: firebase_crashlytics - sha256: d4190479611f42a34a04630d9188f30b592b65013d0f19c408d5249998429864 + sha256: "5410b6ab2009fc6c181ca82e16525fdcb324c5851933bfbb49246543b1005ebc" url: "https://pub.dev" source: hosted - version: "3.0.16" + version: "3.0.17" firebase_crashlytics_platform_interface: dependency: transitive description: name: firebase_crashlytics_platform_interface - sha256: "667955b348b7729ed3d987956021a97fcdd05957df7c27b2d855e6b3f70e9521" + sha256: bf3d3791c51a8448413b5ebd8388fa7db239a3e165db6bbd8ab58ef0e8f61906 url: "https://pub.dev" source: hosted - version: "3.3.16" + version: "3.3.17" flutter: dependency: "direct main" description: flutter @@ -245,5 +245,5 @@ packages: source: hosted version: "2.1.4" sdks: - dart: ">=2.19.0 <3.0.0" + dart: ">=2.19.4 <3.0.0" flutter: ">=1.20.0" diff --git a/plugins/aves_report_crashlytics/pubspec.yaml b/plugins/aves_report_crashlytics/pubspec.yaml index aea210f04..75a38426c 100644 --- a/plugins/aves_report_crashlytics/pubspec.yaml +++ b/plugins/aves_report_crashlytics/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.0 <3.0.0" + sdk: ">=2.19.4 <3.0.0" dependencies: flutter: diff --git a/plugins/aves_services/pubspec.lock b/plugins/aves_services/pubspec.lock index 184bee844..acb670bd7 100644 --- a/plugins/aves_services/pubspec.lock +++ b/plugins/aves_services/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" aves_map: dependency: "direct main" description: @@ -290,5 +290,5 @@ packages: source: hosted version: "2.0.0" sdks: - dart: ">=2.19.0 <3.0.0" + dart: ">=2.19.4 <3.0.0" flutter: ">=3.3.0" diff --git a/plugins/aves_services/pubspec.yaml b/plugins/aves_services/pubspec.yaml index cc949a8db..d2e60c871 100644 --- a/plugins/aves_services/pubspec.yaml +++ b/plugins/aves_services/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.0 <3.0.0" + sdk: ">=2.19.4 <3.0.0" dependencies: flutter: diff --git a/plugins/aves_services_google/pubspec.lock b/plugins/aves_services_google/pubspec.lock index 4b7953c5b..7584f000a 100644 --- a/plugins/aves_services_google/pubspec.lock +++ b/plugins/aves_services_google/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" aves_map: dependency: "direct main" description: @@ -406,5 +406,5 @@ packages: source: hosted version: "2.0.0" sdks: - dart: ">=2.19.0 <3.0.0" + dart: ">=2.19.4 <3.0.0" flutter: ">=3.3.0" diff --git a/plugins/aves_services_google/pubspec.yaml b/plugins/aves_services_google/pubspec.yaml index e9f15853f..259ac7a04 100644 --- a/plugins/aves_services_google/pubspec.yaml +++ b/plugins/aves_services_google/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.0 <3.0.0" + sdk: ">=2.19.4 <3.0.0" dependencies: flutter: diff --git a/plugins/aves_services_huawei/pubspec.lock b/plugins/aves_services_huawei/pubspec.lock index 394c2aa13..91c859936 100644 --- a/plugins/aves_services_huawei/pubspec.lock +++ b/plugins/aves_services_huawei/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" aves_map: dependency: "direct main" description: @@ -336,5 +336,5 @@ packages: source: hosted version: "2.0.0" sdks: - dart: ">=2.19.0 <3.0.0" + dart: ">=2.19.4 <3.0.0" flutter: ">=3.3.0" diff --git a/plugins/aves_services_huawei/pubspec.yaml b/plugins/aves_services_huawei/pubspec.yaml index c50e4c239..b462c1a74 100644 --- a/plugins/aves_services_huawei/pubspec.yaml +++ b/plugins/aves_services_huawei/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.0 <3.0.0" + sdk: ">=2.19.4 <3.0.0" dependencies: flutter: diff --git a/plugins/aves_services_none/pubspec.lock b/plugins/aves_services_none/pubspec.lock index c43870e1d..4a10db68e 100644 --- a/plugins/aves_services_none/pubspec.lock +++ b/plugins/aves_services_none/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" aves_map: dependency: "direct main" description: @@ -297,5 +297,5 @@ packages: source: hosted version: "2.0.0" sdks: - dart: ">=2.19.0 <3.0.0" + dart: ">=2.19.4 <3.0.0" flutter: ">=3.3.0" diff --git a/plugins/aves_services_none/pubspec.yaml b/plugins/aves_services_none/pubspec.yaml index a2b172f39..691b2f7b8 100644 --- a/plugins/aves_services_none/pubspec.yaml +++ b/plugins/aves_services_none/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.0 <3.0.0" + sdk: ">=2.19.4 <3.0.0" dependencies: flutter: diff --git a/plugins/aves_ui/pubspec.lock b/plugins/aves_ui/pubspec.lock index 8ac10f808..6e38c33fc 100644 --- a/plugins/aves_ui/pubspec.lock +++ b/plugins/aves_ui/pubspec.lock @@ -76,4 +76,4 @@ packages: source: hosted version: "2.1.4" sdks: - dart: ">=2.19.0 <3.0.0" + dart: ">=2.19.4 <3.0.0" diff --git a/plugins/aves_ui/pubspec.yaml b/plugins/aves_ui/pubspec.yaml index c39d0df10..3b715f44f 100644 --- a/plugins/aves_ui/pubspec.yaml +++ b/plugins/aves_ui/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.0 <3.0.0" + sdk: ">=2.19.4 <3.0.0" dependencies: flutter: diff --git a/plugins/aves_utils/.gitignore b/plugins/aves_utils/.gitignore new file mode 100644 index 000000000..28124a571 --- /dev/null +++ b/plugins/aves_utils/.gitignore @@ -0,0 +1,30 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +#/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ diff --git a/plugins/aves_utils/.metadata b/plugins/aves_utils/.metadata new file mode 100644 index 000000000..ad910f56c --- /dev/null +++ b/plugins/aves_utils/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 2ad6cd72c040113b47ee9055e722606a490ef0da + channel: stable + +project_type: package diff --git a/plugins/aves_utils/analysis_options.yaml b/plugins/aves_utils/analysis_options.yaml new file mode 100644 index 000000000..f04c6cf0f --- /dev/null +++ b/plugins/aves_utils/analysis_options.yaml @@ -0,0 +1 @@ +include: ../../analysis_options.yaml diff --git a/plugins/aves_utils/lib/aves_utils.dart b/plugins/aves_utils/lib/aves_utils.dart new file mode 100644 index 000000000..1921022e4 --- /dev/null +++ b/plugins/aves_utils/lib/aves_utils.dart @@ -0,0 +1,4 @@ +library aves_utils; + +export 'src/change_notifier.dart'; +export 'src/optional_event_channel.dart'; diff --git a/lib/utils/change_notifier.dart b/plugins/aves_utils/lib/src/change_notifier.dart similarity index 100% rename from lib/utils/change_notifier.dart rename to plugins/aves_utils/lib/src/change_notifier.dart diff --git a/lib/services/common/optional_event_channel.dart b/plugins/aves_utils/lib/src/optional_event_channel.dart similarity index 100% rename from lib/services/common/optional_event_channel.dart rename to plugins/aves_utils/lib/src/optional_event_channel.dart diff --git a/plugins/aves_utils/pubspec.lock b/plugins/aves_utils/pubspec.lock new file mode 100644 index 000000000..6e38c33fc --- /dev/null +++ b/plugins/aves_utils/pubspec.lock @@ -0,0 +1,79 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + characters: + dependency: transitive + description: + name: characters + sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + url: "https://pub.dev" + source: hosted + version: "1.2.1" + collection: + dependency: transitive + description: + name: collection + sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + url: "https://pub.dev" + source: hosted + version: "1.17.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + url: "https://pub.dev" + source: hosted + version: "2.0.1" + js: + dependency: transitive + description: + name: js + sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + url: "https://pub.dev" + source: hosted + version: "0.6.5" + lints: + dependency: transitive + description: + name: lints + sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + url: "https://pub.dev" + source: hosted + version: "2.0.1" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + meta: + dependency: transitive + description: + name: meta + sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + url: "https://pub.dev" + source: hosted + version: "1.8.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" +sdks: + dart: ">=2.19.4 <3.0.0" diff --git a/plugins/aves_utils/pubspec.yaml b/plugins/aves_utils/pubspec.yaml new file mode 100644 index 000000000..3828c7ab0 --- /dev/null +++ b/plugins/aves_utils/pubspec.yaml @@ -0,0 +1,15 @@ +name: aves_utils +version: 0.0.1 +publish_to: none + +environment: + sdk: '>=2.19.4 <3.0.0' + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + flutter_lints: + +flutter: diff --git a/plugins/aves_video/.gitignore b/plugins/aves_video/.gitignore new file mode 100644 index 000000000..28124a571 --- /dev/null +++ b/plugins/aves_video/.gitignore @@ -0,0 +1,30 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +#/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ diff --git a/plugins/aves_video/.metadata b/plugins/aves_video/.metadata new file mode 100644 index 000000000..ad910f56c --- /dev/null +++ b/plugins/aves_video/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 2ad6cd72c040113b47ee9055e722606a490ef0da + channel: stable + +project_type: package diff --git a/plugins/aves_video/analysis_options.yaml b/plugins/aves_video/analysis_options.yaml new file mode 100644 index 000000000..f04c6cf0f --- /dev/null +++ b/plugins/aves_video/analysis_options.yaml @@ -0,0 +1 @@ +include: ../../analysis_options.yaml diff --git a/plugins/aves_video/lib/aves_video.dart b/plugins/aves_video/lib/aves_video.dart new file mode 100644 index 000000000..6c308814d --- /dev/null +++ b/plugins/aves_video/lib/aves_video.dart @@ -0,0 +1,4 @@ +library aves_video; + +export 'src/controller.dart'; +export 'src/stream.dart'; diff --git a/plugins/aves_video/lib/src/controller.dart b/plugins/aves_video/lib/src/controller.dart new file mode 100644 index 000000000..3d658ecb8 --- /dev/null +++ b/plugins/aves_video/lib/src/controller.dart @@ -0,0 +1,116 @@ +import 'dart:async'; + +import 'package:aves_model/aves_model.dart'; +import 'package:aves_video/src/stream.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; + +abstract class AvesVideoController { + final AvesEntryBase _entry; + final PlaybackStateHandler playbackStateHandler; + + AvesEntryBase get entry => _entry; + + static const resumeTimeSaveMinDuration = Duration(minutes: 2); + + AvesVideoController(AvesEntryBase entry, {required this.playbackStateHandler}) : _entry = entry { + entry.visualChangeNotifier.addListener(onVisualChanged); + } + + @mustCallSuper + Future dispose() async { + _entry.visualChangeNotifier.removeListener(onVisualChanged); + await _savePlaybackState(); + } + + Future _savePlaybackState() async { + if (!isReady || duration < resumeTimeSaveMinDuration.inMilliseconds) return; + await playbackStateHandler.saveResumeTime(entryId: _entry.id, position: currentPosition, progress: progress); + } + + Future getResumeTime(BuildContext context) => playbackStateHandler.getResumeTime(entryId: _entry.id, context: context); + + void onVisualChanged(); + + Future play(); + + Future pause(); + + Future seekTo(int targetMillis); + + Future seekToProgress(double progress) => seekTo((duration * progress).toInt()); + + Listenable get playCompletedListenable; + + VideoStatus get status; + + Stream get statusStream; + + Stream get volumeStream; + + Stream get speedStream; + + bool get isReady; + + bool get isPlaying => status == VideoStatus.playing; + + int get duration; + + int get currentPosition; + + double get progress { + final _duration = duration; + return _duration != 0 ? currentPosition.toDouble() / _duration : 0; + } + + Stream get positionStream; + + Stream get timedTextStream; + + ValueNotifier get canCaptureFrameNotifier; + + ValueNotifier get canMuteNotifier; + + ValueNotifier get canSetSpeedNotifier; + + ValueNotifier get canSelectStreamNotifier; + + ValueNotifier get sarNotifier; + + bool get isMuted; + + double get speed; + + double get minSpeed; + + double get maxSpeed; + + set speed(double speed); + + Future selectStream(MediaStreamType type, MediaStreamSummary? selected); + + Future getSelectedStream(MediaStreamType type); + + List get streams; + + Future captureFrame(); + + Future mute(bool muted); + + Widget buildPlayerWidget(BuildContext context); +} + +enum VideoStatus { + idle, + initialized, + paused, + playing, + completed, + error, +} + +abstract class PlaybackStateHandler { + Future getResumeTime({required int entryId, required BuildContext context}); + + Future saveResumeTime({required int entryId, required int position, required double progress}); +} diff --git a/plugins/aves_video/lib/src/stream.dart b/plugins/aves_video/lib/src/stream.dart new file mode 100644 index 000000000..93c4bb05b --- /dev/null +++ b/plugins/aves_video/lib/src/stream.dart @@ -0,0 +1,22 @@ +import 'package:flutter/foundation.dart'; + +enum MediaStreamType { video, audio, text } + +class MediaStreamSummary { + final MediaStreamType type; + final int? index, width, height; + final String? codecName, language, title; + + const MediaStreamSummary({ + required this.type, + required this.index, + required this.codecName, + required this.language, + required this.title, + required this.width, + required this.height, + }); + + @override + String toString() => '$runtimeType#${shortHash(this)}{type: type, index: $index, codecName: $codecName, language: $language, title: $title, width: $width, height: $height}'; +} diff --git a/plugins/aves_video/pubspec.lock b/plugins/aves_video/pubspec.lock new file mode 100644 index 000000000..3902a0a45 --- /dev/null +++ b/plugins/aves_video/pubspec.lock @@ -0,0 +1,86 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + aves_model: + dependency: "direct main" + description: + path: "../aves_model" + relative: true + source: path + version: "0.0.1" + characters: + dependency: transitive + description: + name: characters + sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + url: "https://pub.dev" + source: hosted + version: "1.2.1" + collection: + dependency: transitive + description: + name: collection + sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + url: "https://pub.dev" + source: hosted + version: "1.17.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + url: "https://pub.dev" + source: hosted + version: "2.0.1" + js: + dependency: transitive + description: + name: js + sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + url: "https://pub.dev" + source: hosted + version: "0.6.5" + lints: + dependency: transitive + description: + name: lints + sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + url: "https://pub.dev" + source: hosted + version: "2.0.1" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + meta: + dependency: transitive + description: + name: meta + sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + url: "https://pub.dev" + source: hosted + version: "1.8.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" +sdks: + dart: ">=2.19.4 <3.0.0" diff --git a/plugins/aves_video/pubspec.yaml b/plugins/aves_video/pubspec.yaml new file mode 100644 index 000000000..c4362c548 --- /dev/null +++ b/plugins/aves_video/pubspec.yaml @@ -0,0 +1,17 @@ +name: aves_video +version: 0.0.1 +publish_to: none + +environment: + sdk: '>=2.19.4 <3.0.0' + +dependencies: + flutter: + sdk: flutter + aves_model: + path: ../aves_model + +dev_dependencies: + flutter_lints: + +flutter: diff --git a/pubspec.lock b/pubspec.lock index 33e622d23..9aa79cb26 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: cb3a948a1eebbf8efd987c43f95418269930e912a88bc7b6a5a7658423133635 + sha256: "330d7fcbb72624f5b6d374af8b059b0ef4ba96ba5b8987f874964a1287eb617d" url: "https://pub.dev" source: hosted - version: "1.0.17" + version: "1.0.18" analyzer: dependency: transitive description: @@ -63,6 +63,13 @@ packages: relative: true source: path version: "0.0.1" + aves_model: + dependency: "direct main" + description: + path: "plugins/aves_model" + relative: true + source: path + version: "0.0.1" aves_report: dependency: "direct main" description: @@ -98,6 +105,20 @@ packages: relative: true source: path version: "0.0.1" + aves_utils: + dependency: "direct main" + description: + path: "plugins/aves_utils" + relative: true + source: path + version: "0.0.1" + aves_video: + dependency: "direct main" + description: + path: "plugins/aves_video" + relative: true + source: path + version: "0.0.1" barcode: dependency: transitive description: @@ -305,7 +326,7 @@ packages: description: path: "." ref: aves - resolved-ref: "69193da9a6b7f8b5cec5627253982adc303dbf46" + resolved-ref: "1ff8e6d82466939997c0e04a8e28b04f40641728" url: "https://github.com/deckerst/fijkplayer.git" source: git version: "0.10.0" @@ -321,10 +342,10 @@ packages: dependency: transitive description: name: firebase_core - sha256: "1c121a478af23755b0b93fd4aa70d3bd32a587dd51ef0a3979091ac0d2317d32" + sha256: "75f747cafd7cbd6c00b908e3a7aa59fc31593d46ba8165d9ee8a79e69464a394" url: "https://pub.dev" source: hosted - version: "2.7.1" + version: "2.8.0" firebase_core_platform_interface: dependency: transitive description: @@ -345,18 +366,18 @@ packages: dependency: transitive description: name: firebase_crashlytics - sha256: d4190479611f42a34a04630d9188f30b592b65013d0f19c408d5249998429864 + sha256: "5410b6ab2009fc6c181ca82e16525fdcb324c5851933bfbb49246543b1005ebc" url: "https://pub.dev" source: hosted - version: "3.0.16" + version: "3.0.17" firebase_crashlytics_platform_interface: dependency: transitive description: name: firebase_crashlytics_platform_interface - sha256: "667955b348b7729ed3d987956021a97fcdd05957df7c27b2d855e6b3f70e9521" + sha256: bf3d3791c51a8448413b5ebd8388fa7db239a3e165db6bbd8ab58ef0e8f61906 url: "https://pub.dev" source: hosted - version: "3.3.16" + version: "3.3.17" flex_color_picker: dependency: "direct main" description: @@ -1214,18 +1235,18 @@ packages: dependency: "direct main" description: name: sqflite - sha256: "851d5040552cf911f4cabda08d003eca76b27da3ed0002978272e27c8fbf8ecc" + sha256: "500d6fec583d2c021f2d25a056d96654f910662c64f836cd2063167b8f1fa758" url: "https://pub.dev" source: hosted - version: "2.2.5" + version: "2.2.6" sqflite_common: dependency: transitive description: name: sqflite_common - sha256: bfd6973aaeeb93475bc0d875ac9aefddf7965ef22ce09790eb963992ffc5183f + sha256: "963dad8c4aa2f814ce7d2d5b1da2f36f31bd1a439d8f27e3dc189bb9d26bc684" url: "https://pub.dev" source: hosted - version: "2.4.2+2" + version: "2.4.3" stack_trace: dependency: transitive description: @@ -1508,5 +1529,5 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">=2.19.0 <3.0.0" + dart: ">=2.19.4 <3.0.0" flutter: ">=3.7.7" diff --git a/pubspec.yaml b/pubspec.yaml index 9d33a92ca..e34ee4d0c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,14 +7,14 @@ repository: https://github.com/deckerst/aves # - play changelog: /whatsnew/whatsnew-en-US # - izzy changelog: /fastlane/metadata/android/en-US/changelogs/XX01.txt # - libre changelog: /fastlane/metadata/android/en-US/changelogs/XX.txt -version: 1.8.3+94 +version: 1.8.4+95 publish_to: none environment: # this project bundles Flutter SDK via `flutter_wrapper` # cf https://github.com/passsy/flutter_wrapper flutter: 3.7.7 - sdk: ">=2.19.0 <3.0.0" + sdk: ">=2.19.4 <3.0.0" # following https://github.blog/2021-09-01-improving-git-protocol-security-github/ # dependency GitHub repos should be referenced via `https://`, not `git://` @@ -29,6 +29,8 @@ dependencies: path: plugins/aves_magnifier aves_map: path: plugins/aves_map + aves_model: + path: plugins/aves_model aves_report: path: plugins/aves_report aves_report_platform: @@ -37,8 +39,12 @@ dependencies: path: plugins/aves_services aves_services_platform: path: plugins/aves_services_google + aves_video: + path: plugins/aves_video aves_ui: path: plugins/aves_ui + aves_utils: + path: plugins/aves_utils charts_flutter: git: url: https://github.com/fzyzcjy/charts.git @@ -157,6 +163,10 @@ flutter: # `OutputBuffer` in `/services/common/output_buffer.dart` # adapts from Flutter v3.3.3 `_OutputBuffer` in `/foundation/consolidate_response.dart` # +# `TvLicensePage` in `/widgets/about/tv_license_page.dart` +# adapts from Flutter v3.7.7 `_LicenseData` in `/material/about.dart` +# and `_PackageLicensePage` in `/material/about.dart` +# # `OverlaySnackBar` in `/widgets/common/action_mixins/overlay_snack_bar.dart` # adapts from Flutter v3.3.3 `SnackBar` in `/material/snack_bar.dart` # diff --git a/test/fake/media_fetch_service.dart b/test/fake/media_fetch_service.dart index fa9645ace..50dd1d969 100644 --- a/test/fake/media_fetch_service.dart +++ b/test/fake/media_fetch_service.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/services/media/media_fetch_service.dart'; import 'package:collection/collection.dart'; import 'package:test/fake.dart'; diff --git a/test/fake/media_store_service.dart b/test/fake/media_store_service.dart index eb9082ee2..d2f2ba4ef 100644 --- a/test/fake/media_store_service.dart +++ b/test/fake/media_store_service.dart @@ -1,4 +1,5 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/origins.dart'; import 'package:aves/ref/mime_types.dart'; import 'package:aves/services/common/image_op_events.dart'; import 'package:aves/services/media/media_store_service.dart'; diff --git a/test/fake/metadata_db.dart b/test/fake/metadata_db.dart index 408ac2c3c..9f38ee70e 100644 --- a/test/fake/metadata_db.dart +++ b/test/fake/metadata_db.dart @@ -1,6 +1,6 @@ import 'package:aves/model/covers.dart'; import 'package:aves/model/db/db_metadata.dart'; -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/favourites.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/metadata/address.dart'; diff --git a/test/fake/metadata_fetch_service.dart b/test/fake/metadata_fetch_service.dart index 1891146ea..125ac4d6e 100644 --- a/test/fake/metadata_fetch_service.dart +++ b/test/fake/metadata_fetch_service.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry.dart'; +import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/metadata/catalog.dart'; import 'package:aves/services/metadata/metadata_fetch_service.dart'; import 'package:flutter/foundation.dart'; diff --git a/test/model/collection_source_test.dart b/test/model/collection_source_test.dart index 20edbce4c..ecc7127af 100644 --- a/test/model/collection_source_test.dart +++ b/test/model/collection_source_test.dart @@ -4,6 +4,7 @@ import 'package:aves/model/actions/move_type.dart'; import 'package:aves/model/availability.dart'; import 'package:aves/model/covers.dart'; import 'package:aves/model/db/db_metadata.dart'; +import 'package:aves/model/entry/extensions/favourites.dart'; import 'package:aves/model/favourites.dart'; import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/tag.dart'; diff --git a/test/utils/xmp_utils_test.dart b/test/utils/xmp_utils_test.dart index 489ee1677..84197b9c1 100644 --- a/test/utils/xmp_utils_test.dart +++ b/test/utils/xmp_utils_test.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/entry_metadata_edition.dart'; +import 'package:aves/model/entry/extensions/metadata_edition.dart'; import 'package:aves/utils/xmp_utils.dart'; import 'package:test/test.dart'; import 'package:xml/xml.dart'; diff --git a/whatsnew/whatsnew-en-US b/whatsnew/whatsnew-en-US index 7615d4920..3ee89824b 100644 --- a/whatsnew/whatsnew-en-US +++ b/whatsnew/whatsnew-en-US @@ -1,4 +1,4 @@ -In v1.8.3: +In v1.8.4: - view items in full-screen when selecting them - watch videos using picture-in-picture - navigate with TalkBack