diff --git a/lib/generated/intl/messages_it.dart b/lib/generated/intl/messages_it.dart
index 4cad869c4..8f94a0688 100644
--- a/lib/generated/intl/messages_it.dart
+++ b/lib/generated/intl/messages_it.dart
@@ -230,7 +230,7 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("Aggiungi all\'album"),
"addToEnte": MessageLookupByLibrary.simpleMessage("Aggiungi su ente"),
"addToHiddenAlbum":
- MessageLookupByLibrary.simpleMessage("Add to hidden album"),
+ MessageLookupByLibrary.simpleMessage("Aggiungi ad album nascosto"),
"addViewer":
MessageLookupByLibrary.simpleMessage("Aggiungi in sola lettura"),
"addedAs": MessageLookupByLibrary.simpleMessage("Aggiunto come"),
@@ -707,6 +707,8 @@ class MessageLookup extends MessageLookupByLibrary {
"goToSettings":
MessageLookupByLibrary.simpleMessage("Vai alle impostazioni"),
"googlePlayId": MessageLookupByLibrary.simpleMessage("Google Play ID"),
+ "grantFullAccessPrompt": MessageLookupByLibrary.simpleMessage(
+ "Consenti l\'accesso a tutte le foto nelle Impostazioni"),
"grantPermission":
MessageLookupByLibrary.simpleMessage("Concedi il permesso"),
"groupNearbyPhotos": MessageLookupByLibrary.simpleMessage(
@@ -861,7 +863,7 @@ class MessageLookup extends MessageLookupByLibrary {
"moveToAlbum":
MessageLookupByLibrary.simpleMessage("Sposta nell\'album"),
"moveToHiddenAlbum":
- MessageLookupByLibrary.simpleMessage("Move to hidden album"),
+ MessageLookupByLibrary.simpleMessage("Sposta in album nascosto"),
"movedSuccessfullyTo": m30,
"movedToTrash":
MessageLookupByLibrary.simpleMessage("Spostato nel cestino"),
@@ -910,6 +912,8 @@ class MessageLookup extends MessageLookupByLibrary {
"Ops, impossibile salvare le modifiche"),
"oopsSomethingWentWrong": MessageLookupByLibrary.simpleMessage(
"Oops! Qualcosa è andato storto"),
+ "openSettings":
+ MessageLookupByLibrary.simpleMessage("Apri Impostazioni"),
"openTheItem":
MessageLookupByLibrary.simpleMessage("• Apri la foto o il video"),
"openstreetmapContributors": MessageLookupByLibrary.simpleMessage(
@@ -980,6 +984,8 @@ class MessageLookup extends MessageLookupByLibrary {
"preserveMore": MessageLookupByLibrary.simpleMessage("Salva più foto"),
"pressAndHoldToPlayVideo": MessageLookupByLibrary.simpleMessage(
"Tieni premuto per riprodurre il video"),
+ "pressAndHoldToPlayVideoDetailed": MessageLookupByLibrary.simpleMessage(
+ "Tieni premuto sull\'immagine per riprodurre il video"),
"privacy": MessageLookupByLibrary.simpleMessage("Privacy"),
"privacyPolicyTitle":
MessageLookupByLibrary.simpleMessage("Privacy Policy"),
@@ -1116,6 +1122,8 @@ class MessageLookup extends MessageLookupByLibrary {
"Seleziona gli elementi da aggiungere"),
"selectLanguage":
MessageLookupByLibrary.simpleMessage("Seleziona una lingua"),
+ "selectMorePhotos":
+ MessageLookupByLibrary.simpleMessage("Seleziona più foto"),
"selectReason":
MessageLookupByLibrary.simpleMessage("Seleziona un motivo"),
"selectYourPlan":
@@ -1180,6 +1188,7 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("Condivise con te"),
"sharing":
MessageLookupByLibrary.simpleMessage("Condivisione in corso..."),
+ "showMemories": MessageLookupByLibrary.simpleMessage("Mostra ricordi"),
"signUpTerms": MessageLookupByLibrary.simpleMessage(
"Accetto i termini di servizio e la politica sulla privacy"),
"singleFileDeleteFromDevice": m47,
diff --git a/lib/models/file/file.dart b/lib/models/file/file.dart
index 7de51adb6..a0481cd39 100644
--- a/lib/models/file/file.dart
+++ b/lib/models/file/file.dart
@@ -271,6 +271,9 @@ class EnteFile {
int get width {
return pubMagicMetadata?.w ?? 0;
}
+ bool get hasDimensions {
+ return height != 0 && width != 0;
+ }
// returns true if the file isn't available in the user's gallery
bool get isRemoteFile {
diff --git a/lib/ui/common/fast_scroll_physics.dart b/lib/ui/common/fast_scroll_physics.dart
new file mode 100644
index 000000000..97771af24
--- /dev/null
+++ b/lib/ui/common/fast_scroll_physics.dart
@@ -0,0 +1,24 @@
+import 'package:flutter/material.dart';
+
+class FastScrollPhysics extends PageScrollPhysics {
+ final double speedFactor;
+
+ const FastScrollPhysics({this.speedFactor = 2.0, ScrollPhysics? parent})
+ : super(parent: parent);
+
+ @override
+ FastScrollPhysics applyTo(ScrollPhysics? ancestor) {
+ return FastScrollPhysics(
+ speedFactor: speedFactor,
+ parent: buildParent(ancestor),
+ );
+ }
+
+ @override
+ Simulation? createBallisticSimulation(
+ ScrollMetrics position,
+ double velocity,
+ ) {
+ return super.createBallisticSimulation(position, velocity * speedFactor);
+ }
+}
diff --git a/lib/ui/viewer/file/detail_page.dart b/lib/ui/viewer/file/detail_page.dart
index 34ae1b1ab..9356bee4b 100644
--- a/lib/ui/viewer/file/detail_page.dart
+++ b/lib/ui/viewer/file/detail_page.dart
@@ -7,6 +7,7 @@ import 'package:photos/core/constants.dart';
import 'package:photos/core/errors.dart';
import "package:photos/generated/l10n.dart";
import 'package:photos/models/file/file.dart';
+import "package:photos/ui/common/fast_scroll_physics.dart";
import 'package:photos/ui/tools/editor/image_editor_page.dart';
import "package:photos/ui/viewer/file/file_app_bar.dart";
import "package:photos/ui/viewer/file/file_bottom_bar.dart";
@@ -200,7 +201,7 @@ class _DetailPageState extends State {
},
physics: _shouldDisableScroll
? const NeverScrollableScrollPhysics()
- : const PageScrollPhysics(),
+ : const FastScrollPhysics(speedFactor: 4.0),
controller: _pageController,
itemCount: _files!.length,
);
diff --git a/lib/ui/viewer/file/file_details_widget.dart b/lib/ui/viewer/file/file_details_widget.dart
index 8ae4818ae..4ed1fe844 100644
--- a/lib/ui/viewer/file/file_details_widget.dart
+++ b/lib/ui/viewer/file/file_details_widget.dart
@@ -279,10 +279,10 @@ class _FileDetailsWidgetState extends State {
"ImageLength"];
if (imageWidth != null && imageLength != null) {
_exifData["resolution"] = '$imageWidth x $imageLength';
- _exifData['megaPixels'] =
- ((imageWidth.values.firstAsInt() * imageLength.values.firstAsInt()) /
- 1000000)
- .toStringAsFixed(1);
+ final double megaPixels =
+ (imageWidth.values.firstAsInt() * imageLength.values.firstAsInt()) / 1000000;
+ final double roundedMegaPixels = (megaPixels * 10).round() / 10.0;
+ _exifData['megaPixels'] = roundedMegaPixels..toStringAsFixed(1);
} else {
debugPrint("No image width/height");
}
diff --git a/lib/ui/viewer/file/zoomable_image.dart b/lib/ui/viewer/file/zoomable_image.dart
index a17e54b4b..191053cce 100644
--- a/lib/ui/viewer/file/zoomable_image.dart
+++ b/lib/ui/viewer/file/zoomable_image.dart
@@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:io';
+import "package:flutter/foundation.dart";
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:logging/logging.dart';
@@ -12,6 +13,7 @@ import 'package:photos/core/event_bus.dart';
import 'package:photos/db/files_db.dart';
import 'package:photos/events/files_updated_event.dart';
import 'package:photos/events/local_photos_updated_event.dart';
+import "package:photos/models/file/extensions/file_props.dart";
import 'package:photos/models/file/file.dart';
import "package:photos/models/metadata/file_magic.dart";
import "package:photos/services/file_magic_service.dart";
@@ -19,6 +21,7 @@ import 'package:photos/ui/common/loading_widget.dart';
import 'package:photos/utils/file_util.dart';
import 'package:photos/utils/image_util.dart';
import 'package:photos/utils/thumbnail_util.dart';
+import "package:photos/utils/toast_util.dart";
class ZoomableImage extends StatefulWidget {
final EnteFile photo;
@@ -127,7 +130,6 @@ class _ZoomableImageState extends State
if (cachedThumbnail != null) {
_imageProvider = Image.memory(cachedThumbnail).image;
_loadedSmallThumbnail = true;
- _captureThumbnailDimensions(_imageProvider!);
} else {
getThumbnailFromServer(_photo).then((file) {
final imageProvider = Image.memory(file).image;
@@ -137,7 +139,6 @@ class _ZoomableImageState extends State
setState(() {
_imageProvider = imageProvider;
_loadedSmallThumbnail = true;
- _captureThumbnailDimensions(_imageProvider!);
});
}
}).catchError((e) {
@@ -235,7 +236,10 @@ class _ZoomableImageState extends State
if (mounted) {
precacheImage(imageProvider, context).then((value) async {
if (mounted) {
- await _updatePhotoViewController(imageProvider);
+ await _updatePhotoViewController(
+ previewImageProvider: _imageProvider,
+ finalImageProvider: imageProvider,
+ );
setState(() {
_imageProvider = imageProvider;
_loadedFinalImage = true;
@@ -246,50 +250,62 @@ class _ZoomableImageState extends State
}
}
- Future _captureThumbnailDimensions(ImageProvider imageProvider) async {
- final imageInfo = await getImageInfo(imageProvider);
- _thumbnailWidth = imageInfo.image.width;
- }
-
- Future _updatePhotoViewController(ImageProvider imageProvider) async {
- if (_thumbnailWidth == null || _photoViewController.scale == null) {
- return;
+ Future _updatePhotoViewController({
+ required ImageProvider? previewImageProvider,
+ required ImageProvider finalImageProvider,
+ }) async {
+ final bool shouldFixPosition = previewImageProvider != null &&
+ _isZooming &&
+ _photoViewController.scale != null;
+ ImageInfo? finalImageInfo;
+ if(shouldFixPosition) {
+ if (kDebugMode) {
+ showToast(context,
+ 'Updating photo scale zooming: $_isZooming and scale: ${_photoViewController.scale}');
+ }
+ final prevImageInfo = await getImageInfo(previewImageProvider);
+ finalImageInfo = await getImageInfo(finalImageProvider);
+ final scale = _photoViewController.scale! /
+ (finalImageInfo.image.width / prevImageInfo.image.width);
+ final currentPosition = _photoViewController.value.position;
+ final positionScaleFactor = 1 / scale;
+ final newPosition = currentPosition.scale(
+ positionScaleFactor,
+ positionScaleFactor,
+ );
+ _photoViewController = PhotoViewController(
+ initialPosition: newPosition,
+ initialScale: scale,
+ );
+ }
+ final bool canUpdateMetadata = _photo.canEditMetaInfo;
+ // forcefully get finalImageInfo is dimensions are not available in metadata
+ if (finalImageInfo == null && canUpdateMetadata && !_photo.hasDimensions) {
+ finalImageInfo = await getImageInfo(finalImageProvider);
+ }
+ if (finalImageInfo != null && canUpdateMetadata) {
+ _updateAspectRatioIfNeeded(_photo, finalImageInfo).ignore();
}
- final imageInfo = await getImageInfo(imageProvider);
- final scale = _photoViewController.scale! /
- (imageInfo.image.width / _thumbnailWidth!);
- final currentPosition = _photoViewController.value.position;
- final positionScaleFactor = 1 / scale;
- final newPosition = currentPosition.scale(
- positionScaleFactor,
- positionScaleFactor,
- );
- _photoViewController = PhotoViewController(
- initialPosition: newPosition,
- initialScale: scale,
- );
- _updateAspectRatioIfNeeded(imageInfo).ignore();
}
// Fallback logic to finish back fill and update aspect
// ratio if needed.
- Future _updateAspectRatioIfNeeded(ImageInfo imageInfo) async {
- if (_imageProvider != null &&
- widget.photo.isUploaded &&
- widget.photo.ownerID == _currentUserID) {
- final int h = imageInfo.image.height, w = imageInfo.image.width;
- if (h != 0 &&
- w != 0 &&
- (h != widget.photo.height || w != widget.photo.width)) {
- _logger.info('Updating aspect ratio for ${widget.photo} to $h:$w');
-
- await FileMagicService.instance.updatePublicMagicMetadata([
- widget.photo,
- ], {
- heightKey: h,
- widthKey: w,
- });
+ Future _updateAspectRatioIfNeeded(
+ EnteFile enteFile,
+ ImageInfo imageInfo,
+ ) async {
+ final int h = imageInfo.image.height, w = imageInfo.image.width;
+ if (h != enteFile.height || w != enteFile.width) {
+ if (kDebugMode) {
+ showToast(context, 'Updating aspect ratio');
}
+ _logger.info('Updating aspect ratio for $enteFile to $h:$w');
+ await FileMagicService.instance.updatePublicMagicMetadata([
+ enteFile,
+ ], {
+ heightKey: h,
+ widthKey: w,
+ });
}
}
diff --git a/lib/ui/viewer/file_details/file_properties_item_widget.dart b/lib/ui/viewer/file_details/file_properties_item_widget.dart
index 1411d7588..d0f2916d2 100644
--- a/lib/ui/viewer/file_details/file_properties_item_widget.dart
+++ b/lib/ui/viewer/file_details/file_properties_item_widget.dart
@@ -47,15 +47,24 @@ class _FilePropertiesItemWidgetState extends State {
}
Future> _subTitleSection() async {
- final bool showDimension = widget.exifData["resolution"] != null &&
- widget.exifData["megaPixels"] != null;
+ final StringBuffer dimString = StringBuffer();
+ if (widget.exifData["resolution"] != null &&
+ widget.exifData["megaPixels"] != null) {
+ dimString.write('${widget.exifData["megaPixels"]}MP ');
+ dimString.write('${widget.exifData["resolution"]}');
+ } else if (widget.file.hasDimensions) {
+ final double megaPixels =
+ (widget.file.width * widget.file.height) / 1000000;
+ final double roundedMegaPixels = (megaPixels * 10).round() / 10.0;
+ dimString.write('${roundedMegaPixels.toStringAsFixed(1)}MP ');
+ dimString.write('${widget.file.width} x ${widget.file.height}');
+ }
final subSectionWidgets = [];
- if (showDimension) {
+ if (dimString.isNotEmpty) {
subSectionWidgets.add(
Text(
- "${widget.exifData["megaPixels"]}MP "
- "${widget.exifData["resolution"]} ",
+ dimString.toString(),
style: getEnteTextTheme(context).miniMuted,
),
);