debug page & metadata db draft

This commit is contained in:
Thibault Deckers 2019-08-09 00:15:36 +09:00
parent d6d8c6dea2
commit 21f277bc6a
9 changed files with 218 additions and 26 deletions

View file

@ -1,5 +1,6 @@
import 'package:aves/model/image_decode_service.dart';
import 'package:aves/model/image_entry.dart';
import 'package:aves/model/metadata_storage_service.dart';
import 'package:aves/widgets/album/thumbnail_collection.dart';
import 'package:aves/widgets/common/fake_app_bar.dart';
import 'package:flutter/material.dart';
@ -39,15 +40,21 @@ class _HomePageState extends State<HomePage> {
void initState() {
super.initState();
imageCache.maximumSizeBytes = 100 * 1024 * 1024;
setup();
}
setup() async {
await metadataDb.init();
eventChannel.receiveBroadcastStream().cast<Map>().listen(
(entryMap) => setState(() => entries.add(ImageEntry.fromMap(entryMap))),
onDone: () {
debugPrint('mediastore stream done');
setState(() => done = true);
},
onError: (error) => debugPrint('mediastore stream error=$error'),
);
ImageDecodeService.getImageEntries();
onDone: () {
debugPrint('mediastore stream done');
setState(() => done = true);
},
onError: (error) => debugPrint('mediastore stream error=$error'),
);
await ImageDecodeService.getImageEntries();
}
@override

View file

@ -1,3 +1,5 @@
import 'package:geocoder/model.dart';
import 'mime_types.dart';
class ImageEntry {
@ -101,3 +103,35 @@ class ImageEntry {
return '${d.inHours}:$twoDigitMinutes:$twoDigitSeconds';
}
}
class CatalogMetadata {
final int contentId, dateMillis;
final String keywords;
final double latitude, longitude;
Address address;
CatalogMetadata({this.contentId, this.dateMillis, this.keywords, this.latitude, this.longitude});
factory CatalogMetadata.fromMap(Map map) {
return CatalogMetadata(
contentId: map['contentId'],
dateMillis: map['dateMillis'],
keywords: map['keywords'],
latitude: map['latitude'],
longitude: map['longitude'],
);
}
Map<String, dynamic> toMap() => {
'contentId': contentId,
'dateMillis': dateMillis,
'keywords': keywords,
'latitude': latitude,
'longitude': longitude,
};
@override
String toString() {
return 'CatalogMetadata{contentId: $contentId, dateMillis: $dateMillis, latitude: $latitude, longitude: $longitude, keywords=$keywords}';
}
}

View file

@ -1,3 +1,5 @@
import 'package:aves/model/image_entry.dart';
import 'package:aves/model/metadata_storage_service.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@ -17,21 +19,25 @@ class MetadataService {
return Map();
}
// return map with:
// 'dateMillis': date taken in milliseconds since Epoch (long)
// 'latitude': latitude (double)
// 'longitude': longitude (double)
// 'keywords': space separated XMP subjects (string)
static Future<Map> getCatalogMetadata(String path) async {
static Future<CatalogMetadata> getCatalogMetadata(int contentId, String path) async {
CatalogMetadata metadata;
try {
// return map with:
// 'dateMillis': date taken in milliseconds since Epoch (long)
// 'latitude': latitude (double)
// 'longitude': longitude (double)
// 'keywords': space separated XMP subjects (string)
final result = await platform.invokeMethod('getCatalogMetadata', <String, dynamic>{
'path': path,
});
return result as Map;
}) as Map;
result['contentId'] = contentId;
metadata = CatalogMetadata.fromMap(result);
metadataDb.insert(metadata);
return metadata;
} on PlatformException catch (e) {
debugPrint('getCatalogMetadata failed with exception=${e.message}');
}
return Map();
return null;
}
// return map with string descriptions for: 'aperture' 'exposureTime' 'focalLength' 'iso'

View file

