Skip to content

Commit

Permalink
[mobile] add panorama viewer (#2362)
Browse files Browse the repository at this point in the history
## Description

## Tests
  • Loading branch information
ua741 authored Jul 18, 2024
2 parents 806a772 + 87383ea commit 18fd873
Show file tree
Hide file tree
Showing 14 changed files with 212 additions and 9 deletions.
6 changes: 6 additions & 0 deletions mobile/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ PODS:
- FlutterMacOS
- dart_ui_isolate (0.0.1):
- Flutter
- dchs_motion_sensors (0.0.1):
- Flutter
- device_info_plus (0.0.1):
- Flutter
- ffmpeg-kit-ios-min (6.0)
Expand Down Expand Up @@ -240,6 +242,7 @@ DEPENDENCIES:
- battery_info (from `.symlinks/plugins/battery_info/ios`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`)
- dart_ui_isolate (from `.symlinks/plugins/dart_ui_isolate/ios`)
- dchs_motion_sensors (from `.symlinks/plugins/dchs_motion_sensors/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- ffmpeg_kit_flutter_min (from `.symlinks/plugins/ffmpeg_kit_flutter_min/ios`)
- file_saver (from `.symlinks/plugins/file_saver/ios`)
Expand Down Expand Up @@ -322,6 +325,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/connectivity_plus/darwin"
dart_ui_isolate:
:path: ".symlinks/plugins/dart_ui_isolate/ios"
dchs_motion_sensors:
:path: ".symlinks/plugins/dchs_motion_sensors/ios"
device_info_plus:
:path: ".symlinks/plugins/device_info_plus/ios"
ffmpeg_kit_flutter_min:
Expand Down Expand Up @@ -424,6 +429,7 @@ SPEC CHECKSUMS:
battery_info: 09f5c9ee65394f2291c8c6227bedff345b8a730c
connectivity_plus: ddd7f30999e1faaef5967c23d5b6d503d10434db
dart_ui_isolate: d5bcda83ca4b04f129d70eb90110b7a567aece14
dchs_motion_sensors: 9cef816635a39345cda9f0c4943e061f6429f453
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
ffmpeg-kit-ios-min: 4e9a088f4ee9629435960b9d68e54848975f1931
ffmpeg_kit_flutter_min: 5eff47f4965bf9d1150e98961eb6129f5ae3f28c
Expand Down
2 changes: 2 additions & 0 deletions mobile/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@
"${BUILT_PRODUCTS_DIR}/battery_info/battery_info.framework",
"${BUILT_PRODUCTS_DIR}/connectivity_plus/connectivity_plus.framework",
"${BUILT_PRODUCTS_DIR}/dart_ui_isolate/dart_ui_isolate.framework",
"${BUILT_PRODUCTS_DIR}/dchs_motion_sensors/dchs_motion_sensors.framework",
"${BUILT_PRODUCTS_DIR}/device_info_plus/device_info_plus.framework",
"${BUILT_PRODUCTS_DIR}/file_saver/file_saver.framework",
"${BUILT_PRODUCTS_DIR}/fk_user_agent/fk_user_agent.framework",
Expand Down Expand Up @@ -386,6 +387,7 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/battery_info.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/connectivity_plus.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/dart_ui_isolate.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/dchs_motion_sensors.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/device_info_plus.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_saver.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/fk_user_agent.framework",
Expand Down
1 change: 1 addition & 0 deletions mobile/lib/generated/intl/messages_en.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions mobile/lib/generated/l10n.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions mobile/lib/l10n/intl_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1264,6 +1264,7 @@
}
}
},
"panorama": "Panorama",
"reenterPassword": "Re-enter password",
"reenterPin": "Re-enter PIN",
"deviceLock": "Device lock",
Expand Down
16 changes: 16 additions & 0 deletions mobile/lib/models/file/extensions/file_props.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,22 @@ extension FilePropsExtn on EnteFile {

bool get isVideo => fileType == FileType.video;

bool get hasDims => height > 0 && width > 0;

// return true if the file is a panorama image, null if the dimensions are not available
bool? isPanorama() {
if (fileType != FileType.image) {
return false;
}
if (hasDims) {
if (height > width) {
return height / width >= 2.0;
}
return width / height >= 2.0;
}
return null;
}

bool get canEditMetaInfo => isUploaded && isOwner;

bool get isTrash => this is TrashFile;
Expand Down
2 changes: 1 addition & 1 deletion mobile/lib/models/file/file_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ FileType fileTypeFromAsset(AssetEntity asset) {
case AssetType.image:
type = FileType.image;
// PHAssetMediaSubtype.photoLive.rawValue is 8
// This hack should go away once photos_manager support livePhotos
// This hack should go away once photo_manager support livePhotos
if (asset.subtype > -1 && (asset.subtype & 8) != 0) {
type = FileType.livePhoto;
}
Expand Down
61 changes: 61 additions & 0 deletions mobile/lib/ui/viewer/file/file_bottom_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import "package:logging/logging.dart";
import "package:photos/generated/l10n.dart";
import "package:photos/models/file/extensions/file_props.dart";
import 'package:photos/models/file/file.dart';
import 'package:photos/models/file/file_type.dart';
import 'package:photos/models/file/trash_file.dart';
Expand All @@ -12,7 +13,10 @@ import "package:photos/theme/colors.dart";
import "package:photos/theme/ente_theme.dart";
import "package:photos/ui/actions/file/file_actions.dart";
import 'package:photos/ui/collections/collection_action_sheet.dart';
import "package:photos/ui/viewer/file/panorama_viewer_screen.dart";
import 'package:photos/utils/delete_file_util.dart';
import "package:photos/utils/file_util.dart";
import "package:photos/utils/panorama_util.dart";
import 'package:photos/utils/share_util.dart';

class FileBottomBar extends StatefulWidget {
Expand All @@ -39,12 +43,35 @@ class FileBottomBar extends StatefulWidget {

class FileBottomBarState extends State<FileBottomBar> {
final GlobalKey shareButtonKey = GlobalKey();
late bool isPanorama;
bool _isPanoramaCheckInitiated = false;

@override
void initState() {
super.initState();
isPanorama = widget.file.isPanorama() ?? false;
}

@override
Widget build(BuildContext context) {
_checkPanorama();
return _getBottomBar();
}

// _checkPanorama() method is used to check if the file is a panorama image.
// This handles the case when the the file dims (width and height) are not available.
Future<void> _checkPanorama() async {
if (_isPanoramaCheckInitiated || widget.file.hasDims) {
return;
}
_isPanoramaCheckInitiated = true;
final result = await checkIfPanorama(widget.file);
if (mounted && isPanorama == !result) {
isPanorama = result;
setState(() {});
}
}

void safeRefresh() {
if (mounted) {
setState(() {});
Expand Down Expand Up @@ -126,6 +153,26 @@ class FileBottomBarState extends State<FileBottomBar> {
),
);
}

if (isPanorama) {
children.add(
Tooltip(
message: S.of(context).panorama,
child: Padding(
padding: const EdgeInsets.only(top: 12, bottom: 12),
child: IconButton(
icon: const Icon(
Icons.threesixty,
color: Colors.white,
),
onPressed: () async {
await openPanoramaViewerPage(widget.file);
},
),
),
),
);
}
children.add(
Tooltip(
message: S.of(context).share,
Expand Down Expand Up @@ -219,6 +266,20 @@ class FileBottomBarState extends State<FileBottomBar> {
);
}

Future<void> openPanoramaViewerPage(EnteFile file) async {
final fetchedFile = await getFile(file);
if (fetchedFile == null) {
return;
}
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) {
return PanoramaViewerScreen(file: fetchedFile);
},
),
).ignore();
}

Future<void> _showSingleFileDeleteSheet(EnteFile file) async {
await showSingleFileDeleteSheet(
context,
Expand Down
2 changes: 1 addition & 1 deletion mobile/lib/ui/viewer/file/file_details_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ class _FileDetailsWidgetState extends State<FileDetailsWidget> {
},
),
]);
} else if (flagService.internalUser && widget.file.isVideo) {
} else if (widget.file.isVideo) {
fileDetailsTiles.addAll([
ValueListenableBuilder(
valueListenable: _videoMetadataNotifier,
Expand Down
37 changes: 37 additions & 0 deletions mobile/lib/ui/viewer/file/panorama_viewer_screen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import "dart:io";

import "package:flutter/material.dart";
import "package:panorama_viewer/panorama_viewer.dart";

class PanoramaViewerScreen extends StatelessWidget {
const PanoramaViewerScreen({
super.key,
required this.file,
});

final File file;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0, // Remove shadow
leading: IconButton(
icon: const Icon(
Icons.arrow_back,
size: 18,
),
onPressed: () {
Navigator.of(context).pop();
},
),
),
body: PanoramaViewer(
child: Image.file(
file,
fit: BoxFit.cover,
),
),
);
}
}
1 change: 0 additions & 1 deletion mobile/lib/ui/viewer/file/zoomable_image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import 'dart:async';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import "package:flutter_image_compress/flutter_image_compress.dart";
import 'package:logging/logging.dart';
import 'package:photo_view/photo_view.dart';
Expand Down
28 changes: 28 additions & 0 deletions mobile/lib/utils/panorama_util.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import "package:flutter/painting.dart";
import "package:photos/models/file/extensions/file_props.dart";
import "package:photos/models/file/file.dart";
import "package:photos/models/file/file_type.dart";
import "package:photos/utils/file_util.dart";

/// Check if the file is a panorama image.
Future<bool> checkIfPanorama(EnteFile enteFile) async {
if (enteFile.fileType != FileType.image) {
return false;
}
if (enteFile.isPanorama() != null) {
return enteFile.isPanorama()!;
}
final file = await getFile(enteFile);
if (file == null) {
return false;
}

final image = await decodeImageFromList(await file.readAsBytes());
final width = image.width.toDouble();
final height = image.height.toDouble();

if (height > width) {
return height / width >= 2.0;
}
return width / height >= 2.0;
}
Loading

0 comments on commit 18fd873

Please sign in to comment.