Minor performance improvements during swipe b/w photos (#1361)
This commit is contained in:
commit
d67efccf8c
7 changed files with 115 additions and 53 deletions
13
lib/generated/intl/messages_it.dart
generated
13
lib/generated/intl/messages_it.dart
generated
|
@ -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 <u-terms>termini di servizio</u-terms> e la <u-policy>politica sulla privacy</u-policy>"),
|
||||
"singleFileDeleteFromDevice": m47,
|
||||
|
|
|
@ -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 {
|
||||
|
|
24
lib/ui/common/fast_scroll_physics.dart
Normal file
24
lib/ui/common/fast_scroll_physics.dart
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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<DetailPage> {
|
|||
},
|
||||
physics: _shouldDisableScroll
|
||||
? const NeverScrollableScrollPhysics()
|
||||
: const PageScrollPhysics(),
|
||||
: const FastScrollPhysics(speedFactor: 4.0),
|
||||
controller: _pageController,
|
||||
itemCount: _files!.length,
|
||||
);
|
||||
|
|
|
@ -279,10 +279,10 @@ class _FileDetailsWidgetState extends State<FileDetailsWidget> {
|
|||
"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");
|
||||
}
|
||||
|
|
|
@ -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<ZoomableImage>
|
|||
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<ZoomableImage>
|
|||
setState(() {
|
||||
_imageProvider = imageProvider;
|
||||
_loadedSmallThumbnail = true;
|
||||
_captureThumbnailDimensions(_imageProvider!);
|
||||
});
|
||||
}
|
||||
}).catchError((e) {
|
||||
|
@ -235,7 +236,10 @@ class _ZoomableImageState extends State<ZoomableImage>
|
|||
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<ZoomableImage>
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> _captureThumbnailDimensions(ImageProvider imageProvider) async {
|
||||
final imageInfo = await getImageInfo(imageProvider);
|
||||
_thumbnailWidth = imageInfo.image.width;
|
||||
}
|
||||
|
||||
Future<void> _updatePhotoViewController(ImageProvider imageProvider) async {
|
||||
if (_thumbnailWidth == null || _photoViewController.scale == null) {
|
||||
return;
|
||||
Future<void> _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<void> _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<void> _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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,15 +47,24 @@ class _FilePropertiesItemWidgetState extends State<FilePropertiesItemWidget> {
|
|||
}
|
||||
|
||||
Future<List<Widget>> _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 = <Widget>[];
|
||||
|
||||
if (showDimension) {
|
||||
if (dimString.isNotEmpty) {
|
||||
subSectionWidgets.add(
|
||||
Text(
|
||||
"${widget.exifData["megaPixels"]}MP "
|
||||
"${widget.exifData["resolution"]} ",
|
||||
dimString.toString(),
|
||||
style: getEnteTextTheme(context).miniMuted,
|
||||
),
|
||||
);
|
||||
|
|
Loading…
Add table
Reference in a new issue