@ -0,0 +1,63 @@
import 'package:aves/model/image_entry.dart';
import 'package:flutter/foundation.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
final MetadataDb metadataDb = MetadataDb._private();
class MetadataDb {
Future<Database> _database;
Future<String> get path async => join(await getDatabasesPath(), 'metadata.db');
static final table = 'metadata';
MetadataDb._private();
init() async {
debugPrint('$runtimeType init');
_database = openDatabase(
await path,
onCreate: (db, version) {
return db.execute(
'CREATE TABLE $table(contentId INTEGER PRIMARY KEY, dateMillis INTEGER, keywords TEXT, latitude REAL, longitude REAL)',
);
},
version: 1,
);
}
reset() async {
debugPrint('$runtimeType reset');
(await _database).close();
deleteDatabase(await path);
await init();
}
Future<List<CatalogMetadata>> getAll() async {
debugPrint('$runtimeType getAll');
final db = await _database;
final maps = await db.query(table);
return maps.map((map) => CatalogMetadata.fromMap(map)).toList();
}
Future<CatalogMetadata> get(int contentId) async {
debugPrint('$runtimeType get contentId=$contentId');
final db = await _database;
List<Map> maps = await db.query(table, where: 'contentId = ?', whereArgs: [contentId]);
if (maps.length > 0) {
return CatalogMetadata.fromMap(maps.first);
}
return null;
}
insert(CatalogMetadata metadata) async {
debugPrint('$runtimeType insert metadata=$metadata');
final db = await _database;
await db.insert(
table,
metadata.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
}

View file

@ -1,8 +1,9 @@
import 'package:aves/model/image_entry.dart';
import 'package:aves/widgets/album/thumbnail.dart';
import 'package:aves/utils/date_utils.dart';
import 'package:aves/widgets/album/thumbnail.dart';
import 'package:aves/widgets/common/draggable_scrollbar.dart';
import 'package:aves/widgets/common/outlined_text.dart';
import 'package:aves/widgets/debug_page.dart';
import 'package:aves/widgets/fullscreen/image_page.dart';
import "package:collection/collection.dart";
import 'package:flutter/material.dart';
@ -39,6 +40,9 @@ class ThumbnailCollection extends StatelessWidget {
slivers: [
SliverAppBar(
title: Text('Aves - All'),
actions: [
IconButton(icon: Icon(Icons.whatshot), onPressed: () => goToDebug(context)),
],
floating: true,
),
...sectionKeys.map((sectionKey) {
@ -66,6 +70,15 @@ class ThumbnailCollection extends StatelessWidget {
),
);
}
Future goToDebug(BuildContext context) {
return Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DebugPage(),
),
);
}
}
class SectionSliver extends StatelessWidget {

View file

@ -0,0 +1,52 @@
import 'package:aves/model/image_entry.dart';
import 'package:aves/model/metadata_storage_service.dart';
import 'package:flutter/material.dart';
class DebugPage extends StatefulWidget {
@override
State<StatefulWidget> createState() => DebugPageState();
}
class DebugPageState extends State<DebugPage> {
Future<List<CatalogMetadata>> _dbLoader;
@override
void initState() {
super.initState();
_dbLoader = metadataDb.getAll();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Info'),
),
body: Column(
children: [
RaisedButton(
onPressed: () => metadataDb.reset(),
child: Text('Reset DB'),
),
Expanded(
child: FutureBuilder(
future: _dbLoader,
builder: (futureContext, AsyncSnapshot<List<CatalogMetadata>> snapshot) {
if (snapshot.hasError) return Text(snapshot.error);
if (snapshot.connectionState != ConnectionState.done)
return Center(
child: CircularProgressIndicator(),
);
final metadata = snapshot.data;
return ListView.builder(
itemBuilder: (context, index) => Text(' $index: ${metadata[index]}'),
itemCount: metadata.length,
);
},
),
),
],
),
);
}
}

View file

@ -18,7 +18,8 @@ class InfoPage extends StatefulWidget {
}
class InfoPageState extends State<InfoPage> {
Future<Map> _catalogLoader, _metadataLoader;
Future<Map> _metadataLoader;
Future<CatalogMetadata> _catalogLoader;
bool _scrollStartFromTop = false;
ImageEntry get entry => widget.entry;
@ -36,14 +37,14 @@ class InfoPageState extends State<InfoPage> {
}
initMetadataLoader() {
_catalogLoader = MetadataService.getCatalogMetadata(entry.path).then((metadata) async {
final latitude = metadata['latitude'];
final longitude = metadata['longitude'];
_catalogLoader = MetadataService.getCatalogMetadata(entry.contentId, entry.path).then((metadata) async {
final latitude = metadata.latitude;
final longitude = metadata.longitude;
if (latitude != null && longitude != null) {
final coordinates = Coordinates(latitude, longitude);
final addresses = await Geocoder.local.findAddressesFromCoordinates(coordinates);
if (addresses != null && addresses.length > 0) {
metadata['address'] = addresses.first;
metadata.address = addresses.first;
}
}
return metadata;
@ -96,15 +97,15 @@ class InfoPageState extends State<InfoPage> {
InfoRow('Path', entry.path),
FutureBuilder(
future: _catalogLoader,
builder: (futureContext, AsyncSnapshot<Map> snapshot) {
builder: (futureContext, AsyncSnapshot<CatalogMetadata> snapshot) {
if (snapshot.hasError) return Text(snapshot.error);
if (snapshot.connectionState != ConnectionState.done) return SizedBox.shrink();
final metadata = snapshot.data;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
..._buildLocationSection(metadata['latitude'], metadata['longitude'], metadata['address']),
..._buildTagSection(metadata['keywords']),
..._buildLocationSection(metadata.latitude, metadata.longitude, metadata.address),
..._buildTagSection(metadata.keywords),
],
);
},

View file

@ -103,7 +103,7 @@ packages:
source: hosted
version: "0.3.0"
path:
dependency: transitive
dependency: "direct main"
description:
name: path
url: "https://pub.dartlang.org"
@ -149,6 +149,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.5"
sqflite:
dependency: "direct main"
description:
name: sqflite
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.6+3"
stack_trace:
dependency: transitive
description:
@ -170,6 +177,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
synchronized:
dependency: transitive
description:
name: synchronized
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0+1"
term_glyph:
dependency: transitive
description:

View file

@ -25,8 +25,10 @@ dependencies:
geocoder:
google_maps_flutter:
intl:
path:
photo_view:
screen:
sqflite:
transparent_image:
dev_dependencies: