diff --git a/android/app/build.gradle b/android/app/build.gradle index f360f667e5..49e56f60f6 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -21,16 +21,15 @@ try { android { namespace "com.zulip.flutter" - 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 4635e496f4..4e5a9b10c9 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: (BuildContext context) => 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!), + ), + ), + ), + ); } } 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 0000000000..c9256f5b3b Binary files /dev/null and b/packages/zulip_plugin/android/.gradle/8.10/checksums/checksums.lock differ 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 0000000000..f76dd238ad Binary files /dev/null and b/packages/zulip_plugin/android/.gradle/8.10/fileChanges/last-build.bin differ 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 0000000000..0de9b3037a Binary files /dev/null and b/packages/zulip_plugin/android/.gradle/8.10/fileHashes/fileHashes.bin differ 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 0000000000..eae9df1cfa Binary files /dev/null and b/packages/zulip_plugin/android/.gradle/8.10/fileHashes/fileHashes.lock differ diff --git a/packages/zulip_plugin/android/.gradle/8.10/gc.properties b/packages/zulip_plugin/android/.gradle/8.10/gc.properties new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/zulip_plugin/android/.gradle/8.9/checksums/checksums.lock b/packages/zulip_plugin/android/.gradle/8.9/checksums/checksums.lock new file mode 100644 index 0000000000..7a74eb4416 Binary files /dev/null and b/packages/zulip_plugin/android/.gradle/8.9/checksums/checksums.lock differ diff --git a/packages/zulip_plugin/android/.gradle/8.9/dependencies-accessors/gc.properties b/packages/zulip_plugin/android/.gradle/8.9/dependencies-accessors/gc.properties new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/zulip_plugin/android/.gradle/8.9/fileChanges/last-build.bin b/packages/zulip_plugin/android/.gradle/8.9/fileChanges/last-build.bin new file mode 100644 index 0000000000..f76dd238ad Binary files /dev/null and b/packages/zulip_plugin/android/.gradle/8.9/fileChanges/last-build.bin differ diff --git a/packages/zulip_plugin/android/.gradle/8.9/fileHashes/fileHashes.lock b/packages/zulip_plugin/android/.gradle/8.9/fileHashes/fileHashes.lock new file mode 100644 index 0000000000..b0276c75a9 Binary files /dev/null and b/packages/zulip_plugin/android/.gradle/8.9/fileHashes/fileHashes.lock differ 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 0000000000..7245a5c9f6 Binary files /dev/null and b/packages/zulip_plugin/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ 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..5961de86fb --- /dev/null +++ b/packages/zulip_plugin/android/.gradle/buildOutputCleanup/cache.properties @@ -0,0 +1,2 @@ +#Sun Jan 26 10:24:06 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..e793af6c09 100644 --- a/test/widgets/lightbox_test.dart +++ b/test/widgets/lightbox_test.dart @@ -557,5 +557,77 @@ 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); + }); }); + + Widget _loadingBuilder(BuildContext context, Widget child, ImageChunkEvent? loadingProgress) { + if (loadingProgress == null) return child; + + return Center( + child: CircularProgressIndicator( + value: loadingProgress.expectedTotalBytes != null + ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes! + : null, + ), + ); + } }