SD card storage indicator in drawer and section headers
This commit is contained in:
parent
23eac7c3c7
commit
53dfe85e07
6 changed files with 69 additions and 18 deletions
|
@ -32,6 +32,10 @@ class AndroidFileUtils {
|
||||||
|
|
||||||
bool isDownloadPath(String path) => path == downloadPath;
|
bool isDownloadPath(String path) => path == downloadPath;
|
||||||
|
|
||||||
|
StorageVolume getStorageVolume(String path) => storageVolumes.firstWhere((v) => path.startsWith(v.path), orElse: () => null);
|
||||||
|
|
||||||
|
bool isOnSD(String path) => getStorageVolume(path).isRemovable;
|
||||||
|
|
||||||
AlbumType getAlbumType(String albumDirectory) {
|
AlbumType getAlbumType(String albumDirectory) {
|
||||||
if (albumDirectory != null) {
|
if (albumDirectory != null) {
|
||||||
if (androidFileUtils.isCameraPath(albumDirectory)) return AlbumType.Camera;
|
if (androidFileUtils.isCameraPath(albumDirectory)) return AlbumType.Camera;
|
||||||
|
|
|
@ -93,13 +93,23 @@ class _CollectionDrawerState extends State<CollectionDrawer> {
|
||||||
title: 'Favourites',
|
title: 'Favourites',
|
||||||
filter: FavouriteFilter(),
|
filter: FavouriteFilter(),
|
||||||
);
|
);
|
||||||
final buildAlbumEntry = (String album) => _FilteredCollectionNavTile(
|
final buildAlbumEntry = (String album) {
|
||||||
|
final uniqueName = source.getUniqueAlbumName(album);
|
||||||
|
return _FilteredCollectionNavTile(
|
||||||
source: source,
|
source: source,
|
||||||
leading: IconUtils.getAlbumIcon(context: context, album: album),
|
leading: IconUtils.getAlbumIcon(context: context, album: album),
|
||||||
title: source.getUniqueAlbumName(album),
|
title: uniqueName,
|
||||||
|
trailing: androidFileUtils.isOnSD(album)
|
||||||
|
? const Icon(
|
||||||
|
OMIcons.sdStorage,
|
||||||
|
size: 16,
|
||||||
|
color: Colors.grey,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
dense: true,
|
dense: true,
|
||||||
filter: AlbumFilter(album, source.getUniqueAlbumName(album)),
|
filter: AlbumFilter(album, uniqueName),
|
||||||
);
|
);
|
||||||
|
};
|
||||||
final buildTagEntry = (String tag) => _FilteredCollectionNavTile(
|
final buildTagEntry = (String tag) => _FilteredCollectionNavTile(
|
||||||
source: source,
|
source: source,
|
||||||
leading: Icon(
|
leading: Icon(
|
||||||
|
@ -311,6 +321,7 @@ class _FilteredCollectionNavTile extends StatelessWidget {
|
||||||
final CollectionSource source;
|
final CollectionSource source;
|
||||||
final Widget leading;
|
final Widget leading;
|
||||||
final String title;
|
final String title;
|
||||||
|
final Widget trailing;
|
||||||
final bool dense;
|
final bool dense;
|
||||||
final CollectionFilter filter;
|
final CollectionFilter filter;
|
||||||
|
|
||||||
|
@ -318,6 +329,7 @@ class _FilteredCollectionNavTile extends StatelessWidget {
|
||||||
@required this.source,
|
@required this.source,
|
||||||
@required this.leading,
|
@required this.leading,
|
||||||
@required this.title,
|
@required this.title,
|
||||||
|
this.trailing,
|
||||||
bool dense,
|
bool dense,
|
||||||
@required this.filter,
|
@required this.filter,
|
||||||
}) : dense = dense ?? false;
|
}) : dense = dense ?? false;
|
||||||
|
@ -330,6 +342,7 @@ class _FilteredCollectionNavTile extends StatelessWidget {
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: leading,
|
leading: leading,
|
||||||
title: Text(title),
|
title: Text(title),
|
||||||
|
trailing: trailing,
|
||||||
dense: dense,
|
dense: dense,
|
||||||
onTap: () => _goToCollection(context),
|
onTap: () => _goToCollection(context),
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import 'package:aves/utils/android_file_utils.dart';
|
||||||
import 'package:aves/widgets/album/grid/header_generic.dart';
|
import 'package:aves/widgets/album/grid/header_generic.dart';
|
||||||
import 'package:aves/widgets/common/icons.dart';
|
import 'package:aves/widgets/common/icons.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:outline_material_icons/outline_material_icons.dart';
|
||||||
|
|
||||||
class AlbumSectionHeader extends StatelessWidget {
|
class AlbumSectionHeader extends StatelessWidget {
|
||||||
final String folderPath, albumName;
|
final String folderPath, albumName;
|
||||||
|
@ -26,6 +28,13 @@ class AlbumSectionHeader extends StatelessWidget {
|
||||||
return TitleSectionHeader(
|
return TitleSectionHeader(
|
||||||
leading: albumIcon,
|
leading: albumIcon,
|
||||||
title: albumName,
|
title: albumName,
|
||||||
|
trailing: androidFileUtils.isOnSD(folderPath)
|
||||||
|
? const Icon(
|
||||||
|
OMIcons.sdStorage,
|
||||||
|
size: 16,
|
||||||
|
color: Color(0xFF757575),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,17 +70,19 @@ class SectionHeader extends StatelessWidget {
|
||||||
var headerExtent = 0.0;
|
var headerExtent = 0.0;
|
||||||
if (sectionKey is String) {
|
if (sectionKey is String) {
|
||||||
// only compute height for album headers, as they're the only likely ones to split on multiple lines
|
// only compute height for album headers, as they're the only likely ones to split on multiple lines
|
||||||
final hasIcon = androidFileUtils.getAlbumType(sectionKey) != AlbumType.Default;
|
final hasLeading = androidFileUtils.getAlbumType(sectionKey) != AlbumType.Default;
|
||||||
|
final hasTrailing = androidFileUtils.isOnSD(sectionKey);
|
||||||
final text = source.getUniqueAlbumName(sectionKey);
|
final text = source.getUniqueAlbumName(sectionKey);
|
||||||
final maxWidth = scrollableWidth - TitleSectionHeader.padding.horizontal;
|
final maxWidth = scrollableWidth - TitleSectionHeader.padding.horizontal;
|
||||||
final para = RenderParagraph(
|
final para = RenderParagraph(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
children: [
|
children: [
|
||||||
if (hasIcon)
|
if (hasLeading)
|
||||||
// `RenderParagraph` fails to lay out `WidgetSpan` offscreen as of Flutter v1.17.0
|
// `RenderParagraph` fails to lay out `WidgetSpan` offscreen as of Flutter v1.17.0
|
||||||
// so we use a hair space times a magic number to match leading width
|
// so we use a hair space times a magic number to match leading width
|
||||||
// 23 hair spaces match a width of 40.0
|
TextSpan(text: '\u200A' * 23), // 23 hair spaces match a width of 40.0
|
||||||
TextSpan(text: '\u200A' * 23),
|
if (hasTrailing)
|
||||||
|
TextSpan(text: '\u200A' * 17),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: text,
|
text: text,
|
||||||
style: Constants.titleTextStyle,
|
style: Constants.titleTextStyle,
|
||||||
|
@ -97,13 +99,19 @@ class SectionHeader extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class TitleSectionHeader extends StatelessWidget {
|
class TitleSectionHeader extends StatelessWidget {
|
||||||
final Widget leading;
|
final Widget leading, trailing;
|
||||||
final String title;
|
final String title;
|
||||||
|
|
||||||
const TitleSectionHeader({Key key, this.leading, this.title}) : super(key: key);
|
const TitleSectionHeader({
|
||||||
|
Key key,
|
||||||
|
this.leading,
|
||||||
|
this.title,
|
||||||
|
this.trailing,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
static const leadingDimension = 32.0;
|
static const leadingDimension = 32.0;
|
||||||
static const leadingPadding = EdgeInsets.only(right: 8, bottom: 4);
|
static const leadingPadding = EdgeInsets.only(right: 8, bottom: 4);
|
||||||
|
static const trailingPadding = EdgeInsets.only(left: 8, bottom: 4);
|
||||||
static const padding = EdgeInsets.all(16);
|
static const padding = EdgeInsets.all(16);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -122,6 +130,12 @@ class TitleSectionHeader extends StatelessWidget {
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
text: title,
|
text: title,
|
||||||
|
trailingBuilder: trailing != null
|
||||||
|
? (context, isShadow) => Container(
|
||||||
|
padding: trailingPadding,
|
||||||
|
child: isShadow ? null : trailing,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
style: Constants.titleTextStyle,
|
style: Constants.titleTextStyle,
|
||||||
outlineWidth: 2,
|
outlineWidth: 2,
|
||||||
),
|
),
|
||||||
|
|
|
@ -3,18 +3,19 @@ import 'package:flutter/material.dart';
|
||||||
typedef OutlinedWidgetBuilder = Widget Function(BuildContext context, bool isShadow);
|
typedef OutlinedWidgetBuilder = Widget Function(BuildContext context, bool isShadow);
|
||||||
|
|
||||||
class OutlinedText extends StatelessWidget {
|
class OutlinedText extends StatelessWidget {
|
||||||
final OutlinedWidgetBuilder leadingBuilder;
|
final OutlinedWidgetBuilder leadingBuilder, trailingBuilder;
|
||||||
final String text;
|
final String text;
|
||||||
final TextStyle style;
|
final TextStyle style;
|
||||||
final double outlineWidth;
|
final double outlineWidth;
|
||||||
final Color outlineColor;
|
final Color outlineColor;
|
||||||
|
|
||||||
static const leadingAlignment = PlaceholderAlignment.middle;
|
static const widgetSpanAlignment = PlaceholderAlignment.middle;
|
||||||
|
|
||||||
const OutlinedText({
|
const OutlinedText({
|
||||||
Key key,
|
Key key,
|
||||||
this.leadingBuilder,
|
this.leadingBuilder,
|
||||||
@required this.text,
|
@required this.text,
|
||||||
|
this.trailingBuilder,
|
||||||
@required this.style,
|
@required this.style,
|
||||||
double outlineWidth,
|
double outlineWidth,
|
||||||
Color outlineColor,
|
Color outlineColor,
|
||||||
|
@ -31,7 +32,7 @@ class OutlinedText extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
if (leadingBuilder != null)
|
if (leadingBuilder != null)
|
||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
alignment: leadingAlignment,
|
alignment: widgetSpanAlignment,
|
||||||
child: leadingBuilder(context, true),
|
child: leadingBuilder(context, true),
|
||||||
),
|
),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
|
@ -43,6 +44,11 @@ class OutlinedText extends StatelessWidget {
|
||||||
..color = outlineColor,
|
..color = outlineColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (trailingBuilder != null)
|
||||||
|
WidgetSpan(
|
||||||
|
alignment: widgetSpanAlignment,
|
||||||
|
child: trailingBuilder(context, true),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -51,13 +57,18 @@ class OutlinedText extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
if (leadingBuilder != null)
|
if (leadingBuilder != null)
|
||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
alignment: leadingAlignment,
|
alignment: widgetSpanAlignment,
|
||||||
child: leadingBuilder(context, false),
|
child: leadingBuilder(context, false),
|
||||||
),
|
),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: text,
|
text: text,
|
||||||
style: style,
|
style: style,
|
||||||
),
|
),
|
||||||
|
if (trailingBuilder != null)
|
||||||
|
WidgetSpan(
|
||||||
|
alignment: widgetSpanAlignment,
|
||||||
|
child: trailingBuilder(context, false),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -50,7 +50,7 @@ class DebugPageState extends State<DebugPage> {
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
children: [
|
children: [
|
||||||
const Text('Storage'),
|
const Text('Storage'),
|
||||||
...AndroidFileUtils.storageVolumes.map((v) => Text('${v.description}: ${v.path} (removable: ${v.isRemovable}))')),
|
...AndroidFileUtils.storageVolumes.map((v) => Text('${v.description}: ${v.path} (removable: ${v.isRemovable})')),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
|
|
Loading…
Reference in a new issue