hash/== with equatable

This commit is contained in:
Thibault Deckers 2021-07-28 17:38:22 +09:00
parent d04adf52a2
commit 62c199c745
35 changed files with 224 additions and 480 deletions

View file

@ -55,6 +55,10 @@ android {
defaultConfig { defaultConfig {
applicationId appId applicationId appId
// minSdkVersion constraints:
// - Flutter & other plugins: 16
// - google_maps_flutter v2.0.5: 20
// - Aves native: 19
minSdkVersion 20 minSdkVersion 20
targetSdkVersion 30 targetSdkVersion 30
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()

View file

@ -1,6 +1,7 @@
import 'dart:ui' as ui show Codec; import 'dart:ui' as ui show Codec;
import 'package:aves/services/android_app_service.dart'; import 'package:aves/services/android_app_service.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@ -49,23 +50,18 @@ class AppIconImage extends ImageProvider<AppIconImageKey> {
} }
} }
class AppIconImageKey { @immutable
class AppIconImageKey extends Equatable {
final String packageName; final String packageName;
final double size; final double size;
final double scale; final double scale;
@override
List<Object?> get props => [packageName, size, scale];
const AppIconImageKey({ const AppIconImageKey({
required this.packageName, required this.packageName,
required this.size, required this.size,
this.scale = 1.0, this.scale = 1.0,
}); });
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is AppIconImageKey && other.packageName == packageName && other.size == size && other.scale == scale;
}
@override
int get hashCode => hashValues(packageName, size, scale);
} }

View file

