From 1688e61a96c9223004252dcbea36f7d9ab09509a Mon Sep 17 00:00:00 2001 From: XxAlonexX Date: Fri, 24 Jan 2025 15:03:40 +0530 Subject: [PATCH 1/3] feat: Add video zoom functionality in lightbox Implements pinch-to-zoom and pan gestures for videos in the lightbox view. This allows users to zoom in/out (up to 5x) and pan around while maintaining proper aspect ratio. Fixes #1287 --- lib/widgets/lightbox.dart | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/widgets/lightbox.dart b/lib/widgets/lightbox.dart index 4635e496f4..9ac9d65a58 100644 --- a/lib/widgets/lightbox.dart +++ b/lib/widgets/lightbox.dart @@ -444,6 +444,7 @@ class VideoLightboxPage extends StatefulWidget { class _VideoLightboxPageState extends State with PerAccountStoreAwareStateMixin { VideoPlayerController? _controller; + final TransformationController _transformationController = TransformationController(); @override void onNewStore() { @@ -494,6 +495,7 @@ class _VideoLightboxPageState extends State with PerAccountSt _controller?.removeListener(_handleVideoControllerUpdate); _controller?.dispose(); _controller = null; + _transformationController.dispose(); // The VideoController doesn't emit a pause event // while disposing, so disable the wakelock here // explicitly. @@ -546,21 +548,22 @@ class _VideoLightboxPageState extends State with PerAccountSt return _LightboxPageLayout( routeEntranceAnimation: widget.routeEntranceAnimation, message: widget.message, - buildAppBarBottom: (context) => null, + buildAppBarBottom: null, buildBottomAppBar: _buildBottomAppBar, - child: SafeArea( - child: Center( - child: Stack(alignment: Alignment.center, children: [ - if (_controller != null && _controller!.value.isInitialized) - AspectRatio( + child: _controller == null + ? const Center(child: CircularProgressIndicator()) + : Center( + child: InteractiveViewer( + transformationController: _transformationController, + minScale: 1.0, + maxScale: 5.0, + child: AspectRatio( aspectRatio: _controller!.value.aspectRatio, - child: VideoPlayer(_controller!)), - if (_controller == null || !_controller!.value.isInitialized || _controller!.value.isBuffering) - const SizedBox( - width: 32, - height: 32, - child: CircularProgressIndicator(color: Colors.white)), - ])))); + child: VideoPlayer(_controller!), + ), + ), + ), + ); } } From 426fd9d6d4facd3b4ae16091b05e9036edbc56c4 Mon Sep 17 00:00:00 2001 From: XxAlonexX Date: Sun, 26 Jan 2025 09:49:55 +0530 Subject: [PATCH 2/3] fix: update video zoom test cases to use correct widget testing approach --- android/app/build.gradle | 6 +- android/gradle.properties | 1 + lib/widgets/lightbox.dart | 4 +- .../.gradle/8.10/checksums/checksums.lock | Bin 0 -> 17 bytes .../8.10/dependencies-accessors/gc.properties | 0 .../.gradle/8.10/fileChanges/last-build.bin | Bin 0 -> 1 bytes .../.gradle/8.10/fileHashes/fileHashes.bin | Bin 0 -> 18547 bytes .../.gradle/8.10/fileHashes/fileHashes.lock | Bin 0 -> 17 bytes .../android/.gradle/8.10/gc.properties | 0 .../.gradle/8.9/checksums/checksums.lock | Bin 0 -> 17 bytes .../8.9/dependencies-accessors/gc.properties | 0 .../.gradle/8.9/fileChanges/last-build.bin | Bin 0 -> 1 bytes .../.gradle/8.9/fileHashes/fileHashes.lock | Bin 0 -> 17 bytes .../android/.gradle/8.9/gc.properties | 0 .../buildOutputCleanup.lock | Bin 0 -> 17 bytes .../buildOutputCleanup/cache.properties | 2 + ...AEA5DA50B9E2BDC5D4BB54486C2724D8C85DC39946 | 1 + .../android/.gradle/vcs-1/gc.properties | 0 pubspec.lock | 56 ++++++++-------- test/widgets/lightbox_test.dart | 60 ++++++++++++++++++ 20 files changed, 97 insertions(+), 33 deletions(-) create mode 100644 packages/zulip_plugin/android/.gradle/8.10/checksums/checksums.lock create mode 100644 packages/zulip_plugin/android/.gradle/8.10/dependencies-accessors/gc.properties create mode 100644 packages/zulip_plugin/android/.gradle/8.10/fileChanges/last-build.bin create mode 100644 packages/zulip_plugin/android/.gradle/8.10/fileHashes/fileHashes.bin create mode 100644 packages/zulip_plugin/android/.gradle/8.10/fileHashes/fileHashes.lock create mode 100644 packages/zulip_plugin/android/.gradle/8.10/gc.properties create mode 100644 packages/zulip_plugin/android/.gradle/8.9/checksums/checksums.lock create mode 100644 packages/zulip_plugin/android/.gradle/8.9/dependencies-accessors/gc.properties create mode 100644 packages/zulip_plugin/android/.gradle/8.9/fileChanges/last-build.bin create mode 100644 packages/zulip_plugin/android/.gradle/8.9/fileHashes/fileHashes.lock create mode 100644 packages/zulip_plugin/android/.gradle/8.9/gc.properties create mode 100644 packages/zulip_plugin/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock create mode 100644 packages/zulip_plugin/android/.gradle/buildOutputCleanup/cache.properties create mode 100644 packages/zulip_plugin/android/.gradle/nb-cache/trust/E5CE58C03A05467C790274AEA5DA50B9E2BDC5D4BB54486C2724D8C85DC39946 create mode 100644 packages/zulip_plugin/android/.gradle/vcs-1/gc.properties diff --git a/android/app/build.gradle b/android/app/build.gradle index f360f667e5..5074da5045 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -25,12 +25,12 @@ android { compileSdkVersion flutter.compileSdkVersion compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = '11' } sourceSets { diff --git a/android/gradle.properties b/android/gradle.properties index 0a5710a599..afecdd0437 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -14,3 +14,4 @@ agpVersion=8.5.2 # A helpful discussion is at: # https://stackoverflow.com/a/74425347 kotlinVersion=2.0.10 +kotlin.jvm.target=11 diff --git a/lib/widgets/lightbox.dart b/lib/widgets/lightbox.dart index 9ac9d65a58..922a3922aa 100644 --- a/lib/widgets/lightbox.dart +++ b/lib/widgets/lightbox.dart @@ -311,7 +311,7 @@ class _ImageLightboxPageState extends State<_ImageLightboxPage> { return _LightboxPageLayout( routeEntranceAnimation: widget.routeEntranceAnimation, message: widget.message, - buildAppBarBottom: _buildAppBarBottom, + buildAppBarBottom: (BuildContext context) => null, buildBottomAppBar: _buildBottomAppBar, child: SizedBox.expand( child: InteractiveViewer( @@ -548,7 +548,7 @@ class _VideoLightboxPageState extends State with PerAccountSt return _LightboxPageLayout( routeEntranceAnimation: widget.routeEntranceAnimation, message: widget.message, - buildAppBarBottom: null, + buildAppBarBottom: (BuildContext context) => null, buildBottomAppBar: _buildBottomAppBar, child: _controller == null ? const Center(child: CircularProgressIndicator()) diff --git a/packages/zulip_plugin/android/.gradle/8.10/checksums/checksums.lock b/packages/zulip_plugin/android/.gradle/8.10/checksums/checksums.lock new file mode 100644 index 0000000000000000000000000000000000000000..c9256f5b3bbc5180d07e258acf9abd6b9629eab6 GIT binary patch literal 17 TcmZR6;IeyB|FtJ?8K3|FMb`zp literal 0 HcmV?d00001 diff --git a/packages/zulip_plugin/android/.gradle/8.10/dependencies-accessors/gc.properties b/packages/zulip_plugin/android/.gradle/8.10/dependencies-accessors/gc.properties new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/zulip_plugin/android/.gradle/8.10/fileChanges/last-build.bin b/packages/zulip_plugin/android/.gradle/8.10/fileChanges/last-build.bin new file mode 100644 index 0000000000000000000000000000000000000000..f76dd238ade08917e6712764a16a22005a50573d GIT binary patch literal 1 IcmZPo000310RR91 literal 0 HcmV?d00001 diff --git a/packages/zulip_plugin/android/.gradle/8.10/fileHashes/fileHashes.bin b/packages/zulip_plugin/android/.gradle/8.10/fileHashes/fileHashes.bin new file mode 100644 index 0000000000000000000000000000000000000000..0de9b3037ac521b927ddb49d19c4742d15881e71 GIT binary patch literal 18547 zcmeI(p^8F56adiCBo9H0@TyTz5p0WLT?B0=iyyJof3PYxyC1T_WN_^U*<|n&be>P~ z7i2jDGxsu6?mcy81|g)?>tlr1sJ&7d0RjXF5FkK+009C72oNAZfB*pk1PBly@LvQb zv5_QiCT$k8q%sSkQ;tbtwH)p~_nT9GJnBF151ujZH}W$p0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNCf4g%d+ME2Ofp3Q3MkhH%3 cK8`=Vb-Q@}y~NJ~i|_e)d#|)G9mm+d0gX8;@&Et; literal 0 HcmV?d00001 diff --git a/packages/zulip_plugin/android/.gradle/8.10/fileHashes/fileHashes.lock b/packages/zulip_plugin/android/.gradle/8.10/fileHashes/fileHashes.lock new file mode 100644 index 0000000000000000000000000000000000000000..6ffb9c88c4abef4b61ef2b0af9b0875340d2f275 GIT binary patch literal 17 TcmZQ}o?|9j$GIb6;e3{U_7GXMkP literal 0 HcmV?d00001 diff --git a/packages/zulip_plugin/android/.gradle/8.9/gc.properties b/packages/zulip_plugin/android/.gradle/8.9/gc.properties new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/zulip_plugin/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/packages/zulip_plugin/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock new file mode 100644 index 0000000000000000000000000000000000000000..37a236cc29acaf9c3557f3659563858d84dfcea9 GIT binary patch literal 17 UcmZRc-Fj=&(-|_W86bcK06!Q6_y7O^ literal 0 HcmV?d00001 diff --git a/packages/zulip_plugin/android/.gradle/buildOutputCleanup/cache.properties b/packages/zulip_plugin/android/.gradle/buildOutputCleanup/cache.properties new file mode 100644 index 0000000000..4d793ee99c --- /dev/null +++ b/packages/zulip_plugin/android/.gradle/buildOutputCleanup/cache.properties @@ -0,0 +1,2 @@ +#Sun Jan 26 10:00:58 IST 2025 +gradle.version=8.10 diff --git a/packages/zulip_plugin/android/.gradle/nb-cache/trust/E5CE58C03A05467C790274AEA5DA50B9E2BDC5D4BB54486C2724D8C85DC39946 b/packages/zulip_plugin/android/.gradle/nb-cache/trust/E5CE58C03A05467C790274AEA5DA50B9E2BDC5D4BB54486C2724D8C85DC39946 new file mode 100644 index 0000000000..7e11eca7ca --- /dev/null +++ b/packages/zulip_plugin/android/.gradle/nb-cache/trust/E5CE58C03A05467C790274AEA5DA50B9E2BDC5D4BB54486C2724D8C85DC39946 @@ -0,0 +1 @@ +F9DBEE8947D96C7624685DFC098354BE5ED14573C4884BAC8865EBDD16FEE696 diff --git a/packages/zulip_plugin/android/.gradle/vcs-1/gc.properties b/packages/zulip_plugin/android/.gradle/vcs-1/gc.properties new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pubspec.lock b/pubspec.lock index 4d5f5d63df..0311d91863 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -130,10 +130,10 @@ packages: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.0" charcode: dependency: transitive description: @@ -186,10 +186,10 @@ packages: dependency: "direct main" description: name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf url: "https://pub.dev" source: hosted - version: "1.19.1" + version: "1.19.0" color_models: dependency: "direct overridden" description: @@ -703,10 +703,10 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: @@ -719,10 +719,10 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.15.0" mime: dependency: "direct main" description: @@ -831,10 +831,10 @@ packages: dependency: transitive description: name: petitparser - sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "6.0.2" pigeon: dependency: "direct dev" description: @@ -996,10 +996,10 @@ packages: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.10.1" + version: "1.10.0" sprintf: dependency: transitive description: @@ -1036,18 +1036,18 @@ packages: dependency: "direct dev" description: name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" url: "https://pub.dev" source: hosted - version: "1.12.1" + version: "1.12.0" stream_channel: dependency: transitive description: name: stream_channel - sha256: "4ac0537115a24d772c408a2520ecd0abb99bca2ea9c4e634ccbdbfae64fe17ec" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.2" stream_transform: dependency: transitive description: @@ -1060,10 +1060,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + sha256: "0bd04f5bb74fcd6ff0606a888a30e917af9bd52820b178eaa464beb11dca84b6" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.4.0" sync_http: dependency: transitive description: @@ -1076,34 +1076,34 @@ packages: dependency: transitive description: name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" test: dependency: "direct dev" description: name: test - sha256: "8391fbe68d520daf2314121764d38e37f934c02fd7301ad18307bd93bd6b725d" + sha256: "713a8789d62f3233c46b4a90b174737b2c04cb6ae4500f2aa8b1be8f03f5e67f" url: "https://pub.dev" source: hosted - version: "1.25.14" + version: "1.25.8" test_api: dependency: "direct dev" description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.3" test_core: dependency: transitive description: name: test_core - sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa" + sha256: "12391302411737c176b0b5d6491f466b0dd56d4763e347b6714efbaa74d7953d" url: "https://pub.dev" source: hosted - version: "0.6.8" + version: "0.6.5" timing: dependency: transitive description: @@ -1360,5 +1360,5 @@ packages: source: path version: "0.0.1" sdks: - dart: ">=3.7.0-312.0.dev <4.0.0" - flutter: ">=3.28.0-2.0.pre.38699" + dart: ">=3.6.0 <4.0.0" + flutter: ">=3.27.0" diff --git a/test/widgets/lightbox_test.dart b/test/widgets/lightbox_test.dart index 31a4132a7c..ef64403067 100644 --- a/test/widgets/lightbox_test.dart +++ b/test/widgets/lightbox_test.dart @@ -557,5 +557,65 @@ void main() { check(position).isGreaterThan(basePosition); check(platform.position).equals(position); }); + + testWidgets('video zoom functionality', (tester) async { + await setupPage(tester, videoSrc: Uri.parse(kTestVideoUrl)); + + // Find the InteractiveViewer that wraps the VideoPlayer + final interactiveViewer = tester.widget( + find.ancestor( + of: find.byType(VideoPlayer), + matching: find.byType(InteractiveViewer), + ), + ); + + // Simulate pinch-to-zoom gesture + final center = tester.getCenter(find.byType(VideoPlayer)); + await tester.startGesture(center, kind: PointerDeviceKind.touch); + await tester.startGesture(center + const Offset(50.0, 0.0), kind: PointerDeviceKind.touch); + await tester.pump(); + + // Check if the InteractiveViewer allows zoom + check(interactiveViewer.maxScale).isGreaterThan(1.0); + }); + + testWidgets('video pan functionality', (tester) async { + await setupPage(tester, videoSrc: Uri.parse(kTestVideoUrl)); + + // Find the InteractiveViewer + final interactiveViewer = tester.widget( + find.ancestor( + of: find.byType(VideoPlayer), + matching: find.byType(InteractiveViewer), + ), + ); + + // Check if pan is enabled + check(interactiveViewer.panEnabled).isTrue(); + + // Simulate pan gesture + await tester.drag(find.byType(InteractiveViewer), const Offset(100, 0)); + await tester.pump(); + }); + + testWidgets('maintain aspect ratio during zoom', (tester) async { + await setupPage(tester, videoSrc: Uri.parse(kTestVideoUrl)); + + // Find the VideoPlayer + final videoPlayer = tester.widget(find.byType(VideoPlayer)); + final aspectRatio = videoPlayer.controller?.value.aspectRatio ?? 1.0; + + // Find the InteractiveViewer + final interactiveViewer = tester.widget( + find.ancestor( + of: find.byType(VideoPlayer), + matching: find.byType(InteractiveViewer), + ), + ); + + // Check if the InteractiveViewer preserves aspect ratio + check(interactiveViewer.constrained).isTrue(); + check(aspectRatio).isGreaterThan(0.0); + }); }); } From 3b6e3a559480667e30e7ba3bab0201e6137349e2 Mon Sep 17 00:00:00 2001 From: XxAlonexX Date: Sun, 26 Jan 2025 10:39:20 +0530 Subject: [PATCH 3/3] fixed the errors --- android/app/build.gradle | 1 - lib/widgets/lightbox.dart | 2 +- .../.gradle/8.10/fileHashes/fileHashes.lock | Bin 17 -> 17 bytes .../buildOutputCleanup/buildOutputCleanup.lock | Bin 17 -> 17 bytes .../.gradle/buildOutputCleanup/cache.properties | 2 +- test/widgets/lightbox_test.dart | 12 ++++++++++++ 6 files changed, 14 insertions(+), 3 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 5074da5045..49e56f60f6 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -21,7 +21,6 @@ try { android { namespace "com.zulip.flutter" - compileSdkVersion flutter.compileSdkVersion compileOptions { diff --git a/lib/widgets/lightbox.dart b/lib/widgets/lightbox.dart index 922a3922aa..4e5a9b10c9 100644 --- a/lib/widgets/lightbox.dart +++ b/lib/widgets/lightbox.dart @@ -311,7 +311,7 @@ class _ImageLightboxPageState extends State<_ImageLightboxPage> { return _LightboxPageLayout( routeEntranceAnimation: widget.routeEntranceAnimation, message: widget.message, - buildAppBarBottom: (BuildContext context) => null, + buildAppBarBottom: _buildAppBarBottom, buildBottomAppBar: _buildBottomAppBar, child: SizedBox.expand( child: InteractiveViewer( diff --git a/packages/zulip_plugin/android/.gradle/8.10/fileHashes/fileHashes.lock b/packages/zulip_plugin/android/.gradle/8.10/fileHashes/fileHashes.lock index 6ffb9c88c4abef4b61ef2b0af9b0875340d2f275..eae9df1cfa7e99ed81a3008e750d6ec4736782a6 100644 GIT binary patch literal 17 TcmZQ}o?|9j$GI