debug page & metadata db draft
This commit is contained in:
parent
d6d8c6dea2
commit
21f277bc6a
9 changed files with 218 additions and 26 deletions
|
@ -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
|
||||
|
|
|
@ -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}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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'
|
||||
|
|
63
lib/model/metadata_storage_service.dart
Normal file
63
lib/model/metadata_storage_service.dart
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
52
lib/widgets/debug_page.dart
Normal file
52
lib/widgets/debug_page.dart
Normal 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,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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),
|
||||
],
|
||||
);
|
||||
},
|
||||
|
|
16
pubspec.lock
16
pubspec.lock
|
@ -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:
|
||||
|
|
|
@ -25,8 +25,10 @@ dependencies:
|
|||
geocoder:
|
||||
google_maps_flutter:
|
||||
intl:
|
||||
path:
|
||||
photo_view:
|
||||
screen:
|
||||
sqflite:
|
||||
transparent_image:
|
||||
|
||||
dev_dependencies:
|
||||
|
|
Loading…
Reference in a new issue