init: progressively add entries with saved dates
fullscreen: debug page
This commit is contained in:
parent
3328916c86
commit
2b2e7e31bd
14 changed files with 273 additions and 39 deletions
|
@ -27,7 +27,7 @@ public class MediaStoreStreamHandler implements EventChannel.StreamHandler {
|
|||
}
|
||||
|
||||
void fetchAll(Activity activity) {
|
||||
Log.d(LOG_TAG, "fetchAll start");
|
||||
// Log.d(LOG_TAG, "fetchAll start");
|
||||
// Instant start = Instant.now();
|
||||
Handler handler = new Handler(Looper.getMainLooper());
|
||||
new MediaStoreImageProvider().fetchAll(activity, (entry) -> handler.post(() -> eventSink.success(entry))); // 350ms
|
||||
|
|
|
@ -150,6 +150,9 @@ public class MediaStoreImageProvider extends ImageProvider {
|
|||
Log.w(LOG_TAG, "failed to get size for uri=" + itemUri + ", path=" + path + ", mimeType=" + mimeType);
|
||||
} else {
|
||||
newEntryHandler.handleEntry(entryMap);
|
||||
if (entryCount % 30 == 0) {
|
||||
Thread.sleep(10);
|
||||
}
|
||||
entryCount++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,16 +27,22 @@ class CollectionSource {
|
|||
List<ImageEntry> entries,
|
||||
}) : _rawEntries = entries ?? [];
|
||||
|
||||
final List<DateMetadata> savedDates = [];
|
||||
|
||||
Future<void> loadDates() async {
|
||||
final stopwatch = Stopwatch()..start();
|
||||
savedDates.addAll(await metadataDb.loadDates());
|
||||
debugPrint('$runtimeType loadDates complete in ${stopwatch.elapsed.inMilliseconds}ms for ${savedDates.length} saved entries');
|
||||
}
|
||||
|
||||
Future<void> loadCatalogMetadata() async {
|
||||
final stopwatch = Stopwatch()..start();
|
||||
final saved = await metadataDb.loadMetadataEntries();
|
||||
_rawEntries.forEach((entry) {
|
||||
final contentId = entry.contentId;
|
||||
if (contentId != null) {
|
||||
entry.catalogMetadata = saved.firstWhere((metadata) => metadata.contentId == contentId, orElse: () => null);
|
||||
}
|
||||
entry.catalogMetadata = saved.firstWhere((metadata) => metadata.contentId == contentId, orElse: () => null);
|
||||
});
|
||||
debugPrint('$runtimeType loadCatalogMetadata complete in ${stopwatch.elapsed.inMilliseconds}ms with ${saved.length} saved entries');
|
||||
debugPrint('$runtimeType loadCatalogMetadata complete in ${stopwatch.elapsed.inMilliseconds}ms for ${saved.length} saved entries');
|
||||
onCatalogMetadataChanged();
|
||||
}
|
||||
|
||||
|
@ -45,11 +51,9 @@ class CollectionSource {
|
|||
final saved = await metadataDb.loadAddresses();
|
||||
_rawEntries.forEach((entry) {
|
||||
final contentId = entry.contentId;
|
||||
if (contentId != null) {
|
||||
entry.addressDetails = saved.firstWhere((address) => address.contentId == contentId, orElse: () => null);
|
||||
}
|
||||
entry.addressDetails = saved.firstWhere((address) => address.contentId == contentId, orElse: () => null);
|
||||
});
|
||||
debugPrint('$runtimeType loadAddresses complete in ${stopwatch.elapsed.inMilliseconds}ms with ${saved.length} saved entries');
|
||||
debugPrint('$runtimeType loadAddresses complete in ${stopwatch.elapsed.inMilliseconds}ms for ${saved.length} saved entries');
|
||||
onAddressMetadataChanged();
|
||||
}
|
||||
|
||||
|
@ -124,12 +128,11 @@ class CollectionSource {
|
|||
sortedCities = lister((address) => address.city);
|
||||
}
|
||||
|
||||
void add(ImageEntry entry) {
|
||||
_rawEntries.add(entry);
|
||||
eventBus.fire(EntryAddedEvent(entry));
|
||||
}
|
||||
|
||||
void addAll(Iterable<ImageEntry> entries) {
|
||||
entries.forEach((entry) {
|
||||
final contentId = entry.contentId;
|
||||
entry.catalogDateMillis = savedDates.firstWhere((metadata) => metadata.contentId == contentId, orElse: () => null)?.dateMillis;
|
||||
});
|
||||
_rawEntries.addAll(entries);
|
||||
eventBus.fire(const EntryAddedEvent());
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ class ImageEntry {
|
|||
final int sourceDateTakenMillis;
|
||||
final String bucketDisplayName;
|
||||
final int durationMillis;
|
||||
int _catalogDateMillis;
|
||||
CatalogMetadata _catalogMetadata;
|
||||
AddressDetails _addressDetails;
|
||||
|
||||
|
@ -134,11 +135,11 @@ class ImageEntry {
|
|||
|
||||
DateTime get bestDate {
|
||||
if (_bestDate == null) {
|
||||
if ((_catalogMetadata?.dateMillis ?? 0) > 0) {
|
||||
_bestDate = DateTime.fromMillisecondsSinceEpoch(_catalogMetadata.dateMillis);
|
||||
} else if (sourceDateTakenMillis != null && sourceDateTakenMillis > 0) {
|
||||
if ((_catalogDateMillis ?? 0) > 0) {
|
||||
_bestDate = DateTime.fromMillisecondsSinceEpoch(_catalogDateMillis);
|
||||
} else if ((sourceDateTakenMillis ?? 0) > 0) {
|
||||
_bestDate = DateTime.fromMillisecondsSinceEpoch(sourceDateTakenMillis);
|
||||
} else if (dateModifiedSecs != null && dateModifiedSecs > 0) {
|
||||
} else if ((dateModifiedSecs ?? 0) > 0) {
|
||||
_bestDate = DateTime.fromMillisecondsSinceEpoch(dateModifiedSecs * 1000);
|
||||
}
|
||||
}
|
||||
|
@ -181,13 +182,17 @@ class ImageEntry {
|
|||
|
||||
CatalogMetadata get catalogMetadata => _catalogMetadata;
|
||||
|
||||
set catalogMetadata(CatalogMetadata newMetadata) {
|
||||
_catalogMetadata = newMetadata;
|
||||
set catalogDateMillis(int dateMillis) {
|
||||
_catalogDateMillis = dateMillis;
|
||||
_bestDate = null;
|
||||
}
|
||||
|
||||
set catalogMetadata(CatalogMetadata newMetadata) {
|
||||
if (newMetadata == null) return;
|
||||
catalogDateMillis = newMetadata.dateMillis;
|
||||
_catalogMetadata = newMetadata;
|
||||
_bestTitle = null;
|
||||
if (_catalogMetadata != null) {
|
||||
metadataChangeNotifier.notifyListeners();
|
||||
}
|
||||
metadataChangeNotifier.notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> catalog() async {
|
||||
|
|
|
@ -1,6 +1,32 @@
|
|||
import 'package:flutter/widgets.dart';
|
||||
import 'package:geocoder/model.dart';
|
||||
|
||||
class DateMetadata {
|
||||
final int contentId, dateMillis;
|
||||
|
||||
DateMetadata({
|
||||
this.contentId,
|
||||
this.dateMillis,
|
||||
});
|
||||
|
||||
factory DateMetadata.fromMap(Map map) {
|
||||
return DateMetadata(
|
||||
contentId: map['contentId'],
|
||||
dateMillis: map['dateMillis'] ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() => {
|
||||
'contentId': contentId,
|
||||
'dateMillis': dateMillis,
|
||||
};
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'DateMetadata{contentId=$contentId, dateMillis=$dateMillis}';
|
||||
}
|
||||
}
|
||||
|
||||
class CatalogMetadata {
|
||||
final int contentId, dateMillis, videoRotation;
|
||||
final String xmpSubjects, xmpTitleDescription;
|
||||
|
|
|
@ -12,6 +12,7 @@ class MetadataDb {
|
|||
|
||||
Future<String> get path async => join(await getDatabasesPath(), 'metadata.db');
|
||||
|
||||
static const dateTakenTable = 'dateTaken';
|
||||
static const metadataTable = 'metadata';
|
||||
static const addressTable = 'address';
|
||||
static const favouriteTable = 'favourites';
|
||||
|
@ -23,6 +24,7 @@ class MetadataDb {
|
|||
_database = openDatabase(
|
||||
await path,
|
||||
onCreate: (db, version) async {
|
||||
await db.execute('CREATE TABLE $dateTakenTable(contentId INTEGER PRIMARY KEY, dateMillis INTEGER)');
|
||||
await db.execute('CREATE TABLE $metadataTable(contentId INTEGER PRIMARY KEY, dateMillis INTEGER, videoRotation INTEGER, xmpSubjects TEXT, xmpTitleDescription TEXT, latitude REAL, longitude REAL)');
|
||||
await db.execute('CREATE TABLE $addressTable(contentId INTEGER PRIMARY KEY, addressLine TEXT, countryName TEXT, adminArea TEXT, locality TEXT)');
|
||||
await db.execute('CREATE TABLE $favouriteTable(contentId INTEGER PRIMARY KEY, path TEXT)');
|
||||
|
@ -43,6 +45,25 @@ class MetadataDb {
|
|||
await init();
|
||||
}
|
||||
|
||||
// date taken
|
||||
|
||||
Future<void> clearDates() async {
|
||||
final db = await _database;
|
||||
final count = await db.delete(dateTakenTable, where: '1');
|
||||
debugPrint('$runtimeType clearDates deleted $count entries');
|
||||
}
|
||||
|
||||
Future<List<DateMetadata>> loadDates() async {
|
||||
// final stopwatch = Stopwatch()..start();
|
||||
final db = await _database;
|
||||
final maps = await db.query(dateTakenTable);
|
||||
final metadataEntries = maps.map((map) => DateMetadata.fromMap(map)).toList();
|
||||
// debugPrint('$runtimeType loadDates complete in ${stopwatch.elapsed.inMilliseconds}ms for ${metadataEntries.length} entries');
|
||||
return metadataEntries;
|
||||
}
|
||||
|
||||
// catalog metadata
|
||||
|
||||
Future<void> clearMetadataEntries() async {
|
||||
final db = await _database;
|
||||
final count = await db.delete(metadataTable, where: '1');
|
||||
|
@ -54,7 +75,7 @@ class MetadataDb {
|
|||
final db = await _database;
|
||||
final maps = await db.query(metadataTable);
|
||||
final metadataEntries = maps.map((map) => CatalogMetadata.fromMap(map)).toList();
|
||||
// debugPrint('$runtimeType loadMetadataEntries complete in ${stopwatch.elapsed.inMilliseconds}ms with ${metadataEntries.length} entries');
|
||||
// debugPrint('$runtimeType loadMetadataEntries complete in ${stopwatch.elapsed.inMilliseconds}ms for ${metadataEntries.length} entries');
|
||||
return metadataEntries;
|
||||
}
|
||||
|
||||
|
@ -63,15 +84,26 @@ class MetadataDb {
|
|||
final stopwatch = Stopwatch()..start();
|
||||
final db = await _database;
|
||||
final batch = db.batch();
|
||||
metadataEntries.where((metadata) => metadata != null).forEach((metadata) => batch.insert(
|
||||
metadataTable,
|
||||
metadata.toMap(),
|
||||
metadataEntries.where((metadata) => metadata != null).forEach((metadata) {
|
||||
if (metadata.dateMillis != 0) {
|
||||
batch.insert(
|
||||
dateTakenTable,
|
||||
DateMetadata(contentId: metadata.contentId, dateMillis: metadata.dateMillis).toMap(),
|
||||
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||
));
|
||||
);
|
||||
}
|
||||
batch.insert(
|
||||
metadataTable,
|
||||
metadata.toMap(),
|
||||
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||
);
|
||||
});
|
||||
await batch.commit(noResult: true);
|
||||
debugPrint('$runtimeType saveMetadata complete in ${stopwatch.elapsed.inMilliseconds}ms with ${metadataEntries.length} entries');
|
||||
debugPrint('$runtimeType saveMetadata complete in ${stopwatch.elapsed.inMilliseconds}ms for ${metadataEntries.length} entries');
|
||||
}
|
||||
|
||||
// address
|
||||
|
||||
Future<void> clearAddresses() async {
|
||||
final db = await _database;
|
||||
final count = await db.delete(addressTable, where: '1');
|
||||
|
@ -83,7 +115,7 @@ class MetadataDb {
|
|||
final db = await _database;
|
||||
final maps = await db.query(addressTable);
|
||||
final addresses = maps.map((map) => AddressDetails.fromMap(map)).toList();
|
||||
// debugPrint('$runtimeType loadAddresses complete in ${stopwatch.elapsed.inMilliseconds}ms with ${addresses.length} entries');
|
||||
// debugPrint('$runtimeType loadAddresses complete in ${stopwatch.elapsed.inMilliseconds}ms for ${addresses.length} entries');
|
||||
return addresses;
|
||||
}
|
||||
|
||||
|
@ -98,7 +130,7 @@ class MetadataDb {
|
|||
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||
));
|
||||
await batch.commit(noResult: true);
|
||||
debugPrint('$runtimeType saveAddresses complete in ${stopwatch.elapsed.inMilliseconds}ms with ${addresses.length} entries');
|
||||
debugPrint('$runtimeType saveAddresses complete in ${stopwatch.elapsed.inMilliseconds}ms for ${addresses.length} entries');
|
||||
}
|
||||
|
||||
// favourites
|
||||
|
@ -114,7 +146,7 @@ class MetadataDb {
|
|||
final db = await _database;
|
||||
final maps = await db.query(favouriteTable);
|
||||
final favouriteRows = maps.map((map) => FavouriteRow.fromMap(map)).toList();
|
||||
// debugPrint('$runtimeType loadFavourites complete in ${stopwatch.elapsed.inMilliseconds}ms with ${favouriteRows.length} entries');
|
||||
// debugPrint('$runtimeType loadFavourites complete in ${stopwatch.elapsed.inMilliseconds}ms for ${favouriteRows.length} entries');
|
||||
return favouriteRows;
|
||||
}
|
||||
|
||||
|
@ -129,7 +161,7 @@ class MetadataDb {
|
|||
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||
));
|
||||
await batch.commit(noResult: true);
|
||||
// debugPrint('$runtimeType addFavourites complete in ${stopwatch.elapsed.inMilliseconds}ms with ${favouriteRows.length} entries');
|
||||
// debugPrint('$runtimeType addFavourites complete in ${stopwatch.elapsed.inMilliseconds}ms for ${favouriteRows.length} entries');
|
||||
}
|
||||
|
||||
Future<void> removeFavourites(Iterable<FavouriteRow> favouriteRows) async {
|
||||
|
@ -147,6 +179,6 @@ class MetadataDb {
|
|||
whereArgs: [id],
|
||||
));
|
||||
await batch.commit(noResult: true);
|
||||
// debugPrint('$runtimeType removeFavourites complete in ${stopwatch.elapsed.inMilliseconds}ms with ${favouriteRows.length} entries');
|
||||
// debugPrint('$runtimeType removeFavourites complete in ${stopwatch.elapsed.inMilliseconds}ms for ${favouriteRows.length} entries');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,7 +80,6 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
|||
return ValueListenableBuilder<PageState>(
|
||||
valueListenable: stateNotifier,
|
||||
builder: (context, state, child) {
|
||||
debugPrint('$runtimeType builder state=$state');
|
||||
return AnimatedBuilder(
|
||||
animation: collection.filterChangeNotifier,
|
||||
builder: (context, child) => SliverAppBar(
|
||||
|
|
|
@ -33,7 +33,7 @@ class ThumbnailCollection extends StatelessWidget {
|
|||
builder: (c, mqViewInsetsBottom, child) {
|
||||
return Consumer<CollectionLens>(
|
||||
builder: (context, collection, child) {
|
||||
debugPrint('$runtimeType collection builder entries=${collection.entryCount}');
|
||||
// debugPrint('$runtimeType collection builder entries=${collection.entryCount}');
|
||||
final sectionKeys = collection.sections.keys.toList();
|
||||
final showHeaders = collection.showHeaders;
|
||||
return GridScaleGestureDetector(
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:aves/model/collection_lens.dart';
|
||||
import 'package:aves/model/collection_source.dart';
|
||||
import 'package:aves/model/favourite_repo.dart';
|
||||
|
@ -35,15 +37,20 @@ class MediaStoreSource {
|
|||
if (currentTimeZone != catalogTimeZone) {
|
||||
// clear catalog metadata to get correct date/times when moving to a different time zone
|
||||
debugPrint('$runtimeType clear catalog metadata to get correct date/times');
|
||||
await metadataDb.clearDates();
|
||||
await metadataDb.clearMetadataEntries();
|
||||
settings.catalogTimeZone = currentTimeZone;
|
||||
}
|
||||
await _source.loadDates(); // 100ms for 5400 entries
|
||||
|
||||
var refreshCount = 10;
|
||||
const refreshCountMax = 1000;
|
||||
final allEntries = <ImageEntry>[];
|
||||
_eventChannel.receiveBroadcastStream().cast<Map>().listen(
|
||||
(entryMap) {
|
||||
allEntries.add(ImageEntry.fromMap(entryMap));
|
||||
if (allEntries.length >= 100) {
|
||||
if (allEntries.length >= refreshCount) {
|
||||
refreshCount = min(refreshCount * 10, refreshCountMax);
|
||||
_source.addAll(allEntries);
|
||||
allEntries.clear();
|
||||
// debugPrint('$runtimeType streamed ${_source.entries.length} entries at ${stopwatch.elapsed.inMilliseconds}ms');
|
||||
|
@ -54,7 +61,7 @@ class MediaStoreSource {
|
|||
_source.addAll(allEntries);
|
||||
// TODO reduce setup time until here
|
||||
_source.updateAlbums(); // <50ms
|
||||
await _source.loadCatalogMetadata(); // 650ms
|
||||
await _source.loadCatalogMetadata(); // 400ms for 5400 entries
|
||||
await _source.catalogEntries(); // <50ms
|
||||
await _source.loadAddresses(); // 350ms
|
||||
await _source.locateEntries(); // <50ms
|
||||
|
|
|
@ -21,6 +21,7 @@ class DebugPage extends StatefulWidget {
|
|||
|
||||
class DebugPageState extends State<DebugPage> {
|
||||
Future<int> _dbFileSizeLoader;
|
||||
Future<List<DateMetadata>> _dbDateLoader;
|
||||
Future<List<CatalogMetadata>> _dbMetadataLoader;
|
||||
Future<List<AddressDetails>> _dbAddressLoader;
|
||||
Future<List<FavouriteRow>> _dbFavouritesLoader;
|
||||
|
@ -78,6 +79,23 @@ class DebugPageState extends State<DebugPage> {
|
|||
);
|
||||
},
|
||||
),
|
||||
FutureBuilder(
|
||||
future: _dbDateLoader,
|
||||
builder: (context, AsyncSnapshot<List<DateMetadata>> snapshot) {
|
||||
if (snapshot.hasError) return Text(snapshot.error.toString());
|
||||
if (snapshot.connectionState != ConnectionState.done) return const SizedBox.shrink();
|
||||
return Row(
|
||||
children: [
|
||||
Text('DB date rows: ${snapshot.data.length}'),
|
||||
const Spacer(),
|
||||
RaisedButton(
|
||||
onPressed: () => metadataDb.clearDates().then((_) => _startDbReport()),
|
||||
child: const Text('Clear'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
FutureBuilder(
|
||||
future: _dbMetadataLoader,
|
||||
builder: (context, AsyncSnapshot<List<CatalogMetadata>> snapshot) {
|
||||
|
@ -175,6 +193,7 @@ class DebugPageState extends State<DebugPage> {
|
|||
|
||||
void _startDbReport() {
|
||||
_dbFileSizeLoader = metadataDb.dbFileSize();
|
||||
_dbDateLoader = metadataDb.loadDates();
|
||||
_dbMetadataLoader = metadataDb.loadMetadataEntries();
|
||||
_dbAddressLoader = metadataDb.loadAddresses();
|
||||
_dbFavouritesLoader = metadataDb.loadFavourites();
|
||||
|
|
115
lib/widgets/fullscreen/debug.dart
Normal file
115
lib/widgets/fullscreen/debug.dart
Normal file
|
@ -0,0 +1,115 @@
|
|||
import 'package:aves/model/image_entry.dart';
|
||||
import 'package:aves/model/image_metadata.dart';
|
||||
import 'package:aves/model/metadata_db.dart';
|
||||
import 'package:aves/widgets/fullscreen/info/info_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class FullscreenDebugPage extends StatefulWidget {
|
||||
final ImageEntry entry;
|
||||
|
||||
const FullscreenDebugPage({@required this.entry});
|
||||
|
||||
@override
|
||||
_FullscreenDebugPageState createState() => _FullscreenDebugPageState();
|
||||
}
|
||||
|
||||
class _FullscreenDebugPageState extends State<FullscreenDebugPage> {
|
||||
Future<DateMetadata> _dbDateLoader;
|
||||
Future<CatalogMetadata> _dbMetadataLoader;
|
||||
Future<AddressDetails> _dbAddressLoader;
|
||||
|
||||
int get contentId => widget.entry.contentId;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_startDbReport();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Debug'),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
FutureBuilder(
|
||||
future: _dbDateLoader,
|
||||
builder: (context, AsyncSnapshot<DateMetadata> snapshot) {
|
||||
if (snapshot.hasError) return Text(snapshot.error.toString());
|
||||
if (snapshot.connectionState != ConnectionState.done) return const SizedBox.shrink();
|
||||
final data = snapshot.data;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('DB date:${data == null ? ' no row' : ''}'),
|
||||
if (data != null)
|
||||
InfoRowGroup({
|
||||
'dateMillis': '${data.dateMillis}',
|
||||
}),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
FutureBuilder(
|
||||
future: _dbMetadataLoader,
|
||||
builder: (context, AsyncSnapshot<CatalogMetadata> snapshot) {
|
||||
if (snapshot.hasError) return Text(snapshot.error.toString());
|
||||
if (snapshot.connectionState != ConnectionState.done) return const SizedBox.shrink();
|
||||
final data = snapshot.data;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('DB metadata:${data == null ? ' no row' : ''}'),
|
||||
if (data != null)
|
||||
InfoRowGroup({
|
||||
'dateMillis': '${data.dateMillis}',
|
||||
'videoRotation': '${data.videoRotation}',
|
||||
'latitude': '${data.latitude}',
|
||||
'longitude': '${data.longitude}',
|
||||
'xmpSubjects': '${data.xmpSubjects}',
|
||||
'xmpTitleDescription': '${data.xmpTitleDescription}',
|
||||
}),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
FutureBuilder(
|
||||
future: _dbAddressLoader,
|
||||
builder: (context, AsyncSnapshot<AddressDetails> snapshot) {
|
||||
if (snapshot.hasError) return Text(snapshot.error.toString());
|
||||
if (snapshot.connectionState != ConnectionState.done) return const SizedBox.shrink();
|
||||
final data = snapshot.data;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('DB address:${data == null ? ' no row' : ''}'),
|
||||
if (data != null)
|
||||
InfoRowGroup({
|
||||
'dateMillis': '${data.addressLine}',
|
||||
'countryName': '${data.countryName}',
|
||||
'adminArea': '${data.adminArea}',
|
||||
'locality': '${data.locality}',
|
||||
}),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _startDbReport() {
|
||||
_dbDateLoader = metadataDb.loadDates().then((values) => values.firstWhere((row) => row.contentId == contentId, orElse: () => null));
|
||||
_dbMetadataLoader = metadataDb.loadMetadataEntries().then((values) => values.firstWhere((row) => row.contentId == contentId, orElse: () => null));
|
||||
_dbAddressLoader = metadataDb.loadAddresses().then((values) => values.firstWhere((row) => row.contentId == contentId, orElse: () => null));
|
||||
setState(() {});
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ import 'package:aves/model/image_entry.dart';
|
|||
import 'package:aves/model/image_file_service.dart';
|
||||
import 'package:aves/utils/android_app_service.dart';
|
||||
import 'package:aves/widgets/common/image_providers/uri_image_provider.dart';
|
||||
import 'package:aves/widgets/fullscreen/debug.dart';
|
||||
import 'package:aves/widgets/fullscreen/fullscreen_actions.dart';
|
||||
import 'package:flushbar/flushbar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -63,6 +64,9 @@ class FullscreenActionDelegate {
|
|||
case FullscreenAction.share:
|
||||
AndroidAppService.share(entry.uri, entry.mimeType);
|
||||
break;
|
||||
case FullscreenAction.debug:
|
||||
_goToDebug(context, entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,4 +181,13 @@ class FullscreenActionDelegate {
|
|||
if (newName == null || newName.isEmpty) return;
|
||||
_showFeedback(context, await entry.rename(newName) ? 'Done!' : 'Failed');
|
||||
}
|
||||
|
||||
void _goToDebug(BuildContext context, ImageEntry entry) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => FullscreenDebugPage(entry: entry),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:outline_material_icons/outline_material_icons.dart';
|
||||
|
||||
enum FullscreenAction { delete, edit, info, open, openMap, print, rename, rotateCCW, rotateCW, setAs, share, toggleFavourite }
|
||||
enum FullscreenAction { delete, edit, info, open, openMap, print, rename, rotateCCW, rotateCW, setAs, share, toggleFavourite, debug }
|
||||
|
||||
class FullscreenActions {
|
||||
static const inApp = [
|
||||
|
@ -53,6 +53,8 @@ extension ExtraFullscreenAction on FullscreenAction {
|
|||
return 'Set as…';
|
||||
case FullscreenAction.openMap:
|
||||
return 'Show on map…';
|
||||
case FullscreenAction.debug:
|
||||
return 'Debug';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -83,6 +85,8 @@ extension ExtraFullscreenAction on FullscreenAction {
|
|||
case FullscreenAction.setAs:
|
||||
case FullscreenAction.openMap:
|
||||
return null;
|
||||
case FullscreenAction.debug:
|
||||
return OMIcons.whatshot;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -75,6 +75,10 @@ class FullscreenTopOverlay extends StatelessWidget {
|
|||
...inAppActions.map(_buildPopupMenuItem),
|
||||
const PopupMenuDivider(),
|
||||
...externalAppActions.map(_buildPopupMenuItem),
|
||||
if (kDebugMode) ...[
|
||||
const PopupMenuDivider(),
|
||||
_buildPopupMenuItem(FullscreenAction.debug),
|
||||
]
|
||||
],
|
||||
onSelected: onActionSelected,
|
||||
),
|
||||
|
@ -109,6 +113,8 @@ class FullscreenTopOverlay extends StatelessWidget {
|
|||
case FullscreenAction.edit:
|
||||
case FullscreenAction.setAs:
|
||||
return true;
|
||||
case FullscreenAction.debug:
|
||||
return kDebugMode;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -153,6 +159,7 @@ class FullscreenTopOverlay extends StatelessWidget {
|
|||
case FullscreenAction.open:
|
||||
case FullscreenAction.edit:
|
||||
case FullscreenAction.setAs:
|
||||
case FullscreenAction.debug:
|
||||
break;
|
||||
}
|
||||
return child != null
|
||||
|
@ -188,6 +195,7 @@ class FullscreenTopOverlay extends StatelessWidget {
|
|||
case FullscreenAction.rotateCCW:
|
||||
case FullscreenAction.rotateCW:
|
||||
case FullscreenAction.print:
|
||||
case FullscreenAction.debug:
|
||||
child = MenuRow(text: action.getText(), icon: action.getIcon());
|
||||
break;
|
||||
// external app actions
|
||||
|
|
Loading…
Reference in a new issue