drawer: split albums by type
This commit is contained in:
parent
d407f5b7d5
commit
73bb51895f
4 changed files with 111 additions and 54 deletions
|
@ -2,7 +2,6 @@ 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_page.dart';
|
||||
import 'package:aves/widgets/common/icons.dart';
|
||||
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
||||
import 'package:aves/widgets/common/providers/media_store_collection_provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -70,9 +69,8 @@ class _HomePageState extends State<HomePage> {
|
|||
return;
|
||||
}
|
||||
|
||||
androidFileUtils.init();
|
||||
// TODO notify when icons are ready for drawer and section header refresh
|
||||
unawaited(IconUtils.init()); // 170ms
|
||||
await androidFileUtils.init(); // 170ms
|
||||
|
||||
await settings.init(); // <20ms
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:aves/utils/android_app_service.dart';
|
||||
import 'package:path/path.dart';
|
||||
|
||||
final AndroidFileUtils androidFileUtils = AndroidFileUtils._private();
|
||||
|
@ -5,19 +6,51 @@ final AndroidFileUtils androidFileUtils = AndroidFileUtils._private();
|
|||
class AndroidFileUtils {
|
||||
String externalStorage, dcimPath, downloadPath, picturesPath;
|
||||
|
||||
static Map appNameMap = {};
|
||||
|
||||
AndroidFileUtils._private();
|
||||
|
||||
void init() {
|
||||
Future<void> init() async {
|
||||
// path_provider getExternalStorageDirectory() gives '/storage/emulated/0/Android/data/deckers.thibault.aves/files'
|
||||
externalStorage = '/storage/emulated/0';
|
||||
dcimPath = join(externalStorage, 'DCIM');
|
||||
downloadPath = join(externalStorage, 'Download');
|
||||
picturesPath = join(externalStorage, 'Pictures');
|
||||
appNameMap = await AndroidAppService.getAppNames();
|
||||
}
|
||||
|
||||
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 isScreenRecordingsPath(String path) => path != null && path.startsWith(dcimPath) && path.endsWith('Screen recordings');
|
||||
|
||||
bool isDownloadPath(String path) => path == downloadPath;
|
||||
|
||||
AlbumType getAlbumType(String albumDirectory) {
|
||||
if (albumDirectory != null) {
|
||||
if (androidFileUtils.isCameraPath(albumDirectory)) return AlbumType.Camera;
|
||||
if (androidFileUtils.isDownloadPath(albumDirectory)) return AlbumType.Download;
|
||||
if (androidFileUtils.isScreenRecordingsPath(albumDirectory)) return AlbumType.ScreenRecordings;
|
||||
if (androidFileUtils.isScreenshotsPath(albumDirectory)) return AlbumType.Screenshots;
|
||||
|
||||
final parts = albumDirectory.split(separator);
|
||||
if (albumDirectory.startsWith(androidFileUtils.externalStorage) && appNameMap.keys.contains(parts.last)) return AlbumType.App;
|
||||
}
|
||||
return AlbumType.Default;
|
||||
}
|
||||
|
||||
String getAlbumAppPackageName(String albumDirectory) {
|
||||
final parts = albumDirectory.split(separator);
|
||||
return AndroidFileUtils.appNameMap[parts.last];
|
||||
}
|
||||
}
|
||||
|
||||
enum AlbumType {
|
||||
Default,
|
||||
App,
|
||||
Camera,
|
||||
Download,
|
||||
ScreenRecordings,
|
||||
Screenshots,
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:ui';
|
|||
|
||||
import 'package:aves/model/image_collection.dart';
|
||||
import 'package:aves/model/image_entry.dart';
|
||||
import 'package:aves/utils/android_file_utils.dart';
|
||||
import 'package:aves/widgets/album/filtered_collection_page.dart';
|
||||
import 'package:aves/widgets/common/icons.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -14,8 +15,41 @@ class AllCollectionDrawer extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final collection = Provider.of<ImageCollection>(context);
|
||||
final albums = collection.sortedAlbums;
|
||||
final tags = collection.sortedTags;
|
||||
final regularAlbums = [], appAlbums = [], specialAlbums = [];
|
||||
for (var album in collection.sortedAlbums) {
|
||||
switch (androidFileUtils.getAlbumType(album)) {
|
||||
case AlbumType.Default:
|
||||
regularAlbums.add(album);
|
||||
break;
|
||||
case AlbumType.App:
|
||||
appAlbums.add(album);
|
||||
break;
|
||||
default:
|
||||
specialAlbums.add(album);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final videoEntry = _FilteredCollectionNavTile(
|
||||
collection: collection,
|
||||
leading: const Icon(Icons.video_library),
|
||||
title: 'Videos',
|
||||
filter: (entry) => entry.isVideo,
|
||||
);
|
||||
final buildAlbumEntry = (album) => _FilteredCollectionNavTile(
|
||||
collection: collection,
|
||||
leading: IconUtils.getAlbumIcon(context, album) ?? const Icon(Icons.photo_album),
|
||||
title: collection.getUniqueAlbumName(album, collection.sortedAlbums),
|
||||
filter: (entry) => entry.directory == album,
|
||||
);
|
||||
final buildTagEntry = (tag) => _FilteredCollectionNavTile(
|
||||
collection: collection,
|
||||
leading: const Icon(Icons.label),
|
||||
title: tag,
|
||||
filter: (entry) => entry.xmpSubjects.contains(tag),
|
||||
);
|
||||
|
||||
return Drawer(
|
||||
child: Selector<MediaQueryData, double>(
|
||||
selector: (c, mq) => mq.viewInsets.bottom,
|
||||
|
@ -58,22 +92,22 @@ class AllCollectionDrawer extends StatelessWidget {
|
|||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(children: [
|
||||
Icon(Icons.photo_library),
|
||||
const Icon(Icons.photo_library),
|
||||
const SizedBox(width: 4),
|
||||
Text('${collection.imageCount}'),
|
||||
]),
|
||||
Row(children: [
|
||||
Icon(Icons.video_library),
|
||||
const Icon(Icons.video_library),
|
||||
const SizedBox(width: 4),
|
||||
Text('${collection.videoCount}'),
|
||||
]),
|
||||
Row(children: [
|
||||
Icon(Icons.photo_album),
|
||||
const Icon(Icons.photo_album),
|
||||
const SizedBox(width: 4),
|
||||
Text('${collection.albumCount}'),
|
||||
]),
|
||||
Row(children: [
|
||||
Icon(Icons.label),
|
||||
const Icon(Icons.label),
|
||||
const SizedBox(width: 4),
|
||||
Text('${collection.tagCount}'),
|
||||
]),
|
||||
|
@ -83,26 +117,23 @@ class AllCollectionDrawer extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
_FilteredCollectionNavTile(
|
||||
collection: collection,
|
||||
leading: Icon(Icons.video_library),
|
||||
title: 'Videos',
|
||||
filter: (entry) => entry.isVideo,
|
||||
),
|
||||
videoEntry,
|
||||
if (specialAlbums.isNotEmpty) ...[
|
||||
const Divider(),
|
||||
...albums.map((album) => _FilteredCollectionNavTile(
|
||||
collection: collection,
|
||||
leading: IconUtils.getAlbumIcon(context, album) ?? Icon(Icons.photo_album),
|
||||
title: collection.getUniqueAlbumName(album, albums),
|
||||
filter: (entry) => entry.directory == album,
|
||||
)),
|
||||
...specialAlbums.map(buildAlbumEntry),
|
||||
],
|
||||
if (appAlbums.isNotEmpty) ...[
|
||||
const Divider(),
|
||||
...tags.map((tag) => _FilteredCollectionNavTile(
|
||||
collection: collection,
|
||||
leading: Icon(Icons.label),
|
||||
title: tag,
|
||||
filter: (entry) => entry.xmpSubjects.contains(tag),
|
||||
)),
|
||||
...appAlbums.map(buildAlbumEntry),
|
||||
],
|
||||
if (regularAlbums.isNotEmpty) ...[
|
||||
const Divider(),
|
||||
...regularAlbums.map(buildAlbumEntry),
|
||||
],
|
||||
if (tags.isNotEmpty) ...[
|
||||
const Divider(),
|
||||
...tags.map(buildTagEntry),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import 'dart:ui';
|
||||
|
||||
import 'package:aves/model/image_entry.dart';
|
||||
import 'package:aves/utils/android_app_service.dart';
|
||||
import 'package:aves/utils/android_file_utils.dart';
|
||||
import 'package:aves/widgets/common/app_icon.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class VideoIcon extends StatelessWidget {
|
||||
|
@ -89,30 +87,27 @@ class OverlayIcon extends StatelessWidget {
|
|||
}
|
||||
|
||||
class IconUtils {
|
||||
static Map appNameMap = {};
|
||||
|
||||
static Future<void> init() async {
|
||||
appNameMap = await AndroidAppService.getAppNames();
|
||||
}
|
||||
|
||||
static Widget getAlbumIcon(BuildContext context, String albumDirectory) {
|
||||
if (albumDirectory == null) return null;
|
||||
if (androidFileUtils.isCameraPath(albumDirectory)) return Icon(Icons.photo_camera);
|
||||
if (androidFileUtils.isScreenshotsPath(albumDirectory)) return Icon(Icons.smartphone);
|
||||
if (androidFileUtils.isDownloadPath(albumDirectory)) return Icon(Icons.file_download);
|
||||
|
||||
final parts = albumDirectory.split(separator);
|
||||
if (albumDirectory.startsWith(androidFileUtils.externalStorage) && appNameMap.keys.contains(parts.last)) {
|
||||
final packageName = appNameMap[parts.last];
|
||||
switch (androidFileUtils.getAlbumType(albumDirectory)) {
|
||||
case AlbumType.Camera:
|
||||
return Icon(Icons.photo_camera);
|
||||
case AlbumType.Screenshots:
|
||||
case AlbumType.ScreenRecordings:
|
||||
return Icon(Icons.smartphone);
|
||||
case AlbumType.Download:
|
||||
return Icon(Icons.file_download);
|
||||
case AlbumType.App:
|
||||
return Selector<MediaQueryData, double>(
|
||||
selector: (c, mq) => mq.devicePixelRatio,
|
||||
builder: (c, devicePixelRatio, child) => AppIcon(
|
||||
packageName: packageName,
|
||||
packageName: androidFileUtils.getAlbumAppPackageName(albumDirectory),
|
||||
size: IconTheme.of(context).size,
|
||||
devicePixelRatio: devicePixelRatio,
|
||||
),
|
||||
);
|
||||
}
|
||||
case AlbumType.Default:
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue