collection: special album icons
This commit is contained in:
parent
7807077e23
commit
c7b7a6f4b4
9 changed files with 100 additions and 27 deletions
1
assets/kakaotalk.svg
Normal file
1
assets/kakaotalk.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="2500" height="2500" viewBox="0 0 256 256"><path fill="#FFE812" d="M256 236c0 11.046-8.954 20-20 20H20c-11.046 0-20-8.954-20-20V20C0 8.954 8.954 0 20 0h216c11.046 0 20 8.954 20 20v216z"/><path d="M128 36C70.562 36 24 72.713 24 118c0 29.279 19.466 54.97 48.748 69.477-1.593 5.494-10.237 35.344-10.581 37.689 0 0-.207 1.762.934 2.434s2.483.15 2.483.15c3.272-.457 37.943-24.811 43.944-29.04 5.995.849 12.168 1.29 18.472 1.29 57.438 0 104-36.712 104-82 0-45.287-46.562-82-104-82z"/><path fill="#FFE812" d="M70.5 146.625c-3.309 0-6-2.57-6-5.73V105.25h-9.362c-3.247 0-5.888-2.636-5.888-5.875s2.642-5.875 5.888-5.875h30.724c3.247 0 5.888 2.636 5.888 5.875s-2.642 5.875-5.888 5.875H76.5v35.645c0 3.16-2.691 5.73-6 5.73zM123.112 146.547c-2.502 0-4.416-1.016-4.993-2.65l-2.971-7.778-18.296-.001-2.973 7.783c-.575 1.631-2.488 2.646-4.99 2.646a9.155 9.155 0 0 1-3.814-.828c-1.654-.763-3.244-2.861-1.422-8.52l14.352-37.776c1.011-2.873 4.082-5.833 7.99-5.922 3.919.088 6.99 3.049 8.003 5.928l14.346 37.759c1.826 5.672.236 7.771-1.418 8.532a9.176 9.176 0 0 1-3.814.827c-.001 0 0 0 0 0zm-11.119-21.056L106 108.466l-5.993 17.025h11.986zM138 145.75c-3.171 0-5.75-2.468-5.75-5.5V99.5c0-3.309 2.748-6 6.125-6s6.125 2.691 6.125 6v35.25h12.75c3.171 0 5.75 2.468 5.75 5.5s-2.579 5.5-5.75 5.5H138zM171.334 146.547c-3.309 0-6-2.691-6-6V99.5c0-3.309 2.691-6 6-6s6 2.691 6 6v12.896l16.74-16.74c.861-.861 2.044-1.335 3.328-1.335 1.498 0 3.002.646 4.129 1.772 1.051 1.05 1.678 2.401 1.764 3.804.087 1.415-.384 2.712-1.324 3.653l-13.673 13.671 14.769 19.566a5.951 5.951 0 0 1 1.152 4.445 5.956 5.956 0 0 1-2.328 3.957 5.94 5.94 0 0 1-3.609 1.211 5.953 5.953 0 0 1-4.793-2.385l-14.071-18.644-2.082 2.082v13.091a6.01 6.01 0 0 1-6.002 6.003z"/></svg>
|
After Width: | Height: | Size: 1.7 KiB |
|
@ -3,6 +3,7 @@ import 'package:aves/model/image_entry.dart';
|
||||||
import 'package:aves/model/image_file_service.dart';
|
import 'package:aves/model/image_file_service.dart';
|
||||||
import 'package:aves/model/metadata_db.dart';
|
import 'package:aves/model/metadata_db.dart';
|
||||||
import 'package:aves/model/settings.dart';
|
import 'package:aves/model/settings.dart';
|
||||||
|
import 'package:aves/utils/android_file_utils.dart';
|
||||||
import 'package:aves/widgets/album/all_collection_drawer.dart';
|
import 'package:aves/widgets/album/all_collection_drawer.dart';
|
||||||
import 'package:aves/widgets/album/all_collection_page.dart';
|
import 'package:aves/widgets/album/all_collection_page.dart';
|
||||||
import 'package:aves/widgets/common/fake_app_bar.dart';
|
import 'package:aves/widgets/common/fake_app_bar.dart';
|
||||||
|
@ -12,6 +13,7 @@ import 'package:permission_handler/permission_handler.dart';
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
await settings.init();
|
await settings.init();
|
||||||
|
await androidFileUtils.init();
|
||||||
runApp(AvesApp());
|
runApp(AvesApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:aves/model/image_metadata.dart';
|
||||||
import 'package:aves/model/metadata_db.dart';
|
import 'package:aves/model/metadata_db.dart';
|
||||||
import "package:collection/collection.dart";
|
import "package:collection/collection.dart";
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:path/path.dart';
|
||||||
|
|
||||||
class ImageCollection with ChangeNotifier {
|
class ImageCollection with ChangeNotifier {
|
||||||
final List<ImageEntry> _rawEntries;
|
final List<ImageEntry> _rawEntries;
|
||||||
|
@ -46,7 +47,7 @@ class ImageCollection with ChangeNotifier {
|
||||||
case SortFactor.date:
|
case SortFactor.date:
|
||||||
switch (groupFactor) {
|
switch (groupFactor) {
|
||||||
case GroupFactor.album:
|
case GroupFactor.album:
|
||||||
sections = groupBy(_rawEntries, (entry) => entry.bucketDisplayName);
|
sections = groupBy(_rawEntries, (entry) => entry.directory);
|
||||||
break;
|
break;
|
||||||
case GroupFactor.date:
|
case GroupFactor.date:
|
||||||
sections = groupBy(_rawEntries, (entry) => entry.monthTaken);
|
sections = groupBy(_rawEntries, (entry) => entry.monthTaken);
|
||||||
|
@ -82,7 +83,7 @@ class ImageCollection with ChangeNotifier {
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateAlbums() => albums = _rawEntries.map((entry) => entry.bucketDisplayName).toSet();
|
updateAlbums() => albums = _rawEntries.map((entry) => entry.directory).toSet();
|
||||||
|
|
||||||
updateTags() => tags = _rawEntries.expand((entry) => entry.xmpSubjects).toSet();
|
updateTags() => tags = _rawEntries.expand((entry) => entry.xmpSubjects).toSet();
|
||||||
|
|
||||||
|
@ -153,6 +154,17 @@ class ImageCollection with ChangeNotifier {
|
||||||
sortFactor: sortFactor,
|
sortFactor: sortFactor,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String getUniqueAlbumName(String album, List albums) {
|
||||||
|
final otherAlbums = albums.where((item) => item != album);
|
||||||
|
final parts = album.split(separator);
|
||||||
|
int partCount = 0;
|
||||||
|
String testName;
|
||||||
|
do {
|
||||||
|
testName = separator + parts.skip(parts.length - ++partCount).join(separator);
|
||||||
|
} while (otherAlbums.any((item) => item.endsWith(testName)));
|
||||||
|
return parts.skip(parts.length - partCount).join(separator);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SortFactor { date, size }
|
enum SortFactor { date, size }
|
||||||
|
|
|
@ -13,6 +13,7 @@ import 'mime_types.dart';
|
||||||
class ImageEntry with ChangeNotifier {
|
class ImageEntry with ChangeNotifier {
|
||||||
String uri;
|
String uri;
|
||||||
String path;
|
String path;
|
||||||
|
String directory;
|
||||||
int contentId;
|
int contentId;
|
||||||
final String mimeType;
|
final String mimeType;
|
||||||
int width;
|
int width;
|
||||||
|
@ -41,7 +42,7 @@ class ImageEntry with ChangeNotifier {
|
||||||
this.sourceDateTakenMillis,
|
this.sourceDateTakenMillis,
|
||||||
this.bucketDisplayName,
|
this.bucketDisplayName,
|
||||||
this.durationMillis,
|
this.durationMillis,
|
||||||
});
|
}) : directory = path != null ? dirname(path) : null;
|
||||||
|
|
||||||
factory ImageEntry.fromMap(Map map) {
|
factory ImageEntry.fromMap(Map map) {
|
||||||
return ImageEntry(
|
return ImageEntry(
|
||||||
|
|
26
lib/utils/android_file_utils.dart
Normal file
26
lib/utils/android_file_utils.dart
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import 'package:path/path.dart';
|
||||||
|
|
||||||
|
final AndroidFileUtils androidFileUtils = AndroidFileUtils._private();
|
||||||
|
|
||||||
|
typedef void AndroidFileUtilsCallback(String key, dynamic oldValue, dynamic newValue);
|
||||||
|
|
||||||
|
class AndroidFileUtils {
|
||||||
|
String dcimPath;
|
||||||
|
String picturesPath;
|
||||||
|
|
||||||
|
AndroidFileUtils._private();
|
||||||
|
|
||||||
|
init() async {
|
||||||
|
// TODO TLAD find storage root
|
||||||
|
// getExternalStorageDirectory() gives '/storage/emulated/0/Android/data/deckers.thibault.aves/files'
|
||||||
|
final ext = '/storage/emulated/0';
|
||||||
|
dcimPath = join(ext, 'DCIM');
|
||||||
|
picturesPath = join(ext, 'Pictures');
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isCameraPath(String path) => path != null && path.startsWith(dcimPath) && (path.endsWith('Camera') || path.endsWith('100ANDRO'));
|
||||||
|
|
||||||
|
bool isScreenshotsPath(String path) => path != null && path.startsWith(dcimPath) && path.endsWith('Screenshots');
|
||||||
|
|
||||||
|
bool isKakaoTalkPath(String path) => path != null && path.startsWith(picturesPath) && path.endsWith('KakaoTalk');
|
||||||
|
}
|
|
@ -71,8 +71,8 @@ class AllCollectionDrawer extends StatelessWidget {
|
||||||
...albums.map((album) => _buildFilteredCollectionNavTile(
|
...albums.map((album) => _buildFilteredCollectionNavTile(
|
||||||
context: context,
|
context: context,
|
||||||
leading: Icon(Icons.photo_album),
|
leading: Icon(Icons.photo_album),
|
||||||
title: album,
|
title: collection.getUniqueAlbumName(album, albums),
|
||||||
filter: (entry) => entry.bucketDisplayName == album,
|
filter: (entry) => entry.directory == album,
|
||||||
)),
|
)),
|
||||||
Divider(),
|
Divider(),
|
||||||
...tags.map((tag) => _buildFilteredCollectionNavTile(
|
...tags.map((tag) => _buildFilteredCollectionNavTile(
|
||||||
|
|
|
@ -21,7 +21,7 @@ class DaySectionHeader extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SectionHeader(text: text);
|
return SectionHeader(title: text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,36 +43,46 @@ class MonthSectionHeader extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SectionHeader(text: text);
|
return SectionHeader(title: text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SectionHeader extends StatelessWidget {
|
class SectionHeader extends StatelessWidget {
|
||||||
final String text;
|
final Widget leading;
|
||||||
|
final String title;
|
||||||
|
|
||||||
const SectionHeader({Key key, this.text}) : super(key: key);
|
const SectionHeader({Key key, this.leading, this.title}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final text = OutlinedText(
|
||||||
|
title,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.grey[200],
|
||||||
|
fontSize: 20,
|
||||||
|
fontFamily: 'Concourse Caps',
|
||||||
|
shadows: [
|
||||||
|
Shadow(
|
||||||
|
offset: Offset(0, 2),
|
||||||
|
blurRadius: 3,
|
||||||
|
color: Colors.grey[900],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
outlineColor: Colors.black87,
|
||||||
|
outlineWidth: 2,
|
||||||
|
);
|
||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.all(16),
|
padding: EdgeInsets.all(16),
|
||||||
child: OutlinedText(
|
child: leading != null
|
||||||
text,
|
? Row(
|
||||||
style: TextStyle(
|
children: [
|
||||||
color: Colors.grey[200],
|
leading,
|
||||||
fontSize: 20,
|
SizedBox(width: 8),
|
||||||
fontFamily: 'Concourse Caps',
|
text,
|
||||||
shadows: [
|
],
|
||||||
Shadow(
|
)
|
||||||
offset: Offset(0, 2),
|
: text,
|
||||||
blurRadius: 3,
|
|
||||||
color: Colors.grey[900],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
outlineColor: Colors.black87,
|
|
||||||
outlineWidth: 2,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import 'package:aves/model/image_collection.dart';
|
import 'package:aves/model/image_collection.dart';
|
||||||
import 'package:aves/model/image_entry.dart';
|
import 'package:aves/model/image_entry.dart';
|
||||||
|
import 'package:aves/utils/android_file_utils.dart';
|
||||||
import 'package:aves/widgets/album/sections.dart';
|
import 'package:aves/widgets/album/sections.dart';
|
||||||
import 'package:aves/widgets/album/thumbnail.dart';
|
import 'package:aves/widgets/album/thumbnail.dart';
|
||||||
import 'package:aves/widgets/common/draggable_scrollbar.dart';
|
import 'package:aves/widgets/common/draggable_scrollbar.dart';
|
||||||
import 'package:aves/widgets/fullscreen/image_page.dart';
|
import 'package:aves/widgets/fullscreen/image_page.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_sticky_header/flutter_sticky_header.dart';
|
import 'package:flutter_sticky_header/flutter_sticky_header.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
|
||||||
class ThumbnailCollection extends AnimatedWidget {
|
class ThumbnailCollection extends AnimatedWidget {
|
||||||
final ImageCollection collection;
|
final ImageCollection collection;
|
||||||
|
@ -96,7 +98,10 @@ class SectionSliver extends StatelessWidget {
|
||||||
if (collection.sortFactor == SortFactor.date) {
|
if (collection.sortFactor == SortFactor.date) {
|
||||||
switch (collection.groupFactor) {
|
switch (collection.groupFactor) {
|
||||||
case GroupFactor.album:
|
case GroupFactor.album:
|
||||||
header = SectionHeader(text: sectionKey);
|
header = SectionHeader(
|
||||||
|
leading: getAlbumIcon(context, sectionKey),
|
||||||
|
title: collection.getUniqueAlbumName(sectionKey, sections.keys.toList()),
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case GroupFactor.date:
|
case GroupFactor.date:
|
||||||
header = MonthSectionHeader(date: sectionKey);
|
header = MonthSectionHeader(date: sectionKey);
|
||||||
|
@ -130,6 +135,17 @@ class SectionSliver extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget getAlbumIcon(BuildContext context, String albumDirectory) {
|
||||||
|
if (androidFileUtils.isCameraPath(albumDirectory)) {
|
||||||
|
return Icon(Icons.photo_camera);
|
||||||
|
} else if (androidFileUtils.isScreenshotsPath(albumDirectory)) {
|
||||||
|
return Icon(Icons.smartphone);
|
||||||
|
} else if (androidFileUtils.isKakaoTalkPath(albumDirectory)) {
|
||||||
|
return SvgPicture.asset('assets/kakaotalk.svg', width: IconTheme.of(context).size);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
Future _showFullscreen(BuildContext context, ImageEntry entry) {
|
Future _showFullscreen(BuildContext context, ImageEntry entry) {
|
||||||
return Navigator.push(
|
return Navigator.push(
|
||||||
context,
|
context,
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'package:aves/model/image_entry.dart';
|
||||||
import 'package:aves/model/image_metadata.dart';
|
import 'package:aves/model/image_metadata.dart';
|
||||||
import 'package:aves/model/metadata_db.dart';
|
import 'package:aves/model/metadata_db.dart';
|
||||||
import 'package:aves/model/settings.dart';
|
import 'package:aves/model/settings.dart';
|
||||||
|
import 'package:aves/utils/android_file_utils.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
@ -40,6 +41,10 @@ class DebugPageState extends State<DebugPage> {
|
||||||
body: Column(
|
body: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
Text('Paths'),
|
||||||
|
Text('DCIM path: ${androidFileUtils.dcimPath}'),
|
||||||
|
Text('pictures path: ${androidFileUtils.picturesPath}'),
|
||||||
|
Divider(),
|
||||||
Text('Settings'),
|
Text('Settings'),
|
||||||
Text('collectionGroupFactor: ${settings.collectionGroupFactor}'),
|
Text('collectionGroupFactor: ${settings.collectionGroupFactor}'),
|
||||||
Text('collectionSortFactor: ${settings.collectionSortFactor}'),
|
Text('collectionSortFactor: ${settings.collectionSortFactor}'),
|
||||||
|
|
Loading…
Reference in a new issue