From 1ef9d75b0bd688d96d9f06aed6c8e6f70cd25de3 Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Wed, 21 Dec 2022 18:26:44 +0100 Subject: [PATCH] minor fixes --- lib/model/video/metadata.dart | 3 +- lib/widgets/collection/app_bar.dart | 5 +- lib/widgets/collection/filter_bar.dart | 36 ++++++++------ lib/widgets/common/expandable_filter_row.dart | 7 +-- lib/widgets/common/identity/aves_app_bar.dart | 3 +- .../common/identity/aves_filter_chip.dart | 49 +++++++++++++------ .../entry_editors/tag_editor_page.dart | 6 ++- .../settings/privacy/hidden_items_page.dart | 17 ++++--- scripts/extract_apks.sh | 22 +++++++++ test/model/video/metadata_test.dart | 1 + 10 files changed, 101 insertions(+), 48 deletions(-) create mode 100755 scripts/extract_apks.sh diff --git a/lib/model/video/metadata.dart b/lib/model/video/metadata.dart index c6853c6ee..7245ca79b 100644 --- a/lib/model/video/metadata.dart +++ b/lib/model/video/metadata.dart @@ -22,7 +22,7 @@ import 'package:fijkplayer/fijkplayer.dart'; import 'package:flutter/foundation.dart'; class VideoMetadataFormatter { - static final _dateY4M2D2H2m2s2Pattern = RegExp(r'(\d{4})[-./](\d{1,2})[-./](\d{1,2})([ T](\d{1,2}):(\d{1,2}):(\d{1,2})( ([ap]\.? ?m\.?))?)?'); + static final _dateY4M2D2H2m2s2Pattern = RegExp(r'(\d{4})[-./:](\d{1,2})[-./:](\d{1,2})([ T](\d{1,2}):(\d{1,2}):(\d{1,2})( ([ap]\.? ?m\.?))?)?'); static final _ambiguousDatePatterns = { RegExp(r'^\d{2}[-/]\d{2}[-/]\d{4}$'), }; @@ -127,6 +127,7 @@ class VideoMetadataFormatter { // - `2022-01-28T5:07:46 p. m.Z` // - `2012-1-1T12:00:00Z` // - `2020.10.14` + // - `2016:11:16 18:00:00` // - `2021` (not enough to build a date) var match = _dateY4M2D2H2m2s2Pattern.firstMatch(dateString); diff --git a/lib/widgets/collection/app_bar.dart b/lib/widgets/collection/app_bar.dart index a76e63f95..881d583b4 100644 --- a/lib/widgets/collection/app_bar.dart +++ b/lib/widgets/collection/app_bar.dart @@ -165,6 +165,7 @@ class _CollectionAppBarState extends State with SingleTickerPr builder: (context, _, child) { final isTelevision = device.isTelevision; final actions = _buildActions(context, selection); + final onFilterTap = removableFilters ? collection.removeFilter : null; return AvesAppBar( contentHeight: appBarContentHeight, leading: _buildAppBarLeading( @@ -192,8 +193,8 @@ class _CollectionAppBarState extends State with SingleTickerPr }, child: FilterBar( filters: visibleFilters, - removable: removableFilters, - onTap: removableFilters ? collection.removeFilter : null, + onTap: onFilterTap, + onRemove: onFilterTap, ), ), if (queryEnabled) diff --git a/lib/widgets/collection/filter_bar.dart b/lib/widgets/collection/filter_bar.dart index dfbb3b873..4856c242e 100644 --- a/lib/widgets/collection/filter_bar.dart +++ b/lib/widgets/collection/filter_bar.dart @@ -11,14 +11,13 @@ class FilterBar extends StatefulWidget { static const double preferredHeight = AvesFilterChip.minChipHeight + verticalPadding; final List filters; - final bool removable; - final FilterCallback? onTap; + final FilterCallback? onTap, onRemove; FilterBar({ super.key, required Set filters, - this.removable = false, this.onTap, + this.onRemove, }) : filters = List.from(filters)..sort(); @override @@ -31,8 +30,6 @@ class _FilterBarState extends State { List get filters => widget.filters; - FilterCallback? get onTap => widget.onTap; - @override void didUpdateWidget(covariant FilterBar oldWidget) { super.didUpdateWidget(oldWidget); @@ -104,30 +101,37 @@ class _FilterBarState extends State { } Widget _buildChip(CollectionFilter filter) { + final onTap = widget.onTap != null + ? (filter) { + _userTappedFilter = filter; + widget.onTap?.call(filter); + } + : null; + final onRemove = widget.onRemove != null + ? (filter) { + _userTappedFilter = filter; + widget.onRemove?.call(filter); + } + : null; return _Chip( filter: filter, - removable: widget.removable, single: filters.length == 1, - onTap: onTap != null - ? (filter) { - _userTappedFilter = filter; - onTap!(filter); - } - : null, + onTap: onTap, + onRemove: onRemove, ); } } class _Chip extends StatelessWidget { final CollectionFilter filter; - final bool removable, single; - final FilterCallback? onTap; + final bool single; + final FilterCallback? onTap, onRemove; const _Chip({ required this.filter, - required this.removable, required this.single, required this.onTap, + required this.onRemove, }); @override @@ -138,7 +142,6 @@ class _Chip extends StatelessWidget { child: AvesFilterChip( key: ValueKey(filter), filter: filter, - removable: removable, maxWidth: single ? AvesFilterChip.computeMaxWidth( context, @@ -149,6 +152,7 @@ class _Chip extends StatelessWidget { : null, heroType: HeroType.always, onTap: onTap, + onRemove: onRemove, ), ), ); diff --git a/lib/widgets/common/expandable_filter_row.dart b/lib/widgets/common/expandable_filter_row.dart index cafcf96b2..54d0ebf63 100644 --- a/lib/widgets/common/expandable_filter_row.dart +++ b/lib/widgets/common/expandable_filter_row.dart @@ -67,10 +67,11 @@ class TitledExpandableFilterRow extends StatelessWidget { class ExpandableFilterRow extends StatelessWidget { final List filters; final bool isExpanded; - final bool removable, showGenericIcon; + final bool showGenericIcon; final Widget? Function(CollectionFilter)? leadingBuilder; final HeroType Function(CollectionFilter filter)? heroTypeBuilder; final FilterCallback onTap; + final FilterCallback? onRemove; final OffsetFilterCallback? onLongPress; static const double horizontalPadding = 8; @@ -80,11 +81,11 @@ class ExpandableFilterRow extends StatelessWidget { super.key, required this.filters, required this.isExpanded, - this.removable = false, this.showGenericIcon = true, this.leadingBuilder, this.heroTypeBuilder, required this.onTap, + this.onRemove, required this.onLongPress, }); @@ -143,11 +144,11 @@ class ExpandableFilterRow extends StatelessWidget { // key `album-{path}` is expected by test driver key: Key(filter.key), filter: filter, - removable: removable, showGenericIcon: showGenericIcon, leadingOverride: leadingBuilder?.call(filter), heroType: heroTypeBuilder?.call(filter) ?? HeroType.onTap, onTap: onTap, + onRemove: onRemove, onLongPress: onLongPress, ); } diff --git a/lib/widgets/common/identity/aves_app_bar.dart b/lib/widgets/common/identity/aves_app_bar.dart index 9234d781f..4b556047d 100644 --- a/lib/widgets/common/identity/aves_app_bar.dart +++ b/lib/widgets/common/identity/aves_app_bar.dart @@ -1,5 +1,6 @@ import 'package:aves/model/device.dart'; import 'package:aves/model/settings/settings.dart'; +import 'package:aves/theme/colors.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/widgets/aves_app.dart'; import 'package:aves/widgets/common/fx/blurred.dart'; @@ -63,7 +64,7 @@ class AvesAppBar extends StatelessWidget { : const SizedBox(width: 16), Expanded( child: DefaultTextStyle( - style: Theme.of(context).appBarTheme.titleTextStyle!, + style: context.select((v) => Theme.of(context).appBarTheme.titleTextStyle!), child: Hero( tag: titleHeroTag, flightShuttleBuilder: _flightShuttleBuilder, diff --git a/lib/widgets/common/identity/aves_filter_chip.dart b/lib/widgets/common/identity/aves_filter_chip.dart index 78a7122ca..a58f796b8 100644 --- a/lib/widgets/common/identity/aves_filter_chip.dart +++ b/lib/widgets/common/identity/aves_filter_chip.dart @@ -44,14 +44,14 @@ class AvesFilterDecoration { class AvesFilterChip extends StatefulWidget { final CollectionFilter filter; - final bool removable, showText, showGenericIcon, useFilterColor; + final bool showText, showGenericIcon, useFilterColor; final AvesFilterDecoration? decoration; final String? banner; final Widget? leadingOverride, details; final double padding; final double? maxWidth; final HeroType heroType; - final FilterCallback? onTap; + final FilterCallback? onTap, onRemove; final OffsetFilterCallback? onLongPress; static const double defaultRadius = 32; @@ -65,7 +65,6 @@ class AvesFilterChip extends StatefulWidget { const AvesFilterChip({ super.key, required this.filter, - this.removable = false, this.showText = true, this.showGenericIcon = true, this.useFilterColor = true, @@ -77,6 +76,7 @@ class AvesFilterChip extends StatefulWidget { this.maxWidth, this.heroType = HeroType.onTap, this.onTap, + this.onRemove, this.onLongPress = showDefaultLongPressMenu, }); @@ -154,10 +154,6 @@ class _AvesFilterChipState extends State { double get padding => widget.padding; - FilterCallback? get onTap => widget.onTap; - - OffsetFilterCallback? get onLongPress => widget.onLongPress; - @override void initState() { super.initState(); @@ -219,12 +215,40 @@ class _AvesFilterChipState extends State { final decoration = widget.decoration; final chipBackground = Theme.of(context).scaffoldBackgroundColor; + final onTap = widget.onTap != null + ? () { + WidgetsBinding.instance.addPostFrameCallback((_) => widget.onTap?.call(filter)); + setState(() => _tapped = true); + } + : null; + final onRemove = widget.onRemove != null + ? () { + WidgetsBinding.instance.addPostFrameCallback((_) => widget.onRemove?.call(filter)); + setState(() => _tapped = true); + } + : null; + final onLongPress = widget.onLongPress != null + ? () { + if (_tapPosition != null) { + widget.onLongPress?.call(context, filter, _tapPosition!); + } + } + : null; + Widget? content; if (widget.showText) { final textScaleFactor = MediaQuery.textScaleFactorOf(context); final iconSize = AvesFilterChip.iconSize * textScaleFactor; final leading = widget.leadingOverride ?? filter.iconBuilder(context, iconSize, showGenericIcon: widget.showGenericIcon); - final trailing = widget.removable ? Icon(AIcons.clear, size: iconSize) : null; + final trailing = onRemove != null + ? IconButton( + icon: Icon(AIcons.clear, size: iconSize), + padding: EdgeInsets.zero, + splashRadius: IconTheme.of(context).size, + constraints: const BoxConstraints(), + onPressed: onRemove, + ) + : null; content = Row( mainAxisSize: decoration != null ? MainAxisSize.max : MainAxisSize.min, @@ -317,13 +341,8 @@ class _AvesFilterChipState extends State { // as of Flutter v2.8.0, `InkWell` does not have `onLongPressStart` like `GestureDetector`, // so we get the long press details from the tap instead onTapDown: onLongPress != null ? (details) => _tapPosition = details.globalPosition : null, - onTap: onTap != null - ? () { - WidgetsBinding.instance.addPostFrameCallback((_) => onTap!(filter)); - setState(() => _tapped = true); - } - : null, - onLongPress: onLongPress != null ? () => onLongPress!(context, filter, _tapPosition!) : null, + onTap: onTap, + onLongPress: onLongPress, borderRadius: borderRadius, child: FutureBuilder( future: _colorFuture, diff --git a/lib/widgets/dialogs/entry_editors/tag_editor_page.dart b/lib/widgets/dialogs/entry_editors/tag_editor_page.dart index 96d961f02..e31e7cf96 100644 --- a/lib/widgets/dialogs/entry_editors/tag_editor_page.dart +++ b/lib/widgets/dialogs/entry_editors/tag_editor_page.dart @@ -150,14 +150,16 @@ class _TagEditorPageState extends State { secondChild: ExpandableFilterRow( filters: sortedTags.map((kv) => kv.key).toList(), isExpanded: context.select((v) => v.tagEditorCurrentFilterSectionExpanded), - removable: true, showGenericIcon: false, leadingBuilder: showCount ? (filter) => _TagCount( count: sortedTags.firstWhere((kv) => kv.key == filter).value, ) : null, - onTap: _removeTag, + onTap: (filter) { + // TODO TLAD [#453] + }, + onRemove: _removeTag, onLongPress: null, ), crossFadeState: sortedTags.isEmpty ? CrossFadeState.showFirst : CrossFadeState.showSecond, diff --git a/lib/widgets/settings/privacy/hidden_items_page.dart b/lib/widgets/settings/privacy/hidden_items_page.dart index bbc2ae0f2..f280d48c3 100644 --- a/lib/widgets/settings/privacy/hidden_items_page.dart +++ b/lib/widgets/settings/privacy/hidden_items_page.dart @@ -88,14 +88,15 @@ class _HiddenFilters extends StatelessWidget { child: Wrap( spacing: 8, runSpacing: 8, - children: filterList - .map((filter) => AvesFilterChip( - filter: filter, - removable: true, - onTap: (filter) => settings.changeFilterVisibility({filter}, true), - onLongPress: null, - )) - .toList(), + children: filterList.map((filter) { + void onRemove(CollectionFilter filter) => settings.changeFilterVisibility({filter}, true); + return AvesFilterChip( + filter: filter, + onTap: onRemove, + onRemove: onRemove, + onLongPress: null, + ); + }).toList(), ), ), ], diff --git a/scripts/extract_apks.sh b/scripts/extract_apks.sh new file mode 100755 index 000000000..d7ae3bf4c --- /dev/null +++ b/scripts/extract_apks.sh @@ -0,0 +1,22 @@ +#!/bin/bash +if [ ! -d "scripts" ]; then + cd .. +fi + +BUNDLE="/home/tibo/Downloads/app-play-release.aab" + +# shellcheck disable=SC2001 +OUTPUT=$(sed "s|\.aab|\.apks|" <<<"$BUNDLE") + +KEYS_PATH="android/key.properties" +STORE_PATH=$(sed -n 's|.*storeFile=\(.*\)[\r\n]|\1|p' "$KEYS_PATH") +# shellcheck disable=SC1003 +STORE_PW=$(sed -n 's|.*storePassword=\(.*\)[\r\n]|\1|p' "$KEYS_PATH" | sed 's|\\'\''|'\''|g') +KEY_ALIAS=$(sed -n 's|.*keyAlias=\(.*\)[\r\n]|\1|p' "$KEYS_PATH") +# shellcheck disable=SC1003 +KEY_PW=$(sed -n 's|.*keyPassword=\(.*\)[\r\n]|\1|p' "$KEYS_PATH" | sed 's|\\'\''|'\''|g') + +echo "$BUNDLE -> $OUTPUT" +bundletool build-apks --bundle="$BUNDLE" --output="$OUTPUT" \ + --ks="$STORE_PATH" --ks-pass="pass:$STORE_PW" \ + --ks-key-alias="$KEY_ALIAS" --key-pass="pass:$KEY_PW" diff --git a/test/model/video/metadata_test.dart b/test/model/video/metadata_test.dart index 4423b775b..15a40f85c 100644 --- a/test/model/video/metadata_test.dart +++ b/test/model/video/metadata_test.dart @@ -16,6 +16,7 @@ void main() { expect(VideoMetadataFormatter.parseVideoDate('2022-01-28T5:07:46 p. m.Z'), DateTime(2022, 1, 28, 17, 7, 46).millisecondsSinceEpoch); expect(VideoMetadataFormatter.parseVideoDate('2012-1-1T12:00:00Z'), DateTime(2012, 1, 1, 12, 0, 0).millisecondsSinceEpoch); expect(VideoMetadataFormatter.parseVideoDate('2020.10.14'), DateTime(2020, 10, 14).millisecondsSinceEpoch); + expect(VideoMetadataFormatter.parseVideoDate('2016:11:16 18:00:00'), DateTime(2016, 11, 16, 18, 0, 0).millisecondsSinceEpoch); }); test('Ambiguous date', () {