minor fixes
This commit is contained in:
parent
02131c3836
commit
d16158d8e7
9 changed files with 120 additions and 97 deletions
|
@ -44,6 +44,10 @@ class PlatformAndroidAppService implements AndroidAppService {
|
||||||
if (kakaoTalk != null) {
|
if (kakaoTalk != null) {
|
||||||
kakaoTalk.ownedDirs.add('KakaoTalkDownload');
|
kakaoTalk.ownedDirs.add('KakaoTalkDownload');
|
||||||
}
|
}
|
||||||
|
final imagingEdge = packages.firstWhereOrNull((package) => package.packageName == 'com.sony.playmemories.mobile');
|
||||||
|
if (imagingEdge != null) {
|
||||||
|
imagingEdge.ownedDirs.add('Imaging Edge Mobile');
|
||||||
|
}
|
||||||
return packages;
|
return packages;
|
||||||
} on PlatformException catch (e, stack) {
|
} on PlatformException catch (e, stack) {
|
||||||
await reportService.recordError(e, stack);
|
await reportService.recordError(e, stack);
|
||||||
|
|
|
@ -194,7 +194,9 @@ class PlatformMediaFileService implements MediaFileService {
|
||||||
// `await` here, so that `completeError` will be caught below
|
// `await` here, so that `completeError` will be caught below
|
||||||
return await completer.future;
|
return await completer.future;
|
||||||
} on PlatformException catch (e, stack) {
|
} on PlatformException catch (e, stack) {
|
||||||
await reportService.recordError(e, stack);
|
if (!MimeTypes.knownMediaTypes.contains(mimeType)) {
|
||||||
|
await reportService.recordError(e, stack);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return Uint8List(0);
|
return Uint8List(0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ class _DbTabState extends State<DbTab> {
|
||||||
future: _dbDateLoader,
|
future: _dbDateLoader,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasError) return Text(snapshot.error.toString());
|
if (snapshot.hasError) return Text(snapshot.error.toString());
|
||||||
if (snapshot.connectionState != ConnectionState.done) return const SizedBox.shrink();
|
if (snapshot.connectionState != ConnectionState.done) return const SizedBox();
|
||||||
final data = snapshot.data;
|
final data = snapshot.data;
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
@ -74,7 +74,7 @@ class _DbTabState extends State<DbTab> {
|
||||||
future: _dbEntryLoader,
|
future: _dbEntryLoader,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasError) return Text(snapshot.error.toString());
|
if (snapshot.hasError) return Text(snapshot.error.toString());
|
||||||
if (snapshot.connectionState != ConnectionState.done) return const SizedBox.shrink();
|
if (snapshot.connectionState != ConnectionState.done) return const SizedBox();
|
||||||
final data = snapshot.data;
|
final data = snapshot.data;
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
@ -105,7 +105,7 @@ class _DbTabState extends State<DbTab> {
|
||||||
future: _dbMetadataLoader,
|
future: _dbMetadataLoader,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasError) return Text(snapshot.error.toString());
|
if (snapshot.hasError) return Text(snapshot.error.toString());
|
||||||
if (snapshot.connectionState != ConnectionState.done) return const SizedBox.shrink();
|
if (snapshot.connectionState != ConnectionState.done) return const SizedBox();
|
||||||
final data = snapshot.data;
|
final data = snapshot.data;
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
@ -134,7 +134,7 @@ class _DbTabState extends State<DbTab> {
|
||||||
future: _dbAddressLoader,
|
future: _dbAddressLoader,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasError) return Text(snapshot.error.toString());
|
if (snapshot.hasError) return Text(snapshot.error.toString());
|
||||||
if (snapshot.connectionState != ConnectionState.done) return const SizedBox.shrink();
|
if (snapshot.connectionState != ConnectionState.done) return const SizedBox();
|
||||||
final data = snapshot.data;
|
final data = snapshot.data;
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
@ -158,7 +158,7 @@ class _DbTabState extends State<DbTab> {
|
||||||
future: _dbVideoPlaybackLoader,
|
future: _dbVideoPlaybackLoader,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasError) return Text(snapshot.error.toString());
|
if (snapshot.hasError) return Text(snapshot.error.toString());
|
||||||
if (snapshot.connectionState != ConnectionState.done) return const SizedBox.shrink();
|
if (snapshot.connectionState != ConnectionState.done) return const SizedBox();
|
||||||
final data = snapshot.data;
|
final data = snapshot.data;
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:aves/image_providers/app_icon_image_provider.dart';
|
|
||||||
import 'package:aves/model/actions/entry_info_actions.dart';
|
import 'package:aves/model/actions/entry_info_actions.dart';
|
||||||
import 'package:aves/model/entry.dart';
|
import 'package:aves/model/entry.dart';
|
||||||
import 'package:aves/model/favourites.dart';
|
import 'package:aves/model/favourites.dart';
|
||||||
|
@ -8,15 +7,14 @@ import 'package:aves/model/filters/mime.dart';
|
||||||
import 'package:aves/model/filters/tag.dart';
|
import 'package:aves/model/filters/tag.dart';
|
||||||
import 'package:aves/model/filters/type.dart';
|
import 'package:aves/model/filters/type.dart';
|
||||||
import 'package:aves/model/source/collection_lens.dart';
|
import 'package:aves/model/source/collection_lens.dart';
|
||||||
import 'package:aves/services/common/services.dart';
|
|
||||||
import 'package:aves/theme/format.dart';
|
import 'package:aves/theme/format.dart';
|
||||||
import 'package:aves/theme/icons.dart';
|
import 'package:aves/theme/icons.dart';
|
||||||
import 'package:aves/utils/android_file_utils.dart';
|
|
||||||
import 'package:aves/utils/file_utils.dart';
|
import 'package:aves/utils/file_utils.dart';
|
||||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
|
import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
|
||||||
import 'package:aves/widgets/viewer/info/common.dart';
|
import 'package:aves/widgets/viewer/info/common.dart';
|
||||||
import 'package:aves/widgets/viewer/info/entry_info_action_delegate.dart';
|
import 'package:aves/widgets/viewer/info/entry_info_action_delegate.dart';
|
||||||
|
import 'package:aves/widgets/viewer/info/owner.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -173,79 +171,3 @@ class BasicSection extends StatelessWidget {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class OwnerProp extends StatefulWidget {
|
|
||||||
final AvesEntry entry;
|
|
||||||
|
|
||||||
const OwnerProp({
|
|
||||||
Key? key,
|
|
||||||
required this.entry,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_OwnerPropState createState() => _OwnerPropState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _OwnerPropState extends State<OwnerProp> {
|
|
||||||
late Future<String?> _ownerPackageFuture;
|
|
||||||
|
|
||||||
AvesEntry get entry => widget.entry;
|
|
||||||
|
|
||||||
static const ownerPackageNamePropKey = 'owner_package_name';
|
|
||||||
static const iconSize = 20.0;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
final isMediaContent = entry.uri.startsWith('content://media/external/');
|
|
||||||
if (isMediaContent) {
|
|
||||||
_ownerPackageFuture = metadataFetchService.hasContentResolverProp(ownerPackageNamePropKey).then((exists) {
|
|
||||||
return exists ? metadataFetchService.getContentResolverProp(entry, ownerPackageNamePropKey) : SynchronousFuture(null);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
_ownerPackageFuture = SynchronousFuture(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return FutureBuilder<String?>(
|
|
||||||
future: _ownerPackageFuture,
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
final ownerPackage = snapshot.data;
|
|
||||||
if (ownerPackage == null) return const SizedBox();
|
|
||||||
final appName = androidFileUtils.getCurrentAppName(ownerPackage) ?? ownerPackage;
|
|
||||||
// as of Flutter v1.22.6, `SelectableText` cannot contain `WidgetSpan`
|
|
||||||
// so we use a basic `Text` instead
|
|
||||||
return Text.rich(
|
|
||||||
TextSpan(
|
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text: context.l10n.viewerInfoLabelOwner,
|
|
||||||
style: InfoRowGroup.keyStyle,
|
|
||||||
),
|
|
||||||
WidgetSpan(
|
|
||||||
alignment: PlaceholderAlignment.middle,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
|
||||||
child: Image(
|
|
||||||
image: AppIconImage(
|
|
||||||
packageName: ownerPackage,
|
|
||||||
size: iconSize,
|
|
||||||
),
|
|
||||||
width: iconSize,
|
|
||||||
height: iconSize,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextSpan(
|
|
||||||
text: appName,
|
|
||||||
style: InfoRowGroup.baseStyle,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -121,8 +121,9 @@ class _InfoRowGroupState extends State<InfoRowGroup> {
|
||||||
value = '$value\n';
|
value = '$value\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
// as of Flutter v1.22.6, `SelectableText` cannot contain `WidgetSpan`
|
// as of Flutter v2.5.3, `SelectableText` cannot contain `WidgetSpan`
|
||||||
// so we add padding using multiple hair spaces instead
|
// so we add padding using multiple hair spaces instead
|
||||||
|
// TODO TLAD 2021/10/26 other `InlineSpan` now possible thanks to https://github.com/flutter/flutter/pull/92295
|
||||||
final thisSpaceSize = max(0.0, (baseValueX - keySizes[key]!)) + InfoRowGroup.keyValuePadding;
|
final thisSpaceSize = max(0.0, (baseValueX - keySizes[key]!)) + InfoRowGroup.keyValuePadding;
|
||||||
final spaceCount = (100 * thisSpaceSize / baseSpaceWidth).round();
|
final spaceCount = (100 * thisSpaceSize / baseSpaceWidth).round();
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ class MetadataDirTile extends StatelessWidget {
|
||||||
if (dirName == MetadataDirectory.xmpDirectory) {
|
if (dirName == MetadataDirectory.xmpDirectory) {
|
||||||
return XmpDirTile(
|
return XmpDirTile(
|
||||||
entry: entry,
|
entry: entry,
|
||||||
|
title: title,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
expandedNotifier: expandedDirectoryNotifier,
|
expandedNotifier: expandedDirectoryNotifier,
|
||||||
initiallyExpanded: initiallyExpanded,
|
initiallyExpanded: initiallyExpanded,
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:collection';
|
||||||
|
|
||||||
import 'package:aves/model/entry.dart';
|
import 'package:aves/model/entry.dart';
|
||||||
import 'package:aves/ref/xmp.dart';
|
import 'package:aves/ref/xmp.dart';
|
||||||
|
import 'package:aves/utils/color_utils.dart';
|
||||||
import 'package:aves/widgets/common/identity/aves_expansion_tile.dart';
|
import 'package:aves/widgets/common/identity/aves_expansion_tile.dart';
|
||||||
import 'package:aves/widgets/viewer/info/metadata/xmp_namespaces.dart';
|
import 'package:aves/widgets/viewer/info/metadata/xmp_namespaces.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
@ -9,6 +10,7 @@ import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class XmpDirTile extends StatefulWidget {
|
class XmpDirTile extends StatefulWidget {
|
||||||
final AvesEntry entry;
|
final AvesEntry entry;
|
||||||
|
final String title;
|
||||||
final SplayTreeMap<String, String> tags;
|
final SplayTreeMap<String, String> tags;
|
||||||
final ValueNotifier<String?>? expandedNotifier;
|
final ValueNotifier<String?>? expandedNotifier;
|
||||||
final bool initiallyExpanded;
|
final bool initiallyExpanded;
|
||||||
|
@ -16,6 +18,7 @@ class XmpDirTile extends StatefulWidget {
|
||||||
const XmpDirTile({
|
const XmpDirTile({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.entry,
|
required this.entry,
|
||||||
|
required this.title,
|
||||||
required this.tags,
|
required this.tags,
|
||||||
required this.expandedNotifier,
|
required this.expandedNotifier,
|
||||||
required this.initiallyExpanded,
|
required this.initiallyExpanded,
|
||||||
|
@ -38,7 +41,9 @@ class _XmpDirTileState extends State<XmpDirTile> {
|
||||||
}).entries.map((kv) => XmpNamespace.create(kv.key, Map.fromEntries(kv.value))).toList()
|
}).entries.map((kv) => XmpNamespace.create(kv.key, Map.fromEntries(kv.value))).toList()
|
||||||
..sort((a, b) => compareAsciiUpperCase(a.displayTitle, b.displayTitle));
|
..sort((a, b) => compareAsciiUpperCase(a.displayTitle, b.displayTitle));
|
||||||
return AvesExpansionTile(
|
return AvesExpansionTile(
|
||||||
title: 'XMP',
|
// title may contain parent to distinguish multiple XMP directories
|
||||||
|
title: widget.title,
|
||||||
|
color: stringToColor('XMP'),
|
||||||
expandedNotifier: widget.expandedNotifier,
|
expandedNotifier: widget.expandedNotifier,
|
||||||
initiallyExpanded: widget.initiallyExpanded,
|
initiallyExpanded: widget.initiallyExpanded,
|
||||||
children: [
|
children: [
|
||||||
|
|
97
lib/widgets/viewer/info/owner.dart
Normal file
97
lib/widgets/viewer/info/owner.dart
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
import 'package:aves/app_mode.dart';
|
||||||
|
import 'package:aves/image_providers/app_icon_image_provider.dart';
|
||||||
|
import 'package:aves/model/entry.dart';
|
||||||
|
import 'package:aves/model/settings/settings.dart';
|
||||||
|
import 'package:aves/services/common/services.dart';
|
||||||
|
import 'package:aves/utils/android_file_utils.dart';
|
||||||
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
|
import 'package:aves/widgets/viewer/info/common.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class OwnerProp extends StatefulWidget {
|
||||||
|
final AvesEntry entry;
|
||||||
|
|
||||||
|
const OwnerProp({
|
||||||
|
Key? key,
|
||||||
|
required this.entry,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_OwnerPropState createState() => _OwnerPropState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _OwnerPropState extends State<OwnerProp> {
|
||||||
|
Future<String?> _ownerPackageLoader = SynchronousFuture(null);
|
||||||
|
Future<void> _appNameLoader = SynchronousFuture(null);
|
||||||
|
|
||||||
|
AvesEntry get entry => widget.entry;
|
||||||
|
|
||||||
|
static const ownerPackageNamePropKey = 'owner_package_name';
|
||||||
|
static const iconSize = 20.0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
final isMediaContent = entry.uri.startsWith('content://media/external/');
|
||||||
|
if (isMediaContent) {
|
||||||
|
_ownerPackageLoader = metadataFetchService.hasContentResolverProp(ownerPackageNamePropKey).then((exists) {
|
||||||
|
return exists ? metadataFetchService.getContentResolverProp(entry, ownerPackageNamePropKey) : SynchronousFuture(null);
|
||||||
|
});
|
||||||
|
final isViewerMode = context.read<ValueNotifier<AppMode>>().value == AppMode.view;
|
||||||
|
if (isViewerMode && settings.isInstalledAppAccessAllowed) {
|
||||||
|
_appNameLoader = androidFileUtils.initAppNames();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FutureBuilder<String?>(
|
||||||
|
future: _ownerPackageLoader,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
final ownerPackage = snapshot.data;
|
||||||
|
if (ownerPackage == null) return const SizedBox();
|
||||||
|
|
||||||
|
return FutureBuilder<void>(
|
||||||
|
future: _appNameLoader,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
final appName = androidFileUtils.getCurrentAppName(ownerPackage) ?? ownerPackage;
|
||||||
|
// as of Flutter v2.5.3, `SelectableText` cannot contain `WidgetSpan`
|
||||||
|
// so we use a basic `Text` instead
|
||||||
|
// TODO TLAD 2021/10/26 other `InlineSpan` now possible thanks to https://github.com/flutter/flutter/pull/92295
|
||||||
|
return Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: context.l10n.viewerInfoLabelOwner,
|
||||||
|
style: InfoRowGroup.keyStyle,
|
||||||
|
),
|
||||||
|
WidgetSpan(
|
||||||
|
alignment: PlaceholderAlignment.middle,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||||
|
child: Image(
|
||||||
|
image: AppIconImage(
|
||||||
|
packageName: ownerPackage,
|
||||||
|
size: iconSize,
|
||||||
|
),
|
||||||
|
width: iconSize,
|
||||||
|
height: iconSize,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: appName,
|
||||||
|
style: InfoRowGroup.baseStyle,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1 @@
|
||||||
{
|
{}
|
||||||
"ru": [
|
|
||||||
"resetButtonTooltip",
|
|
||||||
"entryInfoActionEditTags",
|
|
||||||
"settingsViewerMaximumBrightness",
|
|
||||||
"tagEditorPageTitle",
|
|
||||||
"tagEditorPageNewTagFieldLabel",
|
|
||||||
"tagEditorPageAddTagTooltip"
|
|
||||||
]
|
|
||||||
}
|
|
Loading…
Reference in a new issue