@ -3,6 +3,7 @@ import 'dart:math';
import 'dart:ui' as ui show Codec; import 'dart:ui' as ui show Codec;
import 'package:aves/services/services.dart'; import 'package:aves/services/services.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@ -62,7 +63,8 @@ class RegionProvider extends ImageProvider<RegionProviderKey> {
void pause() => imageFileService.cancelRegion(key); void pause() => imageFileService.cancelRegion(key);
} }
class RegionProviderKey { @immutable
class RegionProviderKey extends Equatable {
// do not store the entry as it is, because the key should be constant // do not store the entry as it is, because the key should be constant
// but the entry attributes may change over time // but the entry attributes may change over time
final String uri, mimeType; final String uri, mimeType;
@ -72,6 +74,9 @@ class RegionProviderKey {
final Rectangle<int> region; final Rectangle<int> region;
final Size imageSize; final Size imageSize;
@override
List<Object?> get props => [uri, pageId, rotationDegrees, isFlipped, sampleSize, region, imageSize];
const RegionProviderKey({ const RegionProviderKey({
required this.uri, required this.uri,
required this.mimeType, required this.mimeType,
@ -83,24 +88,6 @@ class RegionProviderKey {
required this.imageSize, required this.imageSize,
}); });
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is RegionProviderKey && other.uri == uri && other.mimeType == mimeType && other.pageId == pageId && other.rotationDegrees == rotationDegrees && other.isFlipped == isFlipped && other.sampleSize == sampleSize && other.region == region && other.imageSize == imageSize;
}
@override
int get hashCode => hashValues(
uri,
mimeType,
pageId,
rotationDegrees,
isFlipped,
sampleSize,
region,
imageSize,
);
@override @override
String toString() => '$runtimeType#${shortHash(this)}{uri=$uri, mimeType=$mimeType, pageId=$pageId, rotationDegrees=$rotationDegrees, isFlipped=$isFlipped, sampleSize=$sampleSize, region=$region, imageSize=$imageSize}'; String toString() => '$runtimeType#${shortHash(this)}{uri=$uri, mimeType=$mimeType, pageId=$pageId, rotationDegrees=$rotationDegrees, isFlipped=$isFlipped, sampleSize=$sampleSize, region=$region, imageSize=$imageSize}';
} }

View file

@ -1,6 +1,7 @@
import 'dart:ui' as ui show Codec; import 'dart:ui' as ui show Codec;
import 'package:aves/services/services.dart'; import 'package:aves/services/services.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@ -63,7 +64,8 @@ class ThumbnailProvider extends ImageProvider<ThumbnailProviderKey> {
void pause() => imageFileService.cancelThumbnail(key); void pause() => imageFileService.cancelThumbnail(key);
} }
class ThumbnailProviderKey { @immutable
class ThumbnailProviderKey extends Equatable {
// do not store the entry as it is, because the key should be constant // do not store the entry as it is, because the key should be constant
// but the entry attributes may change over time // but the entry attributes may change over time
final String uri, mimeType; final String uri, mimeType;
@ -73,6 +75,9 @@ class ThumbnailProviderKey {
final int dateModifiedSecs; final int dateModifiedSecs;
final double extent; final double extent;
@override
List<Object?> get props => [uri, pageId, dateModifiedSecs, extent];
const ThumbnailProviderKey({ const ThumbnailProviderKey({
required this.uri, required this.uri,
required this.mimeType, required this.mimeType,
@ -83,20 +88,6 @@ class ThumbnailProviderKey {
this.extent = 0, this.extent = 0,
}); });
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is ThumbnailProviderKey && other.uri == uri && other.pageId == pageId && other.dateModifiedSecs == dateModifiedSecs && other.extent == extent;
}
@override
int get hashCode => hashValues(
uri,
pageId,
dateModifiedSecs,
extent,
);
@override @override
String toString() => '$runtimeType#${shortHash(this)}{uri=$uri, mimeType=$mimeType, pageId=$pageId, rotationDegrees=$rotationDegrees, isFlipped=$isFlipped, dateModifiedSecs=$dateModifiedSecs, extent=$extent}'; String toString() => '$runtimeType#${shortHash(this)}{uri=$uri, mimeType=$mimeType, pageId=$pageId, rotationDegrees=$rotationDegrees, isFlipped=$isFlipped, dateModifiedSecs=$dateModifiedSecs, extent=$extent}';
} }

View file

@ -3,15 +3,20 @@ import 'dart:ui' as ui show Codec;
import 'package:aves/services/services.dart'; import 'package:aves/services/services.dart';
import 'package:aves/utils/pedantic.dart'; import 'package:aves/utils/pedantic.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
class UriImage extends ImageProvider<UriImage> { @immutable
class UriImage extends ImageProvider<UriImage> with EquatableMixin {
final String uri, mimeType; final String uri, mimeType;
final int? pageId, rotationDegrees, expectedContentLength; final int? pageId, rotationDegrees, expectedContentLength;
final bool isFlipped; final bool isFlipped;
final double scale; final double scale;
@override
List<Object?> get props => [uri, pageId, rotationDegrees, isFlipped, scale];
const UriImage({ const UriImage({
required this.uri, required this.uri,
required this.mimeType, required this.mimeType,
@ -71,22 +76,6 @@ class UriImage extends ImageProvider<UriImage> {
} }
} }
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is UriImage && other.uri == uri && other.mimeType == mimeType && other.rotationDegrees == rotationDegrees && other.isFlipped == isFlipped && other.pageId == pageId && other.scale == scale;
}
@override
int get hashCode => hashValues(
uri,
mimeType,
rotationDegrees,
isFlipped,
pageId,
scale,
);
@override @override
String toString() => '$runtimeType#${shortHash(this)}{uri=$uri, mimeType=$mimeType, rotationDegrees=$rotationDegrees, isFlipped=$isFlipped, pageId=$pageId, scale=$scale}'; String toString() => '$runtimeType#${shortHash(this)}{uri=$uri, mimeType=$mimeType, rotationDegrees=$rotationDegrees, isFlipped=$isFlipped, pageId=$pageId, scale=$scale}';
} }

View file

@ -3,6 +3,7 @@ import 'package:aves/model/filters/album.dart';
import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/filters.dart';
import 'package:aves/services/services.dart'; import 'package:aves/services/services.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@ -77,10 +78,13 @@ class Covers with ChangeNotifier {
} }
@immutable @immutable
class CoverRow { class CoverRow extends Equatable {
final CollectionFilter filter; final CollectionFilter filter;
final int contentId; final int contentId;
@override
List<Object?> get props => [filter, contentId];
const CoverRow({ const CoverRow({
required this.filter, required this.filter,
required this.contentId, required this.contentId,
@ -99,16 +103,4 @@ class CoverRow {
'filter': filter.toJson(), 'filter': filter.toJson(),
'contentId': contentId, 'contentId': contentId,
}; };
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is CoverRow && other.filter == filter && other.contentId == contentId;
}
@override
int get hashCode => hashValues(filter, contentId);
@override
String toString() => '$runtimeType#${shortHash(this)}{filter=$filter, contentId=$contentId}';
} }

View file

@ -1,7 +1,7 @@
import 'package:aves/model/entry.dart'; import 'package:aves/model/entry.dart';
import 'package:aves/services/services.dart'; import 'package:aves/services/services.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
final Favourites favourites = Favourites._private(); final Favourites favourites = Favourites._private();
@ -62,10 +62,13 @@ class Favourites with ChangeNotifier {
} }
@immutable @immutable
class FavouriteRow { class FavouriteRow extends Equatable {
final int contentId; final int contentId;
final String path; final String path;
@override
List<Object?> get props => [contentId, path];
const FavouriteRow({ const FavouriteRow({
required this.contentId, required this.contentId,
required this.path, required this.path,
@ -82,16 +85,4 @@ class FavouriteRow {
'contentId': contentId, 'contentId': contentId,
'path': path, 'path': path,
}; };
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is FavouriteRow && other.contentId == contentId && other.path == path;
}
@override
int get hashCode => hashValues(contentId, path);
@override
String toString() => '$runtimeType#${shortHash(this)}{contentId=$contentId, path=$path}';
} }

View file

@ -16,6 +16,9 @@ class AlbumFilter extends CollectionFilter {
final String album; final String album;
final String? displayName; final String? displayName;
@override
List<Object?> get props => [album];
const AlbumFilter(this.album, this.displayName); const AlbumFilter(this.album, this.displayName);
AlbumFilter.fromMap(Map<String, dynamic> json) AlbumFilter.fromMap(Map<String, dynamic> json)
@ -78,16 +81,4 @@ class AlbumFilter extends CollectionFilter {
// key `album-{path}` is expected by test driver // key `album-{path}` is expected by test driver
@override @override
String get key => '$type-$album'; String get key => '$type-$album';
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is AlbumFilter && other.album == album;
}
@override
int get hashCode => hashValues(type, album);
@override
String toString() => '$runtimeType#${shortHash(this)}{album=$album}';
} }

View file

@ -10,6 +10,9 @@ class FavouriteFilter extends CollectionFilter {
static const instance = FavouriteFilter._private(); static const instance = FavouriteFilter._private();
@override
List<Object?> get props => [];
const FavouriteFilter._private(); const FavouriteFilter._private();
@override @override
@ -37,13 +40,4 @@ class FavouriteFilter extends CollectionFilter {
@override @override
String get key => type; String get key => type;
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is FavouriteFilter;
}
@override
int get hashCode => type.hashCode;
} }

View file

@ -11,10 +11,12 @@ import 'package:aves/model/filters/tag.dart';
import 'package:aves/model/filters/type.dart'; import 'package:aves/model/filters/type.dart';
import 'package:aves/utils/color_utils.dart'; import 'package:aves/utils/color_utils.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
abstract class CollectionFilter implements Comparable<CollectionFilter> { @immutable
abstract class CollectionFilter extends Equatable implements Comparable<CollectionFilter> {
static const List<String> categoryOrder = [ static const List<String> categoryOrder = [
QueryFilter.type, QueryFilter.type,
FavouriteFilter.type, FavouriteFilter.type,
@ -88,20 +90,15 @@ abstract class CollectionFilter implements Comparable<CollectionFilter> {
} }
} }
class FilterGridItem<T extends CollectionFilter> { @immutable
class FilterGridItem<T extends CollectionFilter> with EquatableMixin {
final T filter; final T filter;
final AvesEntry? entry; final AvesEntry? entry;
@override
List<Object?> get props => [filter, entry?.uri];
const FilterGridItem(this.filter, this.entry); const FilterGridItem(this.filter, this.entry);
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is FilterGridItem && other.filter == filter && other.entry == entry;
}
@override
int get hashCode => hashValues(filter, entry);
} }
typedef EntryFilter = bool Function(AvesEntry); typedef EntryFilter = bool Function(AvesEntry);

View file

@ -2,7 +2,6 @@ import 'package:aves/model/filters/filters.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
class LocationFilter extends CollectionFilter { class LocationFilter extends CollectionFilter {
@ -10,14 +9,17 @@ class LocationFilter extends CollectionFilter {
static const locationSeparator = ';'; static const locationSeparator = ';';
final LocationLevel level; final LocationLevel level;
String _location; late final String _location;
String? _countryCode; late final String? _countryCode;
late EntryFilter _test; late final EntryFilter _test;
LocationFilter(this.level, this._location) { @override
final split = _location.split(locationSeparator); List<Object?> get props => [level, _location, _countryCode];
if (split.isNotEmpty) _location = split[0];
if (split.length > 1) _countryCode = split[1]; LocationFilter(this.level, String location) {
final split = location.split(locationSeparator);
_location = split.isNotEmpty ? split[0] : location;
_countryCode = split.length > 1 ? split[1] : null;
if (_location.isEmpty) { if (_location.isEmpty) {
_test = (entry) => !entry.hasGps; _test = (entry) => !entry.hasGps;
@ -75,18 +77,6 @@ class LocationFilter extends CollectionFilter {
@override @override
String get key => '$type-$level-$_location'; String get key => '$type-$level-$_location';
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is LocationFilter && other.level == level && other._location == _location;
}
@override
int get hashCode => hashValues(type, level, _location);
@override
String toString() => '$runtimeType#${shortHash(this)}{level=$level, location=$_location}';
// U+0041 Latin Capital letter A // U+0041 Latin Capital letter A
// U+1F1E6 🇦 REGIONAL INDICATOR SYMBOL LETTER A // U+1F1E6 🇦 REGIONAL INDICATOR SYMBOL LETTER A
static const _countryCodeToFlagDiff = 0x1F1E6 - 0x0041; static const _countryCodeToFlagDiff = 0x1F1E6 - 0x0041;

View file

@ -3,20 +3,22 @@ import 'package:aves/ref/mime_types.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/utils/mime_utils.dart'; import 'package:aves/utils/mime_utils.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
class MimeFilter extends CollectionFilter { class MimeFilter extends CollectionFilter {
static const type = 'mime'; static const type = 'mime';
final String mime; final String mime;
late EntryFilter _test; late final EntryFilter _test;
late String _label; late final String _label;
late IconData _icon; late final IconData _icon;
static final image = MimeFilter(MimeTypes.anyImage); static final image = MimeFilter(MimeTypes.anyImage);
static final video = MimeFilter(MimeTypes.anyVideo); static final video = MimeFilter(MimeTypes.anyVideo);
@override
List<Object?> get props => [mime];
MimeFilter(this.mime) { MimeFilter(this.mime) {
IconData? icon; IconData? icon;
var lowMime = mime.toLowerCase(); var lowMime = mime.toLowerCase();
@ -73,16 +75,4 @@ class MimeFilter extends CollectionFilter {
@override @override
String get key => '$type-$mime'; String get key => '$type-$mime';
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is MimeFilter && other.mime == mime;
}
@override
int get hashCode => hashValues(type, mime);
@override
String toString() => '$runtimeType#${shortHash(this)}{mime=$mime}';
} }

View file

@ -1,12 +1,13 @@
import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/filters.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
class PathFilter extends CollectionFilter { class PathFilter extends CollectionFilter {
static const type = 'path'; static const type = 'path';
final String path; final String path;
@override
List<Object?> get props => [path];
const PathFilter(this.path); const PathFilter(this.path);
PathFilter.fromMap(Map<String, dynamic> json) PathFilter.fromMap(Map<String, dynamic> json)
@ -31,16 +32,4 @@ class PathFilter extends CollectionFilter {
@override @override
String get key => '$type-$path'; String get key => '$type-$path';
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is PathFilter && other.path == path;
}
@override
int get hashCode => hashValues(type, path);
@override
String toString() => '$runtimeType#${shortHash(this)}{path=$path}';
} }

View file

@ -12,7 +12,10 @@ class QueryFilter extends CollectionFilter {
final String query; final String query;
final bool colorful; final bool colorful;
late EntryFilter _test; late final EntryFilter _test;
@override
List<Object?> get props => [query];
QueryFilter(this.query, {this.colorful = true}) { QueryFilter(this.query, {this.colorful = true}) {
var upQuery = query.toUpperCase(); var upQuery = query.toUpperCase();
@ -63,16 +66,4 @@ class QueryFilter extends CollectionFilter {
@override @override
String get key => '$type-$query'; String get key => '$type-$query';
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is QueryFilter && other.query == query;
}
@override
int get hashCode => hashValues(type, query);
@override
String toString() => '$runtimeType#${shortHash(this)}{query=$query}';
} }

View file

@ -1,14 +1,16 @@
import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/filters.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
class TagFilter extends CollectionFilter { class TagFilter extends CollectionFilter {
static const type = 'tag'; static const type = 'tag';
final String tag; final String tag;
late EntryFilter _test; late final EntryFilter _test;
@override
List<Object?> get props => [tag];
TagFilter(this.tag) { TagFilter(this.tag) {
if (tag.isEmpty) { if (tag.isEmpty) {
@ -49,16 +51,4 @@ class TagFilter extends CollectionFilter {
@override @override
String get key => '$type-$tag'; String get key => '$type-$tag';
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is TagFilter && other.tag == tag;
}
@override
int get hashCode => hashValues(type, tag);
@override
String toString() => '$runtimeType#${shortHash(this)}{tag=$tag}';
} }

View file

@ -1,7 +1,6 @@
import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/filters.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
class TypeFilter extends CollectionFilter { class TypeFilter extends CollectionFilter {
@ -14,8 +13,8 @@ class TypeFilter extends CollectionFilter {
static const _sphericalVideo = 'spherical_video'; // subset of videos static const _sphericalVideo = 'spherical_video'; // subset of videos
final String itemType; final String itemType;
late EntryFilter _test; late final EntryFilter _test;
late IconData _icon; late final IconData _icon;
static final animated = TypeFilter._private(_animated); static final animated = TypeFilter._private(_animated);
static final geotiff = TypeFilter._private(_geotiff); static final geotiff = TypeFilter._private(_geotiff);
@ -23,12 +22,19 @@ class TypeFilter extends CollectionFilter {
static final panorama = TypeFilter._private(_panorama); static final panorama = TypeFilter._private(_panorama);
static final sphericalVideo = TypeFilter._private(_sphericalVideo); static final sphericalVideo = TypeFilter._private(_sphericalVideo);
@override
List<Object?> get props => [itemType];
TypeFilter._private(this.itemType) { TypeFilter._private(this.itemType) {
switch (itemType) { switch (itemType) {
case _animated: case _animated:
_test = (entry) => entry.isAnimated; _test = (entry) => entry.isAnimated;
_icon = AIcons.animated; _icon = AIcons.animated;
break; break;
case _geotiff:
_test = (entry) => entry.isGeotiff;
_icon = AIcons.geo;
break;
case _motionPhoto: case _motionPhoto:
_test = (entry) => entry.isMotionPhoto; _test = (entry) => entry.isMotionPhoto;
_icon = AIcons.motionPhoto; _icon = AIcons.motionPhoto;
@ -41,10 +47,6 @@ class TypeFilter extends CollectionFilter {
_test = (entry) => entry.isVideo && entry.is360; _test = (entry) => entry.isVideo && entry.is360;
_icon = AIcons.threeSixty; _icon = AIcons.threeSixty;
break; break;
case _geotiff:
_test = (entry) => entry.isGeotiff;
_icon = AIcons.geo;
break;
} }
} }
@ -91,16 +93,4 @@ class TypeFilter extends CollectionFilter {
@override @override
String get key => '$type-$itemType'; String get key => '$type-$itemType';
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is TypeFilter && other.itemType == itemType;
}
@override
int get hashCode => hashValues(type, itemType);
@override
String toString() => '$runtimeType#${shortHash(this)}{itemType=$itemType}';
} }

View file

@ -1,41 +1,25 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@immutable
class SectionKey { class SectionKey {
const SectionKey(); const SectionKey();
} }
class EntryAlbumSectionKey extends SectionKey { class EntryAlbumSectionKey extends SectionKey with EquatableMixin {
final String? directory; final String? directory;
@override
List<Object?> get props => [directory];
const EntryAlbumSectionKey(this.directory); const EntryAlbumSectionKey(this.directory);
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is EntryAlbumSectionKey && other.directory == directory;
}
@override
int get hashCode => directory.hashCode;
@override
String toString() => '$runtimeType#${shortHash(this)}{directory=$directory}';
} }
class EntryDateSectionKey extends SectionKey { class EntryDateSectionKey extends SectionKey with EquatableMixin {
final DateTime? date; final DateTime? date;
@override
List<Object?> get props => [date];
const EntryDateSectionKey(this.date); const EntryDateSectionKey(this.date);
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is EntryDateSectionKey && other.date == date;
}
@override
int get hashCode => date.hashCode;
@override
String toString() => '$runtimeType#${shortHash(this)}{date=$date}';
} }

View file

@ -1,11 +1,15 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@immutable @immutable
class ImageOpEvent { class ImageOpEvent extends Equatable {
final bool success; final bool success;
final String uri; final String uri;
@override
List<Object?> get props => [success, uri];
const ImageOpEvent({ const ImageOpEvent({
required this.success, required this.success,
required this.uri, required this.uri,
@ -17,18 +21,6 @@ class ImageOpEvent {
uri: map['uri'], uri: map['uri'],
); );
} }
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is ImageOpEvent && other.success == success && other.uri == uri;
}
@override
int get hashCode => hashValues(success, uri);
@override
String toString() => '$runtimeType#${shortHash(this)}{success=$success, uri=$uri}';
} }
class MoveOpEvent extends ImageOpEvent { class MoveOpEvent extends ImageOpEvent {
@ -55,6 +47,9 @@ class MoveOpEvent extends ImageOpEvent {
class ExportOpEvent extends MoveOpEvent { class ExportOpEvent extends MoveOpEvent {
final int? pageId; final int? pageId;
@override
List<Object?> get props => [success, uri, pageId];
const ExportOpEvent({required bool success, required String uri, this.pageId, required Map newFields}) const ExportOpEvent({required bool success, required String uri, this.pageId, required Map newFields})
: super( : super(
success: success, success: success,
@ -70,16 +65,4 @@ class ExportOpEvent extends MoveOpEvent {
newFields: map['newFields'] ?? {}, newFields: map['newFields'] ?? {},
); );
} }
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is ExportOpEvent && other.success == success && other.uri == uri && other.pageId == pageId;
}
@override
int get hashCode => hashValues(success, uri, pageId);
@override
String toString() => '$runtimeType#${shortHash(this)}{success=$success, uri=$uri, pageId=$pageId, newFields=$newFields}';
} }

View file

@ -3,6 +3,7 @@ import 'package:aves/services/services.dart';
import 'package:aves/utils/change_notifier.dart'; import 'package:aves/utils/change_notifier.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@ -155,9 +156,12 @@ class StorageVolume {
} }
@immutable @immutable
class VolumeRelativeDirectory { class VolumeRelativeDirectory extends Equatable {
final String volumePath, relativeDir; final String volumePath, relativeDir;
@override
List<Object?> get props => [volumePath, relativeDir];
const VolumeRelativeDirectory({ const VolumeRelativeDirectory({
required this.volumePath, required this.volumePath,
required this.relativeDir, required this.relativeDir,
@ -187,13 +191,4 @@ class VolumeRelativeDirectory {
final volume = androidFileUtils.storageVolumes.firstWhereOrNull((volume) => volume.path == volumePath); final volume = androidFileUtils.storageVolumes.firstWhereOrNull((volume) => volume.path == volumePath);
return volume?.getDescription(context) ?? volumePath; return volume?.getDescription(context) ?? volumePath;
} }
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is VolumeRelativeDirectory && other.volumePath == volumePath && other.relativeDir == relativeDir;
}
@override
int get hashCode => hashValues(volumePath, relativeDir);
} }

View file

@ -44,7 +44,6 @@ class Constants {
Dependency( Dependency(
name: 'AndroidSVG', name: 'AndroidSVG',
license: 'Apache 2.0', license: 'Apache 2.0',
licenseUrl: 'https://github.com/BigBadaboom/androidsvg/blob/master/LICENSE',
sourceUrl: 'https://github.com/BigBadaboom/androidsvg', sourceUrl: 'https://github.com/BigBadaboom/androidsvg',
), ),
Dependency( Dependency(
@ -56,19 +55,16 @@ class Constants {
Dependency( Dependency(
name: 'CWAC-Document', name: 'CWAC-Document',
license: 'Apache 2.0', license: 'Apache 2.0',
licenseUrl: 'https://github.com/commonsguy/cwac-document/blob/master/LICENSE',
sourceUrl: 'https://github.com/commonsguy/cwac-document', sourceUrl: 'https://github.com/commonsguy/cwac-document',
), ),
Dependency( Dependency(
name: 'Glide', name: 'Glide',
license: 'Apache 2.0, BSD 2-Clause', license: 'Apache 2.0, BSD 2-Clause',
licenseUrl: 'https://github.com/bumptech/glide/blob/master/LICENSE',
sourceUrl: 'https://github.com/bumptech/glide', sourceUrl: 'https://github.com/bumptech/glide',
), ),
Dependency( Dependency(
name: 'Metadata Extractor', name: 'Metadata Extractor',
license: 'Apache 2.0', license: 'Apache 2.0',
licenseUrl: 'https://github.com/drewnoakes/metadata-extractor/blob/master/LICENSE',
sourceUrl: 'https://github.com/drewnoakes/metadata-extractor', sourceUrl: 'https://github.com/drewnoakes/metadata-extractor',
), ),
]; ];
@ -77,49 +73,44 @@ class Constants {
Dependency( Dependency(
name: 'Connectivity Plus', name: 'Connectivity Plus',
license: 'BSD 3-Clause', license: 'BSD 3-Clause',
licenseUrl: 'https://github.com/fluttercommunity/plus_plugins/blob/main/packages/connectivity_plus/LICENSE', licenseUrl: 'https://github.com/fluttercommunity/plus_plugins/blob/main/packages/connectivity_plus/connectivity_plus/LICENSE',
sourceUrl: 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/connectivity_plus', sourceUrl: 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/connectivity_plus',
), ),
Dependency( Dependency(
name: 'FlutterFire (Core, Crashlytics)', name: 'FlutterFire (Core, Crashlytics)',
license: 'BSD 3-Clause', license: 'BSD 3-Clause',
licenseUrl: 'https://github.com/FirebaseExtended/flutterfire/blob/master/LICENSE',
sourceUrl: 'https://github.com/FirebaseExtended/flutterfire', sourceUrl: 'https://github.com/FirebaseExtended/flutterfire',
), ),
Dependency( Dependency(
name: 'fijkplayer (Aves fork)', name: 'fijkplayer (Aves fork)',
license: 'MIT', license: 'MIT',
licenseUrl: 'https://github.com/deckerst/fijkplayer/blob/master/LICENSE',
sourceUrl: 'https://github.com/deckerst/fijkplayer', sourceUrl: 'https://github.com/deckerst/fijkplayer',
), ),
Dependency( Dependency(
name: 'Google API Availability', name: 'Google API Availability',
license: 'MIT', license: 'MIT',
licenseUrl: 'https://github.com/Baseflow/flutter-google-api-availability/blob/master/LICENSE',
sourceUrl: 'https://github.com/Baseflow/flutter-google-api-availability', sourceUrl: 'https://github.com/Baseflow/flutter-google-api-availability',
), ),
Dependency( Dependency(
name: 'Google Maps for Flutter', name: 'Google Maps for Flutter',
license: 'BSD 3-Clause', license: 'BSD 3-Clause',
licenseUrl: 'https://github.com/flutter/plugins/blob/master/packages/google_maps_flutter/google_maps_flutter/LICENSE', licenseUrl: 'https://github.com/flutter/plugins/blob/master/packages/google_maps_flutter/google_maps_flutter/LICENSE',
sourceUrl: 'https://github.com/flutter/plugins/blob/master/packages/google_maps_flutter/google_maps_flutter', sourceUrl: 'https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter/google_maps_flutter',
), ),
Dependency( Dependency(
name: 'Package Info Plus', name: 'Package Info Plus',
license: 'BSD 3-Clause', license: 'BSD 3-Clause',
licenseUrl: 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/package_info_plus/LICENSE', licenseUrl: 'https://github.com/fluttercommunity/plus_plugins/blob/main/packages/package_info_plus/package_info_plus/LICENSE',
sourceUrl: 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/package_info_plus', sourceUrl: 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/package_info_plus',
), ),
Dependency( Dependency(
name: 'Permission Handler', name: 'Permission Handler',
license: 'MIT', license: 'MIT',
licenseUrl: 'https://github.com/Baseflow/flutter-permission-handler/blob/develop/permission_handler/LICENSE',
sourceUrl: 'https://github.com/Baseflow/flutter-permission-handler', sourceUrl: 'https://github.com/Baseflow/flutter-permission-handler',
), ),
Dependency( Dependency(
name: 'Printing', name: 'Printing',
license: 'Apache 2.0', license: 'Apache 2.0',
licenseUrl: 'https://github.com/DavBfr/dart_pdf/blob/master/LICENSE',
sourceUrl: 'https://github.com/DavBfr/dart_pdf', sourceUrl: 'https://github.com/DavBfr/dart_pdf',
), ),
Dependency( Dependency(
@ -130,21 +121,19 @@ class Constants {
), ),
Dependency( Dependency(
name: 'sqflite', name: 'sqflite',
license: 'MIT', license: 'BSD 2-Clause',
licenseUrl: 'https://github.com/tekartik/sqflite/blob/master/sqflite/LICENSE',
sourceUrl: 'https://github.com/tekartik/sqflite', sourceUrl: 'https://github.com/tekartik/sqflite',
), ),
Dependency( Dependency(
name: 'Streams Channel (Aves fork)', name: 'Streams Channel (Aves fork)',
license: 'Apache 2.0', license: 'Apache 2.0',
licenseUrl: 'https://github.com/deckerst/aves_streams_channel/blob/master/LICENSE',
sourceUrl: 'https://github.com/deckerst/aves_streams_channel', sourceUrl: 'https://github.com/deckerst/aves_streams_channel',
), ),
Dependency( Dependency(
name: 'URL Launcher', name: 'URL Launcher',
license: 'BSD 3-Clause', license: 'BSD 3-Clause',
licenseUrl: 'https://github.com/flutter/plugins/blob/master/packages/url_launcher/url_launcher/LICENSE', licenseUrl: 'https://github.com/flutter/plugins/blob/master/packages/url_launcher/url_launcher/LICENSE',
sourceUrl: 'https://github.com/flutter/plugins/blob/master/packages/url_launcher/url_launcher', sourceUrl: 'https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher',
), ),
]; ];
@ -152,37 +141,31 @@ class Constants {
Dependency( Dependency(
name: 'Charts', name: 'Charts',
license: 'Apache 2.0', license: 'Apache 2.0',
licenseUrl: 'https://github.com/google/charts/blob/master/LICENSE',
sourceUrl: 'https://github.com/google/charts', sourceUrl: 'https://github.com/google/charts',
), ),
Dependency( Dependency(
name: 'Decorated Icon', name: 'Decorated Icon',
license: 'MIT', license: 'MIT',
licenseUrl: 'https://github.com/benPesso/flutter_decorated_icon/blob/master/LICENSE',
sourceUrl: 'https://github.com/benPesso/flutter_decorated_icon', sourceUrl: 'https://github.com/benPesso/flutter_decorated_icon',
), ),
Dependency( Dependency(
name: 'Expansion Tile Card (Aves fork)', name: 'Expansion Tile Card (Aves fork)',
license: 'BSD 3-Clause', license: 'BSD 3-Clause',
licenseUrl: 'https://github.com/deckerst/expansion_tile_card/blob/master/LICENSE',
sourceUrl: 'https://github.com/deckerst/expansion_tile_card', sourceUrl: 'https://github.com/deckerst/expansion_tile_card',
), ),
Dependency( Dependency(
name: 'FlexColorPicker', name: 'FlexColorPicker',
license: 'BSD 3-Clause', license: 'BSD 3-Clause',
licenseUrl: 'https://github.com/rydmike/flex_color_picker/blob/master/LICENSE',
sourceUrl: 'https://github.com/rydmike/flex_color_picker', sourceUrl: 'https://github.com/rydmike/flex_color_picker',
), ),
Dependency( Dependency(
name: 'Flutter Highlight', name: 'Flutter Highlight',
license: 'MIT', license: 'MIT',
licenseUrl: 'https://github.com/git-touch/highlight/blob/master/LICENSE',
sourceUrl: 'https://github.com/git-touch/highlight', sourceUrl: 'https://github.com/git-touch/highlight',
), ),
Dependency( Dependency(
name: 'Flutter Map', name: 'Flutter Map',
license: 'BSD 3-Clause', license: 'BSD 3-Clause',
licenseUrl: 'https://github.com/fleaflet/flutter_map/blob/master/LICENSE',
sourceUrl: 'https://github.com/fleaflet/flutter_map', sourceUrl: 'https://github.com/fleaflet/flutter_map',
), ),
Dependency( Dependency(
@ -194,19 +177,16 @@ class Constants {
Dependency( Dependency(
name: 'Flutter Staggered Animations', name: 'Flutter Staggered Animations',
license: 'MIT', license: 'MIT',
licenseUrl: 'https://github.com/mobiten/flutter_staggered_animations/blob/master/LICENSE',
sourceUrl: 'https://github.com/mobiten/flutter_staggered_animations', sourceUrl: 'https://github.com/mobiten/flutter_staggered_animations',
), ),
Dependency( Dependency(
name: 'Material Design Icons Flutter', name: 'Material Design Icons Flutter',
license: 'MIT', license: 'MIT',
licenseUrl: 'https://github.com/ziofat/material_design_icons_flutter/blob/master/LICENSE',
sourceUrl: 'https://github.com/ziofat/material_design_icons_flutter', sourceUrl: 'https://github.com/ziofat/material_design_icons_flutter',
), ),
Dependency( Dependency(
name: 'Overlay Support', name: 'Overlay Support',
license: 'Apache 2.0', license: 'Apache 2.0',
licenseUrl: 'https://github.com/boyan01/overlay_support/blob/master/LICENSE',
sourceUrl: 'https://github.com/boyan01/overlay_support', sourceUrl: 'https://github.com/boyan01/overlay_support',
), ),
Dependency( Dependency(
@ -218,19 +198,16 @@ class Constants {
Dependency( Dependency(
name: 'Panorama', name: 'Panorama',
license: 'Apache 2.0', license: 'Apache 2.0',
licenseUrl: 'https://github.com/zesage/panorama/blob/master/LICENSE',
sourceUrl: 'https://github.com/zesage/panorama', sourceUrl: 'https://github.com/zesage/panorama',
), ),
Dependency( Dependency(
name: 'Percent Indicator', name: 'Percent Indicator',
license: 'BSD 2-Clause', license: 'BSD 2-Clause',
licenseUrl: 'https://github.com/diegoveloper/flutter_percent_indicator/blob/master/LICENSE', sourceUrl: 'https://github.com/diegoveloper/flutter_percent_indicator',
sourceUrl: 'https://github.com/diegoveloper/flutter_percent_indicator/',
), ),
Dependency( Dependency(
name: 'Provider', name: 'Provider',
license: 'MIT', license: 'MIT',
licenseUrl: 'https://github.com/rrousselGit/provider/blob/master/LICENSE',
sourceUrl: 'https://github.com/rrousselGit/provider', sourceUrl: 'https://github.com/rrousselGit/provider',
), ),
]; ];
@ -239,19 +216,21 @@ class Constants {
Dependency( Dependency(
name: 'Collection', name: 'Collection',
license: 'BSD 3-Clause', license: 'BSD 3-Clause',
licenseUrl: 'https://github.com/dart-lang/collection/blob/master/LICENSE',
sourceUrl: 'https://github.com/dart-lang/collection', sourceUrl: 'https://github.com/dart-lang/collection',
), ),
Dependency( Dependency(
name: 'Country Code', name: 'Country Code',
license: 'MIT', license: 'MIT',
licenseUrl: 'https://github.com/denixport/dart.country/blob/master/LICENSE',
sourceUrl: 'https://github.com/denixport/dart.country', sourceUrl: 'https://github.com/denixport/dart.country',
), ),
Dependency(
name: 'Equatable',
license: 'MIT',
sourceUrl: 'https://github.com/felangel/equatable',
),
Dependency( Dependency(
name: 'Event Bus', name: 'Event Bus',
license: 'MIT', license: 'MIT',
licenseUrl: 'https://github.com/marcojakob/dart-event-bus/blob/master/LICENSE',
sourceUrl: 'https://github.com/marcojakob/dart-event-bus', sourceUrl: 'https://github.com/marcojakob/dart-event-bus',
), ),
Dependency( Dependency(
@ -263,49 +242,41 @@ class Constants {
Dependency( Dependency(
name: 'Get It', name: 'Get It',
license: 'MIT', license: 'MIT',
licenseUrl: 'https://github.com/fluttercommunity/get_it/blob/master/LICENSE',
sourceUrl: 'https://github.com/fluttercommunity/get_it', sourceUrl: 'https://github.com/fluttercommunity/get_it',
), ),
Dependency( Dependency(
name: 'Github', name: 'Github',
license: 'MIT', license: 'MIT',
licenseUrl: 'https://github.com/SpinlockLabs/github.dart/blob/master/LICENSE',
sourceUrl: 'https://github.com/SpinlockLabs/github.dart', sourceUrl: 'https://github.com/SpinlockLabs/github.dart',
), ),
Dependency( Dependency(
name: 'Intl', name: 'Intl',
license: 'BSD 3-Clause', license: 'BSD 3-Clause',
licenseUrl: 'https://github.com/dart-lang/intl/blob/master/LICENSE',
sourceUrl: 'https://github.com/dart-lang/intl', sourceUrl: 'https://github.com/dart-lang/intl',
), ),
Dependency( Dependency(
name: 'LatLong2', name: 'LatLong2',
license: 'Apache 2.0', license: 'Apache 2.0',
licenseUrl: 'https://github.com/jifalops/dart-latlong/blob/master/LICENSE',
sourceUrl: 'https://github.com/jifalops/dart-latlong', sourceUrl: 'https://github.com/jifalops/dart-latlong',
), ),
Dependency( Dependency(
name: 'PDF for Dart and Flutter', name: 'PDF for Dart and Flutter',
license: 'Apache 2.0', license: 'Apache 2.0',
licenseUrl: 'https://github.com/DavBfr/dart_pdf/blob/master/LICENSE',
sourceUrl: 'https://github.com/DavBfr/dart_pdf', sourceUrl: 'https://github.com/DavBfr/dart_pdf',
), ),
Dependency( Dependency(
name: 'Tuple', name: 'Tuple',
license: 'BSD 2-Clause', license: 'BSD 2-Clause',
licenseUrl: 'https://github.com/dart-lang/tuple/blob/master/LICENSE',
sourceUrl: 'https://github.com/dart-lang/tuple', sourceUrl: 'https://github.com/dart-lang/tuple',
), ),
Dependency( Dependency(
name: 'Version', name: 'Version',
license: 'BSD 3-Clause', license: 'BSD 3-Clause',
licenseUrl: 'https://github.com/dartninja/version/blob/master/LICENSE',
sourceUrl: 'https://github.com/dartninja/version', sourceUrl: 'https://github.com/dartninja/version',
), ),
Dependency( Dependency(
name: 'XML', name: 'XML',
license: 'MIT', license: 'MIT',
licenseUrl: 'https://github.com/renggli/dart-xml/blob/master/LICENSE',
sourceUrl: 'https://github.com/renggli/dart-xml', sourceUrl: 'https://github.com/renggli/dart-xml',
), ),
]; ];
@ -320,7 +291,7 @@ class Dependency {
const Dependency({ const Dependency({
required this.name, required this.name,
required this.license, required this.license,
required this.licenseUrl, String? licenseUrl,
required this.sourceUrl, required this.sourceUrl,
}); }) : licenseUrl = licenseUrl ?? '$sourceUrl/blob/master/LICENSE';
} }

View file

@ -15,6 +15,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/providers/highlight_info_provider.dart'; import 'package:aves/widgets/common/providers/highlight_info_provider.dart';
import 'package:aves/widgets/home_page.dart'; import 'package:aves/widgets/home_page.dart';
import 'package:aves/widgets/welcome_page.dart'; import 'package:aves/widgets/welcome_page.dart';
import 'package:equatable/equatable.dart';
import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -50,6 +51,7 @@ class _AvesAppState extends State<AvesApp> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
EquatableConfig.stringify = true;
initPlatformServices(); initPlatformServices();
_appSetup = _setup(); _appSetup = _setup();
_mediaStoreChangeChannel.receiveBroadcastStream().listen((event) => _onMediaStoreChange(event as String?)); _mediaStoreChangeChannel.receiveBroadcastStream().listen((event) => _onMediaStoreChange(event as String?));

View file

@ -3,6 +3,7 @@ import 'dart:math';
import 'package:aves/model/source/section_keys.dart'; import 'package:aves/model/source/section_keys.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
@ -221,13 +222,17 @@ class SectionedListLayout<T> {
String toString() => '$runtimeType#${shortHash(this)}{sectionCount=${sections.length} columnCount=$columnCount, tileExtent=$tileExtent}'; String toString() => '$runtimeType#${shortHash(this)}{sectionCount=${sections.length} columnCount=$columnCount, tileExtent=$tileExtent}';
} }
class SectionLayout { @immutable
class SectionLayout extends Equatable {
final SectionKey sectionKey; final SectionKey sectionKey;
final int firstIndex, lastIndex, bodyFirstIndex; final int firstIndex, lastIndex, bodyFirstIndex;
final double minOffset, maxOffset, bodyMinOffset; final double minOffset, maxOffset, bodyMinOffset;
final double headerExtent, tileExtent, spacing, mainAxisStride; final double headerExtent, tileExtent, spacing, mainAxisStride;
final IndexedWidgetBuilder builder; final IndexedWidgetBuilder builder;
@override
List<Object?> get props => [sectionKey, firstIndex, lastIndex, minOffset, maxOffset, headerExtent, tileExtent, spacing];
const SectionLayout({ const SectionLayout({
required this.sectionKey, required this.sectionKey,
required this.firstIndex, required this.firstIndex,
@ -263,15 +268,6 @@ class SectionLayout {
if (scrollOffset < 0) return firstIndex; if (scrollOffset < 0) return firstIndex;
return bodyFirstIndex + (scrollOffset / mainAxisStride).ceil() - 1; return bodyFirstIndex + (scrollOffset / mainAxisStride).ceil() - 1;
} }
@override
bool operator ==(Object other) => identical(this, other) || other is SectionLayout && runtimeType == other.runtimeType && sectionKey == other.sectionKey && firstIndex == other.firstIndex && lastIndex == other.lastIndex && minOffset == other.minOffset && maxOffset == other.maxOffset && headerExtent == other.headerExtent && tileExtent == other.tileExtent && spacing == other.spacing;
@override
int get hashCode => hashValues(sectionKey, firstIndex, lastIndex, minOffset, maxOffset, headerExtent, tileExtent, spacing);
@override
String toString() => '$runtimeType#${shortHash(this)}{sectionKey=$sectionKey, firstIndex=$firstIndex, lastIndex=$lastIndex, minOffset=$minOffset, maxOffset=$maxOffset, headerExtent=$headerExtent, tileExtent=$tileExtent, spacing=$spacing}';
} }
class _GridRow extends MultiChildRenderObjectWidget { class _GridRow extends MultiChildRenderObjectWidget {

View file

@ -1,28 +1,22 @@
import 'dart:ui'; import 'dart:ui';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
@immutable @immutable
class MagnifierState { class MagnifierState extends Equatable {
const MagnifierState({
required this.position,
required this.scale,
required this.source,
});
final Offset position; final Offset position;
final double? scale; final double? scale;
final ChangeSource source; final ChangeSource source;
@override @override
bool operator ==(Object other) => identical(this, other) || other is MagnifierState && runtimeType == other.runtimeType && position == other.position && scale == other.scale; List<Object?> get props => [position, scale, source];
@override const MagnifierState({
int get hashCode => hashValues(position, scale, source); required this.position,
required this.scale,
@override required this.source,
String toString() => '$runtimeType#${shortHash(this)}{position: $position, scale: $scale, source: $source}'; });
} }
enum ChangeSource { internal, gesture, animation } enum ChangeSource { internal, gesture, animation }

View file

@ -6,6 +6,7 @@ import 'package:aves/widgets/common/magnifier/magnifier.dart';
import 'package:aves/widgets/common/magnifier/pan/corner_hit_detector.dart'; import 'package:aves/widgets/common/magnifier/pan/corner_hit_detector.dart';
import 'package:aves/widgets/common/magnifier/scale/scale_boundaries.dart'; import 'package:aves/widgets/common/magnifier/scale/scale_boundaries.dart';
import 'package:aves/widgets/common/magnifier/scale/state.dart'; import 'package:aves/widgets/common/magnifier/scale/state.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
/// Internal widget in which controls all animations lifecycle, core responses /// Internal widget in which controls all animations lifecycle, core responses
@ -276,17 +277,21 @@ class _MagnifierCoreState extends State<MagnifierCore> with TickerProviderStateM
} }
} }
class _CenterWithOriginalSizeDelegate extends SingleChildLayoutDelegate { @immutable
class _CenterWithOriginalSizeDelegate extends SingleChildLayoutDelegate with EquatableMixin {
final Size subjectSize;
final Alignment basePosition;
final bool applyScale;
@override
List<Object?> get props => [subjectSize, basePosition, applyScale];
const _CenterWithOriginalSizeDelegate( const _CenterWithOriginalSizeDelegate(
this.subjectSize, this.subjectSize,
this.basePosition, this.basePosition,
this.applyScale, this.applyScale,
); );
final Size subjectSize;
final Alignment basePosition;
final bool applyScale;
@override @override
Offset getPositionForChild(Size size, Size childSize) { Offset getPositionForChild(Size size, Size childSize) {
final childWidth = applyScale ? subjectSize.width : childSize.width; final childWidth = applyScale ? subjectSize.width : childSize.width;
@ -309,10 +314,4 @@ class _CenterWithOriginalSizeDelegate extends SingleChildLayoutDelegate {
bool shouldRelayout(_CenterWithOriginalSizeDelegate oldDelegate) { bool shouldRelayout(_CenterWithOriginalSizeDelegate oldDelegate) {
return oldDelegate != this; return oldDelegate != this;
} }
@override
bool operator ==(Object other) => identical(this, other) || other is _CenterWithOriginalSizeDelegate && runtimeType == other.runtimeType && subjectSize == other.subjectSize && basePosition == other.basePosition && applyScale == other.applyScale;
@override
int get hashCode => hashValues(subjectSize, basePosition, applyScale);
} }

View file

@ -2,17 +2,22 @@ import 'dart:ui';
import 'package:aves/widgets/common/magnifier/controller/controller.dart'; import 'package:aves/widgets/common/magnifier/controller/controller.dart';
import 'package:aves/widgets/common/magnifier/scale/scale_level.dart'; import 'package:aves/widgets/common/magnifier/scale/scale_level.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
/// Internal class to wrap custom scale boundaries (min, max and initial) /// Internal class to wrap custom scale boundaries (min, max and initial)
/// Also, stores values regarding the two sizes: the container and the child. /// Also, stores values regarding the two sizes: the container and the child.
class ScaleBoundaries { @immutable
class ScaleBoundaries extends Equatable {
final ScaleLevel _minScale; final ScaleLevel _minScale;
final ScaleLevel _maxScale; final ScaleLevel _maxScale;
final ScaleLevel _initialScale; final ScaleLevel _initialScale;
final Size viewportSize; final Size viewportSize;
final Size childSize; final Size childSize;
@override
List<Object?> get props => [_minScale, _maxScale, _initialScale, viewportSize, childSize];
const ScaleBoundaries({ const ScaleBoundaries({
required ScaleLevel minScale, required ScaleLevel minScale,
required ScaleLevel maxScale, required ScaleLevel maxScale,
@ -57,13 +62,4 @@ class ScaleBoundaries {
Offset childToStatePosition(double scale, Offset childPosition) { Offset childToStatePosition(double scale, Offset childPosition) {
return (_childCenter - childPosition) * scale; return (_childCenter - childPosition) * scale;
} }
@override
bool operator ==(Object other) => identical(this, other) || other is ScaleBoundaries && runtimeType == other.runtimeType && _minScale == other._minScale && _maxScale == other._maxScale && _initialScale == other._initialScale && viewportSize == other.viewportSize && childSize == other.childSize;
@override
int get hashCode => hashValues(_minScale, _maxScale, _initialScale, viewportSize, childSize);
@override
String toString() => '$runtimeType#${shortHash(this)}{viewportSize=$viewportSize, childSize=$childSize, initialScale=$initialScale, minScale=$minScale, maxScale=$maxScale}';
} }

View file

@ -1,12 +1,17 @@
import 'dart:math'; import 'dart:math';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
class ScaleLevel { @immutable
class ScaleLevel extends Equatable {
final ScaleReference ref; final ScaleReference ref;
final double factor; final double factor;
@override
List<Object?> get props => [ref, factor];
const ScaleLevel({ const ScaleLevel({
this.ref = ScaleReference.absolute, this.ref = ScaleReference.absolute,
this.factor = 1.0, this.factor = 1.0,
@ -15,18 +20,6 @@ class ScaleLevel {
static double scaleForContained(Size containerSize, Size childSize) => min(containerSize.width / childSize.width, containerSize.height / childSize.height); static double scaleForContained(Size containerSize, Size childSize) => min(containerSize.width / childSize.width, containerSize.height / childSize.height);
static double scaleForCovering(Size containerSize, Size childSize) => max(containerSize.width / childSize.width, containerSize.height / childSize.height); static double scaleForCovering(Size containerSize, Size childSize) => max(containerSize.width / childSize.width, containerSize.height / childSize.height);
@override
String toString() => '$runtimeType#${shortHash(this)}{ref=$ref, factor=$factor}';
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is ScaleLevel && other.ref == ref && other.factor == factor;
}
@override
int get hashCode => hashValues(ref, factor);
} }
enum ScaleReference { absolute, contained, covered } enum ScaleReference { absolute, contained, covered }

View file

@ -1,29 +1,24 @@
import 'dart:ui'; import 'dart:ui';
import 'package:aves/widgets/common/magnifier/controller/state.dart'; import 'package:aves/widgets/common/magnifier/controller/state.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@immutable @immutable
class ScaleStateChange { class ScaleStateChange extends Equatable {
const ScaleStateChange({
required this.state,
required this.source,
this.childFocalPoint,
});
final ScaleState state; final ScaleState state;
final ChangeSource source; final ChangeSource source;
final Offset? childFocalPoint; final Offset? childFocalPoint;
@override @override
bool operator ==(Object other) => identical(this, other) || other is ScaleStateChange && runtimeType == other.runtimeType && state == other.state && childFocalPoint == other.childFocalPoint; List<Object?> get props => [state, source, childFocalPoint];
@override const ScaleStateChange({
int get hashCode => hashValues(state, source, childFocalPoint); required this.state,
required this.source,
@override this.childFocalPoint,
String toString() => '$runtimeType#${shortHash(this)}{scaleState: $state, source: $source, childFocalPoint: $childFocalPoint}'; });
} }
enum ScaleState { enum ScaleState {

View file

@ -2,29 +2,20 @@ import 'package:aves/model/source/section_keys.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:flutter/foundation.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class ChipSectionKey extends SectionKey { class ChipSectionKey extends SectionKey with EquatableMixin {
final String title; final String title;
@override
List<Object?> get props => [title];
const ChipSectionKey({ const ChipSectionKey({
this.title = '', this.title = '',
}); });
Widget? get leading => null; Widget? get leading => null;
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is ChipSectionKey && other.title == title;
}
@override
int get hashCode => title.hashCode;
@override
String toString() => '$runtimeType#${shortHash(this)}{title=$title}';
} }
class AlbumImportanceSectionKey extends ChipSectionKey { class AlbumImportanceSectionKey extends ChipSectionKey {

View file

@ -1,21 +1,17 @@
import 'package:aves/model/entry.dart'; import 'package:aves/model/entry.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
class HeroInfo { @immutable
class HeroInfo extends Equatable {
// hero tag should include a collection identifier, so that it animates // hero tag should include a collection identifier, so that it animates
// between different views of the entry in the same collection (e.g. thumbnails <-> viewer) // between different views of the entry in the same collection (e.g. thumbnails <-> viewer)
// but not between different collection instances, even with the same attributes (e.g. reloading collection page via drawer) // but not between different collection instances, even with the same attributes (e.g. reloading collection page via drawer)
final int? collectionId; final int? collectionId;
final AvesEntry? entry; final AvesEntry? entry;
@override
List<Object?> get props => [collectionId, entry?.uri];
const HeroInfo(this.collectionId, this.entry); const HeroInfo(this.collectionId, this.entry);
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is HeroInfo && other.collectionId == collectionId && other.entry == entry;
}
@override
int get hashCode => hashValues(collectionId, entry);
} }

View file

@ -12,13 +12,18 @@ import 'package:aves/widgets/viewer/info/metadata/xmp_ns/photoshop.dart';
import 'package:aves/widgets/viewer/info/metadata/xmp_ns/tiff.dart'; import 'package:aves/widgets/viewer/info/metadata/xmp_ns/tiff.dart';
import 'package:aves/widgets/viewer/info/metadata/xmp_ns/xmp.dart'; import 'package:aves/widgets/viewer/info/metadata/xmp_ns/xmp.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class XmpNamespace { @immutable
class XmpNamespace extends Equatable {
final String namespace; final String namespace;
final Map<String, String> rawProps; final Map<String, String> rawProps;
@override
List<Object?> get props => [namespace];
const XmpNamespace(this.namespace, this.rawProps); const XmpNamespace(this.namespace, this.rawProps);
factory XmpNamespace.create(String namespace, Map<String, String> rawProps) { factory XmpNamespace.create(String namespace, Map<String, String> rawProps) {
@ -119,20 +124,6 @@ class XmpNamespace {
String formatValue(XmpProp prop) => prop.value; String formatValue(XmpProp prop) => prop.value;
Map<String, InfoLinkHandler> linkifyValues(List<XmpProp> props) => {}; Map<String, InfoLinkHandler> linkifyValues(List<XmpProp> props) => {};
// identity
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is XmpNamespace && other.namespace == namespace;
}
@override
int get hashCode => namespace.hashCode;
@override
String toString() => '$runtimeType#${shortHash(this)}{namespace=$namespace}';
} }
class XmpProp { class XmpProp {

View file

@ -1,13 +1,17 @@
import 'package:aves/widgets/viewer/visual/subtitle/span.dart'; import 'package:aves/widgets/viewer/visual/subtitle/span.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@immutable @immutable
class StyledSubtitleLine with Diagnosticable { class StyledSubtitleLine extends Equatable with Diagnosticable {
final List<StyledSubtitleSpan> spans; final List<StyledSubtitleSpan> spans;
final List<Path>? clip; final List<Path>? clip;
final Offset? position; final Offset? position;
@override
List<Object?> get props => [spans, clip, position];
const StyledSubtitleLine({ const StyledSubtitleLine({
required this.spans, required this.spans,
this.clip, this.clip,
@ -33,17 +37,4 @@ class StyledSubtitleLine with Diagnosticable {
properties.add(DiagnosticsProperty<List<Path>>('clip', clip)); properties.add(DiagnosticsProperty<List<Path>>('clip', clip));
properties.add(DiagnosticsProperty<Offset>('position', position)); properties.add(DiagnosticsProperty<Offset>('position', position));
} }
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is StyledSubtitleLine && other.spans == spans && other.clip == clip && other.position == position;
}
@override
int get hashCode => hashValues(
spans,
clip,
position,
);
} }

View file

@ -1,12 +1,16 @@
import 'package:aves/widgets/viewer/visual/subtitle/style.dart'; import 'package:aves/widgets/viewer/visual/subtitle/style.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@immutable @immutable
class StyledSubtitleSpan with Diagnosticable { class StyledSubtitleSpan extends Equatable with Diagnosticable {
final TextSpan textSpan; final TextSpan textSpan;
final SubtitleStyle extraStyle; final SubtitleStyle extraStyle;
@override
List<Object?> get props => [textSpan, extraStyle];
const StyledSubtitleSpan({ const StyledSubtitleSpan({
required this.textSpan, required this.textSpan,
required this.extraStyle, required this.extraStyle,
@ -28,16 +32,4 @@ class StyledSubtitleSpan with Diagnosticable {
properties.add(DiagnosticsProperty<TextSpan>('textSpan', textSpan)); properties.add(DiagnosticsProperty<TextSpan>('textSpan', textSpan));
properties.add(DiagnosticsProperty<SubtitleStyle>('extraStyle', extraStyle)); properties.add(DiagnosticsProperty<SubtitleStyle>('extraStyle', extraStyle));
} }
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is StyledSubtitleSpan && other.textSpan == textSpan && other.extraStyle == extraStyle;
}
@override
int get hashCode => hashValues(
textSpan,
extraStyle,
);
} }

View file

@ -1,8 +1,9 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@immutable @immutable
class SubtitleStyle with Diagnosticable { class SubtitleStyle extends Equatable with Diagnosticable {
final TextAlign? hAlign; final TextAlign? hAlign;
final TextAlignVertical? vAlign; final TextAlignVertical? vAlign;
final Color? borderColor; final Color? borderColor;
@ -15,6 +16,23 @@ class SubtitleStyle with Diagnosticable {
bool get shearing => (shearX ?? 0) != 0 || (shearY ?? 0) != 0; bool get shearing => (shearX ?? 0) != 0 || (shearY ?? 0) != 0;
@override
List<Object?> get props => [
hAlign,
vAlign,
borderColor,
borderWidth,
edgeBlur,
rotationX,
rotationY,
rotationZ,
scaleX,
scaleY,
shearX,
shearY,
drawingPaths?.length,
];
const SubtitleStyle({ const SubtitleStyle({
this.hAlign, this.hAlign,
this.vAlign, this.vAlign,
@ -80,27 +98,4 @@ class SubtitleStyle with Diagnosticable {
properties.add(DoubleProperty('shearY', shearY)); properties.add(DoubleProperty('shearY', shearY));
properties.add(DiagnosticsProperty<List<Path>>('drawingPaths', drawingPaths)); properties.add(DiagnosticsProperty<List<Path>>('drawingPaths', drawingPaths));
} }
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is SubtitleStyle && other.hAlign == hAlign && other.vAlign == vAlign && other.borderColor == borderColor && other.borderWidth == borderWidth && other.edgeBlur == edgeBlur && other.rotationX == rotationX && other.rotationY == rotationY && other.rotationZ == rotationZ && other.scaleX == scaleX && other.scaleY == scaleY && other.shearX == shearX && other.shearY == shearY && other.drawingPaths == drawingPaths;
}
@override
int get hashCode => hashValues(
hAlign,
vAlign,
borderColor,
borderWidth,
edgeBlur,
rotationX,
rotationY,
rotationZ,
scaleX,
scaleY,
shearX,
shearY,
drawingPaths?.length,
);
} }

View file

@ -183,6 +183,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.1" version: "1.2.1"
equatable:
dependency: "direct main"
description:
name: equatable
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.3"
event_bus: event_bus:
dependency: "direct main" dependency: "direct main"
description: description:

View file

@ -17,6 +17,7 @@ dependencies:
connectivity_plus: connectivity_plus:
country_code: country_code:
decorated_icon: decorated_icon:
equatable:
event_bus: event_bus:
expansion_tile_card: expansion_tile_card:
git: git: