From 94c4a3ee51af48ad988be66a92603bf423b7445b Mon Sep 17 00:00:00 2001 From: Valentin Vignal <32538273+ValentinVignal@users.noreply.github.com> Date: Fri, 10 Jan 2025 08:37:13 +0800 Subject: [PATCH 01/26] [camera]: Activate leak testing for sub packages (#8353) *Replace this paragraph with a description of what this PR is changing or adding, and why. Consider including before/after screenshots.* Follow up of https://github.com/flutter/packages/pull/8287 Activates leak testing for the packages including a `test` folder See the documentation: https://github.com/dart-lang/leak_tracker/blob/main/doc%2Fleak_tracking%2FDETECT.md --- packages/camera/camera_android/pubspec.yaml | 1 + .../camera_android/test/flutter_test_config.dart | 13 +++++++++++++ packages/camera/camera_android_camerax/pubspec.yaml | 1 + .../test/flutter_test_config.dart | 13 +++++++++++++ packages/camera/camera_avfoundation/pubspec.yaml | 1 + .../test/flutter_test_config.dart | 13 +++++++++++++ .../camera/camera_platform_interface/pubspec.yaml | 1 + .../test/flutter_test_config.dart | 13 +++++++++++++ packages/camera/camera_windows/pubspec.yaml | 1 + .../camera_windows/test/flutter_test_config.dart | 13 +++++++++++++ 10 files changed, 70 insertions(+) create mode 100644 packages/camera/camera_android/test/flutter_test_config.dart create mode 100644 packages/camera/camera_android_camerax/test/flutter_test_config.dart create mode 100644 packages/camera/camera_avfoundation/test/flutter_test_config.dart create mode 100644 packages/camera/camera_platform_interface/test/flutter_test_config.dart create mode 100644 packages/camera/camera_windows/test/flutter_test_config.dart diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 10922c7557a9..a5af9af8d52e 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -30,6 +30,7 @@ dev_dependencies: build_runner: ^2.4.11 flutter_test: sdk: flutter + leak_tracker_flutter_testing: any mockito: ^5.4.4 pigeon: ^22.4.1 diff --git a/packages/camera/camera_android/test/flutter_test_config.dart b/packages/camera/camera_android/test/flutter_test_config.dart new file mode 100644 index 000000000000..9907e578b84b --- /dev/null +++ b/packages/camera/camera_android/test/flutter_test_config.dart @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; + +Future testExecutable(FutureOr Function() testMain) async { + LeakTesting.enable(); + LeakTracking.warnForUnsupportedPlatforms = false; + await testMain(); +} diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index e007029d2c18..90fabeb88f96 100644 --- a/packages/camera/camera_android_camerax/pubspec.yaml +++ b/packages/camera/camera_android_camerax/pubspec.yaml @@ -31,6 +31,7 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter + leak_tracker_flutter_testing: any mockito: ^5.4.4 pigeon: ^9.1.0 diff --git a/packages/camera/camera_android_camerax/test/flutter_test_config.dart b/packages/camera/camera_android_camerax/test/flutter_test_config.dart new file mode 100644 index 000000000000..9907e578b84b --- /dev/null +++ b/packages/camera/camera_android_camerax/test/flutter_test_config.dart @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; + +Future testExecutable(FutureOr Function() testMain) async { + LeakTesting.enable(); + LeakTracking.warnForUnsupportedPlatforms = false; + await testMain(); +} diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index 507011775bfd..70c6dccfbd09 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -27,6 +27,7 @@ dev_dependencies: build_runner: ^2.4.9 flutter_test: sdk: flutter + leak_tracker_flutter_testing: any mockito: ^5.4.4 pigeon: ^22.4.2 diff --git a/packages/camera/camera_avfoundation/test/flutter_test_config.dart b/packages/camera/camera_avfoundation/test/flutter_test_config.dart new file mode 100644 index 000000000000..9907e578b84b --- /dev/null +++ b/packages/camera/camera_avfoundation/test/flutter_test_config.dart @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; + +Future testExecutable(FutureOr Function() testMain) async { + LeakTesting.enable(); + LeakTracking.warnForUnsupportedPlatforms = false; + await testMain(); +} diff --git a/packages/camera/camera_platform_interface/pubspec.yaml b/packages/camera/camera_platform_interface/pubspec.yaml index 1f490abc564b..bea9eef23194 100644 --- a/packages/camera/camera_platform_interface/pubspec.yaml +++ b/packages/camera/camera_platform_interface/pubspec.yaml @@ -21,6 +21,7 @@ dev_dependencies: async: ^2.5.0 flutter_test: sdk: flutter + leak_tracker_flutter_testing: any topics: - camera diff --git a/packages/camera/camera_platform_interface/test/flutter_test_config.dart b/packages/camera/camera_platform_interface/test/flutter_test_config.dart new file mode 100644 index 000000000000..9907e578b84b --- /dev/null +++ b/packages/camera/camera_platform_interface/test/flutter_test_config.dart @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; + +Future testExecutable(FutureOr Function() testMain) async { + LeakTesting.enable(); + LeakTracking.warnForUnsupportedPlatforms = false; + await testMain(); +} diff --git a/packages/camera/camera_windows/pubspec.yaml b/packages/camera/camera_windows/pubspec.yaml index 0db033af0ac0..474810845e02 100644 --- a/packages/camera/camera_windows/pubspec.yaml +++ b/packages/camera/camera_windows/pubspec.yaml @@ -28,6 +28,7 @@ dev_dependencies: build_runner: ^2.4.9 flutter_test: sdk: flutter + leak_tracker_flutter_testing: any mockito: ^5.4.4 pigeon: ^22.6.0 diff --git a/packages/camera/camera_windows/test/flutter_test_config.dart b/packages/camera/camera_windows/test/flutter_test_config.dart new file mode 100644 index 000000000000..9907e578b84b --- /dev/null +++ b/packages/camera/camera_windows/test/flutter_test_config.dart @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; + +Future testExecutable(FutureOr Function() testMain) async { + LeakTesting.enable(); + LeakTracking.warnForUnsupportedPlatforms = false; + await testMain(); +} From 6bf90e8e898b21c627d58d8e2c8661d3975e7e2f Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Thu, 9 Jan 2025 21:46:54 -0500 Subject: [PATCH 02/26] [url_launcher][web] Better support for semantics in the Link widget (#6711) - Better support for semantics. - Better support for clicks with a modifier key (e.g. cmd+click). - More robust handling of events (can now correctly handle events received in a different order). - Apply the `target` attribute on semantic links. - Improve tests. Fixes https://github.com/flutter/flutter/issues/143164 Fixes https://github.com/flutter/flutter/issues/146291 --- .../url_launcher_web/CHANGELOG.md | 8 +- .../integration_test/link_widget_test.dart | 985 +++++++++++++++--- .../url_launcher_web/example/pubspec.yaml | 4 +- .../url_launcher_web/lib/src/link.dart | 566 +++++++--- .../url_launcher_web/pubspec.yaml | 6 +- 5 files changed, 1285 insertions(+), 284 deletions(-) diff --git a/packages/url_launcher/url_launcher_web/CHANGELOG.md b/packages/url_launcher/url_launcher_web/CHANGELOG.md index 0baf6389c8b9..f923d118fca3 100644 --- a/packages/url_launcher/url_launcher_web/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_web/CHANGELOG.md @@ -1,6 +1,10 @@ -## NEXT +## 2.4.0 -* Updates minimum supported SDK version to Flutter 3.22/Dart 3.4. +* Enhances handling of out-of-order events. +* Adds support for clicks with a modifier key (e.g. cmd+click). +* Improves support for semantics. +* Applies the `target` attribute to semantic links. +* Updates minimum supported SDK version to Flutter 3.27/Dart 3.6. ## 2.3.3 diff --git a/packages/url_launcher/url_launcher_web/example/integration_test/link_widget_test.dart b/packages/url_launcher/url_launcher_web/example/integration_test/link_widget_test.dart index b3b1890f23c2..b8c11fa32da1 100644 --- a/packages/url_launcher/url_launcher_web/example/integration_test/link_widget_test.dart +++ b/packages/url_launcher/url_launcher_web/example/integration_test/link_widget_test.dart @@ -4,6 +4,7 @@ import 'dart:js_interop'; import 'dart:js_interop_unsafe'; +import 'dart:typed_data'; import 'dart:ui_web' as ui_web; import 'package:flutter/material.dart'; @@ -18,6 +19,24 @@ import 'package:web/web.dart' as html; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + final List pushedRouteNames = []; + late Future Function(String) originalPushFunction; + + setUp(() { + pushedRouteNames.clear(); + originalPushFunction = pushRouteToFrameworkFunction; + pushRouteToFrameworkFunction = (String routeName) { + pushedRouteNames.add(routeName); + return Future.value(ByteData(0)); + }; + }); + + tearDown(() { + pushRouteToFrameworkFunction = originalPushFunction; + pushedRouteNames.clear(); + LinkViewController.debugReset(); + }); + group('Link Widget', () { testWidgets('creates anchor with correct attributes', (WidgetTester tester) async { @@ -77,10 +96,6 @@ void main() { expect(anchor.getAttribute('href'), ui_web.urlStrategy?.prepareExternalUrl(uri3.toString())); expect(anchor.getAttribute('target'), '_self'); - - // Needed when testing on on Chrome98 headless in CI. - // See https://github.com/flutter/flutter/issues/121161 - await tester.pumpAndSettle(); }); testWidgets('sizes itself correctly', (WidgetTester tester) async { @@ -114,10 +129,6 @@ void main() { // `ConstrainedBox` widget. expect(containerSize.width, 100.0); expect(containerSize.height, 100.0); - - // Needed when testing on on Chrome98 headless in CI. - // See https://github.com/flutter/flutter/issues/121161 - await tester.pumpAndSettle(); }); // See: https://github.com/flutter/plugins/pull/3522#discussion_r574703724 @@ -138,10 +149,6 @@ void main() { final html.Element anchor = _findSingleAnchor(); expect(anchor.hasAttribute('href'), false); - - // Needed when testing on on Chrome98 headless in CI. - // See https://github.com/flutter/flutter/issues/121161 - await tester.pumpAndSettle(); }); testWidgets('can be created and disposed', (WidgetTester tester) async { @@ -173,10 +180,6 @@ void main() { 800, maxScrolls: 1000, ); - - // Needed when testing on on Chrome98 headless in CI. - // See https://github.com/flutter/flutter/issues/121161 - await tester.pumpAndSettle(); }); }); @@ -196,221 +199,931 @@ void main() { testWidgets('click to navigate to internal link', (WidgetTester tester) async { - final TestNavigatorObserver observer = TestNavigatorObserver(); final Uri uri = Uri.parse('/foobar'); FollowLink? followLinkCallback; await tester.pumpWidget(MaterialApp( - navigatorObservers: [observer], routes: { '/foobar': (BuildContext context) => const Text('Internal route'), }, - home: Directionality( - textDirection: TextDirection.ltr, - child: WebLinkDelegate(TestLinkInfo( - uri: uri, - target: LinkTarget.blank, - builder: (BuildContext context, FollowLink? followLink) { - followLinkCallback = followLink; - return const SizedBox(width: 100, height: 100); - }, - )), - ), + home: WebLinkDelegate(TestLinkInfo( + uri: uri, + target: LinkTarget.blank, + builder: (BuildContext context, FollowLink? followLink) { + followLinkCallback = followLink; + return const SizedBox(width: 100, height: 100); + }, + )), )); // Platform view creation happens asynchronously. await tester.pumpAndSettle(); await tester.pump(); - expect(observer.currentRouteName, '/'); + expect(pushedRouteNames, isEmpty); expect(testPlugin.launches, isEmpty); final html.Element anchor = _findSingleAnchor(); await followLinkCallback!(); - _simulateClick(anchor); - await tester.pumpAndSettle(); + final html.Event event = _simulateClick(anchor); // Internal links should navigate the app to the specified route. There // should be no calls to `launchUrl`. - expect(observer.currentRouteName, '/foobar'); + expect(pushedRouteNames, ['/foobar']); expect(testPlugin.launches, isEmpty); + expect(event.defaultPrevented, isTrue); }); testWidgets('keydown to navigate to internal link', (WidgetTester tester) async { - final TestNavigatorObserver observer = TestNavigatorObserver(); final Uri uri = Uri.parse('/foobar'); FollowLink? followLinkCallback; await tester.pumpWidget(MaterialApp( - navigatorObservers: [observer], routes: { '/foobar': (BuildContext context) => const Text('Internal route'), }, - home: Directionality( - textDirection: TextDirection.ltr, - child: WebLinkDelegate(TestLinkInfo( - uri: uri, - target: LinkTarget.blank, - builder: (BuildContext context, FollowLink? followLink) { - followLinkCallback = followLink; - return const SizedBox(width: 100, height: 100); - }, - )), - ), + home: WebLinkDelegate(TestLinkInfo( + uri: uri, + target: LinkTarget.blank, + builder: (BuildContext context, FollowLink? followLink) { + followLinkCallback = followLink; + return const SizedBox(width: 100, height: 100); + }, + )), )); // Platform view creation happens asynchronously. await tester.pumpAndSettle(); await tester.pump(); - expect(observer.currentRouteName, '/'); + expect(pushedRouteNames, isEmpty); expect(testPlugin.launches, isEmpty); final html.Element anchor = _findSingleAnchor(); await followLinkCallback!(); - _simulateKeydown(anchor); - await tester.pumpAndSettle(); + final html.KeyboardEvent event = _simulateKeydown(anchor); // Internal links should navigate the app to the specified route. There // should be no calls to `launchUrl`. - expect(observer.currentRouteName, '/foobar'); + expect(pushedRouteNames, ['/foobar']); expect(testPlugin.launches, isEmpty); + expect(event.defaultPrevented, isFalse); }); testWidgets('click to navigate to external link', (WidgetTester tester) async { - final TestNavigatorObserver observer = TestNavigatorObserver(); - final Uri uri = Uri.parse('https://google.com'); + final Uri uri = Uri.parse('https://flutter.dev'); FollowLink? followLinkCallback; await tester.pumpWidget(MaterialApp( - navigatorObservers: [observer], - home: Directionality( - textDirection: TextDirection.ltr, - child: WebLinkDelegate(TestLinkInfo( - uri: uri, - target: LinkTarget.blank, - builder: (BuildContext context, FollowLink? followLink) { - followLinkCallback = followLink; - return const SizedBox(width: 100, height: 100); - }, - )), - ), + home: WebLinkDelegate(TestLinkInfo( + uri: uri, + target: LinkTarget.blank, + builder: (BuildContext context, FollowLink? followLink) { + followLinkCallback = followLink; + return const SizedBox(width: 100, height: 100); + }, + )), )); // Platform view creation happens asynchronously. await tester.pumpAndSettle(); await tester.pump(); - expect(observer.currentRouteName, '/'); + expect(pushedRouteNames, isEmpty); expect(testPlugin.launches, isEmpty); final html.Element anchor = _findSingleAnchor(); await followLinkCallback!(); - _simulateClick(anchor); - await tester.pumpAndSettle(); + final html.Event event = _simulateClick(anchor); // External links that are triggered by a click are left to be handled by // the browser, so there should be no change to the app's route name, and // no calls to `launchUrl`. - expect(observer.currentRouteName, '/'); + expect(pushedRouteNames, isEmpty); expect(testPlugin.launches, isEmpty); + expect(event.defaultPrevented, isFalse); }); testWidgets('keydown to navigate to external link', (WidgetTester tester) async { - final TestNavigatorObserver observer = TestNavigatorObserver(); - final Uri uri = Uri.parse('https://google.com'); + final Uri uri = Uri.parse('https://flutter.dev'); FollowLink? followLinkCallback; await tester.pumpWidget(MaterialApp( - navigatorObservers: [observer], - home: Directionality( - textDirection: TextDirection.ltr, - child: WebLinkDelegate(TestLinkInfo( - uri: uri, - target: LinkTarget.blank, - builder: (BuildContext context, FollowLink? followLink) { - followLinkCallback = followLink; - return const SizedBox(width: 100, height: 100); - }, - )), - ), + home: WebLinkDelegate(TestLinkInfo( + uri: uri, + target: LinkTarget.blank, + builder: (BuildContext context, FollowLink? followLink) { + followLinkCallback = followLink; + return const SizedBox(width: 100, height: 100); + }, + )), )); // Platform view creation happens asynchronously. await tester.pumpAndSettle(); await tester.pump(); - expect(observer.currentRouteName, '/'); + expect(pushedRouteNames, isEmpty); expect(testPlugin.launches, isEmpty); final html.Element anchor = _findSingleAnchor(); await followLinkCallback!(); - _simulateKeydown(anchor); - await tester.pumpAndSettle(); + final html.KeyboardEvent event = _simulateKeydown(anchor); // External links that are triggered by keyboard are handled by calling // `launchUrl`, and there's no change to the app's route name. - expect(observer.currentRouteName, '/'); - expect(testPlugin.launches, ['https://google.com']); + expect(pushedRouteNames, isEmpty); + expect(testPlugin.launches, ['https://flutter.dev']); + expect(event.defaultPrevented, isFalse); }); - }); -} -html.Element _findSingleAnchor() { - final List foundAnchors = []; - final html.NodeList anchors = html.document.querySelectorAll('a'); - for (int i = 0; i < anchors.length; i++) { - final html.Element anchor = anchors.item(i)! as html.Element; - if (anchor.hasProperty(linkViewIdProperty.toJS).toDart) { - foundAnchors.add(anchor); - } - } + testWidgets('click on mismatched link', (WidgetTester tester) async { + final Uri uri1 = Uri.parse('/foobar1'); + final Uri uri2 = Uri.parse('/foobar2'); + FollowLink? followLinkCallback1; + FollowLink? followLinkCallback2; - return foundAnchors.single; -} + await tester.pumpWidget(MaterialApp( + routes: { + '/foobar1': (BuildContext context) => const Text('Internal route 1'), + '/foobar2': (BuildContext context) => const Text('Internal route 2'), + }, + home: Column( + children: [ + WebLinkDelegate(TestLinkInfo( + uri: uri1, + target: LinkTarget.blank, + builder: (BuildContext context, FollowLink? followLink) { + followLinkCallback1 = followLink; + return const SizedBox(width: 100, height: 100); + }, + )), + WebLinkDelegate(TestLinkInfo( + uri: uri2, + target: LinkTarget.blank, + builder: (BuildContext context, FollowLink? followLink) { + followLinkCallback2 = followLink; + return const SizedBox(width: 100, height: 100); + }, + )), + ], + ), + )); + // Platform view creation happens asynchronously. + await tester.pumpAndSettle(); + await tester.pump(); -void _simulateClick(html.Element target) { - // Stop the browser from navigating away from the test suite. - target.addEventListener( - 'click', - (html.Event e) { - e.preventDefault(); - }.toJS); - // Synthesize a click event. - target.dispatchEvent( - html.MouseEvent( - 'click', - html.MouseEventInit( - bubbles: true, - cancelable: true, - ), - ), - ); -} + expect(pushedRouteNames, isEmpty); + expect(testPlugin.launches, isEmpty); -void _simulateKeydown(html.Element target) { - target.dispatchEvent( - html.KeyboardEvent( - 'keydown', - html.KeyboardEventInit( - bubbles: true, - cancelable: true, - code: 'Space', - ), - ), - ); -} + final [ + html.Element anchor1, + html.Element anchor2, + ...List rest, + ] = _findAllAnchors(); + expect(rest, isEmpty); -class TestNavigatorObserver extends NavigatorObserver { - String? currentRouteName; + await followLinkCallback2!(); + // Click on mismatched link. + final html.Event event1 = _simulateClick(anchor1); - @override - void didPush(Route route, Route? previousRoute) { - currentRouteName = route.settings.name; - } + // The link shouldn't have been triggered. + expect(pushedRouteNames, isEmpty); + expect(testPlugin.launches, isEmpty); + expect(event1.defaultPrevented, isTrue); + + // Click on mismatched link (in reverse order). + final html.Event event2 = _simulateClick(anchor2); + await followLinkCallback1!(); + + // The link shouldn't have been triggered. + expect(pushedRouteNames, isEmpty); + expect(testPlugin.launches, isEmpty); + expect(event2.defaultPrevented, isTrue); + + await followLinkCallback2!(); + // Click on the correct link. + final html.Event event = _simulateClick(anchor2); + + // The link should've been triggered now. + expect(pushedRouteNames, ['/foobar2']); + expect(testPlugin.launches, isEmpty); + expect(event.defaultPrevented, isTrue); + }); + + testWidgets('trigger signals are reset after a delay', + (WidgetTester tester) async { + final Uri uri = Uri.parse('/foobar'); + FollowLink? followLinkCallback; + + await tester.pumpWidget(MaterialApp( + routes: { + '/foobar': (BuildContext context) => const Text('Internal route'), + }, + home: WebLinkDelegate(TestLinkInfo( + uri: uri, + target: LinkTarget.blank, + builder: (BuildContext context, FollowLink? followLink) { + followLinkCallback = followLink; + return const SizedBox(width: 100, height: 100); + }, + )), + )); + // Platform view creation happens asynchronously. + await tester.pumpAndSettle(); + await tester.pump(); + + expect(pushedRouteNames, isEmpty); + expect(testPlugin.launches, isEmpty); + + final html.Element anchor = _findSingleAnchor(); + + // A large delay between signals should reset the previous signal. + await followLinkCallback!(); + await Future.delayed(const Duration(seconds: 1)); + final html.Event event1 = _simulateClick(anchor); + + // The link shouldn't have been triggered. + expect(pushedRouteNames, isEmpty); + expect(testPlugin.launches, isEmpty); + expect(event1.defaultPrevented, isFalse); + + await Future.delayed(const Duration(seconds: 1)); + + // Signals with large delay (in reverse order). + final html.Event event2 = _simulateClick(anchor); + await Future.delayed(const Duration(seconds: 1)); + await followLinkCallback!(); + + // The link shouldn't have been triggered. + expect(pushedRouteNames, isEmpty); + expect(testPlugin.launches, isEmpty); + expect(event2.defaultPrevented, isFalse); + + await Future.delayed(const Duration(seconds: 1)); + + // A small delay is okay. + await followLinkCallback!(); + await Future.delayed(const Duration(milliseconds: 100)); + final html.Event event3 = _simulateClick(anchor); + + // The link should've been triggered now. + expect(pushedRouteNames, ['/foobar']); + expect(testPlugin.launches, isEmpty); + expect(event3.defaultPrevented, isTrue); + }); + + testWidgets('ignores clicks on non-Flutter link', + (WidgetTester tester) async { + final Uri uri = Uri.parse('/foobar'); + FollowLink? followLinkCallback; + + await tester.pumpWidget(MaterialApp( + routes: { + '/foobar': (BuildContext context) => const Text('Internal route'), + }, + home: WebLinkDelegate(TestLinkInfo( + uri: uri, + target: LinkTarget.blank, + builder: (BuildContext context, FollowLink? followLink) { + followLinkCallback = followLink; + return const SizedBox(width: 100, height: 100); + }, + )), + )); + // Platform view creation happens asynchronously. + await tester.pumpAndSettle(); + await tester.pump(); + + expect(pushedRouteNames, isEmpty); + expect(testPlugin.launches, isEmpty); + + final html.Element nonFlutterAnchor = html.document.createElement('a'); + nonFlutterAnchor.setAttribute('href', '/non-flutter'); + + await followLinkCallback!(); + final html.Event event = _simulateClick(nonFlutterAnchor); + + // The link shouldn't have been triggered. + expect(pushedRouteNames, isEmpty); + expect(testPlugin.launches, isEmpty); + expect(event.defaultPrevented, isFalse); + }); + + testWidgets('handles cmd+click correctly', (WidgetTester tester) async { + final Uri uri = Uri.parse('/foobar'); + FollowLink? followLinkCallback; + + await tester.pumpWidget(MaterialApp( + routes: { + '/foobar': (BuildContext context) => const Text('Internal route'), + }, + home: WebLinkDelegate(TestLinkInfo( + uri: uri, + target: LinkTarget.blank, + builder: (BuildContext context, FollowLink? followLink) { + followLinkCallback = followLink; + return const SizedBox(width: 100, height: 100); + }, + )), + )); + // Platform view creation happens asynchronously. + await tester.pumpAndSettle(); + await tester.pump(); + + expect(pushedRouteNames, isEmpty); + expect(testPlugin.launches, isEmpty); + + final html.Element anchor = _findSingleAnchor(); + + await followLinkCallback!(); + final html.Event event = _simulateClick(anchor, metaKey: true); + + // When a modifier key is present, we should let the browser handle the + // navigation. That means we do nothing on our side. + expect(pushedRouteNames, isEmpty); + expect(testPlugin.launches, isEmpty); + expect(event.defaultPrevented, isFalse); + }); + + testWidgets('ignores keydown when it is a modifier key', + (WidgetTester tester) async { + final Uri uri = Uri.parse('/foobar'); + FollowLink? followLinkCallback; + + await tester.pumpWidget(MaterialApp( + routes: { + '/foobar': (BuildContext context) => const Text('Internal route'), + }, + home: WebLinkDelegate(TestLinkInfo( + uri: uri, + target: LinkTarget.blank, + builder: (BuildContext context, FollowLink? followLink) { + followLinkCallback = followLink; + return const SizedBox(width: 100, height: 100); + }, + )), + )); + // Platform view creation happens asynchronously. + await tester.pumpAndSettle(); + await tester.pump(); + + expect(pushedRouteNames, isEmpty); + expect(testPlugin.launches, isEmpty); + + final html.Element anchor = _findSingleAnchor(); + + final html.KeyboardEvent event1 = _simulateKeydown(anchor, metaKey: true); + await followLinkCallback!(); + + // When the pressed key is a modifier key, we should ignore it. + expect(pushedRouteNames, isEmpty); + expect(testPlugin.launches, isEmpty); + expect(event1.defaultPrevented, isFalse); + + // If later we receive another trigger, it should work. + final html.KeyboardEvent event2 = _simulateKeydown(anchor); + + // Now the link should be triggered. + expect(pushedRouteNames, ['/foobar']); + expect(testPlugin.launches, isEmpty); + expect(event2.defaultPrevented, isFalse); + }); + }); + + group('Follows links (reversed order)', () { + late TestUrlLauncherPlugin testPlugin; + late UrlLauncherPlatform originalPlugin; + + setUp(() { + originalPlugin = UrlLauncherPlatform.instance; + testPlugin = TestUrlLauncherPlugin(); + UrlLauncherPlatform.instance = testPlugin; + }); + + tearDown(() { + UrlLauncherPlatform.instance = originalPlugin; + }); + + testWidgets('click to navigate to internal link', + (WidgetTester tester) async { + final Uri uri = Uri.parse('/foobar'); + FollowLink? followLinkCallback; + + await tester.pumpWidget(MaterialApp( + routes: { + '/foobar': (BuildContext context) => const Text('Internal route'), + }, + home: WebLinkDelegate(TestLinkInfo( + uri: uri, + target: LinkTarget.blank, + builder: (BuildContext context, FollowLink? followLink) { + followLinkCallback = followLink; + return const SizedBox(width: 100, height: 100); + }, + )), + )); + // Platform view creation happens asynchronously. + await tester.pumpAndSettle(); + await tester.pump(); + + expect(pushedRouteNames, isEmpty); + expect(testPlugin.launches, isEmpty); + + final html.Element anchor = _findSingleAnchor(); + + final html.Event event = _simulateClick(anchor); + await followLinkCallback!(); + + // Internal links should navigate the app to the specified route. There + // should be no calls to `launchUrl`. + expect(pushedRouteNames, ['/foobar']); + expect(testPlugin.launches, isEmpty); + expect(event.defaultPrevented, isTrue); + }); + + testWidgets('keydown to navigate to internal link', + (WidgetTester tester) async { + final Uri uri = Uri.parse('/foobar'); + FollowLink? followLinkCallback; + + await tester.pumpWidget(MaterialApp( + routes: { + '/foobar': (BuildContext context) => const Text('Internal route'), + }, + home: WebLinkDelegate(TestLinkInfo( + uri: uri, + target: LinkTarget.blank, + builder: (BuildContext context, FollowLink? followLink) { + followLinkCallback = followLink; + return const SizedBox(width: 100, height: 100); + }, + )), + )); + // Platform view creation happens asynchronously. + await tester.pumpAndSettle(); + await tester.pump(); + + expect(pushedRouteNames, isEmpty); + expect(testPlugin.launches, isEmpty); + + final html.Element anchor = _findSingleAnchor(); + + final html.KeyboardEvent event = _simulateKeydown(anchor); + await followLinkCallback!(); + + // Internal links should navigate the app to the specified route. There + // should be no calls to `launchUrl`. + expect(pushedRouteNames, ['/foobar']); + expect(testPlugin.launches, isEmpty); + expect(event.defaultPrevented, isFalse); + }); + + testWidgets('click to navigate to external link', + (WidgetTester tester) async { + final Uri uri = Uri.parse('https://flutter.dev'); + FollowLink? followLinkCallback; + + await tester.pumpWidget(MaterialApp( + home: WebLinkDelegate(TestLinkInfo( + uri: uri, + target: LinkTarget.blank, + builder: (BuildContext context, FollowLink? followLink) { + followLinkCallback = followLink; + return const SizedBox(width: 100, height: 100); + }, + )), + )); + // Platform view creation happens asynchronously. + await tester.pumpAndSettle(); + await tester.pump(); + + expect(pushedRouteNames, isEmpty); + expect(testPlugin.launches, isEmpty); + + final html.Element anchor = _findSingleAnchor(); + + final html.Event event = _simulateClick(anchor); + await followLinkCallback!(); + + // External links that are triggered by a click are left to be handled by + // the browser, so there should be no change to the app's route name, and + // no calls to `launchUrl`. + expect(pushedRouteNames, isEmpty); + expect(testPlugin.launches, isEmpty); + expect(event.defaultPrevented, isFalse); + }); + + testWidgets('keydown to navigate to external link', + (WidgetTester tester) async { + final Uri uri = Uri.parse('https://flutter.dev'); + FollowLink? followLinkCallback; + + await tester.pumpWidget(MaterialApp( + home: WebLinkDelegate(TestLinkInfo( + uri: uri, + target: LinkTarget.blank, + builder: (BuildContext context, FollowLink? followLink) { + followLinkCallback = followLink; + return const SizedBox(width: 100, height: 100); + }, + )), + )); + // Platform view creation happens asynchronously. + await tester.pumpAndSettle(); + await tester.pump(); + + expect(pushedRouteNames, isEmpty); + expect(testPlugin.launches, isEmpty); + + final html.Element anchor = _findSingleAnchor(); + + final html.KeyboardEvent event = _simulateKeydown(anchor); + await followLinkCallback!(); + + // External links that are triggered by keyboard are handled by calling + // `launchUrl`, and there's no change to the app's route name. + expect(pushedRouteNames, isEmpty); + expect(testPlugin.launches, ['https://flutter.dev']); + expect(event.defaultPrevented, isFalse); + }); + }); + + group('Link semantics', () { + late TestUrlLauncherPlugin testPlugin; + late UrlLauncherPlatform originalPlugin; + + setUp(() { + originalPlugin = UrlLauncherPlatform.instance; + testPlugin = TestUrlLauncherPlugin(); + UrlLauncherPlatform.instance = testPlugin; + }); + + tearDown(() { + UrlLauncherPlatform.instance = originalPlugin; + }); + + testWidgets('produces the correct semantics tree with a button', + (WidgetTester tester) async { + final SemanticsHandle semanticsHandle = tester.ensureSemantics(); + final Key linkKey = UniqueKey(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: WebLinkDelegate( + key: linkKey, + semanticsIdentifier: 'test-link-12', + TestLinkInfo( + uri: Uri.parse('https://foobar/example?q=1'), + target: LinkTarget.blank, + builder: (BuildContext context, FollowLink? followLink) { + return ElevatedButton( + onPressed: followLink, + child: const Text('Button Link Text'), + ); + }, + ), + ), + )); + + final Finder linkFinder = find.byKey(linkKey); + expect( + tester.getSemantics(find.descendant( + of: linkFinder, + matching: find.byType(Semantics).first, + )), + matchesSemantics( + isLink: true, + identifier: 'test-link-12', + // linkUrl: 'https://foobar/example?q=1', + children: [ + matchesSemantics( + hasTapAction: true, + hasEnabledState: true, + hasFocusAction: true, + isEnabled: true, + isButton: true, + isFocusable: true, + label: 'Button Link Text', + ), + ], + ), + ); + + semanticsHandle.dispose(); + }); + + testWidgets('produces the correct semantics tree with text', + (WidgetTester tester) async { + final SemanticsHandle semanticsHandle = tester.ensureSemantics(); + final Key linkKey = UniqueKey(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: WebLinkDelegate( + key: linkKey, + semanticsIdentifier: 'test-link-43', + TestLinkInfo( + uri: Uri.parse('https://foobar/example?q=1'), + target: LinkTarget.blank, + builder: (BuildContext context, FollowLink? followLink) { + return GestureDetector( + onTap: followLink, + child: const Text('Link Text'), + ); + }, + ), + ), + )); + + final Finder linkFinder = find.byKey(linkKey); + expect( + tester.getSemantics(find.descendant( + of: linkFinder, + matching: find.byType(Semantics), + )), + matchesSemantics( + isLink: true, + hasTapAction: true, + identifier: 'test-link-43', + // linkUrl: 'https://foobar/example?q=1', + label: 'Link Text', + ), + ); + + semanticsHandle.dispose(); + }); + + testWidgets('handles clicks on semantic link with a button', + (WidgetTester tester) async { + final Uri uri = Uri.parse('/foobar'); + FollowLink? followLinkCallback; + + await tester.pumpWidget(MaterialApp( + routes: { + '/foobar': (BuildContext context) => const Text('Internal route'), + }, + home: WebLinkDelegate( + semanticsIdentifier: 'test-link-27', + TestLinkInfo( + uri: uri, + target: LinkTarget.blank, + builder: (BuildContext context, FollowLink? followLink) { + followLinkCallback = followLink; + return ElevatedButton( + onPressed: () {}, + child: const Text('My Button Link'), + ); + }, + ), + ), + )); + // Platform view creation happens asynchronously. + await tester.pumpAndSettle(); + await tester.pump(); + + final html.Element semanticsHost = + html.document.createElement('flt-semantics-host'); + html.document.body!.append(semanticsHost); + final html.Element semanticsAnchor = html.document.createElement('a') + ..setAttribute('id', 'flt-semantic-node-99') + ..setAttribute('flt-semantics-identifier', 'test-link-27') + ..setAttribute('href', '/foobar'); + semanticsHost.append(semanticsAnchor); + final html.Element semanticsContainer = + html.document.createElement('flt-semantics-container'); + semanticsAnchor.append(semanticsContainer); + final html.Element semanticsButton = + html.document.createElement('flt-semantics') + ..setAttribute('role', 'button') + ..textContent = 'My Button Link'; + semanticsContainer.append(semanticsButton); + + expect(pushedRouteNames, isEmpty); + expect(testPlugin.launches, isEmpty); + + await followLinkCallback!(); + // Click on the button (child of the anchor). + final html.Event event1 = _simulateClick(semanticsButton); + + expect(pushedRouteNames, ['/foobar']); + expect(testPlugin.launches, isEmpty); + expect(event1.defaultPrevented, isTrue); + pushedRouteNames.clear(); + + await followLinkCallback!(); + // Click on the anchor itself. + final html.Event event2 = _simulateClick(semanticsAnchor); + + expect(pushedRouteNames, ['/foobar']); + expect(testPlugin.launches, isEmpty); + expect(event2.defaultPrevented, isTrue); + }); + + testWidgets('handles clicks on semantic link with text', + (WidgetTester tester) async { + final Uri uri = Uri.parse('/foobar'); + FollowLink? followLinkCallback; + + await tester.pumpWidget(MaterialApp( + routes: { + '/foobar': (BuildContext context) => const Text('Internal route'), + }, + home: WebLinkDelegate( + semanticsIdentifier: 'test-link-71', + TestLinkInfo( + uri: uri, + target: LinkTarget.blank, + builder: (BuildContext context, FollowLink? followLink) { + followLinkCallback = followLink; + return GestureDetector( + onTap: () {}, + child: const Text('My Link'), + ); + }, + ), + ), + )); + // Platform view creation happens asynchronously. + await tester.pumpAndSettle(); + await tester.pump(); + + final html.Element semanticsHost = + html.document.createElement('flt-semantics-host'); + html.document.body!.append(semanticsHost); + final html.Element semanticsAnchor = html.document.createElement('a') + ..setAttribute('id', 'flt-semantic-node-99') + ..setAttribute('flt-semantics-identifier', 'test-link-71') + ..setAttribute('href', '/foobar') + ..textContent = 'My Text Link'; + semanticsHost.append(semanticsAnchor); + + expect(pushedRouteNames, isEmpty); + expect(testPlugin.launches, isEmpty); + + await followLinkCallback!(); + final html.Event event = _simulateClick(semanticsAnchor); + + expect(pushedRouteNames, ['/foobar']); + expect(testPlugin.launches, isEmpty); + expect(event.defaultPrevented, isTrue); + }); + + // TODO(mdebbar): Remove this test after the engine PR [1] makes it to stable. + // [1] https://github.com/flutter/engine/pull/52720 + testWidgets('handles clicks on (old) semantic link with a button', + (WidgetTester tester) async { + final Uri uri = Uri.parse('/foobar'); + FollowLink? followLinkCallback; + + await tester.pumpWidget(MaterialApp( + routes: { + '/foobar': (BuildContext context) => const Text('Internal route'), + }, + home: WebLinkDelegate(TestLinkInfo( + uri: uri, + target: LinkTarget.blank, + builder: (BuildContext context, FollowLink? followLink) { + followLinkCallback = followLink; + return const SizedBox(width: 100, height: 100); + }, + )), + )); + // Platform view creation happens asynchronously. + await tester.pumpAndSettle(); + await tester.pump(); + + final html.Element semanticsHost = + html.document.createElement('flt-semantics-host'); + html.document.body!.append(semanticsHost); + final html.Element semanticsAnchor = html.document.createElement('a') + ..setAttribute('id', 'flt-semantic-node-99') + ..setAttribute('href', '#'); + semanticsHost.append(semanticsAnchor); + final html.Element semanticsContainer = + html.document.createElement('flt-semantics-container'); + semanticsAnchor.append(semanticsContainer); + final html.Element semanticsButton = + html.document.createElement('flt-semantics') + ..setAttribute('role', 'button') + ..textContent = 'My Button'; + semanticsContainer.append(semanticsButton); + + expect(pushedRouteNames, isEmpty); + expect(testPlugin.launches, isEmpty); + + await followLinkCallback!(); + final html.Event event1 = _simulateClick(semanticsButton); + + // Before the changes land in the web engine, this will not trigger the + // link properly. + expect(pushedRouteNames, []); + expect(testPlugin.launches, isEmpty); + expect(event1.defaultPrevented, isFalse); + }); + + // TODO(mdebbar): Remove this test after the engine PR [1] makes it to stable. + // [1] https://github.com/flutter/engine/pull/52720 + testWidgets('handles clicks on (old) semantic link with text', + (WidgetTester tester) async { + final Uri uri = Uri.parse('/foobar'); + FollowLink? followLinkCallback; + + await tester.pumpWidget(MaterialApp( + routes: { + '/foobar': (BuildContext context) => const Text('Internal route'), + }, + home: WebLinkDelegate(TestLinkInfo( + uri: uri, + target: LinkTarget.blank, + builder: (BuildContext context, FollowLink? followLink) { + followLinkCallback = followLink; + return GestureDetector( + onTap: () {}, + child: const Text('My Link'), + ); + }, + )), + )); + // Platform view creation happens asynchronously. + await tester.pumpAndSettle(); + await tester.pump(); + + final html.Element semanticsHost = + html.document.createElement('flt-semantics-host'); + html.document.body!.append(semanticsHost); + final html.Element semanticsAnchor = html.document.createElement('a') + ..setAttribute('id', 'flt-semantic-node-99') + ..setAttribute('href', '#') + ..textContent = 'My Text Link'; + semanticsHost.append(semanticsAnchor); + + expect(pushedRouteNames, isEmpty); + expect(testPlugin.launches, isEmpty); + + await followLinkCallback!(); + final html.Event event = _simulateClick(semanticsAnchor); + + // Before the changes land in the web engine, this will not trigger the + // link properly. + expect(pushedRouteNames, []); + expect(testPlugin.launches, isEmpty); + expect(event.defaultPrevented, isFalse); + }); + }); +} + +List _findAllAnchors() { + final List foundAnchors = []; + final html.NodeList anchors = html.document.querySelectorAll('a'); + for (int i = 0; i < anchors.length; i++) { + final html.Element anchor = anchors.item(i)! as html.Element; + if (anchor.hasProperty(linkViewIdProperty.toJS).toDart) { + foundAnchors.add(anchor); + } + } + + return foundAnchors; +} + +html.Element _findSingleAnchor() { + return _findAllAnchors().single; +} + +html.MouseEvent _simulateClick(html.Element target, {bool metaKey = false}) { + // // Stop the browser from navigating away from the test suite. + // target.addEventListener( + // 'click', + // (html.Event e) { + // e.preventDefault(); + // }.toJS); + final html.MouseEvent mouseEvent = html.MouseEvent( + 'click', + html.MouseEventInit( + bubbles: true, + cancelable: true, + metaKey: metaKey, + ), + ); + LinkViewController.handleGlobalClick(event: mouseEvent, target: target); + return mouseEvent; +} + +html.KeyboardEvent _simulateKeydown(html.Element target, + {bool metaKey = false}) { + final html.KeyboardEvent keydownEvent = html.KeyboardEvent( + 'keydown', + html.KeyboardEventInit( + bubbles: true, + cancelable: true, + metaKey: metaKey, + // code: 'Space', + )); + LinkViewController.handleGlobalKeydown(event: keydownEvent); + return keydownEvent; } class TestLinkInfo extends LinkInfo { diff --git a/packages/url_launcher/url_launcher_web/example/pubspec.yaml b/packages/url_launcher/url_launcher_web/example/pubspec.yaml index a70f27951b9e..b734fade0f7e 100644 --- a/packages/url_launcher/url_launcher_web/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/example/pubspec.yaml @@ -2,8 +2,8 @@ name: regular_integration_tests publish_to: none environment: - sdk: ^3.4.0 - flutter: ">=3.22.0" + sdk: ^3.6.0 + flutter: ">=3.27.0" dependencies: flutter: diff --git a/packages/url_launcher/url_launcher_web/lib/src/link.dart b/packages/url_launcher/url_launcher_web/lib/src/link.dart index 3a286fb40749..3540ac97f518 100644 --- a/packages/url_launcher/url_launcher_web/lib/src/link.dart +++ b/packages/url_launcher/url_launcher_web/lib/src/link.dart @@ -28,20 +28,51 @@ typedef HtmlViewFactory = html.Element Function(int viewId); /// Factory that returns the link DOM element for each unique view id. HtmlViewFactory get linkViewFactory => LinkViewController._viewFactory; +/// The function used to push routes to the Flutter framework. +@visibleForTesting +Future Function(String) pushRouteToFrameworkFunction = + (String routeName) => pushRouteNameToFramework(null, routeName); + /// The delegate for building the [Link] widget on the web. /// /// It uses a platform view to render an anchor element in the DOM. class WebLinkDelegate extends StatefulWidget { /// Creates a delegate for the given [link]. - const WebLinkDelegate(this.link, {super.key}); + const WebLinkDelegate(this.link, {super.key, this.semanticsIdentifier}); /// Information about the link built by the app. final LinkInfo link; + /// A user-provided identifier to be as the [SemanticsProperties.identifier] + /// for the link. + /// + /// This identifier is optional and is only useful for testing purposes. + final String? semanticsIdentifier; + @override WebLinkDelegateState createState() => WebLinkDelegateState(); } +extension on Uri { + String getHref() { + if (hasScheme) { + // External URIs are not modified. + return toString(); + } + + if (ui_web.urlStrategy == null) { + // If there's no UrlStrategy, we leave the URI as is. + return toString(); + } + + // In case an internal uri is given, the uri must be properly encoded + // using the currently used UrlStrategy. + return ui_web.urlStrategy!.prepareExternalUrl(toString()); + } +} + +int _nextSemanticsIdentifier = 0; + /// The link delegate used on the web platform. /// /// For external URIs, it lets the browser do its thing. For app route names, it @@ -49,6 +80,15 @@ class WebLinkDelegate extends StatefulWidget { class WebLinkDelegateState extends State { late LinkViewController _controller; + late final String _semanticsIdentifier; + + @override + void initState() { + super.initState(); + _semanticsIdentifier = + widget.semanticsIdentifier ?? 'link-${_nextSemanticsIdentifier++}'; + } + @override void didUpdateWidget(WebLinkDelegate oldWidget) { super.didUpdateWidget(oldWidget); @@ -61,7 +101,7 @@ class WebLinkDelegateState extends State { } Future _followLink() { - LinkViewController.registerHitTest(_controller); + LinkViewController.onFollowLink(_controller.viewId); return Future.value(); } @@ -70,88 +110,273 @@ class WebLinkDelegateState extends State { return Stack( fit: StackFit.passthrough, children: [ - widget.link.builder( - context, - widget.link.isDisabled ? null : _followLink, - ), + _buildChild(context), Positioned.fill( - child: PlatformViewLink( - viewType: linkViewType, - onCreatePlatformView: (PlatformViewCreationParams params) { - _controller = LinkViewController.fromParams(params); - return _controller - ..setUri(widget.link.uri) - ..setTarget(widget.link.target); - }, - surfaceFactory: - (BuildContext context, PlatformViewController controller) { - return PlatformViewSurface( - controller: controller, - gestureRecognizers: const >{}, - hitTestBehavior: PlatformViewHitTestBehavior.transparent, - ); - }, - ), + child: _buildPlatformView(context), ), ], ); } + + Widget _buildChild(BuildContext context) { + return Semantics( + link: true, + identifier: _semanticsIdentifier, + linkUrl: widget.link.uri, + child: widget.link.builder( + context, + widget.link.isDisabled ? null : _followLink, + ), + ); + } + + Widget _buildPlatformView(BuildContext context) { + return ExcludeFocus( + child: ExcludeSemantics( + child: PlatformViewLink( + viewType: linkViewType, + onCreatePlatformView: (PlatformViewCreationParams params) { + _controller = + LinkViewController.fromParams(params, _semanticsIdentifier); + return _controller + ..setUri(widget.link.uri) + ..setTarget(widget.link.target); + }, + surfaceFactory: + (BuildContext context, PlatformViewController controller) { + return PlatformViewSurface( + controller: controller, + gestureRecognizers: const >{}, + hitTestBehavior: PlatformViewHitTestBehavior.transparent, + ); + }, + ), + ), + ); + } } final JSAny _useCapture = {'capture': true}.jsify()!; +/// Signature for the function that triggers a link. +typedef TriggerLinkCallback = void Function( + int viewId, html.MouseEvent? mouseEvent); + +/// Keeps track of the signals required to trigger a link. +/// +/// Currently, the signals required are: +/// +/// 1. A FollowLink signal. This signal indicates that a hit test for the link +/// was registered on the app/framework side. +/// +/// 2. A DOM event signal. This signal indicates that a click or keyboard event +/// was received on the link's corresponding DOM element. +/// +/// These signals can arrive in any order depending on how the user triggers +/// the link. +/// +/// Each signal may be accompanied by a view ID. The view IDs, when present, +/// must match in order for the trigger to be considered valid. +/// +/// The signals are reset after a certain delay to prevent them from getting +/// stale. The delay is specified by [staleTimeout]. +class LinkTriggerSignals { + /// Creates a [LinkTriggerSignals] instance that calls [triggerLink] when all + /// the signals are received within a [staleTimeout] duration. + LinkTriggerSignals({ + required this.triggerLink, + required this.staleTimeout, + }); + + /// The function to be called when all signals have been received and the link + /// is ready to be triggered. + final TriggerLinkCallback triggerLink; + + /// Specifies the duration after which the signals are considered stale. + /// + /// Signals have to arrive within [staleTimeout] duration between them to be + /// considered valid. If they don't, the signals are reset. + final Duration staleTimeout; + + bool get _hasAllSignals => _hasFollowLink && _hasDomEvent; + + int? get _viewId { + assert(!isViewIdMismatched); + return _viewIdFromFollowLink ?? _viewIdFromDomEvent; + } + + /// Triggers the link if all signals are ready and there's no view ID + /// mismatch. + /// + /// If the view IDs from the signals are mismatched, the signals are reset and + /// the browser is prevented from navigating. + /// + /// If the signals are ready, the link is triggered and the signals are reset. + void triggerLinkIfReady() { + if (isViewIdMismatched) { + // When view IDs from signals are mismatched, let's reset the signals and + // prevent the browser from navigating. + _mouseEvent?.preventDefault(); + reset(); + return; + } + + if (_hasAllSignals) { + triggerLink(_viewId!, _mouseEvent); + reset(); + } + } + + /// Handles a FollowLink signal from [viewId]. + void onFollowLink({required int viewId}) { + _hasFollowLink = true; + _viewIdFromFollowLink = viewId; + _didUpdate(); + } + + /// Handles a [mouseEvent] signal from a specific [viewId]. + /// + /// * [viewId] identifies the view where the Link widget was rendered to. It + /// is nullable because it cannot be determined for some DOM events. + /// * [mouseEvent] is the DOM MouseEvent that triggered this signal. It is + /// nullable because some signals may come from a keyboard event. + /// + /// If `mouseEvent` is not null, `viewId` becomes mandatory. If `viewId` is + /// not present in this case, a [StateError] is thrown. + void onMouseEvent({ + required int? viewId, + required html.MouseEvent? mouseEvent, + }) { + if (mouseEvent != null && viewId == null) { + throw StateError('`viewId` must be provided for mouse events'); + } + _hasDomEvent = true; + _viewIdFromDomEvent = viewId; + _mouseEvent = mouseEvent; + _didUpdate(); + } + + bool _hasFollowLink = false; + bool _hasDomEvent = false; + + int? _viewIdFromFollowLink; + int? _viewIdFromDomEvent; + + html.MouseEvent? _mouseEvent; + + /// Whether the view ID from various signals have a mismatch. + /// + /// When a signal's view ID is missing, it's not considered a mismatch. + bool get isViewIdMismatched { + if (_viewIdFromFollowLink == null || _viewIdFromDomEvent == null) { + // A missing view ID is not considered a mismatch. + return false; + } + + return _viewIdFromFollowLink != _viewIdFromDomEvent; + } + + Timer? _resetTimer; + + void _didUpdate() { + _resetTimer?.cancel(); + _resetTimer = Timer(staleTimeout, reset); + } + + /// Reset all signals to their initial state. + void reset() { + _resetTimer?.cancel(); + _resetTimer = null; + + _hasFollowLink = false; + _hasDomEvent = false; + + _viewIdFromFollowLink = null; + _viewIdFromDomEvent = null; + + _mouseEvent = null; + } +} + /// Controls link views. class LinkViewController extends PlatformViewController { /// Creates a [LinkViewController] instance with the unique [viewId]. - LinkViewController(this.viewId) { - if (_instances.isEmpty) { + LinkViewController(this.viewId, this._semanticsIdentifier) { + if (_instancesByViewId.isEmpty) { // This is the first controller being created, attach the global click // listener. - - // Why listen in the capture phase? - // - // To ensure we always receive the event even if the engine calls - // `stopPropagation`. - html.window - .addEventListener('keydown', _jsGlobalKeydownListener, _useCapture); - html.window.addEventListener('click', _jsGlobalClickListener); + _attachGlobalListeners(); } - _instances[viewId] = this; + _instancesByViewId[viewId] = this; + _instancesBySemanticsIdentifier[_semanticsIdentifier] = this; } /// Creates and initializes a [LinkViewController] instance with the given /// platform view [params]. factory LinkViewController.fromParams( PlatformViewCreationParams params, + String semanticsIdentifier, ) { final int viewId = params.id; - final LinkViewController controller = LinkViewController(viewId); + final LinkViewController controller = + LinkViewController(viewId, semanticsIdentifier); controller._initialize().then((_) { /// Because _initialize is async, it can happen that [LinkViewController.dispose] /// may get called before this `then` callback. /// Check that the `controller` that was created by this factory is not /// disposed before calling `onPlatformViewCreated`. - if (_instances[viewId] == controller) { + if (_instancesByViewId[viewId] == controller) { params.onPlatformViewCreated(viewId); } }); return controller; } - static final Map _instances = + static final Map _instancesByViewId = {}; + static final Map _instancesBySemanticsIdentifier = + {}; static html.Element _viewFactory(int viewId) { - return _instances[viewId]!._element; + return _instancesByViewId[viewId]!._element; } - static int? _hitTestedViewId; + static final LinkTriggerSignals _triggerSignals = LinkTriggerSignals( + triggerLink: _triggerLink, + staleTimeout: const Duration(milliseconds: 500), + ); static final JSFunction _jsGlobalKeydownListener = _onGlobalKeydown.toJS; static final JSFunction _jsGlobalClickListener = _onGlobalClick.toJS; + static void _attachGlobalListeners() { + // Why listen in the capture phase? + // + // To ensure we always receive the event even if the engine calls + // `stopPropagation`. + html.window + ..addEventListener('keydown', _jsGlobalKeydownListener, _useCapture) + ..addEventListener('click', _jsGlobalClickListener, _useCapture); + + // TODO(mdebbar): Cleanup the global listeners on hot restart. + // https://github.com/flutter/flutter/issues/148133 + } + + static void _detachGlobalListeners() { + html.window + ..removeEventListener('keydown', _jsGlobalKeydownListener, _useCapture) + ..removeEventListener('click', _jsGlobalClickListener, _useCapture); + } + static void _onGlobalKeydown(html.KeyboardEvent event) { + handleGlobalKeydown(event: event); + } + + /// Global keydown handler that's called for every keydown event on the + /// window. + @visibleForTesting + static void handleGlobalKeydown({required html.KeyboardEvent event}) { // Why not use `event.target`? // // Because the target is usually and not the element, so @@ -189,7 +414,7 @@ class LinkViewController extends PlatformViewController { // 2. The user presses the Enter key to trigger the link. // 3. The framework receives the Enter keydown event: // - The event is dispatched to the button widget. - // - The button widget calls `onPressed` and therefor `followLink`. + // - The button widget calls `onPressed` and therefore `followLink`. // - `followLink` calls `LinkViewController.registerHitTest`. // - `LinkViewController.registerHitTest` sets `_hitTestedViewId`. // 4. The `LinkViewController` also receives the keydown event: @@ -197,41 +422,65 @@ class LinkViewController extends PlatformViewController { // - If `_hitTestedViewId` is set, it means the app triggered the link. // - We navigate to the Link's URI. - // The keydown event is not directly associated with the target Link, so - // we need to look for the recently hit tested Link to handle the event. - if (_hitTestedViewId != null) { - _instances[_hitTestedViewId]?._onDomKeydown(); + if (_isModifierKey(event)) { + // Modifier keys (i.e. Shift, Ctrl, Alt, Meta) cannot trigger a Link. + return; } - // After the keyboard event has been received, clean up the hit test state - // so we can start fresh on the next event. - unregisterHitTest(); + + // The keydown event is not directly associated with the target Link, so + // we can't find the `viewId` from the event. + _triggerSignals.onMouseEvent(viewId: null, mouseEvent: null); + _triggerSignals.triggerLinkIfReady(); } static void _onGlobalClick(html.MouseEvent event) { - final int? viewId = getViewIdFromTarget(event); - _instances[viewId]?._onDomClick(event); - // After the DOM click event has been received, clean up the hit test state - // so we can start fresh on the next event. - unregisterHitTest(); + handleGlobalClick( + event: event, + target: event.target as html.Element?, + ); } - /// Call this method to indicate that a hit test has been registered for the - /// given [controller]. - /// - /// The [onClick] callback is invoked when the anchor element receives a - /// `click` from the browser. - static void registerHitTest(LinkViewController controller) { - _hitTestedViewId = controller.viewId; + /// Global click handler that's called for every click event on the window. + @visibleForTesting + static void handleGlobalClick({ + required html.MouseEvent event, + required html.Element? target, + }) { + // We only want to handle clicks that land on *our* links. That could be a + // platform view link or a semantics link. + final int? viewIdFromTarget = + _getViewIdFromLink(target) ?? _getViewIdFromSemanticsLink(target); + + if (viewIdFromTarget == null) { + // The click target was not one of our links, so we don't want to + // interfere with it. + // + // We also want to reset the signals to make sure we start fresh on the + // next click. + _triggerSignals.reset(); + return; + } + + _triggerSignals.onMouseEvent( + viewId: viewIdFromTarget, + mouseEvent: event, + ); + + _triggerSignals.triggerLinkIfReady(); } - /// Removes all information about previously registered hit tests. - static void unregisterHitTest() { - _hitTestedViewId = null; + /// Call this method to indicate that a hit test has been registered for the + /// given [viewId]. + static void onFollowLink(int viewId) { + _triggerSignals.onFollowLink(viewId: viewId); + _triggerSignals.triggerLinkIfReady(); } @override final int viewId; + final String _semanticsIdentifier; + late html.HTMLElement _element; Future _initialize() async { @@ -248,6 +497,9 @@ class LinkViewController extends PlatformViewController { // - https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attr-target _element.setAttribute('rel', 'noreferrer noopener'); + _element.setAttribute('aria-hidden', 'true'); + _element.setAttribute('tabIndex', '-1'); + final Map args = { 'id': viewId, 'viewType': linkViewType, @@ -255,49 +507,38 @@ class LinkViewController extends PlatformViewController { await SystemChannels.platform_views.invokeMethod('create', args); } - void _onDomKeydown() { - assert( - _hitTestedViewId == viewId, - 'Keydown event should only be handled by the hit tested Link', - ); - - if (_isExternalLink) { - // External links are not handled by the browser when triggered via a - // keydown, so we have to launch the url manually. - UrlLauncherPlatform.instance - .launchUrl(_uri.toString(), const LaunchOptions()); + /// Triggers the Link that has already received all the required signals. + /// + /// It also handles logic for external vs internal links, triggered by a mouse + /// vs keyboard event. + static void _triggerLink(int viewId, html.MouseEvent? mouseEvent) { + final LinkViewController controller = _instancesByViewId[viewId]!; + + if (mouseEvent != null && _isModifierKey(mouseEvent)) { + // When the click is accompanied by a modifier key (e.g. cmd+click or + // shift+click), we want to let the browser do its thing (e.g. open a new + // tab or a new window). return; } - // A uri that doesn't have a scheme is an internal route name. In this - // case, we push it via Flutter's navigation system instead of using - // `launchUrl`. - final String routeName = _uri.toString(); - pushRouteNameToFramework(null, routeName); - } - - void _onDomClick(html.MouseEvent event) { - final bool isHitTested = _hitTestedViewId == viewId; - if (!isHitTested) { - // There was no hit test registered for this click. This means the click - // landed on the anchor element but not on the underlying widget. In this - // case, we prevent the browser from following the click. - event.preventDefault(); - return; - } + if (controller._isExternalLink) { + if (mouseEvent == null) { + // When external links are triggered by keyboard, they are not handled by + // the browser. So we have to launch the url manually. + UrlLauncherPlatform.instance + .launchUrl(controller._uri.toString(), const LaunchOptions()); + } - if (_isExternalLink) { - // External links will be handled by the browser, so we don't have to do - // anything. + // When triggerd by a mouse event, external links will be handled by the + // browser, so we don't have to do anything. return; } - // A uri that doesn't have a scheme is an internal route name. In this - // case, we push it via Flutter's navigation system instead of letting the - // browser handle it. - event.preventDefault(); - final String routeName = _uri.toString(); - pushRouteNameToFramework(null, routeName); + // Internal links are pushed through Flutter's navigation system instead of + // letting the browser handle it. + mouseEvent?.preventDefault(); + final String routeName = controller._uri.toString(); + pushRouteToFrameworkFunction(routeName); } Uri? _uri; @@ -311,22 +552,20 @@ class LinkViewController extends PlatformViewController { if (uri == null) { _element.removeAttribute('href'); } else { - String href = uri.toString(); - // in case an internal uri is given, the url mus be properly encoded - // using the currently used [UrlStrategy] - if (!uri.hasScheme) { - href = ui_web.urlStrategy?.prepareExternalUrl(href) ?? href; - } - _element.setAttribute('href', href); + _element.setAttribute('href', uri.getHref()); } } + late LinkTarget _target; + String get _htmlTargetAttribute => _getHtmlTargetAttribute(_target); + /// Set the [LinkTarget] value for this link. void setTarget(LinkTarget target) { - _element.setAttribute('target', _getHtmlTarget(target)); + _target = target; + _element.setAttribute('target', _htmlTargetAttribute); } - String _getHtmlTarget(LinkTarget target) { + String _getHtmlTargetAttribute(LinkTarget target) { switch (target) { case LinkTarget.defaultTarget: case LinkTarget.self: @@ -342,6 +581,40 @@ class LinkViewController extends PlatformViewController { return '_self'; } + /// Finds the view ID in the Link's semantic element. + /// + /// Returns null if [target] is not a semantics element for one of our Links. + static int? _getViewIdFromSemanticsLink(html.Element? target) { + // TODO(mdebbar): The whole could be inside a shadow root. In that case, + // the target is always the shadow root (because we are listening on window). + if (target == null) { + return null; + } + if (!_isWithinSemanticsTree(target)) { + return null; + } + + final html.Element? semanticsLink = _getClosestSemanticsLink(target); + if (semanticsLink == null) { + return null; + } + + final String? semanticsIdentifier = + semanticsLink.getAttribute('flt-semantics-identifier'); + if (semanticsIdentifier == null) { + return null; + } + + final LinkViewController? controller = + _instancesBySemanticsIdentifier[semanticsIdentifier]; + if (controller == null) { + return null; + } + + semanticsLink.setAttribute('target', controller._htmlTargetAttribute); + return controller.viewId; + } + @override Future clearFocus() async { // Currently this does nothing on Flutter Web. @@ -356,49 +629,60 @@ class LinkViewController extends PlatformViewController { @override Future dispose() async { - assert(_instances[viewId] == this); - _instances.remove(viewId); - if (_instances.isEmpty) { - html.window.removeEventListener('click', _jsGlobalClickListener); - html.window.removeEventListener( - 'keydown', _jsGlobalKeydownListener, _useCapture); + assert(_instancesByViewId[viewId] == this); + assert(_instancesBySemanticsIdentifier[_semanticsIdentifier] == this); + + _instancesByViewId.remove(viewId); + _instancesBySemanticsIdentifier.remove(_semanticsIdentifier); + + if (_instancesByViewId.isEmpty) { + _detachGlobalListeners(); } await SystemChannels.platform_views.invokeMethod('dispose', viewId); } -} -/// Finds the view id of the DOM element targeted by the [event]. -int? getViewIdFromTarget(html.Event event) { - final html.Element? linkElement = getLinkElementFromTarget(event); - if (linkElement != null) { - return linkElement.getProperty(linkViewIdProperty.toJS).toDartInt; + /// Resets all link-related state. + @visibleForTesting + static Future debugReset() async { + _triggerSignals.reset(); + for (final LinkViewController instance in _instancesByViewId.values) { + await instance.dispose(); + } } - return null; } -/// Finds the targeted DOM element by the [event]. +/// Finds the view ID in the Link's platform view element. /// -/// It handles the case where the target element is inside a shadow DOM too. -html.Element? getLinkElementFromTarget(html.Event event) { - final html.EventTarget? target = event.target; - if (target != null && target is html.Element) { - if (isLinkElement(target)) { - return target; - } - if (target.shadowRoot != null) { - final html.Node? child = target.shadowRoot!.lastChild; - if (child != null && child is html.Element && isLinkElement(child)) { - return child; - } - } +/// Returns null if [target] is not a platform view of one of our Links. +int? _getViewIdFromLink(html.Element? target) { + final JSString linkViewIdPropertyJS = linkViewIdProperty.toJS; + if (target != null && target.tagName.toLowerCase() == 'a') { + return target.getProperty(linkViewIdPropertyJS)?.toDartInt; } return null; } -/// Checks if the given [element] is a link that was created by -/// [LinkViewController]. -bool isLinkElement(html.Element? element) { - return element != null && - element.tagName == 'A' && - element.hasProperty(linkViewIdProperty.toJS).toDart; +/// Whether [element] is within the semantics tree of a Flutter View. +bool _isWithinSemanticsTree(html.Element element) { + return element.closest('flt-semantics-host') != null; +} + +/// Returns the closest semantics link ancestor of the given [element]. +/// +/// If [element] itself is a link, it is returned. +html.Element? _getClosestSemanticsLink(html.Element element) { + assert(_isWithinSemanticsTree(element)); + return element.closest('a[id^="flt-semantic-node-"]'); +} + +bool _isModifierKey(html.Event event) { + // This method accepts both KeyboardEvent and MouseEvent but there's no common + // interface that contains the `ctrlKey`, `altKey`, `metaKey`, and `shiftKey` + // properties. So we have to cast the event to either `KeyboardEvent` or + // `MouseEvent` to access these properties. + // + // It's safe to cast both event types to `KeyboardEvent` because it's just + // JS-interop and has no concrete runtime type. + event as html.KeyboardEvent; + return event.ctrlKey || event.altKey || event.metaKey || event.shiftKey; } diff --git a/packages/url_launcher/url_launcher_web/pubspec.yaml b/packages/url_launcher/url_launcher_web/pubspec.yaml index 7b9068d19153..ee13f34f7523 100644 --- a/packages/url_launcher/url_launcher_web/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/pubspec.yaml @@ -2,11 +2,11 @@ name: url_launcher_web description: Web platform implementation of url_launcher repository: https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 2.3.3 +version: 2.4.0 environment: - sdk: ^3.4.0 - flutter: ">=3.22.0" + sdk: ^3.6.0 + flutter: ">=3.27.0" flutter: plugin: From 0c4eabea18e619fefcce048ac8aa1b2ee17b6a2e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Jan 2025 04:47:05 +0000 Subject: [PATCH 03/26] [dependabot]: Bump org.json:json from 20240303 to 20241224 in /packages/in_app_purchase/in_app_purchase_android/example/android/app (#8372) Bumps [org.json:json](https://github.com/douglascrockford/JSON-java) from 20240303 to 20241224.
Release notes

Sourced from org.json:json's releases.

20241224

Pull Request Description
#924 pre-release-20241224: updates for next release
#907 Fix a bug when calling JSONArray.addAll() with Collection as Object
#894 update jsonpath from 2.4.0 to 2.9.0
#889 Fixed non-idempotent unit tests in JSONObjectTest
#879 Extend syntax error information
#877, #886, #888, #911, #921 Strict mode opt-in feature
#876 Remove jsonparserconfig ctor- just use withOverwriteDuplicateKey
#867 Improve performance in toString()
Changelog

Sourced from org.json:json's changelog.

Release history:

JSON-java releases can be found by searching the Maven repository for groupId "org.json" and artifactId "json". For example: https://search.maven.org/search?q=g:org.json%20AND%20a:json&core=gav

20241224....Strict mode opt-in feature, and recent commits.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.json:json&package-manager=gradle&previous-version=20240303&new-version=20241224)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- .../in_app_purchase_android/example/android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/in_app_purchase/in_app_purchase_android/example/android/app/build.gradle b/packages/in_app_purchase/in_app_purchase_android/example/android/app/build.gradle index 2d17cfc8b7f2..3a17faebb508 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/android/app/build.gradle +++ b/packages/in_app_purchase/in_app_purchase_android/example/android/app/build.gradle @@ -108,7 +108,7 @@ dependencies { implementation 'com.android.billingclient:billing:6.1.0' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:5.1.1' - testImplementation 'org.json:json:20240303' + testImplementation 'org.json:json:20241224' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' } From db008e0c93299392c0d06e9ec944a305f857be7a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Jan 2025 04:48:52 +0000 Subject: [PATCH 04/26] [dependabot]: Bump the androidx group across 3 directories with 1 update (#8329) Bumps the androidx group with 1 update in the /packages/google_maps_flutter/google_maps_flutter_android/android directory: androidx.annotation:annotation. Bumps the androidx group with 1 update in the /packages/image_picker/image_picker_android/android directory: androidx.annotation:annotation. Bumps the androidx group with 1 update in the /packages/in_app_purchase/in_app_purchase_android/android directory: androidx.annotation:annotation. Updates `androidx.annotation:annotation` from 1.7.0 to 1.9.1 Updates `androidx.annotation:annotation` from 1.9.0 to 1.9.1 Updates `androidx.annotation:annotation` from 1.9.0 to 1.9.1 Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
--- .../google_maps_flutter_android/CHANGELOG.md | 4 ++++ .../google_maps_flutter_android/android/build.gradle | 2 +- .../google_maps_flutter_android/pubspec.yaml | 2 +- packages/image_picker/image_picker_android/CHANGELOG.md | 4 ++++ .../image_picker/image_picker_android/android/build.gradle | 2 +- packages/image_picker/image_picker_android/pubspec.yaml | 2 +- packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md | 4 ++++ .../in_app_purchase_android/android/build.gradle | 2 +- packages/in_app_purchase/in_app_purchase_android/pubspec.yaml | 2 +- 9 files changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md index 88b105370005..e18bd40228cf 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.14.12 + +* Updates androidx.annotation:annotation to 1.9.1. + ## 2.14.11 * Updates internal Pigeon API to use newer features. diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle b/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle index 4a1dc709b445..a0fed3d94588 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle @@ -36,7 +36,7 @@ android { } dependencies { - implementation "androidx.annotation:annotation:1.7.0" + implementation "androidx.annotation:annotation:1.9.1" implementation 'com.google.android.gms:play-services-maps:18.2.0' implementation 'com.google.maps.android:android-maps-utils:3.6.0' androidTestImplementation 'androidx.test:runner:1.2.0' diff --git a/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml index 19c0751c5db5..19db56e19133 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_android description: Android implementation of the google_maps_flutter plugin. repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 2.14.11 +version: 2.14.12 environment: sdk: ^3.5.0 diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md index c1f1a6549d80..a7050dde8ef8 100644 --- a/packages/image_picker/image_picker_android/CHANGELOG.md +++ b/packages/image_picker/image_picker_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.12+20 + +* Updates androidx.annotation:annotation to 1.9.1. + ## 0.8.12+19 * Updates androidx.activity:activity to 1.9.3. diff --git a/packages/image_picker/image_picker_android/android/build.gradle b/packages/image_picker/image_picker_android/android/build.gradle index be599aac336a..7f4fd20bb475 100644 --- a/packages/image_picker/image_picker_android/android/build.gradle +++ b/packages/image_picker/image_picker_android/android/build.gradle @@ -36,7 +36,7 @@ android { } dependencies { implementation 'androidx.core:core:1.13.1' - implementation 'androidx.annotation:annotation:1.9.0' + implementation 'androidx.annotation:annotation:1.9.1' implementation 'androidx.exifinterface:exifinterface:1.3.7' implementation 'androidx.activity:activity:1.9.3' diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml index e2c9d232e01d..14735e3b7df5 100755 --- a/packages/image_picker/image_picker_android/pubspec.yaml +++ b/packages/image_picker/image_picker_android/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_android description: Android implementation of the image_picker plugin. repository: https://github.com/flutter/packages/tree/main/packages/image_picker/image_picker_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.12+19 +version: 0.8.12+20 environment: sdk: ^3.5.0 diff --git a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md index bfa7d76bca0d..36b7371e7b2b 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.6+13 + +* Updates androidx.annotation:annotation to 1.9.1. + ## 0.3.6+12 * Updates README to remove contributor-focused documentation. diff --git a/packages/in_app_purchase/in_app_purchase_android/android/build.gradle b/packages/in_app_purchase/in_app_purchase_android/android/build.gradle index 7a790d8d8406..5a803f676002 100644 --- a/packages/in_app_purchase/in_app_purchase_android/android/build.gradle +++ b/packages/in_app_purchase/in_app_purchase_android/android/build.gradle @@ -59,7 +59,7 @@ android { } dependencies { - implementation 'androidx.annotation:annotation:1.9.0' + implementation 'androidx.annotation:annotation:1.9.1' implementation 'com.android.billingclient:billing:6.2.0' testImplementation 'junit:junit:4.13.2' testImplementation 'org.json:json:20240303' diff --git a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml index 7e18fa0722a6..0932606e7ae8 100644 --- a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_android description: An implementation for the Android platform of the Flutter `in_app_purchase` plugin. This uses the Android BillingClient APIs. repository: https://github.com/flutter/packages/tree/main/packages/in_app_purchase/in_app_purchase_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.3.6+12 +version: 0.3.6+13 environment: sdk: ^3.5.0 From 0c6bb1712a238c4e783ddf37cac48d73b240d636 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Jan 2025 05:12:12 +0000 Subject: [PATCH 05/26] [dependabot]: Bump io.mockk:mockk from 1.13.13 to 1.13.14 in /packages/shared_preferences/shared_preferences_android/android (#8358) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [io.mockk:mockk](https://github.com/mockk/mockk) from 1.13.13 to 1.13.14.
Release notes

Sourced from io.mockk:mockk's releases.

1.13.14

What's Changed

New Contributors

Full Changelog: https://github.com/mockk/mockk/compare/1.13.13...1.13.14

Commits
  • 1b3b01e Version bump
  • e7ec061 Merge pull request #1331 from kpadhiamex/1330-fix-for-relaxed-mocking-simple-...
  • f3a12a2 Merge branch 'master' into 1330-fix-for-relaxed-mocking-simple-value-when-pro...
  • 2264808 Reenable java 17
  • 0ebbb0e Run instrumented test on ubuntu-latest
  • 840f4e7 Change the instrumented test runner
  • dd19ddd Upgrade the macos runner for instrumented tests and add kotlin 2.1.0
  • 12f9fed Added issue link in the test.
  • ede2eb4 Issue: mockk/mockk#1330 fix for relaxed mocking sim...
  • 79abc96 Merge pull request #1314 from VasilisDrettas-tomtom/bugfix/nullable-value-cla...
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=io.mockk:mockk&package-manager=gradle&previous-version=1.13.13&new-version=1.13.14)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- .../shared_preferences_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared_preferences/shared_preferences_android/android/build.gradle b/packages/shared_preferences/shared_preferences_android/android/build.gradle index 9d165eaf3e43..dbef92ea6d81 100644 --- a/packages/shared_preferences/shared_preferences_android/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/android/build.gradle @@ -67,7 +67,7 @@ android { testImplementation 'androidx.test.ext:junit-ktx:1.2.1' testImplementation 'org.robolectric:robolectric:4.12.1' testImplementation 'org.mockito:mockito-inline:5.2.0' - testImplementation 'io.mockk:mockk:1.13.13' + testImplementation 'io.mockk:mockk:1.13.14' } From bc4b44d8c530e636502e47d149721a36f1dc0155 Mon Sep 17 00:00:00 2001 From: Anay Wadhera Date: Fri, 10 Jan 2025 08:20:24 -0500 Subject: [PATCH 06/26] [url_launcher_windows] Correct logging url (#8107) This CL fixes the URL being logged to be unescaped one if applicable instead of the original input URL. Fixes [#159009](https://github.com/flutter/flutter/issues/159009) --- .../url_launcher_windows/CHANGELOG.md | 3 ++- .../url_launcher_windows/pubspec.yaml | 2 +- .../test/url_launcher_windows_test.cpp | 24 +++++++++++++++++++ .../windows/url_launcher_plugin.cpp | 17 ++++++------- 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/packages/url_launcher/url_launcher_windows/CHANGELOG.md b/packages/url_launcher/url_launcher_windows/CHANGELOG.md index 05a2acbd6b16..c5fafdf4b435 100644 --- a/packages/url_launcher/url_launcher_windows/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_windows/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 3.1.4 * Updates minimum supported SDK version to Flutter 3.22/Dart 3.4. +* Fixes an issue where the URL logged would not be unescaped on failure. ## 3.1.3 diff --git a/packages/url_launcher/url_launcher_windows/pubspec.yaml b/packages/url_launcher/url_launcher_windows/pubspec.yaml index 5c81c7786754..51e3e46a28b0 100644 --- a/packages/url_launcher/url_launcher_windows/pubspec.yaml +++ b/packages/url_launcher/url_launcher_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_windows description: Windows implementation of the url_launcher plugin. repository: https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 3.1.3 +version: 3.1.4 environment: sdk: ^3.4.0 diff --git a/packages/url_launcher/url_launcher_windows/windows/test/url_launcher_windows_test.cpp b/packages/url_launcher/url_launcher_windows/windows/test/url_launcher_windows_test.cpp index db46e1a4a983..ac933e2269e2 100644 --- a/packages/url_launcher/url_launcher_windows/windows/test/url_launcher_windows_test.cpp +++ b/packages/url_launcher/url_launcher_windows/windows/test/url_launcher_windows_test.cpp @@ -24,6 +24,7 @@ using flutter::EncodableMap; using flutter::EncodableValue; using ::testing::_; using ::testing::DoAll; +using ::testing::HasSubstr; using ::testing::Pointee; using ::testing::Return; using ::testing::SetArgPointee; @@ -160,5 +161,28 @@ TEST(UrlLauncherPlugin, LaunchUTF8EncodedFileURLSuccess) { EXPECT_TRUE(result.value()); } +TEST(UrlLauncherPlugin, LaunchUTF8LogsUnescapedOnFail) { + std::unique_ptr system = std::make_unique(); + + // Return a failure value (<32) from launching. + EXPECT_CALL( + *system, + ShellExecuteW( + _, StrEq(L"open"), + // 家の管理/スキャナ"), + StrEq( + L"file:///G:/\x5bb6\x306e\x7ba1\x7406/\x30b9\x30ad\x30e3\x30ca"), + _, _, _)) + .WillOnce(Return(reinterpret_cast(0))); + + UrlLauncherPlugin plugin(std::move(system)); + ErrorOr result = plugin.LaunchUrl( + "file:///G:/%E5%AE%B6%E3%81%AE%E7%AE%A1%E7%90%86/" + "%E3%82%B9%E3%82%AD%E3%83%A3%E3%83%8A"); + + ASSERT_TRUE(result.has_error()); + EXPECT_THAT(result.error().message(), HasSubstr("家の管理/スキャナ")); +} + } // namespace test } // namespace url_launcher_windows diff --git a/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.cpp b/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.cpp index b737b4579b8b..373e1f361d6a 100644 --- a/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.cpp +++ b/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.cpp @@ -99,7 +99,7 @@ ErrorOr UrlLauncherPlugin::CanLaunchUrl(const std::string& url) { } ErrorOr UrlLauncherPlugin::LaunchUrl(const std::string& url) { - std::wstring url_wide; + std::string url_to_open; if (url.find("file:") == 0) { // ShellExecuteW does not process %-encoded UTF8 strings in file URLs. DWORD unescaped_len = 0; @@ -108,14 +108,15 @@ ErrorOr UrlLauncherPlugin::LaunchUrl(const std::string& url) { &unescaped_len, URL_UNESCAPE_INPLACE))) { return FlutterError("open_error", "Failed to unescape file URL"); } - url_wide = Utf16FromUtf8(unescaped_url); + url_to_open = unescaped_url; } else { - url_wide = Utf16FromUtf8(url); + url_to_open = url; } - int status = static_cast(reinterpret_cast( - system_apis_->ShellExecuteW(nullptr, TEXT("open"), url_wide.c_str(), - nullptr, nullptr, SW_SHOWNORMAL))); + int status = + static_cast(reinterpret_cast(system_apis_->ShellExecuteW( + nullptr, TEXT("open"), Utf16FromUtf8(url_to_open).c_str(), nullptr, + nullptr, SW_SHOWNORMAL))); // Per ::ShellExecuteW documentation, anything >32 indicates success. if (status <= 32) { @@ -126,8 +127,8 @@ ErrorOr UrlLauncherPlugin::LaunchUrl(const std::string& url) { return false; } std::ostringstream error_message; - error_message << "Failed to open " << url << ": ShellExecute error code " - << status; + error_message << "Failed to open " << url_to_open + << ": ShellExecute error code " << status; return FlutterError("open_error", error_message.str()); } return true; From 66a4d857af0511e5fb8fa9ff1126985474154427 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Jan 2025 17:46:01 +0000 Subject: [PATCH 07/26] [shared_pref]: Bump androidx.datastore:datastore from 1.0.0 to 1.1.1 in /packages/shared_preferences/shared_preferences_android/android (#7306) Bumps androidx.datastore:datastore from 1.0.0 to 1.1.1. [![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=androidx.datastore:datastore&package-manager=gradle&previous-version=1.0.0&new-version=1.1.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) You can trigger a rebase of this PR by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
> **Note** > Automatic rebases have been disabled on this pull request as it has been open for over 30 days. --- .../shared_preferences_android/CHANGELOG.md | 4 ++++ .../shared_preferences_android/android/build.gradle | 6 +++--- .../shared_preferences_android/pubspec.yaml | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md index e8abda3d2628..b47688db6035 100644 --- a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.4.1 + +* Bumps kotlin version to 1.9.10 androidx.datastore:datastore from 1.0.0 to 1.1.1. + ## 2.4.0 * Adds `SharedPreferences` support within `SharedPreferencesAsyncAndroid` API. diff --git a/packages/shared_preferences/shared_preferences_android/android/build.gradle b/packages/shared_preferences/shared_preferences_android/android/build.gradle index dbef92ea6d81..8a0b88a8c0ff 100644 --- a/packages/shared_preferences/shared_preferences_android/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/android/build.gradle @@ -2,7 +2,7 @@ group 'io.flutter.plugins.sharedpreferences' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.7.10' + ext.kotlin_version = '1.9.10' repositories { google() mavenCentral() @@ -59,8 +59,8 @@ android { disable 'AndroidGradlePluginVersion', 'InvalidPackage', 'GradleDependency', 'NewerVersionAvailable' } dependencies { - implementation 'androidx.datastore:datastore:1.0.0' - implementation 'androidx.datastore:datastore-preferences:1.0.0' + implementation 'androidx.datastore:datastore:1.1.1' + implementation 'androidx.datastore:datastore-preferences:1.1.1' implementation 'androidx.preference:preference:1.2.1' testImplementation 'junit:junit:4.13.2' testImplementation 'androidx.test:core-ktx:1.5.0' diff --git a/packages/shared_preferences/shared_preferences_android/pubspec.yaml b/packages/shared_preferences/shared_preferences_android/pubspec.yaml index 594d763c6f8a..f0156474ca21 100644 --- a/packages/shared_preferences/shared_preferences_android/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_android/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_android description: Android implementation of the shared_preferences plugin repository: https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.4.0 +version: 2.4.1 environment: sdk: ^3.5.0 From 9e092d579429e3a674138ec5b6f2afcbd0cf79a8 Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Fri, 10 Jan 2025 15:36:08 -0800 Subject: [PATCH 08/26] Fix dependabot test-dependencies group io.mockk regex (#8406) After #8100, https://github.com/flutter/packages/pull/8358 and https://github.com/flutter/packages/pull/8357 should have been grouped into one dependabot PR to bump io.mockk:mockk to 1.13.14 across all packages. Fix the regex to match `io.mockk:mockk`. --- .github/dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5ce98de58117..f66a07d3a06c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -109,7 +109,7 @@ updates: test-dependencies: patterns: - "androidx.test:*" - - "io.mockk:mockk:*" + - "io.mockk:*" - "junit:junit" - "org.mockito:*" - "org.robolectric:*" From d58b6ed426f5efdad9a3d5a73d144f173ce54d1c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Jan 2025 23:39:04 +0000 Subject: [PATCH 09/26] [dependabot]: Bump io.mockk:mockk from 1.13.13 to 1.13.14 in /packages/pigeon/platform_tests/test_plugin/android (#8357) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [io.mockk:mockk](https://github.com/mockk/mockk) from 1.13.13 to 1.13.14.
Release notes

Sourced from io.mockk:mockk's releases.

1.13.14

What's Changed

New Contributors

Full Changelog: https://github.com/mockk/mockk/compare/1.13.13...1.13.14

Commits
  • 1b3b01e Version bump
  • e7ec061 Merge pull request #1331 from kpadhiamex/1330-fix-for-relaxed-mocking-simple-...
  • f3a12a2 Merge branch 'master' into 1330-fix-for-relaxed-mocking-simple-value-when-pro...
  • 2264808 Reenable java 17
  • 0ebbb0e Run instrumented test on ubuntu-latest
  • 840f4e7 Change the instrumented test runner
  • dd19ddd Upgrade the macos runner for instrumented tests and add kotlin 2.1.0
  • 12f9fed Added issue link in the test.
  • ede2eb4 Issue: mockk/mockk#1330 fix for relaxed mocking sim...
  • 79abc96 Merge pull request #1314 from VasilisDrettas-tomtom/bugfix/nullable-value-cla...
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=io.mockk:mockk&package-manager=gradle&previous-version=1.13.13&new-version=1.13.14)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- packages/pigeon/platform_tests/test_plugin/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pigeon/platform_tests/test_plugin/android/build.gradle b/packages/pigeon/platform_tests/test_plugin/android/build.gradle index 348ded6b8f8c..cae127bd86e2 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/build.gradle +++ b/packages/pigeon/platform_tests/test_plugin/android/build.gradle @@ -66,7 +66,7 @@ android { dependencies { testImplementation 'junit:junit:4.+' - testImplementation "io.mockk:mockk:1.13.13" + testImplementation "io.mockk:mockk:1.13.14" // org.jetbrains.kotlin:kotlin-bom artifact purpose is to align kotlin stdlib and related code versions. // See: https://youtrack.jetbrains.com/issue/KT-55297/kotlin-stdlib-should-declare-constraints-on-kotlin-stdlib-jdk8-and-kotlin-stdlib-jdk7 implementation(platform("org.jetbrains.kotlin:kotlin-bom:2.0.21")) From edcb1eba42027f3a9265b246452724d577abd578 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Jan 2025 23:52:37 +0000 Subject: [PATCH 10/26] [dependabot]: Bump org.json:json from 20241224 to 20250107 in /packages/in_app_purchase/in_app_purchase/example/android/app (#8410) Bumps [org.json:json](https://github.com/douglascrockford/JSON-java) from 20241224 to 20250107.
Release notes

Sourced from org.json:json's releases.

20250107

Pull Request Description
#930 pre-release-20250107: updates for next release
#929 restore moditect in pom.xml
Changelog

Sourced from org.json:json's changelog.

Release history:

JSON-java releases can be found by searching the Maven repository for groupId "org.json" and artifactId "json". For example: https://search.maven.org/search?q=g:org.json%20AND%20a:json&core=gav

20250107....Restore moditect in pom.xml

20241224....Strict mode opt-in feature, and recent commits. This release does not contain module-info.class. It is not recommended if you need this feature.

20240303 Revert optLong/getLong changes, and recent commits.

20240205 Recent commits.

20231013 First release with minimum Java version 1.8. Recent commits, including fixes for CVE-2023-5072.

20230618 Final release with Java 1.6 compatibility. Future releases will require Java 1.8 or greater.

20230227 Fix for CVE-2022-45688 and recent commits

20220924 New License - public domain, and some minor updates

20220320 Wrap StackOverflow with JSONException

20211205 Recent commits and some bug fixes for similar()

20210307 Recent commits and potentially breaking fix to JSONPointer

20201115 Recent commits and first release after project structure change

20200518 Recent commits and snapshot before project structure change

20190722 Recent commits

20180813 POM change to include Automatic-Module-Name (#431) JSONObject(Map) now throws an exception if any of a map keys are null (#405)

20180130 Recent commits

20171018 Checkpoint for recent commits.

20170516 Roll up recent commits.

20160810 Revert code that was breaking opt*() methods.

20160807 This release contains a bug in the JSONObject.opt*() and JSONArray.opt*() methods, it is not recommended for use. Java 1.6 compatability fixed, JSONArray.toList() and JSONObject.toMap(), </tr></table>

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.json:json&package-manager=gradle&previous-version=20241224&new-version=20250107)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- .../in_app_purchase/example/android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/in_app_purchase/in_app_purchase/example/android/app/build.gradle b/packages/in_app_purchase/in_app_purchase/example/android/app/build.gradle index 520bfb96c61e..3bb79c4d934c 100644 --- a/packages/in_app_purchase/in_app_purchase/example/android/app/build.gradle +++ b/packages/in_app_purchase/in_app_purchase/example/android/app/build.gradle @@ -108,7 +108,7 @@ dependencies { implementation 'com.android.billingclient:billing:3.0.2' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:5.0.0' - testImplementation 'org.json:json:20241224' + testImplementation 'org.json:json:20250107' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' } From ce3dfa51e327d6399dd65a9b1a0497f835e0b06e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Jan 2025 23:58:22 +0000 Subject: [PATCH 11/26] [dependabot]: Bump org.json:json from 20240303 to 20250107 in /packages/in_app_purchase/in_app_purchase_android/android (#8413) Bumps [org.json:json](https://github.com/douglascrockford/JSON-java) from 20240303 to 20250107.
Release notes

Sourced from org.json:json's releases.

20250107

Pull Request Description
#930 pre-release-20250107: updates for next release
#929 restore moditect in pom.xml

20241224

Pull Request Description
#924 pre-release-20241224: updates for next release
#907 Fix a bug when calling JSONArray.addAll() with Collection as Object
#894 update jsonpath from 2.4.0 to 2.9.0
#889 Fixed non-idempotent unit tests in JSONObjectTest
#879 Extend syntax error information
#877, #886, #888, #911, #921 Strict mode opt-in feature
#876 Remove jsonparserconfig ctor- just use withOverwriteDuplicateKey
#867 Improve performance in toString()
Changelog

Sourced from org.json:json's changelog.

Release history:

JSON-java releases can be found by searching the Maven repository for groupId "org.json" and artifactId "json". For example: https://search.maven.org/search?q=g:org.json%20AND%20a:json&core=gav

20250107....Restore moditect in pom.xml

20241224....Strict mode opt-in feature, and recent commits. This release does not contain module-info.class.
It is not recommended if you need this feature.

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.json:json&package-manager=gradle&previous-version=20240303&new-version=20250107)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- .../in_app_purchase_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/in_app_purchase/in_app_purchase_android/android/build.gradle b/packages/in_app_purchase/in_app_purchase_android/android/build.gradle index 5a803f676002..29e12ba9d8ad 100644 --- a/packages/in_app_purchase/in_app_purchase_android/android/build.gradle +++ b/packages/in_app_purchase/in_app_purchase_android/android/build.gradle @@ -62,7 +62,7 @@ dependencies { implementation 'androidx.annotation:annotation:1.9.1' implementation 'com.android.billingclient:billing:6.2.0' testImplementation 'junit:junit:4.13.2' - testImplementation 'org.json:json:20240303' + testImplementation 'org.json:json:20250107' testImplementation 'org.mockito:mockito-core:5.4.0' testImplementation 'androidx.test:core:1.5.0' testImplementation 'org.robolectric:robolectric:4.10.3' From f174de605e878c430d586812beb09a1da89353a1 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 10 Jan 2025 19:02:01 -0500 Subject: [PATCH 12/26] Roll Flutter from 4b23b8182888 to 864d4f59dde0 (50 revisions) (#8408) Roll Flutter from 4b23b8182888 to 864d4f59dde0 (50 revisions) https://github.com/flutter/flutter/compare/4b23b8182888...864d4f59dde0 2025-01-10 magder@google.com Mark complex_layout_scroll_perf_macos__timeline_summary unflaky (flutter/flutter#160997) 2025-01-10 magder@google.com Mark hello_world_macos__compile unflaky (flutter/flutter#160998) 2025-01-10 magder@google.com Mark animated_complex_opacity_perf_macos__e2e_summary unflaky (flutter/flutter#160996) 2025-01-10 magder@google.com Mark integration_ui_test_test_macos unflaky (flutter/flutter#160999) 2025-01-10 magder@google.com Mark hot_mode_dev_cycle_macos_target__benchmark unflaky (flutter/flutter#161000) 2025-01-10 matanlurey@users.noreply.github.com Add a virtual-display (VD) platform view test, and refactor tests a bit. (flutter/flutter#161349) 2025-01-10 matanlurey@users.noreply.github.com Remove `CIRRUS_TASK_NAME` from what I can tell, is always omitted on `LUCI` (flutter/flutter#161391) 2025-01-10 matanlurey@users.noreply.github.com Replace the always omitted `CPU` environment variable with `numberOfProcessors`. (flutter/flutter#161392) 2025-01-10 matanlurey@users.noreply.github.com We no longer have a separate engine repo. (flutter/flutter#161400) 2025-01-10 goderbauer@google.com Update Style-guide-for-Flutter-repo.md (flutter/flutter#161344) 2025-01-09 chris@bracken.jp integration_test: Add gitignore of golden image (flutter/flutter#161404) 2025-01-09 goderbauer@google.com Fix link to engine docs in CONTRIBUTING.md (flutter/flutter#161401) 2025-01-09 engine-flutter-autoroll@skia.org Roll Packages from 3fc6b7ace3ff to 65547511c004 (11 revisions) (flutter/flutter#161379) 2025-01-09 32538273+ValentinVignal@users.noreply.github.com Add `mouseCursor` parameter to `ReorderableListView` (flutter/flutter#160246) 2025-01-09 matanlurey@users.noreply.github.com Remove Cirrus CI from Flutter goldens. (flutter/flutter#161396) 2025-01-09 lsaudon@gmail.com remove`useMaterial3: true,` in from the template (flutter/flutter#160525) 2025-01-09 matanlurey@users.noreply.github.com Remove `accept_android_sdk_licenses.sh`, which appears unused. (flutter/flutter#161388) 2025-01-09 a-siva@users.noreply.github.com Roll Dart to Version 3.7.0-312.0.dev (flutter/flutter#161394) 2025-01-09 fluttergithubbot@gmail.com Marks Linux analyzer_benchmark to be flaky (flutter/flutter#161307) 2025-01-09 jonahwilliams@google.com [Impeller] fix scaling of trampoline import of GLES textures into Vulkan. (flutter/flutter#161331) 2025-01-09 58529443+srujzs@users.noreply.github.com Support DDC library bundle format and remove support for DDC module format (flutter/flutter#161276) 2025-01-09 matanlurey@users.noreply.github.com Remove seemingly stale web Cirrus and "Web Installer" instructions (flutter/flutter#161389) 2025-01-09 matanlurey@users.noreply.github.com Proposal to deprecate `webGoldenComparator`. (flutter/flutter#161196) 2025-01-09 nate.w5687@gmail.com `ImplicitlyAnimatedWidgetState` code cleanup (flutter/flutter#160567) 2025-01-09 matanlurey@users.noreply.github.com Exclude `*texture*` as matching for `a: text input` (flutter/flutter#161354) 2025-01-09 jonahwilliams@google.com [Impeller] add opt in flag for SurfaceControl testing. (flutter/flutter#161353) 2025-01-09 a-siva@users.noreply.github.com Roll Dart to Version 3.7.0-307.0.dev (flutter/flutter#161278) 2025-01-09 goderbauer@google.com remove formatter from snippet tool (flutter/flutter#161347) 2025-01-09 robert.ancell@canonical.com Refactor keyboard manager tests (flutter/flutter#160637) 2025-01-09 jonahwilliams@google.com [flutter_tools] ignore viewpost ime and samsung spam messages. (flutter/flutter#161199) 2025-01-09 737941+loic-sharma@users.noreply.github.com Revert "[SwiftPM] Add separate feature flag for the app migration (#158897)" (flutter/flutter#161342) 2025-01-09 flar@google.com git ignore .ccls-cache (flutter/flutter#161340) 2025-01-08 98614782+auto-submit[bot]@users.noreply.github.com Reverts "[SwiftPM] Turn on by default (#161275)" (flutter/flutter#161339) 2025-01-08 jmccandless@google.com Update engine instructions for monorepo (flutter/flutter#161184) 2025-01-08 jonahwilliams@google.com [Impeller] re-enable Adreno 630 (flutter/flutter#161287) 2025-01-08 737941+loic-sharma@users.noreply.github.com [SwiftPM] Turn on by default (flutter/flutter#161275) 2025-01-08 matanlurey@users.noreply.github.com Make the encoding of a `YamlNode` to a `String` more explicit. (flutter/flutter#161270) 2025-01-08 jacksongardner@google.com Normalize the translation column of the color matrix. (flutter/flutter#161109) 2025-01-08 matanlurey@users.noreply.github.com Rename `native_driver` to `android_{driver_extensions|engine_test}` (flutter/flutter#161263) 2025-01-08 jonahwilliams@google.com [Impeller] reland: fix porterduff shader and handle optimized out texture binding in GLES backend. (flutter/flutter#161326) 2025-01-08 jessiewong401@gmail.com Updating AVD Dependency for Android 28 Emulator (flutter/flutter#160978) 2025-01-08 69054810+M97Chahboun@users.noreply.github.com Adds onHover and onLongPress to IconButton widget (flutter/flutter#160032) 2025-01-08 fluttergithubbot@gmail.com Marks Linux linux_desktop_impeller to be unflaky (flutter/flutter#161302) 2025-01-08 98614782+auto-submit[bot]@users.noreply.github.com Reverts "[Impeller] porter duff workarounds for Adreno GPU. (#161273)" (flutter/flutter#161318) 2025-01-08 victorsanniay@gmail.com Revert "fixed keyboardDismissBehavior on scroll without a drag" (flutter/flutter#161277) 2025-01-08 jiahaog@users.noreply.github.com Revert "use uuid from package:uuid instead of from package:usage" (flutter/flutter#161292) ... --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 126bb7a8a02f..85c1173b0833 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -4b23b8182888d5d94645d8f97cdcc79800e44a9a +864d4f59dde0afab749d5530cf9902e59ccbaacf From 07e0f4ba74220de74a631b87b9d1f8bda2d87899 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 11 Jan 2025 00:12:04 +0000 Subject: [PATCH 13/26] [dependabot]: Bump org.json:json from 20241224 to 20250107 in /packages/in_app_purchase/in_app_purchase_android/example/android/app (#8411) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ⚠️ **Dependabot is rebasing this PR** ⚠️ Rebasing might not happen immediately, so don't worry if this takes some time. Note: if you make any changes to this PR yourself, they will take precedence over the rebase. --- Bumps [org.json:json](https://github.com/douglascrockford/JSON-java) from 20241224 to 20250107.
Release notes

Sourced from org.json:json's releases.

20250107

Pull Request Description
#930 pre-release-20250107: updates for next release
#929 restore moditect in pom.xml
Changelog

Sourced from org.json:json's changelog.

Release history:

JSON-java releases can be found by searching the Maven repository for groupId "org.json" and artifactId "json". For example: https://search.maven.org/search?q=g:org.json%20AND%20a:json&core=gav

20250107....Restore moditect in pom.xml

20241224....Strict mode opt-in feature, and recent commits. This release does not contain module-info.class. It is not recommended if you need this feature.

20240303 Revert optLong/getLong changes, and recent commits.

20240205 Recent commits.

20231013 First release with minimum Java version 1.8. Recent commits, including fixes for CVE-2023-5072.

20230618 Final release with Java 1.6 compatibility. Future releases will require Java 1.8 or greater.

20230227 Fix for CVE-2022-45688 and recent commits

20220924 New License - public domain, and some minor updates

20220320 Wrap StackOverflow with JSONException

20211205 Recent commits and some bug fixes for similar()

20210307 Recent commits and potentially breaking fix to JSONPointer

20201115 Recent commits and first release after project structure change

20200518 Recent commits and snapshot before project structure change

20190722 Recent commits

20180813 POM change to include Automatic-Module-Name (#431) JSONObject(Map) now throws an exception if any of a map keys are null (#405)

20180130 Recent commits

20171018 Checkpoint for recent commits.

20170516 Roll up recent commits.

20160810 Revert code that was breaking opt*() methods.

20160807 This release contains a bug in the JSONObject.opt*() and JSONArray.opt*() methods, it is not recommended for use. Java 1.6 compatability fixed, JSONArray.toList() and JSONObject.toMap(), </tr></table>

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.json:json&package-manager=gradle&previous-version=20241224&new-version=20250107)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- .../in_app_purchase_android/example/android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/in_app_purchase/in_app_purchase_android/example/android/app/build.gradle b/packages/in_app_purchase/in_app_purchase_android/example/android/app/build.gradle index 3a17faebb508..99e889208977 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/android/app/build.gradle +++ b/packages/in_app_purchase/in_app_purchase_android/example/android/app/build.gradle @@ -108,7 +108,7 @@ dependencies { implementation 'com.android.billingclient:billing:6.1.0' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:5.1.1' - testImplementation 'org.json:json:20241224' + testImplementation 'org.json:json:20250107' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' } From 3c3bc6832b398cd3b512eb5db001fb4b9ab0367f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 11 Jan 2025 00:47:06 +0000 Subject: [PATCH 14/26] [dependabot]: Bump the test-dependencies group across 2 directories with 1 update (#8412) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the test-dependencies group with 1 update in the /packages/pigeon/platform_tests/test_plugin/android directory: [io.mockk:mockk](https://github.com/mockk/mockk). Bumps the test-dependencies group with 1 update in the /packages/shared_preferences/shared_preferences_android/android directory: [io.mockk:mockk](https://github.com/mockk/mockk). Updates `io.mockk:mockk` from 1.13.13 to 1.13.16
Release notes

Sourced from io.mockk:mockk's releases.

1.13.16

What's Changed

Full Changelog: https://github.com/mockk/mockk/compare/1.13.14...1.13.16

1.13.14

What's Changed

New Contributors

Full Changelog: https://github.com/mockk/mockk/compare/1.13.13...1.13.14

Commits
  • 9d151cb Version bump
  • 59d8d16 Merge pull request #1332 from kpadhiamex/1073-fix-issue-with-mocking-value-cl...
  • f602f77 Used Result.success("") for Kotlin class comparison instead of string name co...
  • 580c1d7 Used Kotlin class comparison instead of name comparison
  • 2ddd49d Fix( Issue #1073): Bug fix for the issue with mocking value classes with coEv...
  • 1b3b01e Version bump
  • e7ec061 Merge pull request #1331 from kpadhiamex/1330-fix-for-relaxed-mocking-simple-...
  • f3a12a2 Merge branch 'master' into 1330-fix-for-relaxed-mocking-simple-value-when-pro...
  • 2264808 Reenable java 17
  • 0ebbb0e Run instrumented test on ubuntu-latest
  • Additional commits viewable in compare view

Updates `io.mockk:mockk` from 1.13.14 to 1.13.16
Release notes

Sourced from io.mockk:mockk's releases.

1.13.16

What's Changed

Full Changelog: https://github.com/mockk/mockk/compare/1.13.14...1.13.16

1.13.14

What's Changed

New Contributors

Full Changelog: https://github.com/mockk/mockk/compare/1.13.13...1.13.14

Commits
  • 9d151cb Version bump
  • 59d8d16 Merge pull request #1332 from kpadhiamex/1073-fix-issue-with-mocking-value-cl...
  • f602f77 Used Result.success("") for Kotlin class comparison instead of string name co...
  • 580c1d7 Used Kotlin class comparison instead of name comparison
  • 2ddd49d Fix( Issue #1073): Bug fix for the issue with mocking value classes with coEv...
  • 1b3b01e Version bump
  • e7ec061 Merge pull request #1331 from kpadhiamex/1330-fix-for-relaxed-mocking-simple-...
  • f3a12a2 Merge branch 'master' into 1330-fix-for-relaxed-mocking-simple-value-when-pro...
  • 2264808 Reenable java 17
  • 0ebbb0e Run instrumented test on ubuntu-latest
  • Additional commits viewable in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
--- packages/pigeon/platform_tests/test_plugin/android/build.gradle | 2 +- .../shared_preferences_android/android/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/pigeon/platform_tests/test_plugin/android/build.gradle b/packages/pigeon/platform_tests/test_plugin/android/build.gradle index cae127bd86e2..bee0d0741c73 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/build.gradle +++ b/packages/pigeon/platform_tests/test_plugin/android/build.gradle @@ -66,7 +66,7 @@ android { dependencies { testImplementation 'junit:junit:4.+' - testImplementation "io.mockk:mockk:1.13.14" + testImplementation "io.mockk:mockk:1.13.16" // org.jetbrains.kotlin:kotlin-bom artifact purpose is to align kotlin stdlib and related code versions. // See: https://youtrack.jetbrains.com/issue/KT-55297/kotlin-stdlib-should-declare-constraints-on-kotlin-stdlib-jdk8-and-kotlin-stdlib-jdk7 implementation(platform("org.jetbrains.kotlin:kotlin-bom:2.0.21")) diff --git a/packages/shared_preferences/shared_preferences_android/android/build.gradle b/packages/shared_preferences/shared_preferences_android/android/build.gradle index 8a0b88a8c0ff..b25d8d6934e9 100644 --- a/packages/shared_preferences/shared_preferences_android/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/android/build.gradle @@ -67,7 +67,7 @@ android { testImplementation 'androidx.test.ext:junit-ktx:1.2.1' testImplementation 'org.robolectric:robolectric:4.12.1' testImplementation 'org.mockito:mockito-inline:5.2.0' - testImplementation 'io.mockk:mockk:1.13.14' + testImplementation 'io.mockk:mockk:1.13.16' } From 5bb6a8ce7f0aae670a3789f974cd92958fcfbf67 Mon Sep 17 00:00:00 2001 From: misos1 <30872003+misos1@users.noreply.github.com> Date: Mon, 13 Jan 2025 19:09:31 +0100 Subject: [PATCH 15/26] [video_player_avfoundation, camera_avfoundation] never overwrite but only upgrade audio session category (#7143) Setting category of `AVAudioSession` was moved into a wrapper which ensures that only changes which do not disable the ability to play in silent mode (`play`) and ability to record (`record`) are considered. If either the new category or old category is `play`/`record` then also the set category will be `play`/`record`. This currently means that the video player will not overwrite `PlayAndRecord` with `Playback` which causes inability to record audio by camera. Options are treated selectively so the video player will no longer overwrite flags set by camera like `DefaultToSpeaker` and camera will not overwrite `MixWithOthers` set by video player `setMixWithOthers`. It will also only change category or options if there is change to prevent lags every time is constructed `VideoPlayerController` with non-null `videoPlayerOptions` which always causes call to `setMixWithOthers`. Test `testSeekToWhilePausedStartsDisplayLinkTemporarily` is failing on the main branch on the tested device: ``` objc XCTAssertEqual([player position], 1234); // (([player position]) equal to (1234)) failed: ("0") is not equal to ("1234") ``` Fixes https://github.com/flutter/flutter/issues/131553 --- .../camera/camera_avfoundation/CHANGELOG.md | 4 ++ .../ios/RunnerTests/FLTCamSampleBufferTests.m | 23 +++++++ .../camera_avfoundation/CameraPlugin.m | 2 +- .../Sources/camera_avfoundation/FLTCam.m | 60 +++++++++++++++++-- .../include/camera_avfoundation/FLTCam.h | 2 +- .../camera/camera_avfoundation/pubspec.yaml | 2 +- .../video_player_avfoundation/CHANGELOG.md | 3 +- .../darwin/RunnerTests/VideoPlayerTests.m | 34 +++++++++++ .../FVPVideoPlayerPlugin.m | 46 ++++++++++++-- .../video_player_avfoundation/pubspec.yaml | 2 +- 10 files changed, 162 insertions(+), 16 deletions(-) diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index f0f4a302ae5a..eba91f29efee 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.17+7 + +* Fixes changing global audio session category to be collision free across plugins. + ## 0.9.17+6 * Updates minimum supported SDK version to Flutter 3.22/Dart 3.4. diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m index b6b78f2dab28..e92d1d21da5c 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m @@ -338,4 +338,27 @@ - (void)testStartWritingShouldNotBeCalledBetweenSampleCreationAndAppending { CFRelease(videoSample); } +- (void)testStartVideoRecordingWithCompletionShouldNotDisableMixWithOthers { + FLTCam *cam = FLTCreateCamWithCaptureSessionQueue(dispatch_queue_create("testing", NULL)); + + id writerMock = OCMClassMock([AVAssetWriter class]); + OCMStub([writerMock alloc]).andReturn(writerMock); + OCMStub([writerMock initWithURL:OCMOCK_ANY fileType:OCMOCK_ANY error:[OCMArg setTo:nil]]) + .andReturn(writerMock); + + [AVAudioSession.sharedInstance setCategory:AVAudioSessionCategoryPlayback + withOptions:AVAudioSessionCategoryOptionMixWithOthers + error:nil]; + + [cam + startVideoRecordingWithCompletion:^(FlutterError *_Nullable error) { + } + messengerForStreaming:nil]; + XCTAssert( + AVAudioSession.sharedInstance.categoryOptions & AVAudioSessionCategoryOptionMixWithOthers, + @"Flag MixWithOthers was removed."); + XCTAssert(AVAudioSession.sharedInstance.category == AVAudioSessionCategoryPlayAndRecord, + @"Category should be PlayAndRecord."); +} + @end diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraPlugin.m b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraPlugin.m index 63a49025231a..1bd44969c4cd 100644 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraPlugin.m +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraPlugin.m @@ -234,7 +234,7 @@ - (void)prepareForVideoRecordingWithCompletion: (nonnull void (^)(FlutterError *_Nullable))completion { __weak typeof(self) weakSelf = self; dispatch_async(self.captureSessionQueue, ^{ - [weakSelf.camera setUpCaptureSessionForAudio]; + [weakSelf.camera setUpCaptureSessionForAudioIfNeeded]; completion(nil); }); } diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTCam.m b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTCam.m index 0b065026f10e..699dbf3a806e 100644 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTCam.m @@ -237,6 +237,8 @@ - (instancetype)initWithMediaSettings:(FCPPlatformMediaSettings *)mediaSettings _videoFormat = kCVPixelFormatType_32BGRA; _inProgressSavePhotoDelegates = [NSMutableDictionary dictionary]; _fileFormat = FCPPlatformImageFileFormatJpeg; + _videoCaptureSession.automaticallyConfiguresApplicationAudioSession = NO; + _audioCaptureSession.automaticallyConfiguresApplicationAudioSession = NO; // To limit memory consumption, limit the number of frames pending processing. // After some testing, 4 was determined to be the best maximum value. @@ -725,7 +727,8 @@ - (void)captureOutput:(AVCaptureOutput *)output if (_isFirstVideoSample) { [_videoWriter startSessionAtSourceTime:currentSampleTime]; // fix sample times not being numeric when pause/resume happens before first sample buffer - // arrives https://github.com/flutter/flutter/issues/132014 + // arrives + // https://github.com/flutter/flutter/issues/132014 _lastVideoSampleTime = currentSampleTime; _lastAudioSampleTime = currentSampleTime; _isFirstVideoSample = NO; @@ -1283,9 +1286,7 @@ - (BOOL)setupWriterForPath:(NSString *)path { return NO; } - if (_mediaSettings.enableAudio && !_isAudioSetup) { - [self setUpCaptureSessionForAudio]; - } + [self setUpCaptureSessionForAudioIfNeeded]; _videoWriter = [[AVAssetWriter alloc] initWithURL:outputURL fileType:AVFileTypeMPEG4 @@ -1365,9 +1366,42 @@ - (BOOL)setupWriterForPath:(NSString *)path { return YES; } -- (void)setUpCaptureSessionForAudio { +// This function, although slightly modified, is also in video_player_avfoundation. +// Both need to do the same thing and run on the same thread (for example main thread). +// Configure application wide audio session manually to prevent overwriting flag +// MixWithOthers by capture session. +// Only change category if it is considered an upgrade which means it can only enable +// ability to play in silent mode or ability to record audio but never disables it, +// that could affect other plugins which depend on this global state. Only change +// category or options if there is change to prevent unnecessary lags and silence. +static void upgradeAudioSessionCategory(AVAudioSessionCategory requestedCategory, + AVAudioSessionCategoryOptions options) { + NSSet *playCategories = [NSSet + setWithObjects:AVAudioSessionCategoryPlayback, AVAudioSessionCategoryPlayAndRecord, nil]; + NSSet *recordCategories = + [NSSet setWithObjects:AVAudioSessionCategoryRecord, AVAudioSessionCategoryPlayAndRecord, nil]; + NSSet *requiredCategories = + [NSSet setWithObjects:requestedCategory, AVAudioSession.sharedInstance.category, nil]; + BOOL requiresPlay = [requiredCategories intersectsSet:playCategories]; + BOOL requiresRecord = [requiredCategories intersectsSet:recordCategories]; + if (requiresPlay && requiresRecord) { + requestedCategory = AVAudioSessionCategoryPlayAndRecord; + } else if (requiresPlay) { + requestedCategory = AVAudioSessionCategoryPlayback; + } else if (requiresRecord) { + requestedCategory = AVAudioSessionCategoryRecord; + } + options = AVAudioSession.sharedInstance.categoryOptions | options; + if ([requestedCategory isEqualToString:AVAudioSession.sharedInstance.category] && + options == AVAudioSession.sharedInstance.categoryOptions) { + return; + } + [AVAudioSession.sharedInstance setCategory:requestedCategory withOptions:options error:nil]; +} + +- (void)setUpCaptureSessionForAudioIfNeeded { // Don't setup audio twice or we will lose the audio. - if (_isAudioSetup) { + if (!_mediaSettings.enableAudio || _isAudioSetup) { return; } @@ -1383,6 +1417,20 @@ - (void)setUpCaptureSessionForAudio { // Setup the audio output. _audioOutput = [[AVCaptureAudioDataOutput alloc] init]; + dispatch_block_t block = ^{ + // Set up options implicit to AVAudioSessionCategoryPlayback to avoid conflicts with other + // plugins like video_player. + upgradeAudioSessionCategory(AVAudioSessionCategoryPlayAndRecord, + AVAudioSessionCategoryOptionDefaultToSpeaker | + AVAudioSessionCategoryOptionAllowBluetoothA2DP | + AVAudioSessionCategoryOptionAllowAirPlay); + }; + if (!NSThread.isMainThread) { + dispatch_sync(dispatch_get_main_queue(), block); + } else { + block(); + } + if ([_audioCaptureSession canAddInput:audioInput]) { [_audioCaptureSession addInput:audioInput]; diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTCam.h b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTCam.h index d8f97926b770..c89ee9f98e5c 100644 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTCam.h +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTCam.h @@ -113,7 +113,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)startImageStreamWithMessenger:(NSObject *)messenger; - (void)stopImageStream; - (void)setZoomLevel:(CGFloat)zoom withCompletion:(void (^)(FlutterError *_Nullable))completion; -- (void)setUpCaptureSessionForAudio; +- (void)setUpCaptureSessionForAudioIfNeeded; @end diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index 70c6dccfbd09..804a8cba7b38 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_avfoundation description: iOS implementation of the camera plugin. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.17+6 +version: 0.9.17+7 environment: sdk: ^3.4.0 diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index 0f0b4dabac26..76b14ba3f961 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.6.6 +* Fixes changing global audio session category to be collision free across plugins. * Updates minimum supported SDK version to Flutter 3.22/Dart 3.4. ## 2.6.5 diff --git a/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m b/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m index f35d38bd9895..559c9f089d6d 100644 --- a/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m +++ b/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m @@ -840,6 +840,40 @@ - (void)testFailedToLoadVideoEventShouldBeAlwaysSent { } #if TARGET_OS_IOS +- (void)testVideoPlayerShouldNotOverwritePlayAndRecordNorDefaultToSpeaker { + NSObject *registrar = [GetPluginRegistry() + registrarForPlugin:@"testVideoPlayerShouldNotOverwritePlayAndRecordNorDefaultToSpeaker"]; + FVPVideoPlayerPlugin *videoPlayerPlugin = + [[FVPVideoPlayerPlugin alloc] initWithRegistrar:registrar]; + FlutterError *error; + + [AVAudioSession.sharedInstance setCategory:AVAudioSessionCategoryPlayAndRecord + withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker + error:nil]; + + [videoPlayerPlugin initialize:&error]; + [videoPlayerPlugin setMixWithOthers:true error:&error]; + XCTAssert(AVAudioSession.sharedInstance.category == AVAudioSessionCategoryPlayAndRecord, + @"Category should be PlayAndRecord."); + XCTAssert( + AVAudioSession.sharedInstance.categoryOptions & AVAudioSessionCategoryOptionDefaultToSpeaker, + @"Flag DefaultToSpeaker was removed."); + XCTAssert( + AVAudioSession.sharedInstance.categoryOptions & AVAudioSessionCategoryOptionMixWithOthers, + @"Flag MixWithOthers should be set."); + + id sessionMock = OCMClassMock([AVAudioSession class]); + OCMStub([sessionMock sharedInstance]).andReturn(sessionMock); + OCMStub([sessionMock category]).andReturn(AVAudioSessionCategoryPlayAndRecord); + OCMStub([sessionMock categoryOptions]) + .andReturn(AVAudioSessionCategoryOptionMixWithOthers | + AVAudioSessionCategoryOptionDefaultToSpeaker); + OCMReject([sessionMock setCategory:OCMOCK_ANY withOptions:0 error:[OCMArg setTo:nil]]) + .ignoringNonObjectArgs(); + + [videoPlayerPlugin setMixWithOthers:true error:&error]; +} + - (void)validateTransformFixForOrientation:(UIImageOrientation)orientation { AVAssetTrack *track = [[FakeAVAssetTrack alloc] initWithOrientation:orientation]; CGAffineTransform t = FVPGetStandardizedTransformForTrack(track); diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerPlugin.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerPlugin.m index 7c70da03d664..0834b10d1ae6 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerPlugin.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerPlugin.m @@ -90,10 +90,46 @@ - (int64_t)onPlayerSetup:(FVPVideoPlayer *)player frameUpdater:(FVPFrameUpdater return textureId; } +// This function, although slightly modified, is also in camera_avfoundation. +// Both need to do the same thing and run on the same thread (for example main thread). +// Do not overwrite PlayAndRecord with Playback which causes inability to record +// audio, do not overwrite all options. +// Only change category if it is considered an upgrade which means it can only enable +// ability to play in silent mode or ability to record audio but never disables it, +// that could affect other plugins which depend on this global state. Only change +// category or options if there is change to prevent unnecessary lags and silence. +#if TARGET_OS_IOS +static void upgradeAudioSessionCategory(AVAudioSessionCategory requestedCategory, + AVAudioSessionCategoryOptions options, + AVAudioSessionCategoryOptions clearOptions) { + NSSet *playCategories = [NSSet + setWithObjects:AVAudioSessionCategoryPlayback, AVAudioSessionCategoryPlayAndRecord, nil]; + NSSet *recordCategories = + [NSSet setWithObjects:AVAudioSessionCategoryRecord, AVAudioSessionCategoryPlayAndRecord, nil]; + NSSet *requiredCategories = + [NSSet setWithObjects:requestedCategory, AVAudioSession.sharedInstance.category, nil]; + BOOL requiresPlay = [requiredCategories intersectsSet:playCategories]; + BOOL requiresRecord = [requiredCategories intersectsSet:recordCategories]; + if (requiresPlay && requiresRecord) { + requestedCategory = AVAudioSessionCategoryPlayAndRecord; + } else if (requiresPlay) { + requestedCategory = AVAudioSessionCategoryPlayback; + } else if (requiresRecord) { + requestedCategory = AVAudioSessionCategoryRecord; + } + options = (AVAudioSession.sharedInstance.categoryOptions & ~clearOptions) | options; + if ([requestedCategory isEqualToString:AVAudioSession.sharedInstance.category] && + options == AVAudioSession.sharedInstance.categoryOptions) { + return; + } + [AVAudioSession.sharedInstance setCategory:requestedCategory withOptions:options error:nil]; +} +#endif + - (void)initialize:(FlutterError *__autoreleasing *)error { #if TARGET_OS_IOS // Allow audio playback when the Ring/Silent switch is set to silent - [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil]; + upgradeAudioSessionCategory(AVAudioSessionCategoryPlayback, 0, 0); #endif [self.playersByTextureId @@ -204,11 +240,11 @@ - (void)setMixWithOthers:(BOOL)mixWithOthers // AVAudioSession doesn't exist on macOS, and audio always mixes, so just no-op. #else if (mixWithOthers) { - [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback - withOptions:AVAudioSessionCategoryOptionMixWithOthers - error:nil]; + upgradeAudioSessionCategory(AVAudioSession.sharedInstance.category, + AVAudioSessionCategoryOptionMixWithOthers, 0); } else { - [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil]; + upgradeAudioSessionCategory(AVAudioSession.sharedInstance.category, 0, + AVAudioSessionCategoryOptionMixWithOthers); } #endif } diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index e6e72fefab27..c20357d78044 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avfoundation description: iOS and macOS implementation of the video_player plugin. repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.6.5 +version: 2.6.6 environment: sdk: ^3.4.0 From d682dcf71e49a1472194f91a61678b304dfbf2a2 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 13 Jan 2025 14:06:03 -0500 Subject: [PATCH 16/26] Roll Flutter from 864d4f59dde0 to 72db8f69e339 (11 revisions) (#8421) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/flutter/flutter/compare/864d4f59dde0...72db8f69e339 2025-01-13 engine-flutter-autoroll@skia.org Roll Packages from 65547511c004 to 3c3bc6832b39 (16 revisions) (flutter/flutter#161515) 2025-01-13 tessertaha@gmail.com Update error message for when leading/trailing width exceeds `ListTile` width and add missing test (flutter/flutter#161091) 2025-01-13 tessertaha@gmail.com Deprecate unused `ButtonStyleButton.iconAlignment` property (flutter/flutter#160023) 2025-01-12 robert.ancell@canonical.com Provide monitor information. (flutter/flutter#161359) 2025-01-11 jonahwilliams@google.com [android_engine_test] Remove background/foreground from surface texture trampoline test. (flutter/flutter#161441) 2025-01-11 matanlurey@users.noreply.github.com Remove some miscellaneous references to Cirrus. (flutter/flutter#161390) 2025-01-11 47866232+chunhtai@users.noreply.github.com add semantics role and tab (flutter/flutter#161260) 2025-01-10 58190796+MitchellGoodwin@users.noreply.github.com CupertinoSheetRoute (flutter/flutter#157568) 2025-01-10 31859944+LongCatIsLooong@users.noreply.github.com Update `TextEditingController.text` documentation to recommend against using it in production code (flutter/flutter#157769) 2025-01-10 github@alexv525.com 🔊 [tool] Add a wirelessly connected device name as `displayName` (flutter/flutter#160497) 2025-01-10 codefu@google.com FixForward: method was renamed (flutter/flutter#161431) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 85c1173b0833..6b965fe696d7 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -864d4f59dde0afab749d5530cf9902e59ccbaacf +72db8f69e33917cd6e7bd16bb04fc6cee42676d0 From 1182ca9870f3543dc625c8197aa5a6a5793554a5 Mon Sep 17 00:00:00 2001 From: Igor Kharakhordin Date: Mon, 13 Jan 2025 20:06:06 +0100 Subject: [PATCH 17/26] [webview_flutter_android] Add additional WebSettings methods (#8270) Exposes native WebSettings such as setAllowContentAccess and setGeolocationEnabled in order to give developers more options to configure the native webview and restrict these features that are enabled by default. Fixes [160070](https://github.com/flutter/flutter/issues/160070) --- .../webview_flutter_android/CHANGELOG.md | 6 ++ .../webviewflutter/AndroidWebkitLibrary.g.kt | 54 ++++++++++++++++++ .../webviewflutter/WebSettingsProxyApi.java | 10 ++++ .../lib/src/android_webkit.g.dart | 56 +++++++++++++++++++ .../lib/src/android_webview_controller.dart | 12 ++++ .../pigeons/android_webkit.dart | 6 ++ .../webview_flutter_android/pubspec.yaml | 2 +- .../test/android_webview_controller_test.dart | 32 +++++++++++ ...android_webview_controller_test.mocks.dart | 50 +++++++++++++++++ ...oid_webview_cookie_manager_test.mocks.dart | 30 ++++++++++ .../webview_android_widget_test.mocks.dart | 20 +++++++ 11 files changed, 277 insertions(+), 1 deletion(-) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 24633e85bd86..539c54c8a9fe 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,9 @@ +## 4.3.0 + +* Adds support for disabling content URL access within WebView and disabling the Geolocation API. + See `AndroidWebViewController.setAllowContentAccess` and + `AndroidWebViewController.setGeolocationEnabled`. + ## 4.2.0 * Adds support for configuring file access permissions. See `AndroidWebViewController.setAllowFileAccess`. diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/AndroidWebkitLibrary.g.kt b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/AndroidWebkitLibrary.g.kt index f28082725487..92cbf74aaa17 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/AndroidWebkitLibrary.g.kt +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/AndroidWebkitLibrary.g.kt @@ -2104,6 +2104,12 @@ abstract class PigeonApiWebSettings( /** Enables or disables file access within WebView. */ abstract fun setAllowFileAccess(pigeon_instance: android.webkit.WebSettings, enabled: Boolean) + /** Enables or disables content URL access within WebView. */ + abstract fun setAllowContentAccess(pigeon_instance: android.webkit.WebSettings, enabled: Boolean) + + /** Sets whether Geolocation is enabled within WebView. */ + abstract fun setGeolocationEnabled(pigeon_instance: android.webkit.WebSettings, enabled: Boolean) + /** Sets the text zoom of the page in percent. */ abstract fun setTextZoom(pigeon_instance: android.webkit.WebSettings, textZoom: Long) @@ -2402,6 +2408,54 @@ abstract class PigeonApiWebSettings( channel.setMessageHandler(null) } } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.webview_flutter_android.WebSettings.setAllowContentAccess", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val pigeon_instanceArg = args[0] as android.webkit.WebSettings + val enabledArg = args[1] as Boolean + val wrapped: List = + try { + api.setAllowContentAccess(pigeon_instanceArg, enabledArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.webview_flutter_android.WebSettings.setGeolocationEnabled", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val pigeon_instanceArg = args[0] as android.webkit.WebSettings + val enabledArg = args[1] as Boolean + val wrapped: List = + try { + api.setGeolocationEnabled(pigeon_instanceArg, enabledArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } run { val channel = BasicMessageChannel( diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebSettingsProxyApi.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebSettingsProxyApi.java index 82fa32b8150e..43966249703a 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebSettingsProxyApi.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebSettingsProxyApi.java @@ -81,6 +81,16 @@ public void setAllowFileAccess(@NonNull WebSettings pigeon_instance, boolean ena pigeon_instance.setAllowFileAccess(enabled); } + @Override + public void setAllowContentAccess(@NonNull WebSettings pigeon_instance, boolean enabled) { + pigeon_instance.setAllowContentAccess(enabled); + } + + @Override + public void setGeolocationEnabled(@NonNull WebSettings pigeon_instance, boolean enabled) { + pigeon_instance.setGeolocationEnabled(enabled); + } + @Override public void setTextZoom(@NonNull WebSettings pigeon_instance, long textZoom) { pigeon_instance.setTextZoom((int) textZoom); diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webkit.g.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webkit.g.dart index fc7174a27d08..3ae3a187867c 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webkit.g.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webkit.g.dart @@ -2615,6 +2615,62 @@ class WebSettings extends PigeonInternalProxyApiBaseClass { } } + /// Enables or disables content URL access within WebView. + Future setAllowContentAccess(bool enabled) async { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _pigeonVar_codecWebSettings; + final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger; + const String pigeonVar_channelName = + 'dev.flutter.pigeon.webview_flutter_android.WebSettings.setAllowContentAccess'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = await pigeonVar_channel + .send([this, enabled]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + /// Sets whether Geolocation is enabled within WebView. + Future setGeolocationEnabled(bool enabled) async { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _pigeonVar_codecWebSettings; + final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger; + const String pigeonVar_channelName = + 'dev.flutter.pigeon.webview_flutter_android.WebSettings.setGeolocationEnabled'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = await pigeonVar_channel + .send([this, enabled]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + /// Sets the text zoom of the page in percent. Future setTextZoom(int textZoom) async { final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_controller.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_controller.dart index 3cde232ca9f9..7dc1c7973764 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_controller.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_controller.dart @@ -599,6 +599,18 @@ class AndroidWebViewController extends PlatformWebViewController { Future setTextZoom(int textZoom) => _webView.settings.setTextZoom(textZoom); + /// Enables or disables content URL access. + /// + /// The default is true. + Future setAllowContentAccess(bool enabled) => + _webView.settings.setAllowContentAccess(enabled); + + /// Sets whether Geolocation is enabled. + /// + /// The default is true. + Future setGeolocationEnabled(bool enabled) => + _webView.settings.setGeolocationEnabled(enabled); + /// Sets the callback that is invoked when the client should show a file /// selector. Future setOnShowFileSelector( diff --git a/packages/webview_flutter/webview_flutter_android/pigeons/android_webkit.dart b/packages/webview_flutter/webview_flutter_android/pigeons/android_webkit.dart index 22435d5b97d2..8d3a4dbed5c8 100644 --- a/packages/webview_flutter/webview_flutter_android/pigeons/android_webkit.dart +++ b/packages/webview_flutter/webview_flutter_android/pigeons/android_webkit.dart @@ -354,6 +354,12 @@ abstract class WebSettings { /// Enables or disables file access within WebView. void setAllowFileAccess(bool enabled); + /// Enables or disables content URL access within WebView. + void setAllowContentAccess(bool enabled); + + /// Sets whether Geolocation is enabled within WebView. + void setGeolocationEnabled(bool enabled); + /// Sets the text zoom of the page in percent. void setTextZoom(int textZoom); diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 44f19b2af19c..bd248b40f5d9 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_android description: A Flutter plugin that provides a WebView widget on Android. repository: https://github.com/flutter/packages/tree/main/packages/webview_flutter/webview_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 4.2.0 +version: 4.3.0 environment: sdk: ^3.5.0 diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.dart index 167b660aa53f..6762d1f01699 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.dart @@ -1526,6 +1526,38 @@ void main() { verify(mockSettings.setMediaPlaybackRequiresUserGesture(true)).called(1); }); + test('setAllowContentAccess', () async { + final MockWebView mockWebView = MockWebView(); + final MockWebSettings mockSettings = MockWebSettings(); + final AndroidWebViewController controller = createControllerWithMocks( + mockWebView: mockWebView, + mockSettings: mockSettings, + ); + + clearInteractions(mockWebView); + + await controller.setAllowContentAccess(false); + + verify(mockWebView.settings).called(1); + verify(mockSettings.setAllowContentAccess(false)).called(1); + }); + + test('setGeolocationEnabled', () async { + final MockWebView mockWebView = MockWebView(); + final MockWebSettings mockSettings = MockWebSettings(); + final AndroidWebViewController controller = createControllerWithMocks( + mockWebView: mockWebView, + mockSettings: mockSettings, + ); + + clearInteractions(mockWebView); + + await controller.setGeolocationEnabled(false); + + verify(mockWebView.settings).called(1); + verify(mockSettings.setGeolocationEnabled(false)).called(1); + }); + test('setTextZoom', () async { final MockWebView mockWebView = MockWebView(); final MockWebSettings mockSettings = MockWebSettings(); diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.mocks.dart index d0254f2bd801..2e13f747bd67 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.mocks.dart @@ -455,6 +455,16 @@ class MockAndroidWebViewController extends _i1.Mock ), ) as _i3.PlatformWebViewControllerCreationParams); + @override + _i8.Future setAllowFileAccess(bool? allow) => (super.noSuchMethod( + Invocation.method( + #setAllowFileAccess, + [allow], + ), + returnValue: _i8.Future.value(), + returnValueForMissingStub: _i8.Future.value(), + ) as _i8.Future); + @override _i8.Future loadFile(String? absoluteFilePath) => (super.noSuchMethod( Invocation.method( @@ -789,6 +799,26 @@ class MockAndroidWebViewController extends _i1.Mock returnValueForMissingStub: _i8.Future.value(), ) as _i8.Future); + @override + _i8.Future setAllowContentAccess(bool? enabled) => (super.noSuchMethod( + Invocation.method( + #setAllowContentAccess, + [enabled], + ), + returnValue: _i8.Future.value(), + returnValueForMissingStub: _i8.Future.value(), + ) as _i8.Future); + + @override + _i8.Future setGeolocationEnabled(bool? enabled) => (super.noSuchMethod( + Invocation.method( + #setGeolocationEnabled, + [enabled], + ), + returnValue: _i8.Future.value(), + returnValueForMissingStub: _i8.Future.value(), + ) as _i8.Future); + @override _i8.Future setOnShowFileSelector( _i8.Future> Function(_i7.FileSelectorParams)? @@ -2638,6 +2668,26 @@ class MockWebSettings extends _i1.Mock implements _i2.WebSettings { returnValueForMissingStub: _i8.Future.value(), ) as _i8.Future); + @override + _i8.Future setAllowContentAccess(bool? enabled) => (super.noSuchMethod( + Invocation.method( + #setAllowContentAccess, + [enabled], + ), + returnValue: _i8.Future.value(), + returnValueForMissingStub: _i8.Future.value(), + ) as _i8.Future); + + @override + _i8.Future setGeolocationEnabled(bool? enabled) => (super.noSuchMethod( + Invocation.method( + #setGeolocationEnabled, + [enabled], + ), + returnValue: _i8.Future.value(), + returnValueForMissingStub: _i8.Future.value(), + ) as _i8.Future); + @override _i8.Future setTextZoom(int? textZoom) => (super.noSuchMethod( Invocation.method( diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_cookie_manager_test.mocks.dart index 303f54d46f64..9a80b3c10f10 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_cookie_manager_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_cookie_manager_test.mocks.dart @@ -178,6 +178,16 @@ class MockAndroidWebViewController extends _i1.Mock ), ) as _i3.PlatformWebViewControllerCreationParams); + @override + _i5.Future setAllowFileAccess(bool? allow) => (super.noSuchMethod( + Invocation.method( + #setAllowFileAccess, + [allow], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override _i5.Future loadFile(String? absoluteFilePath) => (super.noSuchMethod( Invocation.method( @@ -494,6 +504,26 @@ class MockAndroidWebViewController extends _i1.Mock returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override + _i5.Future setAllowContentAccess(bool? enabled) => (super.noSuchMethod( + Invocation.method( + #setAllowContentAccess, + [enabled], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + + @override + _i5.Future setGeolocationEnabled(bool? enabled) => (super.noSuchMethod( + Invocation.method( + #setGeolocationEnabled, + [enabled], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override _i5.Future setOnShowFileSelector( _i5.Future> Function(_i6.FileSelectorParams)? diff --git a/packages/webview_flutter/webview_flutter_android/test/legacy/webview_android_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/legacy/webview_android_widget_test.mocks.dart index 44c8b8adb47f..ff695040404a 100644 --- a/packages/webview_flutter/webview_flutter_android/test/legacy/webview_android_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/legacy/webview_android_widget_test.mocks.dart @@ -355,6 +355,26 @@ class MockWebSettings extends _i1.Mock implements _i2.WebSettings { returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override + _i4.Future setAllowContentAccess(bool? enabled) => (super.noSuchMethod( + Invocation.method( + #setAllowContentAccess, + [enabled], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future setGeolocationEnabled(bool? enabled) => (super.noSuchMethod( + Invocation.method( + #setGeolocationEnabled, + [enabled], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + @override _i4.Future setTextZoom(int? textZoom) => (super.noSuchMethod( Invocation.method( From d1fd6232ec33cd5a25aa762e605c494afced812f Mon Sep 17 00:00:00 2001 From: Olli Helenius Date: Mon, 13 Jan 2025 21:23:50 +0200 Subject: [PATCH 18/26] [camera] Add API support query for image streaming (#8250) Add API support query, `supportsImageStreaming` for checking if the camera platform supports image streaming. As requested on this comment: https://github.com/flutter/packages/pull/8234#discussion_r1871232301 Attempting to follow the [contribution guide wrt. changing federated plugins](https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins). There is no issue to link to, but should I create one? --- packages/camera/camera_android/CHANGELOG.md | 4 ++++ packages/camera/camera_android/lib/src/android_camera.dart | 3 +++ packages/camera/camera_android/pubspec.yaml | 4 ++-- packages/camera/camera_android/test/android_camera_test.dart | 4 ++++ packages/camera/camera_android_camerax/CHANGELOG.md | 4 ++++ .../lib/src/android_camera_camerax.dart | 3 +++ packages/camera/camera_android_camerax/pubspec.yaml | 4 ++-- .../test/android_camera_camerax_test.dart | 5 +++++ packages/camera/camera_avfoundation/CHANGELOG.md | 4 ++++ .../camera_avfoundation/lib/src/avfoundation_camera.dart | 3 +++ packages/camera/camera_avfoundation/pubspec.yaml | 4 ++-- .../camera_avfoundation/test/avfoundation_camera_test.dart | 4 ++++ 12 files changed, 40 insertions(+), 6 deletions(-) diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index d5686e096e72..8090d5ca2fea 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.10.10 + +* Adds API support query for image streaming. + ## 0.10.9+17 * Updates annotations lib to 1.9.1. diff --git a/packages/camera/camera_android/lib/src/android_camera.dart b/packages/camera/camera_android/lib/src/android_camera.dart index 975c6c9be022..616957716164 100644 --- a/packages/camera/camera_android/lib/src/android_camera.dart +++ b/packages/camera/camera_android/lib/src/android_camera.dart @@ -228,6 +228,9 @@ class AndroidCamera extends CameraPlatform { Future resumeVideoRecording(int cameraId) => _hostApi.resumeVideoRecording(); + @override + bool supportsImageStreaming() => true; + @override Stream onStreamedFrameAvailable(int cameraId, {CameraImageStreamOptions? options}) { diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index a5af9af8d52e..677e4e7b3014 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -3,7 +3,7 @@ description: Android implementation of the camera plugin. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.10.9+17 +version: 0.10.10 environment: sdk: ^3.5.0 @@ -19,7 +19,7 @@ flutter: dartPluginClass: AndroidCamera dependencies: - camera_platform_interface: ^2.6.0 + camera_platform_interface: ^2.9.0 flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.2 diff --git a/packages/camera/camera_android/test/android_camera_test.dart b/packages/camera/camera_android/test/android_camera_test.dart index 7c6d55c485b8..e2897b1e579e 100644 --- a/packages/camera/camera_android/test/android_camera_test.dart +++ b/packages/camera/camera_android/test/android_camera_test.dart @@ -726,6 +726,10 @@ void main() { verify(mockCameraApi.resumePreview()).called(1); }); + test('Should report support for image streaming', () async { + expect(camera.supportsImageStreaming(), true); + }); + test('Should start streaming', () async { // Arrange // Act diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index c64efc82427d..7e190cb5ce33 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.13 + +* Adds API support query for image streaming. + ## 0.6.12 * Suppresses deprecation and removal warnings for diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart index 279956f8b172..31cde2fa0fcb 100644 --- a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart +++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart @@ -1062,6 +1062,9 @@ class AndroidCameraCameraX extends CameraPlatform { } } + @override + bool supportsImageStreaming() => true; + /// A new streamed frame is available. /// /// Listening to this stream will start streaming, and canceling will stop. diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index 90fabeb88f96..fb7e6f0e317a 100644 --- a/packages/camera/camera_android_camerax/pubspec.yaml +++ b/packages/camera/camera_android_camerax/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android_camerax description: Android implementation of the camera plugin using the CameraX library. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android_camerax issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.6.12 +version: 0.6.13 environment: sdk: ^3.6.0 @@ -19,7 +19,7 @@ flutter: dependencies: async: ^2.5.0 - camera_platform_interface: ^2.6.0 + camera_platform_interface: ^2.9.0 flutter: sdk: flutter meta: ^1.7.0 diff --git a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart index bf1ff878edd8..89e201b3b329 100644 --- a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart +++ b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart @@ -2165,6 +2165,11 @@ void main() { verify(mockCameraControl.setZoomRatio(zoomRatio)); }); + test('Should report support for image streaming', () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + expect(camera.supportsImageStreaming(), true); + }); + test( 'onStreamedFrameAvailable emits CameraImageData when picked up from CameraImageData stream controller', () async { diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index eba91f29efee..51effb471121 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.18 + +* Adds API support query for image streaming. + ## 0.9.17+7 * Fixes changing global audio session category to be collision free across plugins. diff --git a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart index 286e01940bfa..3e48a5e17d6f 100644 --- a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart +++ b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart @@ -237,6 +237,9 @@ class AVFoundationCamera extends CameraPlatform { await _hostApi.resumeVideoRecording(); } + @override + bool supportsImageStreaming() => true; + @override Stream onStreamedFrameAvailable(int cameraId, {CameraImageStreamOptions? options}) { diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index 804a8cba7b38..dbfd772cb410 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_avfoundation description: iOS implementation of the camera plugin. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.17+7 +version: 0.9.18 environment: sdk: ^3.4.0 @@ -17,7 +17,7 @@ flutter: dartPluginClass: AVFoundationCamera dependencies: - camera_platform_interface: ^2.7.0 + camera_platform_interface: ^2.9.0 flutter: sdk: flutter stream_transform: ^2.0.0 diff --git a/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart b/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart index df04b1a8e8ed..1e8ad7def5d7 100644 --- a/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart +++ b/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart @@ -674,6 +674,10 @@ void main() { verify(mockApi.resumePreview()); }); + test('Should report support for image streaming', () async { + expect(camera.supportsImageStreaming(), true); + }); + test('Should start streaming', () async { final StreamSubscription subscription = camera .onStreamedFrameAvailable(cameraId) From 6a0f1227f9b61b49ce30d703d0656278dd3690a3 Mon Sep 17 00:00:00 2001 From: Nate Wilson Date: Tue, 14 Jan 2025 10:56:14 -0700 Subject: [PATCH 19/26] [two_dimensional_scrollables] prepare for `const AnimationStyle` (#8397) unblocks https://github.com/flutter/flutter/pull/160564 Will go back through and implement `const` constructors in these spots once the framework PR lands. --- .../two_dimensional_scrollables/lib/src/tree_view/tree.dart | 1 + .../test/tree_view/render_tree_test.dart | 1 + .../two_dimensional_scrollables/test/tree_view/tree_test.dart | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/packages/two_dimensional_scrollables/lib/src/tree_view/tree.dart b/packages/two_dimensional_scrollables/lib/src/tree_view/tree.dart index f9fc6c82e7eb..4f7dd216d278 100644 --- a/packages/two_dimensional_scrollables/lib/src/tree_view/tree.dart +++ b/packages/two_dimensional_scrollables/lib/src/tree_view/tree.dart @@ -496,6 +496,7 @@ class TreeView extends StatefulWidget { /// The default [AnimationStyle] used for node expand and collapse animations, /// when one has not been provided in [toggleAnimationStyle]. + // ignore: prefer_const_constructors static AnimationStyle defaultToggleAnimationStyle = AnimationStyle( curve: defaultAnimationCurve, duration: defaultAnimationDuration, diff --git a/packages/two_dimensional_scrollables/test/tree_view/render_tree_test.dart b/packages/two_dimensional_scrollables/test/tree_view/render_tree_test.dart index 3b239380d53f..5ffd419bf7fe 100644 --- a/packages/two_dimensional_scrollables/test/tree_view/render_tree_test.dart +++ b/packages/two_dimensional_scrollables/test/tree_view/render_tree_test.dart @@ -505,6 +505,7 @@ void main() { // Customize the animation treeView = TreeView( tree: treeNodes, + // ignore: prefer_const_constructors toggleAnimationStyle: AnimationStyle( duration: const Duration(milliseconds: 500), curve: Curves.bounceIn, diff --git a/packages/two_dimensional_scrollables/test/tree_view/tree_test.dart b/packages/two_dimensional_scrollables/test/tree_view/tree_test.dart index 5e536402b85a..5db487842728 100644 --- a/packages/two_dimensional_scrollables/test/tree_view/tree_test.dart +++ b/packages/two_dimensional_scrollables/test/tree_view/tree_test.dart @@ -546,6 +546,7 @@ void main() { // Default expect( style, + // ignore: prefer_const_constructors AnimationStyle( duration: TreeView.defaultAnimationDuration, curve: TreeView.defaultAnimationCurve, @@ -574,6 +575,7 @@ void main() { await tester.pumpWidget(MaterialApp( home: TreeView( tree: simpleNodeSet, + // ignore: prefer_const_constructors toggleAnimationStyle: AnimationStyle( curve: Curves.easeIn, duration: const Duration(milliseconds: 200), @@ -757,6 +759,7 @@ void main() { home: TreeView( tree: tree, controller: controller, + // ignore: prefer_const_constructors toggleAnimationStyle: AnimationStyle( curve: Curves.easeInOut, duration: Duration.zero, @@ -843,6 +846,7 @@ void main() { home: TreeView( tree: tree, controller: controller, + // ignore: prefer_const_constructors toggleAnimationStyle: AnimationStyle( curve: Curves.easeInOut, duration: const Duration(milliseconds: 200), From f73cb00e127b3036e5b0459cff8c416fabcfd120 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 14 Jan 2025 14:52:29 -0500 Subject: [PATCH 20/26] Roll Flutter from 72db8f69e339 to 40c2b86fdf65 (33 revisions) (#8428) Roll Flutter from 72db8f69e339 to 40c2b86fdf65 (33 revisions) https://github.com/flutter/flutter/compare/72db8f69e339...40c2b86fdf65 2025-01-14 christopherfujino@gmail.com update changelog for 3.27.2 release (flutter/flutter#161569) 2025-01-14 tessertaha@gmail.com Fix `showLicensePage` does not inherit ambient `Theme` (flutter/flutter#161599) 2025-01-14 30870216+gaaclarke@users.noreply.github.com Added special case for fat width arcs (flutter/flutter#161255) 2025-01-14 matanlurey@users.noreply.github.com Replace `fetch `with `gclient sync`. (flutter/flutter#161565) 2025-01-14 engine-flutter-autoroll@skia.org Roll Packages from 3c3bc6832b39 to d1fd6232ec33 (4 revisions) (flutter/flutter#161597) 2025-01-14 robert.ancell@canonical.com Remove unused method (flutter/flutter#161572) 2025-01-14 hbatagelo@gmail.com Fix crash when closing a window with `Alt+F4` in multi-win Flutter on Windows (flutter/flutter#161375) 2025-01-14 bruno.leroux@gmail.com Update InputDecoration.border documentation (flutter/flutter#161415) 2025-01-14 dkwingsmt@users.noreply.github.com [Web] Allow specifying the strategy on when to use element to display images (flutter/flutter#159917) 2025-01-14 a-siva@users.noreply.github.com Roll Dart to Version 3.7.0-323.0.dev (flutter/flutter#161567) 2025-01-14 goderbauer@google.com Use wildcards (flutter/flutter#161548) 2025-01-14 jmccandless@google.com Autocomplete Options Width (flutter/flutter#143249) 2025-01-13 jason-simmons@users.noreply.github.com Move the analyzer_benchmark to Mac arm64 devicelab bots (flutter/flutter#161405) 2025-01-13 matanlurey@users.noreply.github.com Remove references to `cirrus`, mostly in doc comments. (flutter/flutter#161529) 2025-01-13 flar@google.com Fix paths when running clang-tidy on git diffs (flutter/flutter#161496) 2025-01-13 yjbanov@google.com [web:a11y] treat empty tappables as buttons (flutter/flutter#161360) 2025-01-13 58190796+MitchellGoodwin@users.noreply.github.com Add route settings to CupertinoSheetRoute (flutter/flutter#161528) 2025-01-13 matanlurey@users.noreply.github.com Copy `linux_host_engine` as `linux_host_engine_test`, removing `archives: [...]`. (flutter/flutter#161532) 2025-01-13 matanlurey@users.noreply.github.com Remove last two references to Cirrus CI. (flutter/flutter#161530) 2025-01-13 codefu@google.com Mark `Mac_mokey microbenchmarks` as flakey (flutter/flutter#161550) 2025-01-13 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Match CupertinoPageTransitionsBuilder animation duration to CupertinoPageRoute (#160241)" (flutter/flutter#161555) 2025-01-13 bkonyi@google.com Add validator execution times to `flutter doctor --verbose` (flutter/flutter#158124) 2025-01-13 matanlurey@users.noreply.github.com Explain more specifically how to use `flutter drive`/what it does (flutter/flutter#161450) 2025-01-13 dhankechakishan@gmail.com Fixed repeated strings for incompatible Gradle or AGP version in `create` command (flutter/flutter#161223) 2025-01-13 matanlurey@users.noreply.github.com Remove `WEB_SHARD_COUNT`, which no longer exists post-Cirrus. (flutter/flutter#161527) 2025-01-13 chinmaygarde@google.com [Impeller] Update guidance on prebuilt artifacts. (flutter/flutter#161251) 2025-01-13 flar@google.com Migrate DisplayList unit tests to DL/Impeller geometry classes (flutter/flutter#161453) 2025-01-13 jmccandless@google.com Context menu button callback docs clarification (flutter/flutter#161451) 2025-01-13 43089218+chika3742@users.noreply.github.com Match CupertinoPageTransitionsBuilder animation duration to CupertinoPageRoute (flutter/flutter#160241) 2025-01-13 codefu@google.com Udpate documentation on the third_party directories (flutter/flutter#161407) 2025-01-13 matanlurey@users.noreply.github.com Propagate environment variables when `flutter drive` is invoked. (flutter/flutter#161452) 2025-01-13 34871572+gmackall@users.noreply.github.com Convert base application name handling to kotlin source (start of FGP kt conversion) (flutter/flutter#155963) 2025-01-13 jonahwilliams@google.com [Impeller] remove API 30 restriction for SurfaceControl testing. (flutter/flutter#161438) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: ... --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 6b965fe696d7..092179ece8de 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -72db8f69e33917cd6e7bd16bb04fc6cee42676d0 +40c2b86fdf650cb95ad69915cd724191ee36de32 From 9ea2befdd2d89d5aeaf99568f66a627411309175 Mon Sep 17 00:00:00 2001 From: misos1 <30872003+misos1@users.noreply.github.com> Date: Wed, 15 Jan 2025 17:34:58 +0100 Subject: [PATCH 21/26] [video_player_avfoundation] fix playback speed resetting (#7657) Calling play is the same as setting the rate to 1.0 (or to `defaultRate` depending on ios version, *documentation in this first link is clearly not updated because it does not mention `defaultRate`*): https://developer.apple.com/documentation/avfoundation/avplayer/1386726-play?language=objc https://developer.apple.com/documentation/avfoundation/avplayer/3929373-defaultrate?language=objc *The second link contains a note about not starting playback by setting the rate to 1.0. I assume this is because of the introduction of `defaultRate` (which can be different than 1.0) and not because `play` may do something more than just setting `rate` as that wording is explicit about setting rate to 1.0, it says nothing about any other value.* This is also why https://github.com/flutter/plugins/pull/4331 did not work well. It was setting `rate` to 1.0 (through `play`) then immediately to the value set by `setPlaybackSpeed`. One of them or both caused change to `playbackLikelyToKeepUp` which triggered `observeValueForKeyPath` with `playbackLikelyToKeepUpContext` which in turn called again `updatePlayingState` where was `rate` again changed first to 1.0 then to another value and so on. In this PR the rate is changed only once and then to the same value, seems when `rate` is assigned but not really changed it does not trigger anything. In issues below `seekTo` can trigger `playbackLikelyToKeepUp` change which will call `updatePlayingState` and reset `rate` to 1.0 through `play`. When the speed is set in dart before initialization then the dart side will set it right after calling `play` but even some time after initialization there is still a flood of events calling `updatePlayingState` and it is likely that some of them will call it after `setPlaybackSpeed`. Actually even when `setPlaybackSpeed` was not called on dart side it (dart side) will always change speed after play to 1.0 so it ignores this new `defaultRate` feature. - fixes https://github.com/flutter/flutter/issues/71264 - fixes https://github.com/flutter/flutter/issues/73643 --- .../video_player_avfoundation/CHANGELOG.md | 4 ++ .../darwin/RunnerTests/VideoPlayerTests.m | 37 +++++++++++ .../FVPVideoPlayer.m | 61 ++++++++++++------- .../video_player_avfoundation/pubspec.yaml | 2 +- 4 files changed, 81 insertions(+), 23 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index 76b14ba3f961..9c6f47ee7dd4 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.6.7 + +* Fixes playback speed resetting. + ## 2.6.6 * Fixes changing global audio session category to be collision free across plugins. diff --git a/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m b/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m index 559c9f089d6d..4150250d168c 100644 --- a/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m +++ b/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m @@ -672,6 +672,8 @@ - (void)testSeekToleranceWhenSeekingToEnd { // Change playback speed. [videoPlayerPlugin setPlaybackSpeed:2 forPlayer:textureId.integerValue error:&error]; XCTAssertNil(error); + [videoPlayerPlugin playPlayer:textureId.integerValue error:&error]; + XCTAssertNil(error); XCTAssertEqual(avPlayer.rate, 2); XCTAssertEqual(avPlayer.timeControlStatus, AVPlayerTimeControlStatusWaitingToPlayAtSpecifiedRate); @@ -839,6 +841,41 @@ - (void)testFailedToLoadVideoEventShouldBeAlwaysSent { [self waitForExpectationsWithTimeout:10.0 handler:nil]; } +- (void)testUpdatePlayingStateShouldNotResetRate { + NSObject *registrar = + [GetPluginRegistry() registrarForPlugin:@"testUpdatePlayingStateShouldNotResetRate"]; + + FVPVideoPlayerPlugin *videoPlayerPlugin = [[FVPVideoPlayerPlugin alloc] + initWithAVFactory:[[StubFVPAVFactory alloc] initWithPlayer:nil output:nil] + displayLinkFactory:nil + registrar:registrar]; + + FlutterError *error; + [videoPlayerPlugin initialize:&error]; + XCTAssertNil(error); + FVPCreationOptions *create = [FVPCreationOptions + makeWithAsset:nil + uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" + packageName:nil + formatHint:nil + httpHeaders:@{}]; + NSNumber *textureId = [videoPlayerPlugin createWithOptions:create error:&error]; + FVPVideoPlayer *player = videoPlayerPlugin.playersByTextureId[textureId]; + + XCTestExpectation *initializedExpectation = [self expectationWithDescription:@"initialized"]; + [player onListenWithArguments:nil + eventSink:^(NSDictionary *event) { + if ([event[@"event"] isEqualToString:@"initialized"]) { + [initializedExpectation fulfill]; + } + }]; + [self waitForExpectationsWithTimeout:10 handler:nil]; + + [videoPlayerPlugin setPlaybackSpeed:2 forPlayer:textureId.integerValue error:&error]; + [videoPlayerPlugin playPlayer:textureId.integerValue error:&error]; + XCTAssertEqual(player.player.rate, 2); +} + #if TARGET_OS_IOS - (void)testVideoPlayerShouldNotOverwritePlayAndRecordNorDefaultToSpeaker { NSObject *registrar = [GetPluginRegistry() diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayer.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayer.m index 5892274a37db..087cf401db54 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayer.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayer.m @@ -29,6 +29,8 @@ @interface FVPVideoPlayer () @property(nonatomic) CGAffineTransform preferredTransform; /// Indicates whether the video player is currently playing. @property(nonatomic, readonly) BOOL isPlaying; +/// The target playback speed requested by the plugin client. +@property(nonatomic, readonly) NSNumber *targetPlaybackSpeed; /// Indicates whether the video player has been initialized. @property(nonatomic, readonly) BOOL isInitialized; /// The updater that drives callbacks to the engine to indicate that a new frame is ready. @@ -323,7 +325,15 @@ - (void)updatePlayingState { return; } if (_isPlaying) { - [_player play]; + // Calling play is the same as setting the rate to 1.0 (or to defaultRate depending on iOS + // version) so last set playback speed must be set here if any instead. + // https://github.com/flutter/flutter/issues/71264 + // https://github.com/flutter/flutter/issues/73643 + if (_targetPlaybackSpeed) { + [self updateRate]; + } else { + [_player play]; + } } else { [_player pause]; } @@ -332,6 +342,32 @@ - (void)updatePlayingState { _displayLink.running = _isPlaying || self.waitingForFrame; } +/// Synchronizes the player's playback rate with targetPlaybackSpeed, constrained by the playback +/// rate capabilities of the player's current item. +- (void)updateRate { + // See https://developer.apple.com/library/archive/qa/qa1772/_index.html for an explanation of + // these checks. + // If status is not AVPlayerItemStatusReadyToPlay then both canPlayFastForward + // and canPlaySlowForward are always false and it is unknown whether video can + // be played at these speeds, updatePlayingState will be called again when + // status changes to AVPlayerItemStatusReadyToPlay. + float speed = _targetPlaybackSpeed.floatValue; + BOOL readyToPlay = _player.currentItem.status == AVPlayerItemStatusReadyToPlay; + if (speed > 2.0 && !_player.currentItem.canPlayFastForward) { + if (!readyToPlay) { + return; + } + speed = 2.0; + } + if (speed < 1.0 && !_player.currentItem.canPlaySlowForward) { + if (!readyToPlay) { + return; + } + speed = 1.0; + } + _player.rate = speed; +} + - (void)sendFailedToLoadVideoEvent { if (_eventSink == nil) { return; @@ -473,27 +509,8 @@ - (void)setVolume:(double)volume { } - (void)setPlaybackSpeed:(double)speed { - // See https://developer.apple.com/library/archive/qa/qa1772/_index.html for an explanation of - // these checks. - if (speed > 2.0 && !_player.currentItem.canPlayFastForward) { - if (_eventSink != nil) { - _eventSink([FlutterError errorWithCode:@"VideoError" - message:@"Video cannot be fast-forwarded beyond 2.0x" - details:nil]); - } - return; - } - - if (speed < 1.0 && !_player.currentItem.canPlaySlowForward) { - if (_eventSink != nil) { - _eventSink([FlutterError errorWithCode:@"VideoError" - message:@"Video cannot be slow-forwarded" - details:nil]); - } - return; - } - - _player.rate = speed; + _targetPlaybackSpeed = @(speed); + [self updatePlayingState]; } - (CVPixelBufferRef)copyPixelBuffer { diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index c20357d78044..c7f9dadb777b 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avfoundation description: iOS and macOS implementation of the video_player plugin. repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.6.6 +version: 2.6.7 environment: sdk: ^3.4.0 From 6d98122ce211cf2622b0a50096b769c3e54d6bb3 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 15 Jan 2025 12:16:20 -0500 Subject: [PATCH 22/26] Roll Flutter (stable) from 17025dd88227 to 68415ad1d920 (4 revisions) (#8434) https://github.com/flutter/flutter/compare/17025dd88227...68415ad1d920 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-stable-packages Please CC stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Flutter (stable): https://github.com/flutter/flutter/issues/new/choose To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_stable.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_stable.version b/.ci/flutter_stable.version index 557fe15614bb..5c6f92135c6e 100644 --- a/.ci/flutter_stable.version +++ b/.ci/flutter_stable.version @@ -1 +1 @@ -17025dd88227cd9532c33fa78f5250d548d87e9a +68415ad1d920f6fe5ec284f5c2febf7c4dd5b0b3 From e949ba7563a169c3a10f4541df13029edbd2293d Mon Sep 17 00:00:00 2001 From: LouiseHsu Date: Wed, 15 Jan 2025 15:58:40 -0800 Subject: [PATCH 23/26] [in_app_purchase_storekit] expose `jsonRepresentation` for Transactions (#8430) Exposes [jsonRepresentation](https://developer.apple.com/documentation/storekit/transaction/jsonrepresentation) for Transactions. Helpful for developers to who want to access the properties of Transaction directly if they arent already exposed. Fixes https://github.com/flutter/flutter/issues/158882 --- .../in_app_purchase_storekit/CHANGELOG.md | 4 +++ .../InAppPurchasePlugin+StoreKit2.swift | 6 ++-- .../StoreKit2/StoreKit2Translators.swift | 3 +- .../Classes/StoreKit2/sk2_pigeon.g.swift | 10 +++++-- .../InAppPurchaseStoreKit2PluginTests.swift | 28 +++++++++++++++++++ .../lib/src/sk2_pigeon.g.dart | 5 ++++ .../pigeons/sk2_pigeon.dart | 2 ++ .../in_app_purchase_storekit/pubspec.yaml | 2 +- 8 files changed, 52 insertions(+), 8 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md index 289f3c36b037..8952b5a0be3f 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.20+4 + +* Exposes `jsonRepresentation` field for transactions. + ## 0.3.20+3 * Fixes `finishTransaction` not completing. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/StoreKit2/InAppPurchasePlugin+StoreKit2.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/StoreKit2/InAppPurchasePlugin+StoreKit2.swift index af4a19345890..672860beec65 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/StoreKit2/InAppPurchasePlugin+StoreKit2.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/StoreKit2/InAppPurchasePlugin+StoreKit2.swift @@ -179,10 +179,10 @@ extension InAppPurchasePlugin: InAppPurchase2API { } } - // MARK: - Convenience Functions + // MARK: - Internal Convenience Functions /// Helper function that fetches and unwraps all verified transactions - private func rawTransactions() async -> [Transaction] { + func rawTransactions() async -> [Transaction] { var transactions: [Transaction] = [] for await verificationResult in Transaction.all { switch verificationResult { @@ -196,7 +196,7 @@ extension InAppPurchasePlugin: InAppPurchase2API { } /// Helper function to fetch specific transaction - private func fetchTransaction(by id: UInt64) async throws -> Transaction? { + func fetchTransaction(by id: UInt64) async throws -> Transaction? { for await result in Transaction.all { switch result { case .verified(let transaction): diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/StoreKit2/StoreKit2Translators.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/StoreKit2/StoreKit2Translators.swift index dc662b9cbdd6..80658bceafa7 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/StoreKit2/StoreKit2Translators.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/StoreKit2/StoreKit2Translators.swift @@ -200,7 +200,8 @@ extension Transaction { purchasedQuantity: Int64(purchasedQuantity), appAccountToken: appAccountToken?.uuidString, restoring: receipt != nil, - receiptData: receipt + receiptData: receipt, + jsonRepresentation: String(decoding: jsonRepresentation, as: UTF8.self) ) } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/StoreKit2/sk2_pigeon.g.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/StoreKit2/sk2_pigeon.g.swift index c6923001143f..5defe53680e1 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/StoreKit2/sk2_pigeon.g.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/StoreKit2/sk2_pigeon.g.swift @@ -318,6 +318,7 @@ struct SK2TransactionMessage { var restoring: Bool var receiptData: String? = nil var error: SK2ErrorMessage? = nil + var jsonRepresentation: String? = nil // swift-format-ignore: AlwaysUseLowerCamelCase static func fromList(_ pigeonVar_list: [Any?]) -> SK2TransactionMessage? { @@ -331,6 +332,7 @@ struct SK2TransactionMessage { let restoring = pigeonVar_list[7] as! Bool let receiptData: String? = nilOrValue(pigeonVar_list[8]) let error: SK2ErrorMessage? = nilOrValue(pigeonVar_list[9]) + let jsonRepresentation: String? = nilOrValue(pigeonVar_list[10]) return SK2TransactionMessage( id: id, @@ -342,7 +344,8 @@ struct SK2TransactionMessage { appAccountToken: appAccountToken, restoring: restoring, receiptData: receiptData, - error: error + error: error, + jsonRepresentation: jsonRepresentation ) } func toList() -> [Any?] { @@ -357,6 +360,7 @@ struct SK2TransactionMessage { restoring, receiptData, error, + jsonRepresentation, ] } } @@ -524,8 +528,8 @@ class InAppPurchase2APISetup { static var codec: FlutterStandardMessageCodec { sk2_pigeonPigeonCodec.shared } /// Sets up an instance of `InAppPurchase2API` to handle messages through the `binaryMessenger`. static func setUp( - binaryMessenger: FlutterBinaryMessenger, api: InAppPurchase2API?, - messageChannelSuffix: String = "" + binaryMessenger: FlutterBinaryMessenger, + api: InAppPurchase2API?, messageChannelSuffix: String = "" ) { let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" let canMakePaymentsChannel = FlutterBasicMessageChannel( diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchaseStoreKit2PluginTests.swift b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchaseStoreKit2PluginTests.swift index b16548adc329..1a861ef73053 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchaseStoreKit2PluginTests.swift +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchaseStoreKit2PluginTests.swift @@ -129,6 +129,34 @@ final class InAppPurchase2PluginTests: XCTestCase { XCTAssert(fetchedProductMsg?.count == 0) } + func testGetTransactionJsonRepresentation() async throws { + let expectation = self.expectation(description: "Purchase request should succeed") + + plugin.purchase(id: "consumable", options: nil) { result in + switch result { + case .success(_): + expectation.fulfill() + case .failure(let error): + XCTFail("Purchase should NOT fail. Failed with \(error)") + } + } + + await fulfillment(of: [expectation], timeout: 5) + + let transaction = try await plugin.fetchTransaction( + by: UInt64(session.allTransactions()[0].originalTransactionIdentifier)) + + guard let transaction = transaction else { + XCTFail("Transaction does not exist.") + return + } + + let jsonRepresentationString = String(decoding: transaction.jsonRepresentation, as: UTF8.self) + + XCTAssert(jsonRepresentationString.localizedStandardContains("Type\":\"Consumable")) + XCTAssert(jsonRepresentationString.localizedStandardContains("storefront\":\"USA")) + } + //TODO(louisehsu): Add testing for lower versions. @available(iOS 17.0, macOS 14.0, *) func testGetProductsWithStoreKitError() async throws { diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/sk2_pigeon.g.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/sk2_pigeon.g.dart index 1339471a088b..c4476badce33 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/sk2_pigeon.g.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/sk2_pigeon.g.dart @@ -306,6 +306,7 @@ class SK2TransactionMessage { this.restoring = false, this.receiptData, this.error, + this.jsonRepresentation, }); int id; @@ -328,6 +329,8 @@ class SK2TransactionMessage { SK2ErrorMessage? error; + String? jsonRepresentation; + Object encode() { return [ id, @@ -340,6 +343,7 @@ class SK2TransactionMessage { restoring, receiptData, error, + jsonRepresentation, ]; } @@ -356,6 +360,7 @@ class SK2TransactionMessage { restoring: result[7]! as bool, receiptData: result[8] as String?, error: result[9] as SK2ErrorMessage?, + jsonRepresentation: result[10] as String?, ); } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pigeons/sk2_pigeon.dart b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/sk2_pigeon.dart index 77239af47236..c6493ce17eed 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pigeons/sk2_pigeon.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/sk2_pigeon.dart @@ -146,6 +146,7 @@ class SK2TransactionMessage { this.appAccountToken, this.error, this.receiptData, + this.jsonRepresentation, this.restoring = false}); final int id; final int originalId; @@ -157,6 +158,7 @@ class SK2TransactionMessage { final bool restoring; final String? receiptData; final SK2ErrorMessage? error; + final String? jsonRepresentation; } class SK2ErrorMessage { diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml index c8e929e0b6b2..07adfb8ab1b6 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_storekit description: An implementation for the iOS and macOS platforms of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/packages/tree/main/packages/in_app_purchase/in_app_purchase_storekit issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.3.20+3 +version: 0.3.20+4 environment: sdk: ^3.4.0 From 973e8b59e24ba80d3c36a2bcfa914fcfd5e19943 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 16 Jan 2025 11:28:48 -0500 Subject: [PATCH 24/26] Roll Flutter from 40c2b86fdf65 to 5517cc9b3b3b (28 revisions) (#8441) https://github.com/flutter/flutter/compare/40c2b86fdf65...5517cc9b3b3b 2025-01-15 37028599+EArminjon@users.noreply.github.com feat: Change default value of keyboardDismissBehavior (flutter/flutter#158580) 2025-01-15 137456488+flutter-pub-roller-bot@users.noreply.github.com Roll pub packages (flutter/flutter#161680) 2025-01-15 jacob.simionato@gmail.com Revert "Autocomplete Options Width" (flutter/flutter#161666) 2025-01-15 stuartmorgan@google.com Update two_dimensional_scrollables triage routing (flutter/flutter#161667) 2025-01-15 flar@google.com [DisplayList] Migrate from SkRSXform to Impeller RSTransform (flutter/flutter#161652) 2025-01-15 engine-flutter-autoroll@skia.org Roll Packages from d1fd6232ec33 to f73cb00e127b (2 revisions) (flutter/flutter#161672) 2025-01-15 bruno.leroux@gmail.com Fix DropdownMenu isCollapsed decoration does not Reduce height (flutter/flutter#161427) 2025-01-15 jason-simmons@users.noreply.github.com Manual roll of Skia to e7b8d078851f (flutter/flutter#161609) 2025-01-15 tessertaha@gmail.com Fix `TabBar` glitchy elastic `Tab` animation (flutter/flutter#161514) 2025-01-15 137456488+flutter-pub-roller-bot@users.noreply.github.com Roll pub packages (flutter/flutter#161643) 2025-01-15 matanlurey@users.noreply.github.com Exclude the top-level `engine` directory from `generate_gradle_lockfiles`. (flutter/flutter#161635) 2025-01-15 137456488+flutter-pub-roller-bot@users.noreply.github.com Roll pub packages (flutter/flutter#161632) 2025-01-15 matanlurey@users.noreply.github.com Refactor `android_engine_test`, make it easier to debug/deflake locally. (flutter/flutter#161534) 2025-01-15 jonahwilliams@google.com [Impeller] null check device buffer in image encoding. (flutter/flutter#161194) 2025-01-15 nabilamevia2003@gmail.com Feature/twitter keyboard (flutter/flutter#161025) 2025-01-15 jessiewong401@gmail.com Fixed XiaoMi statusBar Bug (flutter/flutter#161271) 2025-01-15 goderbauer@google.com Clean up engine's analysis_options.yaml (flutter/flutter#161554) 2025-01-14 34871572+gmackall@users.noreply.github.com Remove `gradle_deprecated_settings` test app, and remove reference from lockfile exclusion yaml (flutter/flutter#161622) 2025-01-14 devoncarew@google.com [deps] remove no-longer-used repo deps (flutter/flutter#161605) 2025-01-14 flar@google.com [DisplayList] remove obsolete use of Skia goemetry objects in DL utils (flutter/flutter#161553) 2025-01-14 dkwingsmt@users.noreply.github.com [Engine] Support asymmetrical rounded superellipses (flutter/flutter#161409) 2025-01-14 737941+loic-sharma@users.noreply.github.com [SwiftPM] Make 'flutter build ios-framework' generate an empty Package.swift (flutter/flutter#161464) 2025-01-14 1961493+harryterkelsen@users.noreply.github.com [canvaskit] Fix GIF decode failure (flutter/flutter#161536) 2025-01-14 jonahwilliams@google.com [Impeller] fixes for AHB swapchains. (flutter/flutter#161562) 2025-01-14 goderbauer@google.com Last Engine<>Framework lint sync (flutter/flutter#161560) 2025-01-14 goderbauer@google.com Check that localization files of stocks app are up-to-date (flutter/flutter#161608) 2025-01-14 43054281+camsim99@users.noreply.github.com [Android] Actually remove dev dependencies from release builds (flutter/flutter#161343) 2025-01-14 bkonyi@google.com Update package revisions to latest (flutter/flutter#161525) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 092179ece8de..5c5d3b2938d4 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -40c2b86fdf650cb95ad69915cd724191ee36de32 +5517cc9b3b3bcf12431b47f495e342a30b738835 From cfc56a2103c469bc6fd3ddf12056147bb24dd1cd Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Fri, 17 Jan 2025 11:51:26 -0800 Subject: [PATCH 25/26] [local_auth_darwin] Handle when FaceID hardware is available but permissions have been denied for the app (#8348) LocalAuth [`-canEvaluatePolicy::`](https://developer.apple.com/documentation/localauthentication/lacontext/canevaluatepolicy(_:error:)?language=objc) returns error `LAErrorBiometryNotAvailable` when either Face ID isn't available at the hardware level, OR the Face ID is available/setup but the permissions for the app were denied. If it returns `LAErrorBiometryNotAvailable`, also check `biometryType` since this _should_ be none if there really is no biometry, but gets populated with [`LABiometryTypeTouchID`](https://developer.apple.com/documentation/localauthentication/labiometrytype/touchid?language=objc) when the FaceID hardware is available, but permission is denied. I tried this on a iPhone 16 Pro which has Face ID but not Touch ID support. Fixes https://github.com/flutter/flutter/issues/160083 --- .../local_auth/local_auth_darwin/CHANGELOG.md | 3 ++- .../Tests/FLALocalAuthPluginTests.swift | 24 +++++++++++++++++-- .../local_auth_darwin/FLALocalAuthPlugin.m | 5 ++++ .../local_auth/local_auth_darwin/pubspec.yaml | 2 +- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/packages/local_auth/local_auth_darwin/CHANGELOG.md b/packages/local_auth/local_auth_darwin/CHANGELOG.md index c5fcf90d34a0..78c17dbc4c9c 100644 --- a/packages/local_auth/local_auth_darwin/CHANGELOG.md +++ b/packages/local_auth/local_auth_darwin/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 1.4.3 +* Handles when biometry hardware is available but permissions have been denied for the app. * Updates minimum supported SDK version to Flutter 3.22/Dart 3.4. ## 1.4.2 diff --git a/packages/local_auth/local_auth_darwin/darwin/Tests/FLALocalAuthPluginTests.swift b/packages/local_auth/local_auth_darwin/darwin/Tests/FLALocalAuthPluginTests.swift index 73da7123b38f..c8e1324a2770 100644 --- a/packages/local_auth/local_auth_darwin/darwin/Tests/FLALocalAuthPluginTests.swift +++ b/packages/local_auth/local_auth_darwin/darwin/Tests/FLALocalAuthPluginTests.swift @@ -447,7 +447,7 @@ class FLALocalAuthPluginTests: XCTestCase { XCTAssertNil(error) } - func testDeviceSupportsBiometrics_withNoBiometricHardware() { + func testDeviceSupportsBiometrics_withBiometryNotAvailable() { let stubAuthContext = StubAuthContext() let alertFactory = StubAlertFactory() let viewProvider = StubViewProvider() @@ -456,7 +456,8 @@ class FLALocalAuthPluginTests: XCTestCase { alertFactory: alertFactory, viewProvider: viewProvider) stubAuthContext.expectBiometrics = true - stubAuthContext.canEvaluateError = NSError(domain: "error", code: 0) + stubAuthContext.canEvaluateError = NSError( + domain: "error", code: LAError.biometryNotAvailable.rawValue) var error: FlutterError? let result = plugin.deviceCanSupportBiometricsWithError(&error) @@ -464,6 +465,25 @@ class FLALocalAuthPluginTests: XCTestCase { XCTAssertNil(error) } + func testDeviceSupportsBiometrics_withBiometryNotAvailableLoadedBiometryType() { + let stubAuthContext = StubAuthContext() + let alertFactory = StubAlertFactory() + let viewProvider = StubViewProvider() + let plugin = FLALocalAuthPlugin( + contextFactory: StubAuthContextFactory(contexts: [stubAuthContext]), + alertFactory: alertFactory, viewProvider: viewProvider) + + stubAuthContext.expectBiometrics = true + stubAuthContext.biometryType = LABiometryType.touchID + stubAuthContext.canEvaluateError = NSError( + domain: "error", code: LAError.biometryNotAvailable.rawValue) + + var error: FlutterError? + let result = plugin.deviceCanSupportBiometricsWithError(&error) + XCTAssertTrue(result!.boolValue) + XCTAssertNil(error) + } + func testGetEnrolledBiometricsWithFaceID() { let stubAuthContext = StubAuthContext() let alertFactory = StubAlertFactory() diff --git a/packages/local_auth/local_auth_darwin/darwin/local_auth_darwin/Sources/local_auth_darwin/FLALocalAuthPlugin.m b/packages/local_auth/local_auth_darwin/darwin/local_auth_darwin/Sources/local_auth_darwin/FLALocalAuthPlugin.m index bc50a59a41f3..33bd25df70e9 100644 --- a/packages/local_auth/local_auth_darwin/darwin/local_auth_darwin/Sources/local_auth_darwin/FLALocalAuthPlugin.m +++ b/packages/local_auth/local_auth_darwin/darwin/local_auth_darwin/Sources/local_auth_darwin/FLALocalAuthPlugin.m @@ -317,6 +317,11 @@ - (nullable NSNumber *)deviceCanSupportBiometricsWithError: if (authError.code == LAErrorBiometryNotEnrolled) { return @YES; } + // Biometry hardware is available, but possibly permissions were denied. + if (authError.code == LAErrorBiometryNotAvailable && + context.biometryType != LABiometryTypeNone) { + return @YES; + } } return @NO; diff --git a/packages/local_auth/local_auth_darwin/pubspec.yaml b/packages/local_auth/local_auth_darwin/pubspec.yaml index 8aff58fe3698..df3b43489f24 100644 --- a/packages/local_auth/local_auth_darwin/pubspec.yaml +++ b/packages/local_auth/local_auth_darwin/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_darwin description: iOS implementation of the local_auth plugin. repository: https://github.com/flutter/packages/tree/main/packages/local_auth/local_auth_darwin issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.4.2 +version: 1.4.3 environment: sdk: ^3.4.0 From 205960d8442295d3f368e442787d002638189ea6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jan 2025 19:52:49 +0000 Subject: [PATCH 26/26] [dependabot]: Bump the gradle-plugin group across 3 directories with 1 update (#8328) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the gradle-plugin group with 1 update in the /packages/interactive_media_ads/android directory: [org.jetbrains.kotlin:kotlin-gradle-plugin](https://github.com/JetBrains/kotlin). Bumps the gradle-plugin group with 1 update in the /packages/shared_preferences/shared_preferences_android/android directory: [org.jetbrains.kotlin:kotlin-gradle-plugin](https://github.com/JetBrains/kotlin). Bumps the gradle-plugin group with 1 update in the /packages/webview_flutter/webview_flutter_android/android directory: [org.jetbrains.kotlin:kotlin-gradle-plugin](https://github.com/JetBrains/kotlin). Updates `org.jetbrains.kotlin:kotlin-gradle-plugin` from 1.9.10 to 2.1.0
Release notes

Sourced from org.jetbrains.kotlin:kotlin-gradle-plugin's releases.

Kotlin 2.1.0

Changelog

Analysis API

New Features

  • KT-68603 KotlinDirectInheritorsProvider: add an option to ignore non-kotlin results

Performance Improvements

  • KT-70757 Performance problem in KaFirVisibilityChecker for KaFirPsiJavaClassSymbol

Fixes

  • KT-70437 Class reference is not resolvable
  • KT-57733 Analysis API: Use optimized ModuleWithDependenciesScopes in combined symbol providers
  • KT-72389 K2: False positive "Redundant 'protected' modifier" for protected property inside protected constructor from private or internal class
  • KT-69190 K2: False-positive "redundant private modifier"
  • KT-64984 Analysis API: Support Wasm target
  • KT-70375 K2: NPE at org.jetbrains.kotlin.analysis.api.fir.symbols.KaFirNamedClassSymbolBase.createPointer
  • KT-71259 K2 evaluator: Invalid smart cast info collecting for Code Fragments
  • KT-69360 Lack of implicit receiver for the last statement under lambda in scripts
  • KT-70890 Analysis API: Experiment with weak references to LL FIR/analysis sessions in session caches
  • KT-70657 Analysis API: Inner types from classes with generics are incorrectly represented by the compiled jars
  • KT-71055 Suspend calls inside 'analyze()' break the block guarantees
  • KT-70815 Analysis API: Implement stop-the-world session invalidation
  • KT-69819 K2 IDE: LHS type in callable references is unresolved when it has type arguments and is qualified
  • KT-68761 Analysis API: Experiment with limited-size cache in KaFirSessionProvider
  • KT-70384 Analysis API Standalone: The same class in the same two renamed jars is unresolved
  • KT-71067 Exceptions from references cancel Find Usages
  • KT-69535 Redesign 'containingSymbol'
  • KT-71025 K2 IDE: Scopes in "importingScopeContext" have reversed ordering and "indexInTower" values
  • KT-67483 K2 IDE: Serializable plugin causes infinite resolve recursion when there is a star import from a class with annotation call
  • KT-69416 K2 IDE / Completion: “No classifier found” on simple value creating
  • KT-70257 CCE: class kotlin.UInt cannot be cast to class java.lang.Number
  • KT-70376 K2 IDE / Kotlin Debugger: IAE “Only componentN functions should be cached this way, but got: toString” on evaluating toString() method for value class
  • KT-70264 AA: service registration via XML fails with AbstractMethodError in Lint CLI
  • KT-69950 Analysis API: Introduce isSubtypeOf(ClassId)
  • KT-68625 K2: “lazyResolveToPhase(STATUS) cannot be called from a transformer with a phase STATUS.”
  • KT-67665 K2: contract violation for value class with a constructor parameter with an implicit type
  • KT-67009 Analysis API: Add abbreviated type tests for type aliases from source modules
  • KT-69977 KaFirFunctionalType#getAbbreviation is always null
  • KT-68341 Analysis API: Expanded function types from libraries don't have an abbreviated type
  • KT-68857 Analysis API: Refactor annotations
  • KT-70386 Do not filter out overloads from different libraries in dangling files
  • KT-65552 K2: CANNOT_CHECK_FOR_ERASED in KtTypeCodeFragment
  • KT-65803 K2: Analysis API: KtFirTypeProvider#getSubstitutedSuperTypes throws an exception in the case of "Wrong number of type arguments"
  • KT-68896 Support VirtualFile binary dependency inputs to Analysis API modules
  • KT-69395 K2 IDE: incorrect overload selection from binary dependencies in a shared native source set

... (truncated)

Changelog

Sourced from org.jetbrains.kotlin:kotlin-gradle-plugin's changelog.

2.1.0

Analysis API

New Features

  • KT-68603 KotlinDirectInheritorsProvider: add an option to ignore non-kotlin results

Performance Improvements

  • KT-70757 Performance problem in KaFirVisibilityChecker for KaFirPsiJavaClassSymbol

Fixes

  • KT-70437 Class reference is not resolvable
  • KT-57733 Analysis API: Use optimized ModuleWithDependenciesScopes in combined symbol providers
  • KT-72389 K2: False positive "Redundant 'protected' modifier" for protected property inside protected constructor from private or internal class
  • KT-69190 K2: False-positive "redundant private modifier"
  • KT-64984 Analysis API: Support Wasm target
  • KT-70375 K2: NPE at org.jetbrains.kotlin.analysis.api.fir.symbols.KaFirNamedClassSymbolBase.createPointer
  • KT-71259 K2 evaluator: Invalid smart cast info collecting for Code Fragments
  • KT-69360 Lack of implicit receiver for the last statement under lambda in scripts
  • KT-70890 Analysis API: Experiment with weak references to LL FIR/analysis sessions in session caches
  • KT-70657 Analysis API: Inner types from classes with generics are incorrectly represented by the compiled jars
  • KT-71055 Suspend calls inside 'analyze()' break the block guarantees
  • KT-70815 Analysis API: Implement stop-the-world session invalidation
  • KT-69819 K2 IDE: LHS type in callable references is unresolved when it has type arguments and is qualified
  • KT-68761 Analysis API: Experiment with limited-size cache in KaFirSessionProvider
  • KT-70384 Analysis API Standalone: The same class in the same two renamed jars is unresolved
  • KT-71067 Exceptions from references cancel Find Usages
  • KT-69535 Redesign 'containingSymbol'
  • KT-71025 K2 IDE: Scopes in "importingScopeContext" have reversed ordering and "indexInTower" values
  • KT-67483 K2 IDE: Serializable plugin causes infinite resolve recursion when there is a star import from a class with annotation call
  • KT-69416 K2 IDE / Completion: “No classifier found” on simple value creating
  • KT-70257 CCE: class kotlin.UInt cannot be cast to class java.lang.Number
  • KT-70376 K2 IDE / Kotlin Debugger: IAE “Only componentN functions should be cached this way, but got: toString” on evaluating toString() method for value class
  • KT-70264 AA: service registration via XML fails with AbstractMethodError in Lint CLI
  • KT-69950 Analysis API: Introduce isSubtypeOf(ClassId)
  • KT-68625 K2: “lazyResolveToPhase(STATUS) cannot be called from a transformer with a phase STATUS.”
  • KT-67665 K2: contract violation for value class with a constructor parameter with an implicit type
  • KT-67009 Analysis API: Add abbreviated type tests for type aliases from source modules
  • KT-69977 KaFirFunctionalType#getAbbreviation is always null
  • KT-68341 Analysis API: Expanded function types from libraries don't have an abbreviated type
  • KT-68857 Analysis API: Refactor annotations
  • KT-70386 Do not filter out overloads from different libraries in dangling files
  • KT-65552 K2: CANNOT_CHECK_FOR_ERASED in KtTypeCodeFragment
  • KT-65803 K2: Analysis API: KtFirTypeProvider#getSubstitutedSuperTypes throws an exception in the case of "Wrong number of type arguments"
  • KT-68896 Support VirtualFile binary dependency inputs to Analysis API modules
  • KT-69395 K2 IDE: incorrect overload selection from binary dependencies in a shared native source set
  • KT-68573 ISE: "Unexpected constant value (kotlin/annotation/AnnotationTarget, CLASS)" at Kt1DescUtilsKt.toKtConstantValue()

... (truncated)

Commits
  • 5dd9cea Add ChangeLog for 2.1.0
  • be31f19 [Gradle] Fix documentation publishing to Kotlinlang
  • f959bf2 Add ChangeLog for 2.1.0-RC2
  • b21df7b [Gradle] Update info about versioning
  • a7dabb6 [Gradle] Fix templates extraction
  • 858b914 [Gradle] Add KDoc for KotlinTargetsDsl
  • 1026b47 [Gradle] Add KDoc for KotlinTargetWithTests
  • 54452c0 [Gradle] Add KDoc for KotlinTestRun
  • d10e47a [Gradle] Add KDoc for KotlinExecution
  • 1208eec fix: clarifications and language polishing
  • Additional commits viewable in compare view

Updates `org.jetbrains.kotlin:kotlin-gradle-plugin` from 1.7.10 to 2.1.0
Release notes

Sourced from org.jetbrains.kotlin:kotlin-gradle-plugin's releases.

Kotlin 2.1.0

Changelog

Analysis API

New Features

  • KT-68603 KotlinDirectInheritorsProvider: add an option to ignore non-kotlin results

Performance Improvements

  • KT-70757 Performance problem in KaFirVisibilityChecker for KaFirPsiJavaClassSymbol

Fixes

  • KT-70437 Class reference is not resolvable
  • KT-57733 Analysis API: Use optimized ModuleWithDependenciesScopes in combined symbol providers
  • KT-72389 K2: False positive "Redundant 'protected' modifier" for protected property inside protected constructor from private or internal class
  • KT-69190 K2: False-positive "redundant private modifier"
  • KT-64984 Analysis API: Support Wasm target
  • KT-70375 K2: NPE at org.jetbrains.kotlin.analysis.api.fir.symbols.KaFirNamedClassSymbolBase.createPointer
  • KT-71259 K2 evaluator: Invalid smart cast info collecting for Code Fragments
  • KT-69360 Lack of implicit receiver for the last statement under lambda in scripts
  • KT-70890 Analysis API: Experiment with weak references to LL FIR/analysis sessions in session caches
  • KT-70657 Analysis API: Inner types from classes with generics are incorrectly represented by the compiled jars
  • KT-71055 Suspend calls inside 'analyze()' break the block guarantees
  • KT-70815 Analysis API: Implement stop-the-world session invalidation
  • KT-69819 K2 IDE: LHS type in callable references is unresolved when it has type arguments and is qualified
  • KT-68761 Analysis API: Experiment with limited-size cache in KaFirSessionProvider
  • KT-70384 Analysis API Standalone: The same class in the same two renamed jars is unresolved
  • KT-71067 Exceptions from references cancel Find Usages
  • KT-69535 Redesign 'containingSymbol'
  • KT-71025 K2 IDE: Scopes in "importingScopeContext" have reversed ordering and "indexInTower" values
  • KT-67483 K2 IDE: Serializable plugin causes infinite resolve recursion when there is a star import from a class with annotation call
  • KT-69416 K2 IDE / Completion: “No classifier found” on simple value creating
  • KT-70257 CCE: class kotlin.UInt cannot be cast to class java.lang.Number
  • KT-70376 K2 IDE / Kotlin Debugger: IAE “Only componentN functions should be cached this way, but got: toString” on evaluating toString() method for value class
  • KT-70264 AA: service registration via XML fails with AbstractMethodError in Lint CLI
  • KT-69950 Analysis API: Introduce isSubtypeOf(ClassId)
  • KT-68625 K2: “lazyResolveToPhase(STATUS) cannot be called from a transformer with a phase STATUS.”
  • KT-67665 K2: contract violation for value class with a constructor parameter with an implicit type
  • KT-67009 Analysis API: Add abbreviated type tests for type aliases from source modules
  • KT-69977 KaFirFunctionalType#getAbbreviation is always null
  • KT-68341 Analysis API: Expanded function types from libraries don't have an abbreviated type
  • KT-68857 Analysis API: Refactor annotations
  • KT-70386 Do not filter out overloads from different libraries in dangling files
  • KT-65552 K2: CANNOT_CHECK_FOR_ERASED in KtTypeCodeFragment
  • KT-65803 K2: Analysis API: KtFirTypeProvider#getSubstitutedSuperTypes throws an exception in the case of "Wrong number of type arguments"
  • KT-68896 Support VirtualFile binary dependency inputs to Analysis API modules
  • KT-69395 K2 IDE: incorrect overload selection from binary dependencies in a shared native source set

... (truncated)

Changelog

Sourced from org.jetbrains.kotlin:kotlin-gradle-plugin's changelog.

2.1.0

Analysis API

New Features

  • KT-68603 KotlinDirectInheritorsProvider: add an option to ignore non-kotlin results

Performance Improvements

  • KT-70757 Performance problem in KaFirVisibilityChecker for KaFirPsiJavaClassSymbol

Fixes

  • KT-70437 Class reference is not resolvable
  • KT-57733 Analysis API: Use optimized ModuleWithDependenciesScopes in combined symbol providers
  • KT-72389 K2: False positive "Redundant 'protected' modifier" for protected property inside protected constructor from private or internal class
  • KT-69190 K2: False-positive "redundant private modifier"
  • KT-64984 Analysis API: Support Wasm target
  • KT-70375 K2: NPE at org.jetbrains.kotlin.analysis.api.fir.symbols.KaFirNamedClassSymbolBase.createPointer
  • KT-71259 K2 evaluator: Invalid smart cast info collecting for Code Fragments
  • KT-69360 Lack of implicit receiver for the last statement under lambda in scripts
  • KT-70890 Analysis API: Experiment with weak references to LL FIR/analysis sessions in session caches
  • KT-70657 Analysis API: Inner types from classes with generics are incorrectly represented by the compiled jars
  • KT-71055 Suspend calls inside 'analyze()' break the block guarantees
  • KT-70815 Analysis API: Implement stop-the-world session invalidation
  • KT-69819 K2 IDE: LHS type in callable references is unresolved when it has type arguments and is qualified
  • KT-68761 Analysis API: Experiment with limited-size cache in KaFirSessionProvider
  • KT-70384 Analysis API Standalone: The same class in the same two renamed jars is unresolved
  • KT-71067 Exceptions from references cancel Find Usages
  • KT-69535 Redesign 'containingSymbol'
  • KT-71025 K2 IDE: Scopes in "importingScopeContext" have reversed ordering and "indexInTower" values
  • KT-67483 K2 IDE: Serializable plugin causes infinite resolve recursion when there is a star import from a class with annotation call
  • KT-69416 K2 IDE / Completion: “No classifier found” on simple value creating
  • KT-70257 CCE: class kotlin.UInt cannot be cast to class java.lang.Number
  • KT-70376 K2 IDE / Kotlin Debugger: IAE “Only componentN functions should be cached this way, but got: toString” on evaluating toString() method for value class
  • KT-70264 AA: service registration via XML fails with AbstractMethodError in Lint CLI
  • KT-69950 Analysis API: Introduce isSubtypeOf(ClassId)
  • KT-68625 K2: “lazyResolveToPhase(STATUS) cannot be called from a transformer with a phase STATUS.”
  • KT-67665 K2: contract violation for value class with a constructor parameter with an implicit type
  • KT-67009 Analysis API: Add abbreviated type tests for type aliases from source modules
  • KT-69977 KaFirFunctionalType#getAbbreviation is always null
  • KT-68341 Analysis API: Expanded function types from libraries don't have an abbreviated type
  • KT-68857 Analysis API: Refactor annotations
  • KT-70386 Do not filter out overloads from different libraries in dangling files
  • KT-65552 K2: CANNOT_CHECK_FOR_ERASED in KtTypeCodeFragment
  • KT-65803 K2: Analysis API: KtFirTypeProvider#getSubstitutedSuperTypes throws an exception in the case of "Wrong number of type arguments"
  • KT-68896 Support VirtualFile binary dependency inputs to Analysis API modules
  • KT-69395 K2 IDE: incorrect overload selection from binary dependencies in a shared native source set
  • KT-68573 ISE: "Unexpected constant value (kotlin/annotation/AnnotationTarget, CLASS)" at Kt1DescUtilsKt.toKtConstantValue()

... (truncated)

Commits
  • 5dd9cea Add ChangeLog for 2.1.0
  • be31f19 [Gradle] Fix documentation publishing to Kotlinlang
  • f959bf2 Add ChangeLog for 2.1.0-RC2
  • b21df7b [Gradle] Update info about versioning
  • a7dabb6 [Gradle] Fix templates extraction
  • 858b914 [Gradle] Add KDoc for KotlinTargetsDsl
  • 1026b47 [Gradle] Add KDoc for KotlinTargetWithTests
  • 54452c0 [Gradle] Add KDoc for KotlinTestRun
  • d10e47a [Gradle] Add KDoc for KotlinExecution
  • 1208eec fix: clarifications and language polishing
  • Additional commits viewable in compare view

Updates `org.jetbrains.kotlin:kotlin-gradle-plugin` from 1.9.10 to 2.1.0
Release notes

Sourced from org.jetbrains.kotlin:kotlin-gradle-plugin's releases.

Kotlin 2.1.0

Changelog

Analysis API

New Features

  • KT-68603 KotlinDirectInheritorsProvider: add an option to ignore non-kotlin results

Performance Improvements

  • KT-70757 Performance problem in KaFirVisibilityChecker for KaFirPsiJavaClassSymbol

Fixes

  • KT-70437 Class reference is not resolvable
  • KT-57733 Analysis API: Use optimized ModuleWithDependenciesScopes in combined symbol providers
  • KT-72389 K2: False positive "Redundant 'protected' modifier" for protected property inside protected constructor from private or internal class
  • KT-69190 K2: False-positive "redundant private modifier"
  • KT-64984 Analysis API: Support Wasm target
  • KT-70375 K2: NPE at org.jetbrains.kotlin.analysis.api.fir.symbols.KaFirNamedClassSymbolBase.createPointer
  • KT-71259 K2 evaluator: Invalid smart cast info collecting for Code Fragments
  • KT-69360 Lack of implicit receiver for the last statement under lambda in scripts
  • KT-70890 Analysis API: Experiment with weak references to LL FIR/analysis sessions in session caches
  • KT-70657 Analysis API: Inner types from classes with generics are incorrectly represented by the compiled jars
  • KT-71055 Suspend calls inside 'analyze()' break the block guarantees
  • KT-70815 Analysis API: Implement stop-the-world session invalidation
  • KT-69819 K2 IDE: LHS type in callable references is unresolved when it has type arguments and is qualified
  • KT-68761 Analysis API: Experiment with limited-size cache in KaFirSessionProvider
  • KT-70384 Analysis API Standalone: The same class in the same two renamed jars is unresolved
  • KT-71067 Exceptions from references cancel Find Usages
  • KT-69535 Redesign 'containingSymbol'
  • KT-71025 K2 IDE: Scopes in "importingScopeContext" have reversed ordering and "indexInTower" values
  • KT-67483 K2 IDE: Serializable plugin causes infinite resolve recursion when there is a star import from a class with annotation call
  • KT-69416 K2 IDE / Completion: “No classifier found” on simple value creating
  • KT-70257 CCE: class kotlin.UInt cannot be cast to class java.lang.Number
  • KT-70376 K2 IDE / Kotlin Debugger: IAE “Only componentN functions should be cached this way, but got: toString” on evaluating toString() method for value class
  • KT-70264 AA: service registration via XML fails with AbstractMethodError in Lint CLI
  • KT-69950 Analysis API: Introduce isSubtypeOf(ClassId)
  • KT-68625 K2: “lazyResolveToPhase(STATUS) cannot be called from a transformer with a phase STATUS.”
  • KT-67665 K2: contract violation for value class with a constructor parameter with an implicit type
  • KT-67009 Analysis API: Add abbreviated type tests for type aliases from source modules
  • KT-69977 KaFirFunctionalType#getAbbreviation is always null
  • KT-68341 Analysis API: Expanded function types from libraries don't have an abbreviated type
  • KT-68857 Analysis API: Refactor annotations
  • KT-70386 Do not filter out overloads from different libraries in dangling files
  • KT-65552 K2: CANNOT_CHECK_FOR_ERASED in KtTypeCodeFragment
  • KT-65803 K2: Analysis API: KtFirTypeProvider#getSubstitutedSuperTypes throws an exception in the case of "Wrong number of type arguments"
  • KT-68896 Support VirtualFile binary dependency inputs to Analysis API modules
  • KT-69395 K2 IDE: incorrect overload selection from binary dependencies in a shared native source set

... (truncated)

Changelog

Sourced from org.jetbrains.kotlin:kotlin-gradle-plugin's changelog.

2.1.0

Analysis API

New Features

  • KT-68603 KotlinDirectInheritorsProvider: add an option to ignore non-kotlin results

Performance Improvements

  • KT-70757 Performance problem in KaFirVisibilityChecker for KaFirPsiJavaClassSymbol

Fixes

  • KT-70437 Class reference is not resolvable
  • KT-57733 Analysis API: Use optimized ModuleWithDependenciesScopes in combined symbol providers
  • KT-72389 K2: False positive "Redundant 'protected' modifier" for protected property inside protected constructor from private or internal class
  • KT-69190 K2: False-positive "redundant private modifier"
  • KT-64984 Analysis API: Support Wasm target
  • KT-70375 K2: NPE at org.jetbrains.kotlin.analysis.api.fir.symbols.KaFirNamedClassSymbolBase.createPointer
  • KT-71259 K2 evaluator: Invalid smart cast info collecting for Code Fragments
  • KT-69360 Lack of implicit receiver for the last statement under lambda in scripts
  • KT-70890 Analysis API: Experiment with weak references to LL FIR/analysis sessions in session caches
  • KT-70657 Analysis API: Inner types from classes with generics are incorrectly represented by the compiled jars
  • KT-71055 Suspend calls inside 'analyze()' break the block guarantees
  • KT-70815 Analysis API: Implement stop-the-world session invalidation
  • KT-69819 K2 IDE: LHS type in callable references is unresolved when it has type arguments and is qualified
  • KT-68761 Analysis API: Experiment with limited-size cache in KaFirSessionProvider
  • KT-70384 Analysis API Standalone: The same class in the same two renamed jars is unresolved
  • KT-71067 Exceptions from references cancel Find Usages
  • KT-69535 Redesign 'containingSymbol'
  • KT-71025 K2 IDE: Scopes in "importingScopeContext" have reversed ordering and "indexInTower" values
  • KT-67483 K2 IDE: Serializable plugin causes infinite resolve recursion when there is a star import from a class with annotation call
  • KT-69416 K2 IDE / Completion: “No classifier found” on simple value creating
  • KT-70257 CCE: class kotlin.UInt cannot be cast to class java.lang.Number
  • KT-70376 K2 IDE / Kotlin Debugger: IAE “Only componentN functions should be cached this way, but got: toString” on evaluating toString() method for value class
  • KT-70264 AA: service registration via XML fails with AbstractMethodError in Lint CLI
  • KT-69950 Analysis API: Introduce isSubtypeOf(ClassId)
  • KT-68625 K2: “lazyResolveToPhase(STATUS) cannot be called from a transformer with a phase STATUS.”
  • KT-67665 K2: contract violation for value class with a constructor parameter with an implicit type
  • KT-67009 Analysis API: Add abbreviated type tests for type aliases from source modules
  • KT-69977 KaFirFunctionalType#getAbbreviation is always null
  • KT-68341 Analysis API: Expanded function types from libraries don't have an abbreviated type
  • KT-68857 Analysis API: Refactor annotations
  • KT-70386 Do not filter out overloads from different libraries in dangling files
  • KT-65552 K2: CANNOT_CHECK_FOR_ERASED in KtTypeCodeFragment
  • KT-65803 K2: Analysis API: KtFirTypeProvider#getSubstitutedSuperTypes throws an exception in the case of "Wrong number of type arguments"
  • KT-68896 Support VirtualFile binary dependency inputs to Analysis API modules
  • KT-69395 K2 IDE: incorrect overload selection from binary dependencies in a shared native source set
  • KT-68573 ISE: "Unexpected constant value (kotlin/annotation/AnnotationTarget, CLASS)" at Kt1DescUtilsKt.toKtConstantValue()

... (truncated)

Commits
  • 5dd9cea Add ChangeLog for 2.1.0
  • be31f19 [Gradle] Fix documentation publishing to Kotlinlang
  • f959bf2 Add ChangeLog for 2.1.0-RC2
  • b21df7b [Gradle] Update info about versioning
  • a7dabb6 [Gradle] Fix templates extraction
  • 858b914 [Gradle] Add KDoc for KotlinTargetsDsl
  • 1026b47 [Gradle] Add KDoc for KotlinTargetWithTests
  • 54452c0 [Gradle] Add KDoc for KotlinTestRun
  • d10e47a [Gradle] Add KDoc for KotlinExecution
  • 1208eec fix: clarifications and language polishing
  • Additional commits viewable in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
--- packages/interactive_media_ads/CHANGELOG.md | 4 ++++ packages/interactive_media_ads/android/build.gradle | 2 +- .../packages/interactive_media_ads/AdsRequestProxyApi.kt | 2 +- .../interactive_media_ads/AdsRequestProxyAPIDelegate.swift | 2 +- packages/interactive_media_ads/pubspec.yaml | 2 +- .../shared_preferences_android/CHANGELOG.md | 4 ++++ .../shared_preferences_android/android/build.gradle | 2 +- .../shared_preferences_android/pubspec.yaml | 2 +- packages/webview_flutter/webview_flutter_android/CHANGELOG.md | 4 ++++ .../webview_flutter_android/android/build.gradle | 2 +- packages/webview_flutter/webview_flutter_android/pubspec.yaml | 2 +- 11 files changed, 20 insertions(+), 8 deletions(-) diff --git a/packages/interactive_media_ads/CHANGELOG.md b/packages/interactive_media_ads/CHANGELOG.md index 77ff5ea6b226..9986455137b3 100644 --- a/packages/interactive_media_ads/CHANGELOG.md +++ b/packages/interactive_media_ads/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.3+5 + +* Bumps gradle-plugin to 2.1.0. + ## 0.2.3+4 * Adds remaining methods for internal wrapper of the iOS native `IMAAdDisplayContainer`. diff --git a/packages/interactive_media_ads/android/build.gradle b/packages/interactive_media_ads/android/build.gradle index 2a3dc43d1aa6..789f58207eac 100644 --- a/packages/interactive_media_ads/android/build.gradle +++ b/packages/interactive_media_ads/android/build.gradle @@ -2,7 +2,7 @@ group 'dev.flutter.packages.interactive_media_ads' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.9.10' + ext.kotlin_version = '2.1.0' repositories { google() mavenCentral() diff --git a/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/AdsRequestProxyApi.kt b/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/AdsRequestProxyApi.kt index 3dec0274efbd..eb886b70af3a 100644 --- a/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/AdsRequestProxyApi.kt +++ b/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/AdsRequestProxyApi.kt @@ -21,7 +21,7 @@ class AdsRequestProxyApi(override val pigeonRegistrar: ProxyApiRegistrar) : * * This must match the version in pubspec.yaml. */ - const val pluginVersion = "0.2.3+4" + const val pluginVersion = "0.2.3+5" } override fun setAdTagUrl(pigeon_instance: AdsRequest, adTagUrl: String) { diff --git a/packages/interactive_media_ads/ios/interactive_media_ads/Sources/interactive_media_ads/AdsRequestProxyAPIDelegate.swift b/packages/interactive_media_ads/ios/interactive_media_ads/Sources/interactive_media_ads/AdsRequestProxyAPIDelegate.swift index fbd8ffff3be6..1707e13ffab9 100644 --- a/packages/interactive_media_ads/ios/interactive_media_ads/Sources/interactive_media_ads/AdsRequestProxyAPIDelegate.swift +++ b/packages/interactive_media_ads/ios/interactive_media_ads/Sources/interactive_media_ads/AdsRequestProxyAPIDelegate.swift @@ -13,7 +13,7 @@ class AdsRequestProxyAPIDelegate: PigeonApiDelegateIMAAdsRequest { /// The current version of the `interactive_media_ads` plugin. /// /// This must match the version in pubspec.yaml. - static let pluginVersion = "0.2.3+4" + static let pluginVersion = "0.2.3+5" func pigeonDefaultConstructor( pigeonApi: PigeonApiIMAAdsRequest, adTagUrl: String, adDisplayContainer: IMAAdDisplayContainer, diff --git a/packages/interactive_media_ads/pubspec.yaml b/packages/interactive_media_ads/pubspec.yaml index 6ac1fc8a1f38..bcf59fdb9633 100644 --- a/packages/interactive_media_ads/pubspec.yaml +++ b/packages/interactive_media_ads/pubspec.yaml @@ -2,7 +2,7 @@ name: interactive_media_ads description: A Flutter plugin for using the Interactive Media Ads SDKs on Android and iOS. repository: https://github.com/flutter/packages/tree/main/packages/interactive_media_ads issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+interactive_media_ads%22 -version: 0.2.3+4 # This must match the version in +version: 0.2.3+5 # This must match the version in # `android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/AdsRequestProxyApi.kt` and # `ios/interactive_media_ads/Sources/interactive_media_ads/AdsRequestProxyAPIDelegate.swift` diff --git a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md index b47688db6035..197babaebb6c 100644 --- a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.4.2 + +* Bumps gradle-plugin to 2.1.0. + ## 2.4.1 * Bumps kotlin version to 1.9.10 androidx.datastore:datastore from 1.0.0 to 1.1.1. diff --git a/packages/shared_preferences/shared_preferences_android/android/build.gradle b/packages/shared_preferences/shared_preferences_android/android/build.gradle index b25d8d6934e9..16bbdace333e 100644 --- a/packages/shared_preferences/shared_preferences_android/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/android/build.gradle @@ -2,7 +2,7 @@ group 'io.flutter.plugins.sharedpreferences' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.9.10' + ext.kotlin_version = '2.1.0' repositories { google() mavenCentral() diff --git a/packages/shared_preferences/shared_preferences_android/pubspec.yaml b/packages/shared_preferences/shared_preferences_android/pubspec.yaml index f0156474ca21..e2f143a4a87e 100644 --- a/packages/shared_preferences/shared_preferences_android/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_android/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_android description: Android implementation of the shared_preferences plugin repository: https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.4.1 +version: 2.4.2 environment: sdk: ^3.5.0 diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 539c54c8a9fe..b883c27cde75 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.3.1 + +* Bumps gradle-plugin to 2.1.0. + ## 4.3.0 * Adds support for disabling content URL access within WebView and disabling the Geolocation API. diff --git a/packages/webview_flutter/webview_flutter_android/android/build.gradle b/packages/webview_flutter/webview_flutter_android/android/build.gradle index 12c459eaf8b8..c7b9a14357f7 100644 --- a/packages/webview_flutter/webview_flutter_android/android/build.gradle +++ b/packages/webview_flutter/webview_flutter_android/android/build.gradle @@ -2,7 +2,7 @@ group 'io.flutter.plugins.webviewflutter' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.9.10' + ext.kotlin_version = '2.1.0' repositories { google() mavenCentral() diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index bd248b40f5d9..4796e93732c6 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_android description: A Flutter plugin that provides a WebView widget on Android. repository: https://github.com/flutter/packages/tree/main/packages/webview_flutter/webview_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 4.3.0 +version: 4.3.1 environment: sdk: ^3.5.0