2021-08-13 16:10:20 +02:00
|
|
|
import 'dart:async';
|
|
|
|
|
2021-07-12 13:39:55 +02:00
|
|
|
import 'package:android_intent_plus/android_intent.dart';
|
2021-04-10 06:28:12 +02:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:intl/intl.dart';
|
2021-12-03 18:31:27 +01:00
|
|
|
import 'package:kiwi/kiwi.dart';
|
2021-04-10 06:28:12 +02:00
|
|
|
import 'package:logging/logging.dart';
|
|
|
|
import 'package:nc_photos/account.dart';
|
2021-07-25 07:00:38 +02:00
|
|
|
import 'package:nc_photos/app_localizations.dart';
|
2021-09-04 14:35:04 +02:00
|
|
|
import 'package:nc_photos/debug_util.dart';
|
2021-12-03 18:31:27 +01:00
|
|
|
import 'package:nc_photos/di_container.dart';
|
2021-04-10 06:28:12 +02:00
|
|
|
import 'package:nc_photos/double_extension.dart';
|
|
|
|
import 'package:nc_photos/entity/album.dart';
|
2021-08-13 22:18:35 +02:00
|
|
|
import 'package:nc_photos/entity/album/cover_provider.dart';
|
2021-07-05 09:54:01 +02:00
|
|
|
import 'package:nc_photos/entity/album/item.dart';
|
2021-06-24 18:26:56 +02:00
|
|
|
import 'package:nc_photos/entity/album/provider.dart';
|
2022-08-20 12:51:09 +02:00
|
|
|
import 'package:nc_photos/entity/exif_extension.dart';
|
2021-04-10 06:28:12 +02:00
|
|
|
import 'package:nc_photos/entity/file.dart';
|
|
|
|
import 'package:nc_photos/k.dart' as k;
|
2021-08-31 14:00:55 +02:00
|
|
|
import 'package:nc_photos/notified_action.dart';
|
2021-04-10 17:21:34 +02:00
|
|
|
import 'package:nc_photos/platform/features.dart' as features;
|
2021-04-29 17:42:44 +02:00
|
|
|
import 'package:nc_photos/platform/k.dart' as platform_k;
|
2021-04-10 06:28:12 +02:00
|
|
|
import 'package:nc_photos/snack_bar_manager.dart';
|
|
|
|
import 'package:nc_photos/theme.dart';
|
2022-01-28 20:34:38 +01:00
|
|
|
import 'package:nc_photos/use_case/list_file_tag.dart';
|
2021-09-25 07:58:30 +02:00
|
|
|
import 'package:nc_photos/use_case/remove_from_album.dart';
|
2021-04-10 06:28:12 +02:00
|
|
|
import 'package:nc_photos/use_case/update_album.dart';
|
2021-06-21 12:39:17 +02:00
|
|
|
import 'package:nc_photos/use_case/update_property.dart';
|
2021-11-26 11:10:18 +01:00
|
|
|
import 'package:nc_photos/widget/animated_visibility.dart';
|
2021-11-17 15:41:11 +01:00
|
|
|
import 'package:nc_photos/widget/gps_map.dart';
|
2021-11-17 21:21:21 +01:00
|
|
|
import 'package:nc_photos/widget/handler/add_selection_to_album_handler.dart';
|
2022-01-22 13:02:24 +01:00
|
|
|
import 'package:nc_photos/widget/handler/archive_selection_handler.dart';
|
2022-01-26 20:12:35 +01:00
|
|
|
import 'package:nc_photos/widget/list_tile_center_leading.dart';
|
2021-06-21 12:39:17 +02:00
|
|
|
import 'package:nc_photos/widget/photo_date_time_edit_dialog.dart';
|
2022-01-31 10:47:37 +01:00
|
|
|
import 'package:path/path.dart' as path_lib;
|
2021-04-19 18:14:32 +02:00
|
|
|
import 'package:tuple/tuple.dart';
|
2021-04-10 06:28:12 +02:00
|
|
|
|
|
|
|
class ViewerDetailPane extends StatefulWidget {
|
|
|
|
const ViewerDetailPane({
|
2021-07-23 22:05:57 +02:00
|
|
|
Key? key,
|
|
|
|
required this.account,
|
|
|
|
required this.file,
|
2021-08-13 22:18:35 +02:00
|
|
|
this.album,
|
2021-09-14 23:00:24 +02:00
|
|
|
this.onSlideshowPressed,
|
2021-04-10 06:28:12 +02:00
|
|
|
}) : super(key: key);
|
|
|
|
|
|
|
|
@override
|
|
|
|
createState() => _ViewerDetailPaneState();
|
|
|
|
|
|
|
|
final Account account;
|
|
|
|
final File file;
|
2021-08-13 22:18:35 +02:00
|
|
|
|
|
|
|
/// The album this file belongs to, or null
|
|
|
|
final Album? album;
|
2021-09-14 23:00:24 +02:00
|
|
|
|
|
|
|
final VoidCallback? onSlideshowPressed;
|
2021-04-10 06:28:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
class _ViewerDetailPaneState extends State<ViewerDetailPane> {
|
2022-07-05 22:20:24 +02:00
|
|
|
_ViewerDetailPaneState() {
|
|
|
|
final c = KiwiContainer().resolve<DiContainer>();
|
|
|
|
assert(require(c));
|
|
|
|
_c = c;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool require(DiContainer c) =>
|
|
|
|
DiContainer.has(c, DiType.fileRepo) &&
|
|
|
|
DiContainer.has(c, DiType.albumRepo);
|
|
|
|
|
2021-04-10 06:28:12 +02:00
|
|
|
@override
|
|
|
|
initState() {
|
|
|
|
super.initState();
|
|
|
|
|
2021-06-21 12:39:17 +02:00
|
|
|
_dateTime = widget.file.bestDateTime.toLocal();
|
2021-04-10 06:28:12 +02:00
|
|
|
if (widget.file.metadata == null) {
|
|
|
|
_log.info("[initState] Metadata missing in File");
|
|
|
|
} else {
|
|
|
|
_log.info("[initState] Metadata exists in File");
|
2021-07-23 22:05:57 +02:00
|
|
|
if (widget.file.metadata!.exif != null) {
|
2021-06-20 13:49:08 +02:00
|
|
|
_initMetadata();
|
|
|
|
}
|
2021-04-10 06:28:12 +02:00
|
|
|
}
|
2022-01-28 20:34:38 +01:00
|
|
|
_initTags();
|
2021-11-26 11:10:18 +01:00
|
|
|
|
|
|
|
// postpone loading map to improve responsiveness
|
|
|
|
Future.delayed(const Duration(milliseconds: 750)).then((_) {
|
|
|
|
if (mounted) {
|
|
|
|
setState(() {
|
|
|
|
_shouldBlockGpsMap = false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
2021-04-10 06:28:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
build(BuildContext context) {
|
2021-06-27 07:18:13 +02:00
|
|
|
final dateStr = DateFormat(DateFormat.YEAR_ABBR_MONTH_DAY,
|
|
|
|
Localizations.localeOf(context).languageCode)
|
|
|
|
.format(_dateTime);
|
|
|
|
final timeStr = DateFormat(DateFormat.HOUR_MINUTE,
|
|
|
|
Localizations.localeOf(context).languageCode)
|
|
|
|
.format(_dateTime);
|
2021-04-10 06:28:12 +02:00
|
|
|
|
|
|
|
String sizeSubStr = "";
|
|
|
|
const space = " ";
|
2021-06-20 13:49:08 +02:00
|
|
|
if (widget.file.metadata?.imageWidth != null &&
|
|
|
|
widget.file.metadata?.imageHeight != null) {
|
2021-07-23 22:05:57 +02:00
|
|
|
final pixelCount = widget.file.metadata!.imageWidth! *
|
|
|
|
widget.file.metadata!.imageHeight!;
|
2021-04-10 06:28:12 +02:00
|
|
|
if (pixelCount >= 500000) {
|
|
|
|
final mpCount = pixelCount / 1000000.0;
|
2021-08-29 13:51:43 +02:00
|
|
|
sizeSubStr += L10n.global().megapixelCount(mpCount.toStringAsFixed(1));
|
2021-04-10 06:28:12 +02:00
|
|
|
sizeSubStr += space;
|
|
|
|
}
|
2021-07-23 22:05:57 +02:00
|
|
|
sizeSubStr += _byteSizeToString(widget.file.contentLength ?? 0);
|
2021-04-10 06:28:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
String cameraSubStr = "";
|
|
|
|
if (_fNumber != null) {
|
2021-07-23 22:05:57 +02:00
|
|
|
cameraSubStr += "f/${_fNumber!.toStringAsFixed(1)}$space";
|
2021-04-10 06:28:12 +02:00
|
|
|
}
|
|
|
|
if (_exposureTime != null) {
|
2021-08-29 13:51:43 +02:00
|
|
|
cameraSubStr += L10n.global().secondCountSymbol(_exposureTime!);
|
2021-04-10 06:28:12 +02:00
|
|
|
cameraSubStr += space;
|
|
|
|
}
|
|
|
|
if (_focalLength != null) {
|
2021-08-29 13:51:43 +02:00
|
|
|
cameraSubStr += L10n.global()
|
2021-07-23 22:05:57 +02:00
|
|
|
.millimeterCountSymbol(_focalLength!.toStringAsFixedTruncated(2));
|
2021-04-10 06:28:12 +02:00
|
|
|
cameraSubStr += space;
|
|
|
|
}
|
|
|
|
if (_isoSpeedRatings != null) {
|
|
|
|
cameraSubStr += "ISO$_isoSpeedRatings$space";
|
|
|
|
}
|
|
|
|
cameraSubStr = cameraSubStr.trim();
|
|
|
|
|
2021-06-21 12:39:17 +02:00
|
|
|
return Material(
|
|
|
|
type: MaterialType.transparency,
|
|
|
|
child: Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
2021-08-28 20:45:28 +02:00
|
|
|
SingleChildScrollView(
|
|
|
|
scrollDirection: Axis.horizontal,
|
|
|
|
child: Row(
|
|
|
|
children: [
|
2021-11-18 09:05:33 +01:00
|
|
|
if (_canRemoveFromAlbum)
|
2021-08-28 20:45:28 +02:00
|
|
|
_DetailPaneButton(
|
|
|
|
icon: Icons.remove_outlined,
|
2021-08-29 13:51:43 +02:00
|
|
|
label: L10n.global().removeFromAlbumTooltip,
|
2021-08-28 20:45:28 +02:00
|
|
|
onPressed: () => _onRemoveFromAlbumPressed(context),
|
|
|
|
),
|
|
|
|
if (widget.album != null &&
|
2022-07-11 20:14:42 +02:00
|
|
|
widget.album!.albumFile?.isOwned(widget.account.userId) ==
|
2021-08-28 20:45:28 +02:00
|
|
|
true)
|
|
|
|
_DetailPaneButton(
|
|
|
|
icon: Icons.photo_album_outlined,
|
2021-08-29 13:51:43 +02:00
|
|
|
label: L10n.global().useAsAlbumCoverTooltip,
|
2021-08-28 20:45:28 +02:00
|
|
|
onPressed: () => _onSetAlbumCoverPressed(context),
|
|
|
|
),
|
2021-08-21 19:58:17 +02:00
|
|
|
_DetailPaneButton(
|
2022-01-22 13:04:18 +01:00
|
|
|
icon: Icons.add,
|
2021-08-29 13:51:43 +02:00
|
|
|
label: L10n.global().addToAlbumTooltip,
|
2021-08-28 20:45:28 +02:00
|
|
|
onPressed: () => _onAddToAlbumPressed(context),
|
2021-08-21 19:58:17 +02:00
|
|
|
),
|
2021-08-28 20:45:28 +02:00
|
|
|
if (widget.file.isArchived == true)
|
|
|
|
_DetailPaneButton(
|
|
|
|
icon: Icons.unarchive_outlined,
|
2021-08-29 13:51:43 +02:00
|
|
|
label: L10n.global().unarchiveTooltip,
|
2021-08-28 20:45:28 +02:00
|
|
|
onPressed: () => _onUnarchivePressed(context),
|
|
|
|
)
|
|
|
|
else
|
|
|
|
_DetailPaneButton(
|
|
|
|
icon: Icons.archive_outlined,
|
2021-08-29 13:51:43 +02:00
|
|
|
label: L10n.global().archiveTooltip,
|
2021-08-28 20:45:28 +02:00
|
|
|
onPressed: () => _onArchivePressed(context),
|
|
|
|
),
|
2021-09-14 23:00:24 +02:00
|
|
|
_DetailPaneButton(
|
|
|
|
icon: Icons.slideshow_outlined,
|
|
|
|
label: L10n.global().slideshowTooltip,
|
|
|
|
onPressed: widget.onSlideshowPressed,
|
|
|
|
),
|
2021-08-28 20:45:28 +02:00
|
|
|
],
|
|
|
|
),
|
2021-04-17 10:59:16 +02:00
|
|
|
),
|
2021-09-15 08:58:06 +02:00
|
|
|
const Padding(
|
|
|
|
padding: EdgeInsets.symmetric(horizontal: 32),
|
|
|
|
child: Divider(),
|
2021-04-17 10:59:16 +02:00
|
|
|
),
|
2021-04-10 06:28:12 +02:00
|
|
|
ListTile(
|
2022-01-26 20:12:35 +01:00
|
|
|
leading: ListTileCenterLeading(
|
2021-08-26 19:26:51 +02:00
|
|
|
child: Icon(
|
|
|
|
Icons.image_outlined,
|
|
|
|
color: AppTheme.getSecondaryTextColor(context),
|
|
|
|
),
|
2021-04-17 10:59:16 +02:00
|
|
|
),
|
2022-01-31 10:47:37 +01:00
|
|
|
title: Text(path_lib.basenameWithoutExtension(widget.file.path)),
|
2021-06-21 12:39:17 +02:00
|
|
|
subtitle: Text(widget.file.strippedPath),
|
|
|
|
),
|
2022-07-11 20:14:42 +02:00
|
|
|
if (!widget.file.isOwned(widget.account.userId))
|
2021-08-26 19:26:31 +02:00
|
|
|
ListTile(
|
2022-01-26 20:12:35 +01:00
|
|
|
leading: ListTileCenterLeading(
|
2021-08-26 19:26:31 +02:00
|
|
|
child: Icon(
|
|
|
|
Icons.share_outlined,
|
|
|
|
color: AppTheme.getSecondaryTextColor(context),
|
|
|
|
),
|
|
|
|
),
|
2022-07-11 21:45:20 +02:00
|
|
|
title: Text(widget.file.ownerDisplayName ??
|
|
|
|
widget.file.ownerId!.toString()),
|
2021-08-29 13:51:43 +02:00
|
|
|
subtitle: Text(L10n.global().fileSharedByDescription),
|
2021-08-26 19:26:31 +02:00
|
|
|
),
|
2022-01-28 20:34:38 +01:00
|
|
|
if (_tags.isNotEmpty)
|
|
|
|
ListTile(
|
|
|
|
leading: Icon(
|
|
|
|
Icons.local_offer_outlined,
|
|
|
|
color: AppTheme.getSecondaryTextColor(context),
|
|
|
|
),
|
|
|
|
title: SizedBox(
|
|
|
|
height: 40,
|
|
|
|
child: ListView.separated(
|
|
|
|
scrollDirection: Axis.horizontal,
|
|
|
|
itemCount: _tags.length,
|
|
|
|
itemBuilder: (context, index) => Center(
|
|
|
|
child: Wrap(
|
|
|
|
children: [
|
|
|
|
Container(
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
color: AppTheme.getUnfocusedIconColor(context),
|
|
|
|
borderRadius:
|
|
|
|
const BorderRadius.all(Radius.circular(8)),
|
|
|
|
),
|
|
|
|
padding: const EdgeInsets.symmetric(
|
|
|
|
horizontal: 8, vertical: 4),
|
|
|
|
alignment: Alignment.center,
|
|
|
|
child: Text(
|
|
|
|
_tags[index],
|
|
|
|
style: TextStyle(
|
|
|
|
fontSize: 12,
|
|
|
|
color:
|
|
|
|
AppTheme.getPrimaryTextColorInverse(context),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
separatorBuilder: (context, index) =>
|
|
|
|
const SizedBox(width: 8),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
2021-04-10 06:28:12 +02:00
|
|
|
ListTile(
|
2021-04-17 10:59:16 +02:00
|
|
|
leading: Icon(
|
2021-06-21 12:39:17 +02:00
|
|
|
Icons.calendar_today_outlined,
|
2021-04-17 10:59:16 +02:00
|
|
|
color: AppTheme.getSecondaryTextColor(context),
|
|
|
|
),
|
2021-06-21 12:39:17 +02:00
|
|
|
title: Text("$dateStr $timeStr"),
|
|
|
|
trailing: Icon(
|
|
|
|
Icons.edit_outlined,
|
2021-04-17 10:59:16 +02:00
|
|
|
color: AppTheme.getSecondaryTextColor(context),
|
|
|
|
),
|
2021-06-21 12:39:17 +02:00
|
|
|
onTap: () => _onDateTimeTap(context),
|
2021-04-10 06:28:12 +02:00
|
|
|
),
|
2021-06-21 12:39:17 +02:00
|
|
|
if (widget.file.metadata?.imageWidth != null &&
|
|
|
|
widget.file.metadata?.imageHeight != null)
|
|
|
|
ListTile(
|
2022-01-26 20:12:35 +01:00
|
|
|
leading: ListTileCenterLeading(
|
2021-08-28 18:31:19 +02:00
|
|
|
child: Icon(
|
|
|
|
Icons.aspect_ratio,
|
|
|
|
color: AppTheme.getSecondaryTextColor(context),
|
|
|
|
),
|
2021-06-21 12:39:17 +02:00
|
|
|
),
|
|
|
|
title: Text(
|
2021-07-23 22:05:57 +02:00
|
|
|
"${widget.file.metadata!.imageWidth} x ${widget.file.metadata!.imageHeight}"),
|
2021-06-21 12:39:17 +02:00
|
|
|
subtitle: Text(sizeSubStr),
|
|
|
|
)
|
|
|
|
else
|
|
|
|
ListTile(
|
2022-01-26 20:12:35 +01:00
|
|
|
leading: Icon(
|
|
|
|
Icons.aspect_ratio,
|
|
|
|
color: AppTheme.getSecondaryTextColor(context),
|
2021-06-21 12:39:17 +02:00
|
|
|
),
|
2021-07-23 22:05:57 +02:00
|
|
|
title: Text(_byteSizeToString(widget.file.contentLength ?? 0)),
|
2021-04-10 17:21:34 +02:00
|
|
|
),
|
2021-06-21 12:39:17 +02:00
|
|
|
if (_model != null)
|
|
|
|
ListTile(
|
2022-01-26 20:12:35 +01:00
|
|
|
leading: ListTileCenterLeading(
|
2021-08-28 18:31:19 +02:00
|
|
|
child: Icon(
|
|
|
|
Icons.camera_outlined,
|
|
|
|
color: AppTheme.getSecondaryTextColor(context),
|
|
|
|
),
|
2021-06-21 12:39:17 +02:00
|
|
|
),
|
2021-07-23 22:05:57 +02:00
|
|
|
title: Text(_model!),
|
2021-06-21 12:39:17 +02:00
|
|
|
subtitle: cameraSubStr.isNotEmpty ? Text(cameraSubStr) : null,
|
|
|
|
),
|
|
|
|
if (features.isSupportMapView && _gps != null)
|
2021-11-26 11:10:18 +01:00
|
|
|
AnimatedVisibility(
|
|
|
|
opacity: _shouldBlockGpsMap ? 0 : 1,
|
|
|
|
curve: Curves.easeInOut,
|
|
|
|
duration: k.animationDurationNormal,
|
|
|
|
child: SizedBox(
|
|
|
|
height: 256,
|
|
|
|
child: GpsMap(
|
|
|
|
center: _gps!,
|
|
|
|
zoom: 16,
|
|
|
|
onTap: _onMapTap,
|
|
|
|
),
|
2021-06-21 12:39:17 +02:00
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
2021-04-10 06:28:12 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-06-20 13:49:08 +02:00
|
|
|
/// Convert EXIF data to readable format
|
|
|
|
void _initMetadata() {
|
2021-07-23 22:05:57 +02:00
|
|
|
final exif = widget.file.metadata!.exif!;
|
2021-06-20 13:49:08 +02:00
|
|
|
_log.info("[_initMetadata] $exif");
|
|
|
|
|
|
|
|
if (exif.make != null && exif.model != null) {
|
|
|
|
_model = "${exif.make} ${exif.model}";
|
|
|
|
}
|
|
|
|
if (exif.fNumber != null) {
|
2021-07-23 22:05:57 +02:00
|
|
|
_fNumber = exif.fNumber!.toDouble();
|
2021-06-20 13:49:08 +02:00
|
|
|
}
|
|
|
|
if (exif.exposureTime != null) {
|
2021-07-23 22:05:57 +02:00
|
|
|
if (exif.exposureTime!.denominator == 1) {
|
|
|
|
_exposureTime = exif.exposureTime!.numerator.toString();
|
2021-06-20 13:49:08 +02:00
|
|
|
} else {
|
|
|
|
_exposureTime = exif.exposureTime.toString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (exif.focalLength != null) {
|
2021-07-23 22:05:57 +02:00
|
|
|
_focalLength = exif.focalLength!.toDouble();
|
2021-06-20 13:49:08 +02:00
|
|
|
}
|
|
|
|
if (exif.isoSpeedRatings != null) {
|
2021-07-23 22:05:57 +02:00
|
|
|
_isoSpeedRatings = exif.isoSpeedRatings!;
|
2021-06-20 13:49:08 +02:00
|
|
|
}
|
2022-08-20 12:51:09 +02:00
|
|
|
final lat = exif.gpsLatitudeDeg;
|
|
|
|
final lng = exif.gpsLongitudeDeg;
|
|
|
|
if (lat != null && lng != null) {
|
2021-06-20 13:49:08 +02:00
|
|
|
_log.fine("GPS: ($lat, $lng)");
|
|
|
|
_gps = Tuple2(lat, lng);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-28 20:34:38 +01:00
|
|
|
Future<void> _initTags() async {
|
|
|
|
final c = KiwiContainer().resolve<DiContainer>();
|
|
|
|
try {
|
|
|
|
final tags = await ListFileTag(c)(widget.account, widget.file);
|
|
|
|
_tags.addAll(tags.map((t) => t.displayName));
|
|
|
|
} catch (e, stackTrace) {
|
|
|
|
_log.shout("[_initTags] Failed while ListFileTag", e, stackTrace);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-21 19:58:17 +02:00
|
|
|
Future<void> _onRemoveFromAlbumPressed(BuildContext context) async {
|
|
|
|
assert(widget.album!.provider is AlbumStaticProvider);
|
2021-09-08 11:31:24 +02:00
|
|
|
try {
|
|
|
|
await NotifiedAction(
|
|
|
|
() async {
|
2021-09-25 07:58:30 +02:00
|
|
|
final thisItem = AlbumStaticProvider.of(widget.album!)
|
|
|
|
.items
|
|
|
|
.whereType<AlbumFileItem>()
|
|
|
|
.firstWhere((element) => element.file.path == widget.file.path);
|
2021-12-03 18:31:27 +01:00
|
|
|
await RemoveFromAlbum(KiwiContainer().resolve<DiContainer>())(
|
2021-09-25 07:58:30 +02:00
|
|
|
widget.account, widget.album!, [thisItem]);
|
2021-08-21 19:58:17 +02:00
|
|
|
if (mounted) {
|
|
|
|
Navigator.of(context).pop();
|
|
|
|
}
|
2021-09-08 11:31:24 +02:00
|
|
|
},
|
|
|
|
null,
|
|
|
|
L10n.global().removeSelectedFromAlbumSuccessNotification(1),
|
|
|
|
failureText: L10n.global().removeSelectedFromAlbumFailureNotification,
|
|
|
|
)();
|
|
|
|
} catch (e, stackTrace) {
|
|
|
|
_log.shout("[_onRemoveFromAlbumPressed] Failed while updating album", e,
|
|
|
|
stackTrace);
|
|
|
|
}
|
2021-08-21 19:58:17 +02:00
|
|
|
}
|
|
|
|
|
2021-08-13 22:18:35 +02:00
|
|
|
Future<void> _onSetAlbumCoverPressed(BuildContext context) async {
|
|
|
|
assert(widget.album != null);
|
|
|
|
_log.info(
|
|
|
|
"[_onSetAlbumCoverPressed] Set '${widget.file.path}' as album cover for '${widget.album!.name}'");
|
2021-09-08 11:31:24 +02:00
|
|
|
try {
|
|
|
|
await NotifiedAction(
|
|
|
|
() async {
|
2022-07-05 22:20:24 +02:00
|
|
|
await UpdateAlbum(_c.albumRepo)(
|
2021-08-13 22:18:35 +02:00
|
|
|
widget.account,
|
|
|
|
widget.album!.copyWith(
|
|
|
|
coverProvider: AlbumManualCoverProvider(
|
|
|
|
coverFile: widget.file,
|
|
|
|
),
|
|
|
|
));
|
2021-09-08 11:31:24 +02:00
|
|
|
},
|
|
|
|
L10n.global().setAlbumCoverProcessingNotification,
|
|
|
|
L10n.global().setAlbumCoverSuccessNotification,
|
|
|
|
failureText: L10n.global().setAlbumCoverFailureNotification,
|
|
|
|
)();
|
|
|
|
} catch (e, stackTrace) {
|
|
|
|
_log.shout("[_onSetAlbumCoverPressed] Failed while updating album", e,
|
|
|
|
stackTrace);
|
|
|
|
}
|
2021-08-13 22:18:35 +02:00
|
|
|
}
|
|
|
|
|
2021-11-17 21:21:21 +01:00
|
|
|
Future<void> _onAddToAlbumPressed(BuildContext context) {
|
|
|
|
return AddSelectionToAlbumHandler()(
|
2021-04-10 06:28:12 +02:00
|
|
|
context: context,
|
2021-11-17 21:21:21 +01:00
|
|
|
account: widget.account,
|
|
|
|
selectedFiles: [widget.file],
|
|
|
|
clearSelection: () {},
|
|
|
|
);
|
2021-04-10 06:28:12 +02:00
|
|
|
}
|
|
|
|
|
2021-08-13 16:10:20 +02:00
|
|
|
Future<void> _onArchivePressed(BuildContext context) async {
|
|
|
|
_log.info("[_onArchivePressed] Archive file: ${widget.file.path}");
|
2022-01-22 13:02:24 +01:00
|
|
|
final count =
|
|
|
|
await ArchiveSelectionHandler(KiwiContainer().resolve<DiContainer>())(
|
|
|
|
account: widget.account,
|
|
|
|
selectedFiles: [widget.file],
|
|
|
|
);
|
|
|
|
if (count == 1) {
|
|
|
|
if (mounted) {
|
|
|
|
Navigator.of(context).pop();
|
|
|
|
}
|
2021-09-08 11:31:24 +02:00
|
|
|
}
|
2021-08-13 16:10:20 +02:00
|
|
|
}
|
|
|
|
|
2022-07-28 20:45:43 +02:00
|
|
|
Future<void> _onUnarchivePressed(BuildContext context) async {
|
2021-08-13 16:10:20 +02:00
|
|
|
_log.info("[_onUnarchivePressed] Unarchive file: ${widget.file.path}");
|
2021-09-08 11:31:24 +02:00
|
|
|
try {
|
|
|
|
await NotifiedAction(
|
|
|
|
() async {
|
2022-07-05 22:20:24 +02:00
|
|
|
await UpdateProperty(_c.fileRepo)
|
2021-08-13 16:10:20 +02:00
|
|
|
.updateIsArchived(widget.account, widget.file, false);
|
2021-08-29 13:57:10 +02:00
|
|
|
if (mounted) {
|
|
|
|
Navigator.of(context).pop();
|
|
|
|
}
|
2021-09-08 11:31:24 +02:00
|
|
|
},
|
|
|
|
L10n.global().unarchiveSelectedProcessingNotification(1),
|
|
|
|
L10n.global().unarchiveSelectedSuccessNotification,
|
|
|
|
failureText: L10n.global().unarchiveSelectedFailureNotification(1),
|
|
|
|
)();
|
|
|
|
} catch (e, stackTrace) {
|
|
|
|
_log.shout(
|
2021-12-02 09:27:11 +01:00
|
|
|
"[_onUnarchivePressed] Failed while archiving file: ${logFilename(widget.file.path)}",
|
2021-09-08 11:31:24 +02:00
|
|
|
e,
|
|
|
|
stackTrace);
|
|
|
|
}
|
2021-08-13 16:10:20 +02:00
|
|
|
}
|
|
|
|
|
2021-04-10 17:21:34 +02:00
|
|
|
void _onMapTap() {
|
2021-04-29 17:42:44 +02:00
|
|
|
if (platform_k.isAndroid) {
|
2021-04-10 17:21:34 +02:00
|
|
|
final intent = AndroidIntent(
|
|
|
|
action: "action_view",
|
2021-07-23 22:05:57 +02:00
|
|
|
data: Uri.encodeFull("geo:${_gps!.item1},${_gps!.item2}?z=16"),
|
2021-04-10 17:21:34 +02:00
|
|
|
);
|
|
|
|
intent.launch();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-21 12:39:17 +02:00
|
|
|
void _onDateTimeTap(BuildContext context) {
|
|
|
|
showDialog(
|
|
|
|
context: context,
|
|
|
|
builder: (context) => PhotoDateTimeEditDialog(initialDateTime: _dateTime),
|
|
|
|
).then((value) async {
|
|
|
|
if (value == null || value is! DateTime) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
try {
|
2022-07-05 22:20:24 +02:00
|
|
|
await UpdateProperty(_c.fileRepo)
|
2021-06-21 12:39:17 +02:00
|
|
|
.updateOverrideDateTime(widget.account, widget.file, value);
|
2021-08-29 13:57:10 +02:00
|
|
|
if (mounted) {
|
|
|
|
setState(() {
|
|
|
|
_dateTime = value;
|
|
|
|
});
|
|
|
|
}
|
2021-06-21 12:39:17 +02:00
|
|
|
} catch (e, stacktrace) {
|
|
|
|
_log.shout(
|
2021-12-02 09:27:11 +01:00
|
|
|
"[_onDateTimeTap] Failed while updateOverrideDateTime: ${logFilename(widget.file.path)}",
|
2021-06-21 12:39:17 +02:00
|
|
|
e,
|
|
|
|
stacktrace);
|
|
|
|
SnackBarManager().showSnackBar(SnackBar(
|
2021-08-29 13:51:43 +02:00
|
|
|
content: Text(L10n.global().updateDateTimeFailureNotification),
|
2021-06-21 12:39:17 +02:00
|
|
|
duration: k.snackBarDurationNormal,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}).catchError((e, stacktrace) {
|
|
|
|
_log.shout("[_onDateTimeTap] Failed while showDialog", e, stacktrace);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-11-18 09:05:33 +01:00
|
|
|
bool _checkCanRemoveFromAlbum() {
|
|
|
|
if (widget.album == null ||
|
|
|
|
widget.album!.provider is! AlbumStaticProvider) {
|
|
|
|
return false;
|
|
|
|
}
|
2022-07-11 20:14:42 +02:00
|
|
|
if (widget.album!.albumFile?.isOwned(widget.account.userId) == true) {
|
2021-11-18 09:05:33 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
final thisItem = AlbumStaticProvider.of(widget.album!)
|
|
|
|
.items
|
|
|
|
.whereType<AlbumFileItem>()
|
|
|
|
.firstWhere(
|
|
|
|
(element) => element.file.compareServerIdentity(widget.file));
|
2022-07-11 20:14:42 +02:00
|
|
|
if (thisItem.addedBy == widget.account.userId) {
|
2021-11-18 09:05:33 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} catch (_) {}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-07-05 22:20:24 +02:00
|
|
|
late final DiContainer _c;
|
|
|
|
|
2021-07-23 22:05:57 +02:00
|
|
|
late DateTime _dateTime;
|
2021-04-10 06:28:12 +02:00
|
|
|
// EXIF data
|
2021-07-23 22:05:57 +02:00
|
|
|
String? _model;
|
|
|
|
double? _fNumber;
|
|
|
|
String? _exposureTime;
|
|
|
|
double? _focalLength;
|
|
|
|
int? _isoSpeedRatings;
|
|
|
|
Tuple2<double, double>? _gps;
|
2021-04-10 06:28:12 +02:00
|
|
|
|
2022-01-28 20:34:38 +01:00
|
|
|
final _tags = <String>[];
|
|
|
|
|
2021-11-18 09:05:33 +01:00
|
|
|
late final bool _canRemoveFromAlbum = _checkCanRemoveFromAlbum();
|
|
|
|
|
2021-11-26 11:10:18 +01:00
|
|
|
var _shouldBlockGpsMap = true;
|
|
|
|
|
2021-04-10 06:28:12 +02:00
|
|
|
static final _log =
|
|
|
|
Logger("widget.viewer_detail_pane._ViewerDetailPaneState");
|
|
|
|
}
|
|
|
|
|
|
|
|
class _DetailPaneButton extends StatelessWidget {
|
2021-07-23 22:05:57 +02:00
|
|
|
const _DetailPaneButton({
|
|
|
|
Key? key,
|
|
|
|
required this.icon,
|
|
|
|
required this.label,
|
|
|
|
required this.onPressed,
|
|
|
|
}) : super(key: key);
|
2021-04-10 06:28:12 +02:00
|
|
|
|
|
|
|
@override
|
|
|
|
build(BuildContext context) {
|
|
|
|
return TextButton(
|
|
|
|
onPressed: onPressed,
|
|
|
|
child: SizedBox(
|
|
|
|
width: 96,
|
|
|
|
height: 96,
|
|
|
|
child: Padding(
|
2021-09-14 23:00:39 +02:00
|
|
|
padding: const EdgeInsets.fromLTRB(8, 16, 8, 0),
|
2021-04-10 06:28:12 +02:00
|
|
|
child: Column(
|
|
|
|
children: [
|
2021-04-17 10:59:16 +02:00
|
|
|
Icon(
|
|
|
|
icon,
|
2021-08-14 18:09:46 +02:00
|
|
|
color: AppTheme.getSecondaryTextColor(context),
|
2021-04-17 10:59:16 +02:00
|
|
|
),
|
2021-04-10 06:28:12 +02:00
|
|
|
const SizedBox(height: 4),
|
|
|
|
Text(
|
|
|
|
label,
|
|
|
|
textAlign: TextAlign.center,
|
2021-04-17 10:59:16 +02:00
|
|
|
style: TextStyle(
|
|
|
|
fontSize: 12,
|
|
|
|
color: AppTheme.getSecondaryTextColor(context),
|
|
|
|
),
|
2021-04-10 06:28:12 +02:00
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
final IconData icon;
|
|
|
|
final String label;
|
2021-07-23 22:05:57 +02:00
|
|
|
final VoidCallback? onPressed;
|
2021-04-10 06:28:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
String _byteSizeToString(int byteSize) {
|
|
|
|
const units = ["B", "KB", "MB", "GB"];
|
|
|
|
var remain = byteSize.toDouble();
|
|
|
|
int i = 0;
|
|
|
|
while (i < units.length) {
|
|
|
|
final next = remain / 1024;
|
|
|
|
if (next < 1) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
remain = next;
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
return "${remain.toStringAsFixed(2)}${units[i]}";
|
|
|
|
}
|