diff --git a/lib/widgets/app_debug_page.dart b/lib/widgets/app_debug_page.dart deleted file mode 100644 index d96244588..000000000 --- a/lib/widgets/app_debug_page.dart +++ /dev/null @@ -1,370 +0,0 @@ -import 'dart:collection'; - -import 'package:aves/model/favourite_repo.dart'; -import 'package:aves/model/image_entry.dart'; -import 'package:aves/model/image_metadata.dart'; -import 'package:aves/model/metadata_db.dart'; -import 'package:aves/model/settings/settings.dart'; -import 'package:aves/model/source/collection_source.dart'; -import 'package:aves/services/android_app_service.dart'; -import 'package:aves/services/image_file_service.dart'; -import 'package:aves/utils/android_file_utils.dart'; -import 'package:aves/utils/file_utils.dart'; -import 'package:aves/widgets/common/data_providers/media_query_data_provider.dart'; -import 'package:aves/widgets/common/icons.dart'; -import 'package:aves/widgets/fullscreen/info/common.dart'; -import 'package:firebase_analytics/firebase_analytics.dart'; -import 'package:firebase_core/firebase_core.dart'; -import 'package:firebase_crashlytics/firebase_crashlytics.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; -import 'package:flutter_svg/flutter_svg.dart'; - -class AppDebugPage extends StatefulWidget { - static const routeName = '/debug'; - - final CollectionSource source; - - const AppDebugPage({this.source}); - - @override - State createState() => AppDebugPageState(); -} - -class AppDebugPageState extends State { - Future _dbFileSizeLoader; - Future> _dbEntryLoader; - Future> _dbDateLoader; - Future> _dbMetadataLoader; - Future> _dbAddressLoader; - Future> _dbFavouritesLoader; - Future _envLoader; - - List get entries => widget.source.rawEntries; - - @override - void initState() { - super.initState(); - _startDbReport(); - _envLoader = AndroidAppService.getEnv(); - } - - @override - Widget build(BuildContext context) { - return MediaQueryDataProvider( - child: DefaultTabController( - length: 4, - child: Scaffold( - appBar: AppBar( - title: Text('Debug'), - bottom: TabBar( - tabs: [ - Tab(icon: Icon(AIcons.debug)), - Tab(icon: Icon(AIcons.settings)), - Tab(icon: Icon(AIcons.removableStorage)), - Tab(icon: Icon(AIcons.android)), - ], - ), - ), - body: SafeArea( - child: TabBarView( - children: [ - _buildGeneralTabView(), - _buildSettingsTabView(), - _buildStorageTabView(), - _buildEnvTabView(), - ], - ), - ), - ), - ), - ); - } - - Widget _buildGeneralTabView() { - final catalogued = entries.where((entry) => entry.isCatalogued); - final withGps = catalogued.where((entry) => entry.hasGps); - final located = withGps.where((entry) => entry.isLocated); - return ListView( - padding: EdgeInsets.all(16), - children: [ - Text('Time dilation'), - Slider( - value: timeDilation, - onChanged: (v) => setState(() => timeDilation = v), - min: 1.0, - max: 10.0, - divisions: 9, - label: '$timeDilation', - ), - Divider(), - Row( - children: [ - Expanded( - child: Text('Crashlytics'), - ), - SizedBox(width: 8), - ElevatedButton( - onPressed: FirebaseCrashlytics.instance.crash, - child: Text('Crash'), - ), - ], - ), - Row( - children: [ - Expanded( - child: Text('Analytics'), - ), - SizedBox(width: 8), - ElevatedButton( - onPressed: () => FirebaseAnalytics().logEvent( - name: 'debug_test', - parameters: {'time': DateTime.now().toIso8601String()}, - ), - child: Text('Send event'), - ), - ], - ), - Text('Firebase data collection: ${Firebase.app().isAutomaticDataCollectionEnabled ? 'enabled' : 'disabled'}'), - Text('Crashlytics collection: ${FirebaseCrashlytics.instance.isCrashlyticsCollectionEnabled ? 'enabled' : 'disabled'}'), - Divider(), - Text('Entries: ${entries.length}'), - Text('Catalogued: ${catalogued.length}'), - Text('With GPS: ${withGps.length}'), - Text('With address: ${located.length}'), - Divider(), - Row( - children: [ - Expanded( - child: Text('Image cache:\n\t${imageCache.currentSize}/${imageCache.maximumSize} items\n\t${formatFilesize(imageCache.currentSizeBytes)}/${formatFilesize(imageCache.maximumSizeBytes)}'), - ), - SizedBox(width: 8), - ElevatedButton( - onPressed: () { - imageCache.clear(); - setState(() {}); - }, - child: Text('Clear'), - ), - ], - ), - Row( - children: [ - Expanded( - child: Text('SVG cache: ${PictureProvider.cacheCount} items'), - ), - SizedBox(width: 8), - ElevatedButton( - onPressed: () { - PictureProvider.clearCache(); - setState(() {}); - }, - child: Text('Clear'), - ), - ], - ), - Row( - children: [ - Expanded( - child: Text('Glide disk cache: ?'), - ), - SizedBox(width: 8), - ElevatedButton( - onPressed: ImageFileService.clearSizedThumbnailDiskCache, - child: Text('Clear'), - ), - ], - ), - Divider(), - FutureBuilder( - future: _dbFileSizeLoader, - builder: (context, snapshot) { - if (snapshot.hasError) return Text(snapshot.error.toString()); - if (snapshot.connectionState != ConnectionState.done) return SizedBox.shrink(); - return Row( - children: [ - Expanded( - child: Text('DB file size: ${formatFilesize(snapshot.data)}'), - ), - SizedBox(width: 8), - ElevatedButton( - onPressed: () => metadataDb.reset().then((_) => _startDbReport()), - child: Text('Reset'), - ), - ], - ); - }, - ), - FutureBuilder( - future: _dbEntryLoader, - builder: (context, snapshot) { - if (snapshot.hasError) return Text(snapshot.error.toString()); - if (snapshot.connectionState != ConnectionState.done) return SizedBox.shrink(); - return Row( - children: [ - Expanded( - child: Text('DB entry rows: ${snapshot.data.length}'), - ), - SizedBox(width: 8), - ElevatedButton( - onPressed: () => metadataDb.clearEntries().then((_) => _startDbReport()), - child: Text('Clear'), - ), - ], - ); - }, - ), - FutureBuilder( - future: _dbDateLoader, - builder: (context, snapshot) { - if (snapshot.hasError) return Text(snapshot.error.toString()); - if (snapshot.connectionState != ConnectionState.done) return SizedBox.shrink(); - return Row( - children: [ - Expanded( - child: Text('DB date rows: ${snapshot.data.length}'), - ), - SizedBox(width: 8), - ElevatedButton( - onPressed: () => metadataDb.clearDates().then((_) => _startDbReport()), - child: Text('Clear'), - ), - ], - ); - }, - ), - FutureBuilder( - future: _dbMetadataLoader, - builder: (context, snapshot) { - if (snapshot.hasError) return Text(snapshot.error.toString()); - if (snapshot.connectionState != ConnectionState.done) return SizedBox.shrink(); - return Row( - children: [ - Expanded( - child: Text('DB metadata rows: ${snapshot.data.length}'), - ), - SizedBox(width: 8), - ElevatedButton( - onPressed: () => metadataDb.clearMetadataEntries().then((_) => _startDbReport()), - child: Text('Clear'), - ), - ], - ); - }, - ), - FutureBuilder( - future: _dbAddressLoader, - builder: (context, snapshot) { - if (snapshot.hasError) return Text(snapshot.error.toString()); - if (snapshot.connectionState != ConnectionState.done) return SizedBox.shrink(); - return Row( - children: [ - Expanded( - child: Text('DB address rows: ${snapshot.data.length}'), - ), - SizedBox(width: 8), - ElevatedButton( - onPressed: () => metadataDb.clearAddresses().then((_) => _startDbReport()), - child: Text('Clear'), - ), - ], - ); - }, - ), - FutureBuilder( - future: _dbFavouritesLoader, - builder: (context, snapshot) { - if (snapshot.hasError) return Text(snapshot.error.toString()); - if (snapshot.connectionState != ConnectionState.done) return SizedBox.shrink(); - return Row( - children: [ - Expanded( - child: Text('DB favourite rows: ${snapshot.data.length} (${favourites.count} in memory)'), - ), - SizedBox(width: 8), - ElevatedButton( - onPressed: () => favourites.clear().then((_) => _startDbReport()), - child: Text('Clear'), - ), - ], - ); - }, - ), - ], - ); - } - - Widget _buildSettingsTabView() { - return ListView( - padding: EdgeInsets.all(16), - children: [ - Row( - children: [ - Expanded( - child: Text('Settings'), - ), - SizedBox(width: 8), - ElevatedButton( - onPressed: () => settings.reset().then((_) => setState(() {})), - child: Text('Reset'), - ), - ], - ), - InfoRowGroup({ - 'collectionGroupFactor': '${settings.collectionGroupFactor}', - 'collectionSortFactor': '${settings.collectionSortFactor}', - 'collectionTileExtent': '${settings.collectionTileExtent}', - 'infoMapZoom': '${settings.infoMapZoom}', - 'pinnedFilters': '${settings.pinnedFilters}', - 'searchHistory': '${settings.searchHistory}', - }), - ], - ); - } - - Widget _buildStorageTabView() { - return ListView( - padding: EdgeInsets.all(16), - children: [ - ...androidFileUtils.storageVolumes.expand((v) => [ - Text(v.path), - InfoRowGroup({ - 'description': '${v.description}', - 'isEmulated': '${v.isEmulated}', - 'isPrimary': '${v.isPrimary}', - 'isRemovable': '${v.isRemovable}', - 'state': '${v.state}', - }), - Divider(), - ]) - ], - ); - } - - Widget _buildEnvTabView() { - return ListView( - padding: EdgeInsets.all(16), - children: [ - FutureBuilder( - future: _envLoader, - builder: (context, snapshot) { - if (snapshot.hasError) return Text(snapshot.error.toString()); - if (snapshot.connectionState != ConnectionState.done) return SizedBox.shrink(); - final data = SplayTreeMap.of(snapshot.data.map((k, v) => MapEntry(k.toString(), v?.toString() ?? 'null'))); - return InfoRowGroup(data); - }, - ), - ], - ); - } - - void _startDbReport() { - _dbFileSizeLoader = metadataDb.dbFileSize(); - _dbEntryLoader = metadataDb.loadEntries(); - _dbDateLoader = metadataDb.loadDates(); - _dbMetadataLoader = metadataDb.loadMetadataEntries(); - _dbAddressLoader = metadataDb.loadAddresses(); - _dbFavouritesLoader = metadataDb.loadFavourites(); - setState(() {}); - } -} diff --git a/lib/widgets/collection/grid/header_generic.dart b/lib/widgets/collection/grid/header_generic.dart index e314fa14c..eabeb55d0 100644 --- a/lib/widgets/collection/grid/header_generic.dart +++ b/lib/widgets/collection/grid/header_generic.dart @@ -214,7 +214,7 @@ class SectionSelectableLeading extends StatelessWidget { child: IconButton( iconSize: 26, padding: EdgeInsets.only(top: 1), - alignment: Alignment.topLeft, + alignment: AlignmentDirectional.topStart, icon: Icon(selected ? AIcons.selected : AIcons.unselected), onPressed: onPressed, tooltip: selected ? 'Deselect section' : 'Select section', diff --git a/lib/widgets/collection/thumbnail/overlay.dart b/lib/widgets/collection/thumbnail/overlay.dart index fb46bb6f2..bde0dd789 100644 --- a/lib/widgets/collection/thumbnail/overlay.dart +++ b/lib/widgets/collection/thumbnail/overlay.dart @@ -97,7 +97,7 @@ class ThumbnailSelectionOverlay extends StatelessWidget { ); child = AnimatedContainer( duration: duration, - alignment: Alignment.topRight, + alignment: AlignmentDirectional.topEnd, color: selected ? Colors.black54 : Colors.transparent, child: child, ); diff --git a/lib/widgets/common/aves_expansion_tile.dart b/lib/widgets/common/aves_expansion_tile.dart index 85b76a31b..9155483ed 100644 --- a/lib/widgets/common/aves_expansion_tile.dart +++ b/lib/widgets/common/aves_expansion_tile.dart @@ -9,12 +9,13 @@ class AvesExpansionTile extends StatelessWidget { const AvesExpansionTile({ @required this.title, - @required this.children, this.expandedNotifier, + @required this.children, }); @override Widget build(BuildContext context) { + final enabled = children?.isNotEmpty == true; return Theme( data: Theme.of(context).copyWith( // color used by the `ExpansionTileCard` for selected text and icons @@ -27,12 +28,17 @@ class AvesExpansionTile extends StatelessWidget { title: HighlightTitle( title, fontSize: 18, + enabled: enabled, + ), + expandable: enabled, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Divider(thickness: 1, height: 1), + SizedBox(height: 4), + if (enabled) ...children, + ], ), - children: [ - Divider(thickness: 1, height: 1), - SizedBox(height: 4), - ...children, - ], baseColor: Colors.grey[900], expandedColor: Colors.grey[850], ), diff --git a/lib/widgets/common/highlight_title.dart b/lib/widgets/common/highlight_title.dart index 1fbb5cab7..671e61740 100644 --- a/lib/widgets/common/highlight_title.dart +++ b/lib/widgets/common/highlight_title.dart @@ -5,19 +5,23 @@ import 'package:flutter/material.dart'; class HighlightTitle extends StatelessWidget { final String name; final double fontSize; + final bool enabled; const HighlightTitle( this.name, { this.fontSize = 20, + this.enabled = true, }); + static const disabledColor = Colors.grey; + @override Widget build(BuildContext context) { return Align( alignment: AlignmentDirectional.centerStart, child: Container( decoration: HighlightDecoration( - color: stringToColor(name), + color: enabled ? stringToColor(name) : disabledColor, ), margin: EdgeInsets.symmetric(vertical: 4.0), child: Text( diff --git a/lib/widgets/debug/android_env.dart b/lib/widgets/debug/android_env.dart new file mode 100644 index 000000000..cb0fa1aa5 --- /dev/null +++ b/lib/widgets/debug/android_env.dart @@ -0,0 +1,47 @@ +import 'dart:collection'; + +import 'package:aves/services/android_app_service.dart'; +import 'package:aves/widgets/common/aves_expansion_tile.dart'; +import 'package:aves/widgets/fullscreen/info/common.dart'; +import 'package:flutter/material.dart'; + +class DebugAndroidEnvironmentSection extends StatefulWidget { + @override + _DebugAndroidEnvironmentSectionState createState() => _DebugAndroidEnvironmentSectionState(); +} + +class _DebugAndroidEnvironmentSectionState extends State with AutomaticKeepAliveClientMixin { + Future _loader; + + @override + void initState() { + super.initState(); + _loader = AndroidAppService.getEnv(); + } + + @override + Widget build(BuildContext context) { + super.build(context); + + return AvesExpansionTile( + title: 'Android Environment', + children: [ + Padding( + padding: EdgeInsets.only(left: 8, right: 8, bottom: 8), + child: FutureBuilder( + future: _loader, + builder: (context, snapshot) { + if (snapshot.hasError) return Text(snapshot.error.toString()); + if (snapshot.connectionState != ConnectionState.done) return SizedBox.shrink(); + final data = SplayTreeMap.of(snapshot.data.map((k, v) => MapEntry(k.toString(), v?.toString() ?? 'null'))); + return InfoRowGroup(data); + }, + ), + ), + ], + ); + } + + @override + bool get wantKeepAlive => true; +} diff --git a/lib/widgets/debug/app_debug_page.dart b/lib/widgets/debug/app_debug_page.dart new file mode 100644 index 000000000..e08623b5d --- /dev/null +++ b/lib/widgets/debug/app_debug_page.dart @@ -0,0 +1,88 @@ +import 'package:aves/model/image_entry.dart'; +import 'package:aves/model/source/collection_source.dart'; +import 'package:aves/widgets/common/aves_expansion_tile.dart'; +import 'package:aves/widgets/common/data_providers/media_query_data_provider.dart'; +import 'package:aves/widgets/debug/android_env.dart'; +import 'package:aves/widgets/debug/cache.dart'; +import 'package:aves/widgets/debug/database.dart'; +import 'package:aves/widgets/debug/firebase.dart'; +import 'package:aves/widgets/debug/settings.dart'; +import 'package:aves/widgets/debug/storage.dart'; +import 'package:aves/widgets/fullscreen/info/common.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; + +class AppDebugPage extends StatefulWidget { + static const routeName = '/debug'; + + final CollectionSource source; + + const AppDebugPage({this.source}); + + @override + State createState() => AppDebugPageState(); +} + +class AppDebugPageState extends State { + List get entries => widget.source.rawEntries; + + @override + Widget build(BuildContext context) { + return MediaQueryDataProvider( + child: Scaffold( + appBar: AppBar( + title: Text('Debug'), + ), + body: SafeArea( + child: ListView( + padding: EdgeInsets.all(8), + children: [ + _buildGeneralTabView(), + DebugAndroidEnvironmentSection(), + DebugCacheSection(), + DebugAppDatabaseSection(), + DebugFirebaseSection(), + DebugSettingsSection(), + DebugStorageSection(), + ], + ), + ), + ), + ); + } + + Widget _buildGeneralTabView() { + final catalogued = entries.where((entry) => entry.isCatalogued); + final withGps = catalogued.where((entry) => entry.hasGps); + final located = withGps.where((entry) => entry.isLocated); + return AvesExpansionTile( + title: 'General', + children: [ + Padding( + padding: EdgeInsets.all(8), + child: Text('Time dilation'), + ), + Slider( + value: timeDilation, + onChanged: (v) => setState(() => timeDilation = v), + min: 1.0, + max: 10.0, + divisions: 9, + label: '$timeDilation', + ), + Divider(), + Padding( + padding: EdgeInsets.only(left: 8, right: 8, bottom: 8), + child: InfoRowGroup( + { + 'Entries': '${entries.length}', + 'Catalogued': '${catalogued.length}', + 'With GPS': '${withGps.length}', + 'With address': '${located.length}', + }, + ), + ), + ], + ); + } +} diff --git a/lib/widgets/debug/cache.dart b/lib/widgets/debug/cache.dart new file mode 100644 index 000000000..eeb85e308 --- /dev/null +++ b/lib/widgets/debug/cache.dart @@ -0,0 +1,77 @@ +import 'package:aves/services/image_file_service.dart'; +import 'package:aves/utils/file_utils.dart'; +import 'package:aves/widgets/common/aves_expansion_tile.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class DebugCacheSection extends StatefulWidget { + @override + _DebugCacheSectionState createState() => _DebugCacheSectionState(); +} + +class _DebugCacheSectionState extends State with AutomaticKeepAliveClientMixin { + @override + Widget build(BuildContext context) { + super.build(context); + + return AvesExpansionTile( + title: 'Cache', + children: [ + Padding( + padding: EdgeInsets.symmetric(horizontal: 8), + child: Column( + children: [ + Row( + children: [ + Expanded( + child: Text('Image cache:\n\t${imageCache.currentSize}/${imageCache.maximumSize} items\n\t${formatFilesize(imageCache.currentSizeBytes)}/${formatFilesize(imageCache.maximumSizeBytes)}'), + ), + SizedBox(width: 8), + ElevatedButton( + onPressed: () { + imageCache.clear(); + + setState(() {}); + }, + child: Text('Clear'), + ), + ], + ), + Row( + children: [ + Expanded( + child: Text('SVG cache: ${PictureProvider.cacheCount} items'), + ), + SizedBox(width: 8), + ElevatedButton( + onPressed: () { + PictureProvider.clearCache(); + + setState(() {}); + }, + child: Text('Clear'), + ), + ], + ), + Row( + children: [ + Expanded( + child: Text('Glide disk cache: ?'), + ), + SizedBox(width: 8), + ElevatedButton( + onPressed: ImageFileService.clearSizedThumbnailDiskCache, + child: Text('Clear'), + ), + ], + ), + ], + ), + ), + ], + ); + } + + @override + bool get wantKeepAlive => true; +} diff --git a/lib/widgets/debug/database.dart b/lib/widgets/debug/database.dart new file mode 100644 index 000000000..5c8f9ff19 --- /dev/null +++ b/lib/widgets/debug/database.dart @@ -0,0 +1,184 @@ +import 'package:aves/model/favourite_repo.dart'; +import 'package:aves/model/image_entry.dart'; +import 'package:aves/model/image_metadata.dart'; +import 'package:aves/model/metadata_db.dart'; +import 'package:aves/utils/file_utils.dart'; +import 'package:aves/widgets/common/aves_expansion_tile.dart'; +import 'package:flutter/material.dart'; + +class DebugAppDatabaseSection extends StatefulWidget { + @override + _DebugAppDatabaseSectionState createState() => _DebugAppDatabaseSectionState(); +} + +class _DebugAppDatabaseSectionState extends State with AutomaticKeepAliveClientMixin { + Future _dbFileSizeLoader; + Future> _dbEntryLoader; + Future> _dbDateLoader; + Future> _dbMetadataLoader; + Future> _dbAddressLoader; + Future> _dbFavouritesLoader; + + @override + void initState() { + super.initState(); + _startDbReport(); + } + + @override + Widget build(BuildContext context) { + super.build(context); + + return AvesExpansionTile( + title: 'Database', + children: [ + Padding( + padding: EdgeInsets.symmetric(horizontal: 8), + child: Column( + children: [ + FutureBuilder( + future: _dbFileSizeLoader, + builder: (context, snapshot) { + if (snapshot.hasError) return Text(snapshot.error.toString()); + + if (snapshot.connectionState != ConnectionState.done) return SizedBox.shrink(); + + return Row( + children: [ + Expanded( + child: Text('DB file size: ${formatFilesize(snapshot.data)}'), + ), + SizedBox(width: 8), + ElevatedButton( + onPressed: () => metadataDb.reset().then((_) => _startDbReport()), + child: Text('Reset'), + ), + ], + ); + }, + ), + FutureBuilder( + future: _dbEntryLoader, + builder: (context, snapshot) { + if (snapshot.hasError) return Text(snapshot.error.toString()); + + if (snapshot.connectionState != ConnectionState.done) return SizedBox.shrink(); + + return Row( + children: [ + Expanded( + child: Text('entry rows: ${snapshot.data.length}'), + ), + SizedBox(width: 8), + ElevatedButton( + onPressed: () => metadataDb.clearEntries().then((_) => _startDbReport()), + child: Text('Clear'), + ), + ], + ); + }, + ), + FutureBuilder( + future: _dbDateLoader, + builder: (context, snapshot) { + if (snapshot.hasError) return Text(snapshot.error.toString()); + + if (snapshot.connectionState != ConnectionState.done) return SizedBox.shrink(); + + return Row( + children: [ + Expanded( + child: Text('date rows: ${snapshot.data.length}'), + ), + SizedBox(width: 8), + ElevatedButton( + onPressed: () => metadataDb.clearDates().then((_) => _startDbReport()), + child: Text('Clear'), + ), + ], + ); + }, + ), + FutureBuilder( + future: _dbMetadataLoader, + builder: (context, snapshot) { + if (snapshot.hasError) return Text(snapshot.error.toString()); + + if (snapshot.connectionState != ConnectionState.done) return SizedBox.shrink(); + + return Row( + children: [ + Expanded( + child: Text('metadata rows: ${snapshot.data.length}'), + ), + SizedBox(width: 8), + ElevatedButton( + onPressed: () => metadataDb.clearMetadataEntries().then((_) => _startDbReport()), + child: Text('Clear'), + ), + ], + ); + }, + ), + FutureBuilder( + future: _dbAddressLoader, + builder: (context, snapshot) { + if (snapshot.hasError) return Text(snapshot.error.toString()); + + if (snapshot.connectionState != ConnectionState.done) return SizedBox.shrink(); + + return Row( + children: [ + Expanded( + child: Text('address rows: ${snapshot.data.length}'), + ), + SizedBox(width: 8), + ElevatedButton( + onPressed: () => metadataDb.clearAddresses().then((_) => _startDbReport()), + child: Text('Clear'), + ), + ], + ); + }, + ), + FutureBuilder( + future: _dbFavouritesLoader, + builder: (context, snapshot) { + if (snapshot.hasError) return Text(snapshot.error.toString()); + + if (snapshot.connectionState != ConnectionState.done) return SizedBox.shrink(); + + return Row( + children: [ + Expanded( + child: Text('favourite rows: ${snapshot.data.length} (${favourites.count} in memory)'), + ), + SizedBox(width: 8), + ElevatedButton( + onPressed: () => favourites.clear().then((_) => _startDbReport()), + child: Text('Clear'), + ), + ], + ); + }, + ), + ], + ), + ), + ], + ); + } + + void _startDbReport() { + _dbFileSizeLoader = metadataDb.dbFileSize(); + _dbEntryLoader = metadataDb.loadEntries(); + _dbDateLoader = metadataDb.loadDates(); + _dbMetadataLoader = metadataDb.loadMetadataEntries(); + _dbAddressLoader = metadataDb.loadAddresses(); + _dbFavouritesLoader = metadataDb.loadFavourites(); + setState(() {}); + } + + @override + bool get wantKeepAlive => true; +} diff --git a/lib/widgets/debug/firebase.dart b/lib/widgets/debug/firebase.dart new file mode 100644 index 000000000..f98d14a91 --- /dev/null +++ b/lib/widgets/debug/firebase.dart @@ -0,0 +1,43 @@ +import 'package:aves/widgets/common/aves_expansion_tile.dart'; +import 'package:aves/widgets/fullscreen/info/common.dart'; +import 'package:firebase_analytics/firebase_analytics.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_crashlytics/firebase_crashlytics.dart'; +import 'package:flutter/material.dart'; + +class DebugFirebaseSection extends StatelessWidget { + @override + Widget build(BuildContext context) { + return AvesExpansionTile( + title: 'Firebase', + children: [ + Padding( + padding: EdgeInsets.symmetric(horizontal: 8), + child: Row( + children: [ + ElevatedButton( + onPressed: FirebaseCrashlytics.instance.crash, + child: Text('Crash'), + ), + SizedBox(width: 8), + ElevatedButton( + onPressed: () => FirebaseAnalytics().logEvent( + name: 'debug_test', + parameters: {'time': DateTime.now().toIso8601String()}, + ), + child: Text('Send event'), + ), + ], + ), + ), + Padding( + padding: EdgeInsets.only(left: 8, right: 8, bottom: 8), + child: InfoRowGroup({ + 'Firebase data collection enabled': '${Firebase.app().isAutomaticDataCollectionEnabled}', + 'Crashlytics collection enabled': '${FirebaseCrashlytics.instance.isCrashlyticsCollectionEnabled}', + }), + ) + ], + ); + } +} diff --git a/lib/widgets/debug/settings.dart b/lib/widgets/debug/settings.dart new file mode 100644 index 000000000..36e68c18f --- /dev/null +++ b/lib/widgets/debug/settings.dart @@ -0,0 +1,40 @@ +import 'package:aves/model/settings/settings.dart'; +import 'package:aves/widgets/common/aves_expansion_tile.dart'; +import 'package:aves/widgets/fullscreen/info/common.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class DebugSettingsSection extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Consumer(builder: (context, settings, child) { + String toMultiline(Iterable l) => l.isNotEmpty ? '\n${l.join('\n')}' : '$l'; + return AvesExpansionTile( + title: 'Settings', + children: [ + Padding( + padding: EdgeInsets.symmetric(horizontal: 8), + child: ElevatedButton( + onPressed: () => settings.reset(), + child: Text('Reset'), + ), + ), + SwitchListTile( + value: settings.hasAcceptedTerms, + onChanged: (v) => settings.hasAcceptedTerms = v, + title: Text('hasAcceptedTerms'), + ), + Padding( + padding: EdgeInsets.only(left: 8, right: 8, bottom: 8), + child: InfoRowGroup({ + 'collectionTileExtent': '${settings.collectionTileExtent}', + 'infoMapZoom': '${settings.infoMapZoom}', + 'pinnedFilters': toMultiline(settings.pinnedFilters), + 'searchHistory': toMultiline(settings.searchHistory), + }), + ), + ], + ); + }); + } +} diff --git a/lib/widgets/debug/storage.dart b/lib/widgets/debug/storage.dart new file mode 100644 index 000000000..804f2f1d1 --- /dev/null +++ b/lib/widgets/debug/storage.dart @@ -0,0 +1,32 @@ +import 'package:aves/utils/android_file_utils.dart'; +import 'package:aves/widgets/common/aves_expansion_tile.dart'; +import 'package:aves/widgets/fullscreen/info/common.dart'; +import 'package:flutter/material.dart'; + +class DebugStorageSection extends StatelessWidget { + @override + Widget build(BuildContext context) { + return AvesExpansionTile( + title: 'Storage Volumes', + children: [ + ...androidFileUtils.storageVolumes.expand((v) => [ + Padding( + padding: EdgeInsets.all(8), + child: Text(v.path), + ), + Padding( + padding: EdgeInsets.symmetric(horizontal: 8), + child: InfoRowGroup({ + 'description': '${v.description}', + 'isEmulated': '${v.isEmulated}', + 'isPrimary': '${v.isPrimary}', + 'isRemovable': '${v.isRemovable}', + 'state': '${v.state}', + }), + ), + Divider(), + ]) + ], + ); + } +} diff --git a/lib/widgets/drawer/app_drawer.dart b/lib/widgets/drawer/app_drawer.dart index 43cfa5f57..c09791276 100644 --- a/lib/widgets/drawer/app_drawer.dart +++ b/lib/widgets/drawer/app_drawer.dart @@ -10,9 +10,9 @@ import 'package:aves/model/source/location.dart'; import 'package:aves/model/source/tag.dart'; import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/widgets/about/about_page.dart'; -import 'package:aves/widgets/app_debug_page.dart'; import 'package:aves/widgets/common/aves_logo.dart'; import 'package:aves/widgets/common/icons.dart'; +import 'package:aves/widgets/debug/app_debug_page.dart'; import 'package:aves/widgets/drawer/collection_tile.dart'; import 'package:aves/widgets/drawer/tile.dart'; import 'package:aves/widgets/filter_grids/albums_page.dart'; diff --git a/lib/widgets/fullscreen/debug/metadata.dart b/lib/widgets/fullscreen/debug/metadata.dart index e097d1c0d..4b1ad5bd2 100644 --- a/lib/widgets/fullscreen/debug/metadata.dart +++ b/lib/widgets/fullscreen/debug/metadata.dart @@ -60,16 +60,17 @@ class _MetadataTabState extends State { })); return AvesExpansionTile( title: title, - children: [ - Container( - alignment: AlignmentDirectional.topStart, - padding: EdgeInsets.only(left: 8, right: 8, bottom: 8), - child: InfoRowGroup( - data, - maxValueLength: Constants.infoGroupMaxValueLength, - ), - ) - ], + children: data.isNotEmpty + ? [ + Padding( + padding: EdgeInsets.only(left: 8, right: 8, bottom: 8), + child: InfoRowGroup( + data, + maxValueLength: Constants.infoGroupMaxValueLength, + ), + ) + ] + : null, ); } diff --git a/lib/widgets/fullscreen/info/metadata_section.dart b/lib/widgets/fullscreen/info/metadata_section.dart index eb5be71e3..f7ce36e0b 100644 --- a/lib/widgets/fullscreen/info/metadata_section.dart +++ b/lib/widgets/fullscreen/info/metadata_section.dart @@ -154,13 +154,9 @@ class _MetadataSectionSliverState extends State with Auto expandedNotifier: _expandedDirectoryNotifier, children: [ if (prefixChildren.isNotEmpty) - Align( - alignment: AlignmentDirectional.topStart, - child: Wrap(children: prefixChildren), - ), + Wrap(children: prefixChildren), if (thumbnail != null) thumbnail, - Container( - alignment: Alignment.topLeft, + Padding( padding: EdgeInsets.only(left: 8, right: 8, bottom: 8), child: InfoRowGroup(dir.tags, maxValueLength: Constants.infoGroupMaxValueLength), ), diff --git a/lib/widgets/settings/settings_page.dart b/lib/widgets/settings/settings_page.dart index e10355fb1..2000c2405 100644 --- a/lib/widgets/settings/settings_page.dart +++ b/lib/widgets/settings/settings_page.dart @@ -27,35 +27,32 @@ class _SettingsPageState extends State { @override Widget build(BuildContext context) { return MediaQueryDataProvider( - child: DefaultTabController( - length: 4, - child: Scaffold( - appBar: AppBar( - title: Text('Settings'), - ), - body: SafeArea( - child: Consumer( - builder: (context, settings, child) => AnimationLimiter( - child: ListView( - padding: EdgeInsets.all(8), - children: AnimationConfiguration.toStaggeredList( - duration: Durations.staggeredAnimation, - delay: Durations.staggeredAnimationDelay, - childAnimationBuilder: (child) => SlideAnimation( - verticalOffset: 50.0, - child: FadeInAnimation( - child: child, - ), + child: Scaffold( + appBar: AppBar( + title: Text('Settings'), + ), + body: SafeArea( + child: Consumer( + builder: (context, settings, child) => AnimationLimiter( + child: ListView( + padding: EdgeInsets.all(8), + children: AnimationConfiguration.toStaggeredList( + duration: Durations.staggeredAnimation, + delay: Durations.staggeredAnimationDelay, + childAnimationBuilder: (child) => SlideAnimation( + verticalOffset: 50.0, + child: FadeInAnimation( + child: child, ), - children: [ - _buildNavigationSection(context), - _buildDisplaySection(context), - _buildThumbnailsSection(context), - _buildViewerSection(context), - _buildSearchSection(context), - _buildPrivacySection(context), - ], ), + children: [ + _buildNavigationSection(context), + _buildDisplaySection(context), + _buildThumbnailsSection(context), + _buildViewerSection(context), + _buildSearchSection(context), + _buildPrivacySection(context), + ], ), ), ), @@ -220,9 +217,8 @@ class _SettingsPageState extends State { onChanged: (v) => settings.isCrashlyticsEnabled = v, title: Text('Allow anonymous analytics and crash reporting'), ), - Container( - alignment: AlignmentDirectional.topStart, - padding: EdgeInsets.only(bottom: 16), + Padding( + padding: EdgeInsets.only(top: 8, bottom: 16), child: GrantedDirectories(), ), ], diff --git a/pubspec.lock b/pubspec.lock index ab17ce9bf..16f35ec1b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -169,7 +169,7 @@ packages: description: path: "." ref: HEAD - resolved-ref: edb6b11bb448fc2f30e566a20605b37093503176 + resolved-ref: "51fe2b12588356fade82ce65daef5482beed54e7" url: "git://github.com/deckerst/expansion_tile_card.git" source: git version: "1.0.3"