diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 0051c4f7652..fae21094cd7 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,6 +1,7 @@ ## NEXT * Updates minimum supported SDK version to Flutter 3.29/Dart 3.7. +* Updates README to reflect that only Android API 24+ is supported. ## 0.11.2 diff --git a/packages/camera/camera/README.md b/packages/camera/camera/README.md index 15d23d88a4e..cb016d68c96 100644 --- a/packages/camera/camera/README.md +++ b/packages/camera/camera/README.md @@ -8,7 +8,7 @@ A Flutter plugin for iOS, Android and Web allowing access to the device cameras. | | Android | iOS | Web | |----------------|---------|-----------|------------------------| -| **Support** | SDK 23+ | iOS 12.0+ | [See `camera_web `][1] | +| **Support** | SDK 24+ | iOS 12.0+ | [See `camera_web `][1] | ## Features @@ -37,12 +37,6 @@ If editing `Info.plist` as text, add: ### Android -Change the minimum Android sdk version to 23 (or higher) in your `android/app/build.gradle` file. - -```groovy -minSdk = 23 -``` - The endorsed [`camera_android_camerax`][2] implementation of the camera plugin built with CameraX has better support for more devices than `camera_android`, but has some limitations; please see [this list][3] for more details. If you wish to use the [`camera_android`][4] implementation of the camera plugin diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index 98abc270191..c43f2ab7e91 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.10.10+7 + +* Updates minimum supported SDK version to Flutter 3.35. +* Removes code for supporting API 21-23. + ## 0.10.10+6 * Bumps com.android.tools.build:gradle to 8.12.1. diff --git a/packages/camera/camera_android/android/build.gradle b/packages/camera/camera_android/android/build.gradle index 61c2171db64..ff2c0157444 100644 --- a/packages/camera/camera_android/android/build.gradle +++ b/packages/camera/camera_android/android/build.gradle @@ -34,7 +34,7 @@ buildFeatures { compileSdk = 36 defaultConfig { - minSdkVersion 21 + minSdkVersion 24 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } lintOptions { diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 9d398b2e025..94c8b9ed899 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -891,12 +891,7 @@ public void pauseVideoRecording() { } try { - if (SdkCapabilityChecker.supportsVideoPause()) { - mediaRecorder.pause(); - } else { - throw new Messages.FlutterError( - "videoRecordingFailed", "pauseVideoRecording requires Android API +24.", null); - } + mediaRecorder.pause(); } catch (IllegalStateException e) { throw new Messages.FlutterError("videoRecordingFailed", e.getMessage(), null); } @@ -908,12 +903,7 @@ public void resumeVideoRecording() { } try { - if (SdkCapabilityChecker.supportsVideoPause()) { - mediaRecorder.resume(); - } else { - throw new Messages.FlutterError( - "videoRecordingFailed", "resumeVideoRecording requires Android API +24.", null); - } + mediaRecorder.resume(); } catch (IllegalStateException e) { throw new Messages.FlutterError("videoRecordingFailed", e.getMessage(), null); } diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraApiImpl.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraApiImpl.java index 324403e929e..b9e34a9a090 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraApiImpl.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraApiImpl.java @@ -86,9 +86,6 @@ private Long instantiateCamera(String cameraName, Messages.PlatformMediaSettings return flutterSurfaceTexture.id(); } - // We move catching CameraAccessException out of onMethodCall because it causes a crash - // on plugin registration for sdks incompatible with Camera2 (< 21). We want this plugin to - // to be able to compile with <21 sdks for apps that want the camera and support earlier version. @SuppressWarnings("ConstantConditions") private void handleException(Exception exception, Messages.Result result) { // The code below exactly preserves the format of the native exceptions generated by pre-Pigeon diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java index 2c3eda880de..8bf5de0046f 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java @@ -224,7 +224,6 @@ public interface CameraProperties { * @return android.graphics.Rect Area of the image sensor which corresponds to active pixels prior * to the application of any geometric distortion correction. */ - @RequiresApi(api = VERSION_CODES.M) @NonNull Rect getSensorInfoPreCorrectionActiveArraySize(); diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPropertiesImpl.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPropertiesImpl.java index dc8e9b736f2..bedb69ef0a2 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPropertiesImpl.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPropertiesImpl.java @@ -140,7 +140,6 @@ public Size getSensorInfoPixelArraySize() { return cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE); } - @RequiresApi(api = VERSION_CODES.M) @NonNull @Override public Rect getSensorInfoPreCorrectionActiveArraySize() { diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/SdkCapabilityChecker.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/SdkCapabilityChecker.java index 1ff512672d9..0770f257838 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/SdkCapabilityChecker.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/SdkCapabilityChecker.java @@ -34,24 +34,12 @@ public static boolean supportsEncoderProfiles() { return SDK_VERSION >= Build.VERSION_CODES.S; } - @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.M) - public static boolean supportsMarshmallowNoiseReductionModes() { - // See https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES - return SDK_VERSION >= Build.VERSION_CODES.M; - } - @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.P) public static boolean supportsSessionConfiguration() { // See https://developer.android.com/reference/android/hardware/camera2/params/SessionConfiguration return SDK_VERSION >= Build.VERSION_CODES.P; } - @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.N) - public static boolean supportsVideoPause() { - // See https://developer.android.com/reference/androidx/camera/video/VideoRecordEvent.Pause - return SDK_VERSION >= Build.VERSION_CODES.N; - } - @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.R) public static boolean supportsZoomRatio() { // See https://developer.android.com/reference/android/hardware/camera2/CaptureRequest#CONTROL_ZOOM_RATIO diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeature.java index ae7c2262ea7..6bff002faba 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeature.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeature.java @@ -10,7 +10,6 @@ import androidx.annotation.NonNull; import io.flutter.BuildConfig; import io.flutter.plugins.camera.CameraProperties; -import io.flutter.plugins.camera.SdkCapabilityChecker; import io.flutter.plugins.camera.features.CameraFeature; import java.util.HashMap; @@ -35,12 +34,10 @@ public NoiseReductionFeature(@NonNull CameraProperties cameraProperties) { NOISE_REDUCTION_MODES.put(NoiseReductionMode.fast, CaptureRequest.NOISE_REDUCTION_MODE_FAST); NOISE_REDUCTION_MODES.put( NoiseReductionMode.highQuality, CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY); - if (SdkCapabilityChecker.supportsMarshmallowNoiseReductionModes()) { - NOISE_REDUCTION_MODES.put( - NoiseReductionMode.minimal, CaptureRequest.NOISE_REDUCTION_MODE_MINIMAL); - NOISE_REDUCTION_MODES.put( - NoiseReductionMode.zeroShutterLag, CaptureRequest.NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG); - } + NOISE_REDUCTION_MODES.put( + NoiseReductionMode.minimal, CaptureRequest.NOISE_REDUCTION_MODE_MINIMAL); + NOISE_REDUCTION_MODES.put( + NoiseReductionMode.zeroShutterLag, CaptureRequest.NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG); } @NonNull diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java index da3ab44312b..d4242c60776 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java @@ -669,32 +669,22 @@ public void pauseVideoRecording_shouldNotThrowWhenNotRecording() { } @Test - public void pauseVideoRecording_shouldCallPauseWhenRecordingAndOnAPIN() { + public void pauseVideoRecording_shouldCallPauseWhenRecording() { MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); camera.mediaRecorder = mockMediaRecorder; camera.recordingVideo = true; - SdkCapabilityChecker.SDK_VERSION = 24; camera.pauseVideoRecording(); verify(mockMediaRecorder, times(1)).pause(); } - @Test - public void pauseVideoRecording_shouldSendVideoRecordingFailedErrorWhenVersionCodeSmallerThenN() { - camera.recordingVideo = true; - SdkCapabilityChecker.SDK_VERSION = 23; - - assertThrows(Messages.FlutterError.class, camera::pauseVideoRecording); - } - @Test public void pauseVideoRecording_shouldSendVideoRecordingFailedErrorWhenMediaRecorderPauseThrowsIllegalStateException() { MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); camera.mediaRecorder = mockMediaRecorder; camera.recordingVideo = true; - SdkCapabilityChecker.SDK_VERSION = 24; IllegalStateException expectedException = new IllegalStateException("Test error message"); @@ -711,11 +701,10 @@ public void resumeVideoRecording_shouldNotThrowWhenNotRecording() { } @Test - public void resumeVideoRecording_shouldCallPauseWhenRecordingAndOnAPIN() { + public void resumeVideoRecording_shouldCallPauseWhenRecording() { MediaRecorder mockMediaRecorder = mock(MediaRecorder.class); camera.mediaRecorder = mockMediaRecorder; camera.recordingVideo = true; - SdkCapabilityChecker.SDK_VERSION = 24; camera.resumeVideoRecording(); @@ -866,15 +855,6 @@ public void setDescriptionWhileRecording_shouldErrorWhenNotRecording() { () -> camera.setDescriptionWhileRecording(newCameraProperties)); } - @Test - public void - resumeVideoRecording_shouldSendVideoRecordingFailedErrorWhenVersionCodeSmallerThanN() { - camera.recordingVideo = true; - SdkCapabilityChecker.SDK_VERSION = 23; - - assertThrows(Messages.FlutterError.class, camera::resumeVideoRecording); - } - @Test public void resumeVideoRecording_shouldSendVideoRecordingFailedErrorWhenMediaRecorderPauseThrowsIllegalStateException() { diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeatureTest.java index 93f81c6c153..9aae6b540d5 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeatureTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeatureTest.java @@ -16,25 +16,9 @@ import android.hardware.camera2.CaptureRequest; import io.flutter.plugins.camera.CameraProperties; -import io.flutter.plugins.camera.SdkCapabilityChecker; -import org.junit.After; -import org.junit.Before; import org.junit.Test; public class NoiseReductionFeatureTest { - @Before - public void before() { - // Make sure the SDK_VERSION field returns 23, to allow using all available - // noise reduction modes in tests. - SdkCapabilityChecker.SDK_VERSION = 23; - } - - @After - public void after() { - // Make sure we reset the SDK_VERSION field to it's original value. - SdkCapabilityChecker.SDK_VERSION = 0; - } - @Test public void getDebugName_shouldReturnTheNameOfTheFeature() { CameraProperties mockCameraProperties = mock(CameraProperties.class); diff --git a/packages/camera/camera_android/example/integration_test/camera_test.dart b/packages/camera/camera_android/example/integration_test/camera_test.dart index 24c00b8a0d2..c890173bf71 100644 --- a/packages/camera/camera_android/example/integration_test/camera_test.dart +++ b/packages/camera/camera_android/example/integration_test/camera_test.dart @@ -81,8 +81,8 @@ void main() { testWidgets( 'Capture specific video resolutions', (WidgetTester tester) async { - final List cameras = - await CameraPlatform.instance.availableCameras(); + final List cameras = await CameraPlatform.instance + .availableCameras(); if (cameras.isEmpty) { return; } @@ -114,8 +114,8 @@ void main() { ); testWidgets('Pause and resume video recording', (WidgetTester tester) async { - final List cameras = - await CameraPlatform.instance.availableCameras(); + final List cameras = await CameraPlatform.instance + .availableCameras(); if (cameras.isEmpty) { return; } @@ -164,8 +164,8 @@ void main() { }); testWidgets('Set description while recording', (WidgetTester tester) async { - final List cameras = - await CameraPlatform.instance.availableCameras(); + final List cameras = await CameraPlatform.instance + .availableCameras(); if (cameras.length < 2) { return; } @@ -201,8 +201,8 @@ void main() { }); testWidgets('Set description', (WidgetTester tester) async { - final List cameras = - await CameraPlatform.instance.availableCameras(); + final List cameras = await CameraPlatform.instance + .availableCameras(); if (cameras.length < 2) { return; } @@ -216,8 +216,8 @@ void main() { }); testWidgets('image streaming', (WidgetTester tester) async { - final List cameras = - await CameraPlatform.instance.availableCameras(); + final List cameras = await CameraPlatform.instance + .availableCameras(); if (cameras.isEmpty) { return; } @@ -246,8 +246,8 @@ void main() { }); testWidgets('recording with image stream', (WidgetTester tester) async { - final List cameras = - await CameraPlatform.instance.availableCameras(); + final List cameras = await CameraPlatform.instance + .availableCameras(); if (cameras.isEmpty) { return; } @@ -284,8 +284,8 @@ void main() { group('Camera settings', () { Future getCamera() async { - final List cameras = - await CameraPlatform.instance.availableCameras(); + final List cameras = await CameraPlatform.instance + .availableCameras(); expect(cameras.isNotEmpty, equals(true)); // Prefer back camera, as it allows more customizations. diff --git a/packages/camera/camera_android/example/lib/camera_controller.dart b/packages/camera/camera_android/example/lib/camera_controller.dart index 6eb6b753e0e..ad2d7b7ccfe 100644 --- a/packages/camera/camera_android/example/lib/camera_controller.dart +++ b/packages/camera/camera_android/example/lib/camera_controller.dart @@ -131,20 +131,17 @@ class CameraValue { exposureMode: exposureMode ?? this.exposureMode, focusMode: focusMode ?? this.focusMode, deviceOrientation: deviceOrientation ?? this.deviceOrientation, - lockedCaptureOrientation: - lockedCaptureOrientation == null - ? this.lockedCaptureOrientation - : lockedCaptureOrientation.orNull, - recordingOrientation: - recordingOrientation == null - ? this.recordingOrientation - : recordingOrientation.orNull, + lockedCaptureOrientation: lockedCaptureOrientation == null + ? this.lockedCaptureOrientation + : lockedCaptureOrientation.orNull, + recordingOrientation: recordingOrientation == null + ? this.recordingOrientation + : recordingOrientation.orNull, isPreviewPaused: isPreviewPaused ?? this.isPreviewPaused, description: description ?? this.description, - previewPauseOrientation: - previewPauseOrientation == null - ? this.previewPauseOrientation - : previewPauseOrientation.orNull, + previewPauseOrientation: previewPauseOrientation == null + ? this.previewPauseOrientation + : previewPauseOrientation.orNull, ); } @@ -364,16 +361,12 @@ class CameraController extends ValueNotifier { } /// Pause video recording. - /// - /// This feature is only available on iOS and Android sdk 24+. Future pauseVideoRecording() async { await CameraPlatform.instance.pauseVideoRecording(_cameraId); value = value.copyWith(isRecordingPaused: true); } /// Resume video recording after pausing. - /// - /// This feature is only available on iOS and Android sdk 24+. Future resumeVideoRecording() async { await CameraPlatform.instance.resumeVideoRecording(_cameraId); value = value.copyWith(isRecordingPaused: false); diff --git a/packages/camera/camera_android/example/lib/camera_preview.dart b/packages/camera/camera_android/example/lib/camera_preview.dart index 11a5e92b633..2de861aa402 100644 --- a/packages/camera/camera_android/example/lib/camera_preview.dart +++ b/packages/camera/camera_android/example/lib/camera_preview.dart @@ -23,25 +23,26 @@ class CameraPreview extends StatelessWidget { Widget build(BuildContext context) { return controller.value.isInitialized ? ValueListenableBuilder( - valueListenable: controller, - builder: (BuildContext context, Object? value, Widget? child) { - final double cameraAspectRatio = - controller.value.previewSize!.width / - controller.value.previewSize!.height; - return AspectRatio( - aspectRatio: - _isLandscape() ? cameraAspectRatio : (1 / cameraAspectRatio), - child: Stack( - fit: StackFit.expand, - children: [ - _wrapInRotatedBox(child: controller.buildPreview()), - child ?? Container(), - ], - ), - ); - }, - child: child, - ) + valueListenable: controller, + builder: (BuildContext context, Object? value, Widget? child) { + final double cameraAspectRatio = + controller.value.previewSize!.width / + controller.value.previewSize!.height; + return AspectRatio( + aspectRatio: _isLandscape() + ? cameraAspectRatio + : (1 / cameraAspectRatio), + child: Stack( + fit: StackFit.expand, + children: [ + _wrapInRotatedBox(child: controller.buildPreview()), + child ?? Container(), + ], + ), + ); + }, + child: child, + ) : Container(); } @@ -74,7 +75,7 @@ class CameraPreview extends StatelessWidget { return controller.value.isRecordingVideo ? controller.value.recordingOrientation! : (controller.value.previewPauseOrientation ?? - controller.value.lockedCaptureOrientation ?? - controller.value.deviceOrientation); + controller.value.lockedCaptureOrientation ?? + controller.value.deviceOrientation); } } diff --git a/packages/camera/camera_android/example/lib/main.dart b/packages/camera/camera_android/example/lib/main.dart index e8bd4e77179..1056b94e960 100644 --- a/packages/camera/camera_android/example/lib/main.dart +++ b/packages/camera/camera_android/example/lib/main.dart @@ -140,8 +140,8 @@ class _CameraExampleHomeState extends State border: Border.all( color: controller != null && controller!.value.isRecordingVideo - ? Colors.redAccent - : Colors.grey, + ? Colors.redAccent + : Colors.grey, width: 3.0, ), ), @@ -189,9 +189,8 @@ class _CameraExampleHomeState extends State behavior: HitTestBehavior.opaque, onScaleStart: _handleScaleStart, onScaleUpdate: _handleScaleUpdate, - onTapDown: - (TapDownDetails details) => - onViewFinderTap(details, constraints), + onTapDown: (TapDownDetails details) => + onViewFinderTap(details, constraints), ); }, ), @@ -237,28 +236,26 @@ class _CameraExampleHomeState extends State SizedBox( width: 64.0, height: 64.0, - child: - (localVideoController == null) - ? ( - // The captured image on the web contains a network-accessible URL - // pointing to a location within the browser. It may be displayed - // either with Image.network or Image.memory after loading the image - // bytes to memory. - kIsWeb - ? Image.network(imageFile!.path) - : Image.file(File(imageFile!.path))) - : Container( - decoration: BoxDecoration( - border: Border.all(color: Colors.pink), - ), - child: Center( - child: AspectRatio( - aspectRatio: - localVideoController.value.aspectRatio, - child: VideoPlayer(localVideoController), - ), + child: (localVideoController == null) + ? ( + // The captured image on the web contains a network-accessible URL + // pointing to a location within the browser. It may be displayed + // either with Image.network or Image.memory after loading the image + // bytes to memory. + kIsWeb + ? Image.network(imageFile!.path) + : Image.file(File(imageFile!.path))) + : Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.pink), + ), + child: Center( + child: AspectRatio( + aspectRatio: localVideoController.value.aspectRatio, + child: VideoPlayer(localVideoController), ), ), + ), ), ], ), @@ -281,19 +278,21 @@ class _CameraExampleHomeState extends State // The exposure and focus mode are currently not supported on the web. ...!kIsWeb ? [ - IconButton( - icon: const Icon(Icons.exposure), - color: Colors.blue, - onPressed: - controller != null ? onExposureModeButtonPressed : null, - ), - IconButton( - icon: const Icon(Icons.filter_center_focus), - color: Colors.blue, - onPressed: - controller != null ? onFocusModeButtonPressed : null, - ), - ] + IconButton( + icon: const Icon(Icons.exposure), + color: Colors.blue, + onPressed: controller != null + ? onExposureModeButtonPressed + : null, + ), + IconButton( + icon: const Icon(Icons.filter_center_focus), + color: Colors.blue, + onPressed: controller != null + ? onFocusModeButtonPressed + : null, + ), + ] : [], IconButton( icon: Icon(enableAudio ? Icons.volume_up : Icons.volume_mute), @@ -307,10 +306,9 @@ class _CameraExampleHomeState extends State : Icons.screen_rotation, ), color: Colors.blue, - onPressed: - controller != null - ? onCaptureOrientationLockButtonPressed - : null, + onPressed: controller != null + ? onCaptureOrientationLockButtonPressed + : null, ), ], ), @@ -330,47 +328,39 @@ class _CameraExampleHomeState extends State children: [ IconButton( icon: const Icon(Icons.flash_off), - color: - controller?.value.flashMode == FlashMode.off - ? Colors.orange - : Colors.blue, - onPressed: - controller != null - ? () => onSetFlashModeButtonPressed(FlashMode.off) - : null, + color: controller?.value.flashMode == FlashMode.off + ? Colors.orange + : Colors.blue, + onPressed: controller != null + ? () => onSetFlashModeButtonPressed(FlashMode.off) + : null, ), IconButton( icon: const Icon(Icons.flash_auto), - color: - controller?.value.flashMode == FlashMode.auto - ? Colors.orange - : Colors.blue, - onPressed: - controller != null - ? () => onSetFlashModeButtonPressed(FlashMode.auto) - : null, + color: controller?.value.flashMode == FlashMode.auto + ? Colors.orange + : Colors.blue, + onPressed: controller != null + ? () => onSetFlashModeButtonPressed(FlashMode.auto) + : null, ), IconButton( icon: const Icon(Icons.flash_on), - color: - controller?.value.flashMode == FlashMode.always - ? Colors.orange - : Colors.blue, - onPressed: - controller != null - ? () => onSetFlashModeButtonPressed(FlashMode.always) - : null, + color: controller?.value.flashMode == FlashMode.always + ? Colors.orange + : Colors.blue, + onPressed: controller != null + ? () => onSetFlashModeButtonPressed(FlashMode.always) + : null, ), IconButton( icon: const Icon(Icons.highlight), - color: - controller?.value.flashMode == FlashMode.torch - ? Colors.orange - : Colors.blue, - onPressed: - controller != null - ? () => onSetFlashModeButtonPressed(FlashMode.torch) - : null, + color: controller?.value.flashMode == FlashMode.torch + ? Colors.orange + : Colors.blue, + onPressed: controller != null + ? () => onSetFlashModeButtonPressed(FlashMode.torch) + : null, ), ], ), @@ -380,16 +370,14 @@ class _CameraExampleHomeState extends State Widget _exposureModeControlRowWidget() { final ButtonStyle styleAuto = TextButton.styleFrom( - foregroundColor: - controller?.value.exposureMode == ExposureMode.auto - ? Colors.orange - : Colors.blue, + foregroundColor: controller?.value.exposureMode == ExposureMode.auto + ? Colors.orange + : Colors.blue, ); final ButtonStyle styleLocked = TextButton.styleFrom( - foregroundColor: - controller?.value.exposureMode == ExposureMode.locked - ? Colors.orange - : Colors.blue, + foregroundColor: controller?.value.exposureMode == ExposureMode.locked + ? Colors.orange + : Colors.blue, ); return SizeTransition( @@ -405,12 +393,10 @@ class _CameraExampleHomeState extends State children: [ TextButton( style: styleAuto, - onPressed: - controller != null - ? () => onSetExposureModeButtonPressed( - ExposureMode.auto, - ) - : null, + onPressed: controller != null + ? () => + onSetExposureModeButtonPressed(ExposureMode.auto) + : null, onLongPress: () { if (controller != null) { CameraPlatform.instance.setExposurePoint( @@ -424,20 +410,18 @@ class _CameraExampleHomeState extends State ), TextButton( style: styleLocked, - onPressed: - controller != null - ? () => onSetExposureModeButtonPressed( - ExposureMode.locked, - ) - : null, + onPressed: controller != null + ? () => onSetExposureModeButtonPressed( + ExposureMode.locked, + ) + : null, child: const Text('LOCKED'), ), TextButton( style: styleLocked, - onPressed: - controller != null - ? () => controller!.setExposureOffset(0.0) - : null, + onPressed: controller != null + ? () => controller!.setExposureOffset(0.0) + : null, child: const Text('RESET OFFSET'), ), ], @@ -454,9 +438,9 @@ class _CameraExampleHomeState extends State label: _currentExposureOffset.toString(), onChanged: _minAvailableExposureOffset == - _maxAvailableExposureOffset - ? null - : setExposureOffset, + _maxAvailableExposureOffset + ? null + : setExposureOffset, ), Text(_maxAvailableExposureOffset.toString()), ], @@ -470,16 +454,14 @@ class _CameraExampleHomeState extends State Widget _focusModeControlRowWidget() { final ButtonStyle styleAuto = TextButton.styleFrom( - foregroundColor: - controller?.value.focusMode == FocusMode.auto - ? Colors.orange - : Colors.blue, + foregroundColor: controller?.value.focusMode == FocusMode.auto + ? Colors.orange + : Colors.blue, ); final ButtonStyle styleLocked = TextButton.styleFrom( - foregroundColor: - controller?.value.focusMode == FocusMode.locked - ? Colors.orange - : Colors.blue, + foregroundColor: controller?.value.focusMode == FocusMode.locked + ? Colors.orange + : Colors.blue, ); return SizeTransition( @@ -495,10 +477,9 @@ class _CameraExampleHomeState extends State children: [ TextButton( style: styleAuto, - onPressed: - controller != null - ? () => onSetFocusModeButtonPressed(FocusMode.auto) - : null, + onPressed: controller != null + ? () => onSetFocusModeButtonPressed(FocusMode.auto) + : null, onLongPress: () { if (controller != null) { CameraPlatform.instance.setFocusPoint( @@ -512,11 +493,9 @@ class _CameraExampleHomeState extends State ), TextButton( style: styleLocked, - onPressed: - controller != null - ? () => - onSetFocusModeButtonPressed(FocusMode.locked) - : null, + onPressed: controller != null + ? () => onSetFocusModeButtonPressed(FocusMode.locked) + : null, child: const Text('LOCKED'), ), ], @@ -540,56 +519,57 @@ class _CameraExampleHomeState extends State color: Colors.blue, onPressed: cameraController != null && - cameraController.value.isInitialized && - !cameraController.value.isRecordingVideo - ? onTakePictureButtonPressed - : null, + cameraController.value.isInitialized && + !cameraController.value.isRecordingVideo + ? onTakePictureButtonPressed + : null, ), IconButton( icon: const Icon(Icons.videocam), color: Colors.blue, onPressed: cameraController != null && - cameraController.value.isInitialized && - !cameraController.value.isRecordingVideo - ? onVideoRecordButtonPressed - : null, + cameraController.value.isInitialized && + !cameraController.value.isRecordingVideo + ? onVideoRecordButtonPressed + : null, ), IconButton( icon: cameraController != null && - (!cameraController.value.isRecordingVideo || - cameraController.value.isRecordingPaused) - ? const Icon(Icons.play_arrow) - : const Icon(Icons.pause), + (!cameraController.value.isRecordingVideo || + cameraController.value.isRecordingPaused) + ? const Icon(Icons.play_arrow) + : const Icon(Icons.pause), color: Colors.blue, onPressed: cameraController != null && - cameraController.value.isInitialized && - cameraController.value.isRecordingVideo - ? cameraController.value.isRecordingPaused - ? onResumeButtonPressed - : onPauseButtonPressed - : null, + cameraController.value.isInitialized && + cameraController.value.isRecordingVideo + ? cameraController.value.isRecordingPaused + ? onResumeButtonPressed + : onPauseButtonPressed + : null, ), IconButton( icon: const Icon(Icons.stop), color: Colors.red, onPressed: cameraController != null && - cameraController.value.isInitialized && - cameraController.value.isRecordingVideo - ? onStopButtonPressed - : null, + cameraController.value.isInitialized && + cameraController.value.isRecordingVideo + ? onStopButtonPressed + : null, ), IconButton( icon: const Icon(Icons.pause_presentation), color: cameraController != null && cameraController.value.isPreviewPaused - ? Colors.red - : Colors.blue, - onPressed: - cameraController == null ? null : onPausePreviewButtonPressed, + ? Colors.red + : Colors.blue, + onPressed: cameraController == null + ? null + : onPausePreviewButtonPressed, ), ], ); @@ -668,8 +648,9 @@ class _CameraExampleHomeState extends State final CameraController cameraController = CameraController( cameraDescription, mediaSettings: MediaSettings( - resolutionPreset: - kIsWeb ? ResolutionPreset.max : ResolutionPreset.medium, + resolutionPreset: kIsWeb + ? ResolutionPreset.max + : ResolutionPreset.medium, enableAudio: enableAudio, ), imageFormatGroup: ImageFormatGroup.jpeg, @@ -690,13 +671,17 @@ class _CameraExampleHomeState extends State // The exposure mode is currently not supported on the web. ...!kIsWeb ? >[ - CameraPlatform.instance - .getMinExposureOffset(cameraController.cameraId) - .then((double value) => _minAvailableExposureOffset = value), - CameraPlatform.instance - .getMaxExposureOffset(cameraController.cameraId) - .then((double value) => _maxAvailableExposureOffset = value), - ] + CameraPlatform.instance + .getMinExposureOffset(cameraController.cameraId) + .then( + (double value) => _minAvailableExposureOffset = value, + ), + CameraPlatform.instance + .getMaxExposureOffset(cameraController.cameraId) + .then( + (double value) => _maxAvailableExposureOffset = value, + ), + ] : >[], CameraPlatform.instance .getMaxZoomLevel(cameraController.cameraId) @@ -1018,10 +1003,9 @@ class _CameraExampleHomeState extends State return; } - final VideoPlayerController vController = - kIsWeb - ? VideoPlayerController.networkUrl(Uri.parse(videoFile!.path)) - : VideoPlayerController.file(File(videoFile!.path)); + final VideoPlayerController vController = kIsWeb + ? VideoPlayerController.networkUrl(Uri.parse(videoFile!.path)) + : VideoPlayerController.file(File(videoFile!.path)); videoPlayerListener = () { if (videoController != null) { diff --git a/packages/camera/camera_android/example/pubspec.yaml b/packages/camera/camera_android/example/pubspec.yaml index 8a362e8c5e1..f4d614defd6 100644 --- a/packages/camera/camera_android/example/pubspec.yaml +++ b/packages/camera/camera_android/example/pubspec.yaml @@ -3,8 +3,8 @@ description: Demonstrates how to use the camera plugin. publish_to: none environment: - sdk: ^3.7.0 - flutter: ">=3.29.0" + sdk: ^3.9.0 + flutter: ">=3.35.0" dependencies: camera_android: diff --git a/packages/camera/camera_android/lib/src/android_camera.dart b/packages/camera/camera_android/lib/src/android_camera.dart index 19c5b0f8f18..7d72601b54d 100644 --- a/packages/camera/camera_android/lib/src/android_camera.dart +++ b/packages/camera/camera_android/lib/src/android_camera.dart @@ -67,8 +67,8 @@ class AndroidCamera extends CameraPlatform { @override Future> availableCameras() async { try { - final List cameraDescriptions = - await _hostApi.getAvailableCameras(); + final List cameraDescriptions = await _hostApi + .getAvailableCameras(); return cameraDescriptions.map(( PlatformCameraDescription cameraDescription, ) { diff --git a/packages/camera/camera_android/lib/src/messages.g.dart b/packages/camera/camera_android/lib/src/messages.g.dart index babb6fc14d4..d927eab32ec 100644 --- a/packages/camera/camera_android/lib/src/messages.g.dart +++ b/packages/camera/camera_android/lib/src/messages.g.dart @@ -311,8 +311,9 @@ class CameraApi { BinaryMessenger? binaryMessenger, String messageChannelSuffix = '', }) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty + ? '.$messageChannelSuffix' + : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -1098,8 +1099,9 @@ abstract class CameraGlobalEventApi { BinaryMessenger? binaryMessenger, String messageChannelSuffix = '', }) { - messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + messageChannelSuffix = messageChannelSuffix.isNotEmpty + ? '.$messageChannelSuffix' + : ''; { final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( @@ -1156,8 +1158,9 @@ abstract class CameraEventApi { BinaryMessenger? binaryMessenger, String messageChannelSuffix = '', }) { - messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + messageChannelSuffix = messageChannelSuffix.isNotEmpty + ? '.$messageChannelSuffix' + : ''; { final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 7bcbf9ff570..b9a0da19604 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -3,11 +3,11 @@ 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.10+6 +version: 0.10.10+7 environment: - sdk: ^3.7.0 - flutter: ">=3.29.0" + sdk: ^3.9.0 + flutter: ">=3.35.0" flutter: plugin: diff --git a/packages/camera/camera_android/test/android_camera_test.dart b/packages/camera/camera_android/test/android_camera_test.dart index 248ff3174c7..7f8f7776986 100644 --- a/packages/camera/camera_android/test/android_camera_test.dart +++ b/packages/camera/camera_android/test/android_camera_test.dart @@ -420,8 +420,8 @@ void main() { test('Should receive device orientation change events', () async { // Act - final Stream eventStream = - camera.onDeviceOrientationChanged(); + final Stream eventStream = camera + .onDeviceOrientationChanged(); final StreamQueue streamQueue = StreamQueue(eventStream); diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index 218c09bca86..8f6efb51383 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,6 +1,7 @@ ## NEXT * Updates minimum supported SDK version to Flutter 3.29/Dart 3.7. +* Updates README to reflect that only Android API 24+ is supported. ## 1.2.0 diff --git a/packages/image_picker/image_picker/README.md b/packages/image_picker/image_picker/README.md index 59bd065a2ae..39f1d9b8e73 100755 --- a/packages/image_picker/image_picker/README.md +++ b/packages/image_picker/image_picker/README.md @@ -8,7 +8,7 @@ and taking new pictures with the camera. | | Android | iOS | Linux | macOS | Web | Windows | |-------------|---------|---------|-------|--------|---------------------------------|-------------| -| **Support** | SDK 21+ | iOS 12+ | Any | 10.14+ | [See `image_picker_for_web`](https://pub.dev/packages/image_picker_for_web#limitations-on-the-web-platform) | Windows 10+ | +| **Support** | SDK 24+ | iOS 12+ | Any | 10.14+ | [See `image_picker_for_web`](https://pub.dev/packages/image_picker_for_web#limitations-on-the-web-platform) | Windows 10+ | ## Setup @@ -38,9 +38,6 @@ _Privacy - Microphone Usage Description_ in the visual editor. ### Android -Starting with version **0.8.1** the Android implementation support to pick -(multiple) images on Android 4.3 or higher. - No configuration required - the plugin should work out of the box. It is however highly recommended to prepare for Android killing the application when low on memory. How to prepare for this is discussed in the [Handling MainActivity destruction on Android](#handling-mainactivity-destruction-on-android) diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md index 83e7e5c32a3..0be168f5807 100644 --- a/packages/image_picker/image_picker_android/CHANGELOG.md +++ b/packages/image_picker/image_picker_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.8.13+2 + +* Updates minimum supported SDK version to Flutter 3.35. +* Removes obsolete code related to supporting SDK <24. + ## 0.8.13+1 * Bumps com.android.tools.build:gradle to 8.12.1. diff --git a/packages/image_picker/image_picker_android/android/build.gradle b/packages/image_picker/image_picker_android/android/build.gradle index 581b7c1e7c1..868a4dbd266 100644 --- a/packages/image_picker/image_picker_android/android/build.gradle +++ b/packages/image_picker/image_picker_android/android/build.gradle @@ -26,7 +26,7 @@ android { compileSdk = flutter.compileSdkVersion defaultConfig { - minSdkVersion 21 + minSdkVersion 24 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } lintOptions { diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java index c76d6b63670..7262377f481 100644 --- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java +++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java @@ -988,14 +988,9 @@ private void finishWithError(String errorCode, String errorMessage) { } private void useFrontCamera(Intent intent) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { - intent.putExtra( - "android.intent.extras.CAMERA_FACING", CameraCharacteristics.LENS_FACING_FRONT); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - intent.putExtra("android.intent.extra.USE_FRONT_CAMERA", true); - } - } else { - intent.putExtra("android.intent.extras.CAMERA_FACING", 1); + intent.putExtra("android.intent.extras.CAMERA_FACING", CameraCharacteristics.LENS_FACING_FRONT); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + intent.putExtra("android.intent.extra.USE_FRONT_CAMERA", true); } } } diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java index d14056423c4..60b20c5a772 100644 --- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java +++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java @@ -45,8 +45,8 @@ private static PackageInfo getPermissionsPackageInfoPreApi33( } /** - * Camera permission need request if it present in manifest, because for M or great for take Photo - * ar Video by intent need it permission, even if the camera permission is not used. + * Camera permission needs to be requested if it is present in the manifest, even if the camera + * permission is not used. * *

Camera permission may be used in another package, as example flutter_barcode_reader. * https://github.com/flutter/flutter/issues/29837 @@ -54,8 +54,7 @@ private static PackageInfo getPermissionsPackageInfoPreApi33( * @return returns true, if need request camera permission, otherwise false */ static boolean needRequestCameraPermission(Context context) { - boolean greatOrEqualM = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; - return greatOrEqualM && isPermissionPresentInManifest(context, Manifest.permission.CAMERA); + return isPermissionPresentInManifest(context, Manifest.permission.CAMERA); } /** diff --git a/packages/image_picker/image_picker_android/example/lib/main.dart b/packages/image_picker/image_picker_android/example/lib/main.dart index 6e7b2875aba..d11bb9e4501 100755 --- a/packages/image_picker/image_picker_android/example/lib/main.dart +++ b/packages/image_picker/image_picker_android/example/lib/main.dart @@ -130,21 +130,20 @@ class _MyHomePageState extends State { maxHeight: maxHeight, imageQuality: quality, ); - final List pickedFileList = - isMedia - ? await _picker.getMedia( - options: MediaOptions( - allowMultiple: allowMultiple, - imageOptions: imageOptions, - limit: limit, - ), - ) - : await _picker.getMultiImageWithOptions( - options: MultiImagePickerOptions( - imageOptions: imageOptions, - limit: limit, - ), - ); + final List pickedFileList = isMedia + ? await _picker.getMedia( + options: MediaOptions( + allowMultiple: allowMultiple, + imageOptions: imageOptions, + limit: limit, + ), + ) + : await _picker.getMultiImageWithOptions( + options: MultiImagePickerOptions( + imageOptions: imageOptions, + limit: limit, + ), + ); if (pickedFileList.isNotEmpty && context.mounted) { _showPickedSnackBar(context, pickedFileList); } @@ -282,21 +281,23 @@ class _MyHomePageState extends State { ), Semantics( label: 'image_picker_example_picked_image', - child: - mime == null || mime.startsWith('image/') - ? Image.file( - File(_mediaFileList![index].path), - errorBuilder: ( - BuildContext context, - Object error, - StackTrace? stackTrace, - ) { - return const Center( - child: Text('This image type is not supported'), - ); - }, - ) - : _buildInlineVideoPlayer(index), + child: mime == null || mime.startsWith('image/') + ? Image.file( + File(_mediaFileList![index].path), + errorBuilder: + ( + BuildContext context, + Object error, + StackTrace? stackTrace, + ) { + return const Center( + child: Text( + 'This image type is not supported', + ), + ); + }, + ) + : _buildInlineVideoPlayer(index), ), ], ); @@ -366,39 +367,35 @@ class _MyHomePageState extends State { appBar: AppBar(title: Text(widget.title!)), body: Align( alignment: Alignment.topCenter, - child: - !kIsWeb && defaultTargetPlatform == TargetPlatform.android - ? FutureBuilder( - future: retrieveLostData(), - builder: ( - BuildContext context, - AsyncSnapshot snapshot, - ) { - switch (snapshot.connectionState) { - case ConnectionState.none: - case ConnectionState.waiting: + child: !kIsWeb && defaultTargetPlatform == TargetPlatform.android + ? FutureBuilder( + future: retrieveLostData(), + builder: (BuildContext context, AsyncSnapshot snapshot) { + switch (snapshot.connectionState) { + case ConnectionState.none: + case ConnectionState.waiting: + return const Text( + 'You have not yet picked an image.', + textAlign: TextAlign.center, + ); + case ConnectionState.done: + return _handlePreview(); + case ConnectionState.active: + if (snapshot.hasError) { + return Text( + 'Pick image/video error: ${snapshot.error}}', + textAlign: TextAlign.center, + ); + } else { return const Text( 'You have not yet picked an image.', textAlign: TextAlign.center, ); - case ConnectionState.done: - return _handlePreview(); - case ConnectionState.active: - if (snapshot.hasError) { - return Text( - 'Pick image/video error: ${snapshot.error}}', - textAlign: TextAlign.center, - ); - } else { - return const Text( - 'You have not yet picked an image.', - textAlign: TextAlign.center, - ); - } - } - }, - ) - : _handlePreview(), + } + } + }, + ) + : _handlePreview(), ), floatingActionButton: Column( mainAxisAlignment: MainAxisAlignment.end, @@ -599,22 +596,18 @@ class _MyHomePageState extends State { TextButton( child: const Text('PICK'), onPressed: () { - final double? width = - maxWidthController.text.isNotEmpty - ? double.parse(maxWidthController.text) - : null; - final double? height = - maxHeightController.text.isNotEmpty - ? double.parse(maxHeightController.text) - : null; - final int? quality = - qualityController.text.isNotEmpty - ? int.parse(qualityController.text) - : null; - final int? limit = - limitController.text.isNotEmpty - ? int.parse(limitController.text) - : null; + final double? width = maxWidthController.text.isNotEmpty + ? double.parse(maxWidthController.text) + : null; + final double? height = maxHeightController.text.isNotEmpty + ? double.parse(maxHeightController.text) + : null; + final int? quality = qualityController.text.isNotEmpty + ? int.parse(qualityController.text) + : null; + final int? limit = limitController.text.isNotEmpty + ? int.parse(limitController.text) + : null; onPick(width, height, quality, limit); Navigator.of(context).pop(); }, diff --git a/packages/image_picker/image_picker_android/example/pubspec.yaml b/packages/image_picker/image_picker_android/example/pubspec.yaml index 2e950379b87..4dbd12f2e3b 100644 --- a/packages/image_picker/image_picker_android/example/pubspec.yaml +++ b/packages/image_picker/image_picker_android/example/pubspec.yaml @@ -3,8 +3,8 @@ description: Demonstrates how to use the image_picker plugin. publish_to: none environment: - sdk: ^3.7.0 - flutter: ">=3.29.0" + sdk: ^3.9.0 + flutter: ">=3.35.0" dependencies: flutter: diff --git a/packages/image_picker/image_picker_android/lib/image_picker_android.dart b/packages/image_picker/image_picker_android/lib/image_picker_android.dart index 32c7a6e4b33..3af678424de 100644 --- a/packages/image_picker/image_picker_android/lib/image_picker_android.dart +++ b/packages/image_picker/image_picker_android/lib/image_picker_android.dart @@ -376,15 +376,15 @@ class ImagePickerAndroid extends ImagePickerPlatform { assert(result.paths.isEmpty != (result.error == null)); final CacheRetrievalError? error = result.error; - final PlatformException? exception = - error == null - ? null - : PlatformException(code: error.code, message: error.message); + final PlatformException? exception = error == null + ? null + : PlatformException(code: error.code, message: error.message); // Entries are guaranteed not to be null, even though that's not currently // expressible in Pigeon. - final List pickedFileList = - result.paths.map((String? path) => XFile(path!)).toList(); + final List pickedFileList = result.paths + .map((String? path) => XFile(path!)) + .toList(); return LostDataResponse( file: pickedFileList.isEmpty ? null : pickedFileList.last, diff --git a/packages/image_picker/image_picker_android/lib/src/messages.g.dart b/packages/image_picker/image_picker_android/lib/src/messages.g.dart index 5b3313a9bf1..8d0a0c49992 100644 --- a/packages/image_picker/image_picker_android/lib/src/messages.g.dart +++ b/packages/image_picker/image_picker_android/lib/src/messages.g.dart @@ -285,8 +285,9 @@ class ImagePickerApi { BinaryMessenger? binaryMessenger, String messageChannelSuffix = '', }) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty + ? '.$messageChannelSuffix' + : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml index e029f617330..c7818f72128 100755 --- a/packages/image_picker/image_picker_android/pubspec.yaml +++ b/packages/image_picker/image_picker_android/pubspec.yaml @@ -2,11 +2,11 @@ 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.13+1 +version: 0.8.13+2 environment: - sdk: ^3.7.0 - flutter: ">=3.29.0" + sdk: ^3.9.0 + flutter: ">=3.35.0" flutter: plugin: diff --git a/packages/image_picker/image_picker_android/test/test_api.g.dart b/packages/image_picker/image_picker_android/test/test_api.g.dart index b0f4a46568e..7ccdca4da85 100644 --- a/packages/image_picker/image_picker_android/test/test_api.g.dart +++ b/packages/image_picker/image_picker_android/test/test_api.g.dart @@ -120,8 +120,9 @@ abstract class TestHostImagePickerApi { BinaryMessenger? binaryMessenger, String messageChannelSuffix = '', }) { - messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + messageChannelSuffix = messageChannelSuffix.isNotEmpty + ? '.$messageChannelSuffix' + : ''; { final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index 9dd19517d5c..d8cf7652a76 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,7 +1,7 @@ ## NEXT * Updates minimum supported SDK version to Flutter 3.29/Dart 3.7. -* Updates README to indicate that Andoid SDK <21 is no longer supported. +* Updates README to reflect that only Android API 24+ is supported. ## 2.3.0 diff --git a/packages/local_auth/local_auth/README.md b/packages/local_auth/local_auth/README.md index 2a7e4c043a8..e09626472e7 100644 --- a/packages/local_auth/local_auth/README.md +++ b/packages/local_auth/local_auth/README.md @@ -8,9 +8,9 @@ the user. On supported devices, this includes authentication with biometrics such as fingerprint or facial recognition. -| | Android | iOS | macOS | Windows | -|-------------|-----------|-------|--------|-------------| -| **Support** | SDK 21+\* | 12.0+ | 10.14+ | Windows 10+ | +| | Android | iOS | macOS | Windows | +|-------------|---------|-------|--------|-------------| +| **Support** | SDK 24+ | 12.0+ | 10.14+ | Windows 10+ | ## Usage @@ -203,12 +203,9 @@ app has not been updated to use Face ID. ## Android Integration -\* The plugin will build and run on SDK 21+, but `isDeviceSupported()` will -always return false before SDK 23 (Android 6.0). - ### Activity Changes -Note that `local_auth` requires the use of a `FragmentActivity` instead of an +`local_auth` requires the use of a `FragmentActivity` instead of an `Activity`. To update your application: * If you are using `FlutterActivity` directly, change it to diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index 379ca38da24..5178a9e3b22 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.0.53 + +* Removes obsolete code related to supporting SDK <24. +* Updates minimum supported SDK version to Flutter 3.35. + ## 1.0.52 * Bumps com.android.tools.build:gradle to 8.12.1. diff --git a/packages/local_auth/local_auth_android/android/build.gradle b/packages/local_auth/local_auth_android/android/build.gradle index e8707d30ce4..b6fe5d8d0cc 100644 --- a/packages/local_auth/local_auth_android/android/build.gradle +++ b/packages/local_auth/local_auth_android/android/build.gradle @@ -26,7 +26,7 @@ android { compileSdk = flutter.compileSdkVersion defaultConfig { - minSdkVersion 21 + minSdkVersion 24 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java b/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java index 09af7c6656c..cb0124bb291 100644 --- a/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java +++ b/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java @@ -172,7 +172,7 @@ void onAuthenticationCompleted(Result result, AuthResult value) { @VisibleForTesting public boolean isDeviceSecure() { if (keyguardManager == null) return false; - return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && keyguardManager.isDeviceSecure()); + return keyguardManager.isDeviceSecure(); } private boolean canAuthenticateWithBiometrics() { diff --git a/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java b/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java index 1fbc20a7803..0ebaf7a89b3 100644 --- a/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java +++ b/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java @@ -341,14 +341,6 @@ public void getEnrolledBiometrics_shouldAddStrongBiometrics() { } @Test - @Config(sdk = 22) - public void isDeviceSecure_returnsFalseOnBelowApi23() { - final LocalAuthPlugin plugin = new LocalAuthPlugin(); - assertFalse(plugin.isDeviceSecure()); - } - - @Test - @Config(sdk = 23) public void isDeviceSecure_returnsTrueIfDeviceIsSecure() { final LocalAuthPlugin plugin = new LocalAuthPlugin(); KeyguardManager mockKeyguardManager = mock(KeyguardManager.class); diff --git a/packages/local_auth/local_auth_android/example/lib/main.dart b/packages/local_auth/local_auth_android/example/lib/main.dart index 16790279b75..b0cb1805438 100644 --- a/packages/local_auth/local_auth_android/example/lib/main.dart +++ b/packages/local_auth/local_auth_android/example/lib/main.dart @@ -34,11 +34,9 @@ class _MyAppState extends State { super.initState(); LocalAuthPlatform.instance.isDeviceSupported().then( (bool isSupported) => setState( - () => - _supportState = - isSupported - ? _SupportState.supported - : _SupportState.unsupported, + () => _supportState = isSupported + ? _SupportState.supported + : _SupportState.unsupported, ), ); } @@ -46,8 +44,8 @@ class _MyAppState extends State { Future _checkBiometrics() async { late bool deviceSupportsBiometrics; try { - deviceSupportsBiometrics = - await LocalAuthPlatform.instance.deviceSupportsBiometrics(); + deviceSupportsBiometrics = await LocalAuthPlatform.instance + .deviceSupportsBiometrics(); } on PlatformException catch (e) { deviceSupportsBiometrics = false; print(e); @@ -64,8 +62,8 @@ class _MyAppState extends State { Future _getEnrolledBiometrics() async { late List availableBiometrics; try { - availableBiometrics = - await LocalAuthPlatform.instance.getEnrolledBiometrics(); + availableBiometrics = await LocalAuthPlatform.instance + .getEnrolledBiometrics(); } on PlatformException catch (e) { availableBiometrics = []; print(e); diff --git a/packages/local_auth/local_auth_android/example/pubspec.yaml b/packages/local_auth/local_auth_android/example/pubspec.yaml index 3a10bee4329..c75acd437c4 100644 --- a/packages/local_auth/local_auth_android/example/pubspec.yaml +++ b/packages/local_auth/local_auth_android/example/pubspec.yaml @@ -3,8 +3,8 @@ description: Demonstrates how to use the local_auth_android plugin. publish_to: none environment: - sdk: ^3.7.0 - flutter: ">=3.29.0" + sdk: ^3.9.0 + flutter: ">=3.35.0" dependencies: flutter: diff --git a/packages/local_auth/local_auth_android/lib/src/messages.g.dart b/packages/local_auth/local_auth_android/lib/src/messages.g.dart index ad0c2be01ef..b033519d24f 100644 --- a/packages/local_auth/local_auth_android/lib/src/messages.g.dart +++ b/packages/local_auth/local_auth_android/lib/src/messages.g.dart @@ -208,8 +208,9 @@ class LocalAuthApi { BinaryMessenger? binaryMessenger, String messageChannelSuffix = '', }) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty + ? '.$messageChannelSuffix' + : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index ff3031a3260..cccf3f166c2 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -2,11 +2,11 @@ name: local_auth_android description: Android implementation of the local_auth plugin. repository: https://github.com/flutter/packages/tree/main/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.52 +version: 1.0.53 environment: - sdk: ^3.7.0 - flutter: ">=3.29.0" + sdk: ^3.9.0 + flutter: ">=3.35.0" flutter: plugin: diff --git a/packages/url_launcher/url_launcher_android/CHANGELOG.md b/packages/url_launcher/url_launcher_android/CHANGELOG.md index ad86af3646c..fe1d4e6e22d 100644 --- a/packages/url_launcher/url_launcher_android/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 6.3.19 + +* Updates minimum supported SDK version to Flutter 3.35. +* Removes obsolete code related to supporting SDK <24. + ## 6.3.18 * Bumps com.android.tools.build:gradle to 8.12.1. diff --git a/packages/url_launcher/url_launcher_android/android/build.gradle b/packages/url_launcher/url_launcher_android/android/build.gradle index 390519a37a8..825ddeabad5 100644 --- a/packages/url_launcher/url_launcher_android/android/build.gradle +++ b/packages/url_launcher/url_launcher_android/android/build.gradle @@ -29,7 +29,7 @@ android { compileSdk = flutter.compileSdkVersion defaultConfig { - minSdkVersion 21 + minSdkVersion 24 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/packages/url_launcher/url_launcher_android/android/src/main/java/io/flutter/plugins/urllauncher/WebViewActivity.java b/packages/url_launcher/url_launcher_android/android/src/main/java/io/flutter/plugins/urllauncher/WebViewActivity.java index 3ae49cece79..026e86a4fc1 100644 --- a/packages/url_launcher/url_launcher_android/android/src/main/java/io/flutter/plugins/urllauncher/WebViewActivity.java +++ b/packages/url_launcher/url_launcher_android/android/src/main/java/io/flutter/plugins/urllauncher/WebViewActivity.java @@ -9,7 +9,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.os.Build; import android.os.Bundle; import android.os.Message; import android.provider.Browser; @@ -20,7 +19,6 @@ import android.webkit.WebViewClient; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; import androidx.annotation.VisibleForTesting; import androidx.core.content.ContextCompat; import java.util.Collections; @@ -49,7 +47,6 @@ public void onReceive(Context context, Intent intent) { private final WebViewClient webViewClient = new WebViewClient() { - @RequiresApi(Build.VERSION_CODES.N) @Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { view.loadUrl(request.getUrl().toString()); @@ -75,17 +72,6 @@ public boolean shouldOverrideUrlLoading( webview.loadUrl(request.getUrl().toString()); return true; } - - /* - * This method is deprecated in API 24. Still overridden to support - * earlier Android versions. - */ - @SuppressWarnings("deprecation") - @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - webview.loadUrl(url); - return true; - } }; final WebView newWebView = new WebView(webview.getContext()); diff --git a/packages/url_launcher/url_launcher_android/pubspec.yaml b/packages/url_launcher/url_launcher_android/pubspec.yaml index 321fe880682..117cf02c367 100644 --- a/packages/url_launcher/url_launcher_android/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/pubspec.yaml @@ -2,7 +2,8 @@ name: url_launcher_android description: Android implementation of the url_launcher plugin. repository: https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.3.18 +version: 6.3.19 + environment: sdk: ^3.7.0 flutter: ">=3.29.0" diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index 237a1b3f7f8..fad345bad30 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,6 +1,7 @@ ## NEXT * Updates minimum supported SDK version to Flutter 3.29/Dart 3.7. +* Updates README to reflect that only Android API 24+ is supported. ## 4.13.0 diff --git a/packages/webview_flutter/webview_flutter/README.md b/packages/webview_flutter/webview_flutter/README.md index 8bd3e087015..5c0d2b8aaf3 100644 --- a/packages/webview_flutter/webview_flutter/README.md +++ b/packages/webview_flutter/webview_flutter/README.md @@ -11,7 +11,7 @@ On Android the WebView widget is backed by a [WebView](https://developer.android | | Android | iOS | macOS | |-------------|---------|-------|--------| -| **Support** | SDK 21+ | 12.0+ | 10.14+ | +| **Support** | SDK 24+ | 12.0+ | 10.14+ | ## Usage @@ -160,10 +160,9 @@ for more details. ### PlatformView Implementation on Android -The PlatformView implementation for Android uses Texture Layer Hybrid Composition on versions 23+ -and automatically fallbacks to Hybrid Composition for version 21-23. See section +The PlatformView implementation for Android uses Texture Layer Hybrid Composition. See section `Platform-Specific Features` and [AndroidWebViewWidgetCreationParams.displayWithHybridComposition](https://pub.dev/documentation/webview_flutter_android/latest/webview_flutter_android/AndroidWebViewWidgetCreationParams/displayWithHybridComposition.html) -to manually switch to Hybrid Composition on versions 23+. +to manually switch to Hybrid Composition. ### API Changes diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 0ed4b202c74..99218347cd2 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 4.10.2 + +* Updates minimum supported SDK version to Flutter 3.35. +* Removes obsolete code related to supporting SDK <24. + ## 4.10.1 * Bumps com.android.tools.build:gradle to 8.12.1 and kotlin_version to 2.2.10. diff --git a/packages/webview_flutter/webview_flutter_android/README.md b/packages/webview_flutter/webview_flutter_android/README.md index 10cc91b2bae..a87a4ace48f 100644 --- a/packages/webview_flutter/webview_flutter_android/README.md +++ b/packages/webview_flutter/webview_flutter_android/README.md @@ -19,7 +19,7 @@ specific mode, you can set it explicitly. ### Texture Layer Hybrid Composition -This is the current default mode for versions >=23. This is a new display mode used by most +This is the current default mode, and is the display mode used by most plugins starting with Flutter 3.0. This is more performant than Hybrid Composition, but has some limitations from using an Android [SurfaceTexture](https://developer.android.com/reference/android/graphics/SurfaceTexture). See: @@ -28,11 +28,11 @@ See: ### Hybrid Composition -This is the current default mode for versions <23. It ensures that the WebView will display and work -as expected, at the cost of some performance. See: +This ensures that the WebView will display and work as expected in the edge cases noted above, at +the cost of some performance. See: * https://docs.flutter.dev/platform-integration/android/platform-views#performance -This can be configured for versions >=23 with +This can be configured with `AndroidWebViewWidgetCreationParams.displayWithHybridComposition`. See https://pub.dev/packages/webview_flutter#platform-specific-features for more details on setting platform-specific features in the main plugin. diff --git a/packages/webview_flutter/webview_flutter_android/android/build.gradle b/packages/webview_flutter/webview_flutter_android/android/build.gradle index de718b4d311..ee074731600 100644 --- a/packages/webview_flutter/webview_flutter_android/android/build.gradle +++ b/packages/webview_flutter/webview_flutter_android/android/build.gradle @@ -38,7 +38,7 @@ android { } defaultConfig { - minSdkVersion 21 + minSdkVersion 24 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } 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 3a9932d3f4f..5a44f311f7f 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 @@ -1,7 +1,7 @@ // 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. -// Autogenerated from Pigeon (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v25.5.0), do not edit directly. // See also: https://pub.dev/packages/pigeon @file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") @@ -687,7 +687,7 @@ private class AndroidWebkitLibraryPigeonProxyApiBaseCodec( registrar.getPigeonApiWebResourceRequest().pigeon_newInstance(value) {} } else if (value is android.webkit.WebResourceResponse) { registrar.getPigeonApiWebResourceResponse().pigeon_newInstance(value) {} - } else if (android.os.Build.VERSION.SDK_INT >= 23 && value is android.webkit.WebResourceError) { + } else if (value is android.webkit.WebResourceError) { registrar.getPigeonApiWebResourceError().pigeon_newInstance(value) {} } else if (value is androidx.webkit.WebResourceErrorCompat) { registrar.getPigeonApiWebResourceErrorCompat().pigeon_newInstance(value) {} @@ -999,7 +999,7 @@ abstract class PigeonApiWebResourceRequest( abstract fun isForMainFrame(pigeon_instance: android.webkit.WebResourceRequest): Boolean /** Whether the request was a result of a server-side redirect. */ - abstract fun isRedirect(pigeon_instance: android.webkit.WebResourceRequest): Boolean? + abstract fun isRedirect(pigeon_instance: android.webkit.WebResourceRequest): Boolean /** Whether a gesture (such as a click) was associated with the request. */ abstract fun hasGesture(pigeon_instance: android.webkit.WebResourceRequest): Boolean @@ -1124,16 +1124,13 @@ abstract class PigeonApiWebResourceError( open val pigeonRegistrar: AndroidWebkitLibraryPigeonProxyApiRegistrar ) { /** The error code of the error. */ - @androidx.annotation.RequiresApi(api = 23) abstract fun errorCode(pigeon_instance: android.webkit.WebResourceError): Long /** The string describing the error. */ - @androidx.annotation.RequiresApi(api = 23) abstract fun description(pigeon_instance: android.webkit.WebResourceError): String @Suppress("LocalVariableName", "FunctionName") /** Creates a Dart instance of WebResourceError and attaches it to [pigeon_instanceArg]. */ - @androidx.annotation.RequiresApi(api = 23) fun pigeon_newInstance( pigeon_instanceArg: android.webkit.WebResourceError, callback: (Result) -> Unit @@ -3112,7 +3109,6 @@ abstract class PigeonApiWebViewClient( } /** Report web resource loading error to the host application. */ - @androidx.annotation.RequiresApi(api = 23) fun onReceivedRequestError( pigeon_instanceArg: android.webkit.WebViewClient, webViewArg: android.webkit.WebView, @@ -3180,42 +3176,6 @@ abstract class PigeonApiWebViewClient( } } - /** Report an error to the host application. */ - fun onReceivedError( - pigeon_instanceArg: android.webkit.WebViewClient, - webViewArg: android.webkit.WebView, - errorCodeArg: Long, - descriptionArg: String, - failingUrlArg: String, - callback: (Result) -> Unit - ) { - if (pigeonRegistrar.ignoreCallsToDart) { - callback( - Result.failure( - AndroidWebKitError("ignore-calls-error", "Calls to Dart are being ignored.", ""))) - return - } - val binaryMessenger = pigeonRegistrar.binaryMessenger - val codec = pigeonRegistrar.codec - val channelName = "dev.flutter.pigeon.webview_flutter_android.WebViewClient.onReceivedError" - val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send( - listOf(pigeon_instanceArg, webViewArg, errorCodeArg, descriptionArg, failingUrlArg)) { - if (it is List<*>) { - if (it.size > 1) { - callback( - Result.failure( - AndroidWebKitError(it[0] as String, it[1] as String, it[2] as String?))) - } else { - callback(Result.success(Unit)) - } - } else { - callback( - Result.failure(AndroidWebkitLibraryPigeonUtils.createConnectionError(channelName))) - } - } - } - /** * Give the host application a chance to take control when a URL is about to be loaded in the * current WebView. diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/ProxyApiRegistrar.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/ProxyApiRegistrar.java index 84e025a7a0c..ce406a578de 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/ProxyApiRegistrar.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/ProxyApiRegistrar.java @@ -12,7 +12,6 @@ import android.util.Log; import androidx.annotation.ChecksSdkIntAtLeast; import androidx.annotation.NonNull; -import androidx.annotation.RequiresApi; import io.flutter.plugin.common.BinaryMessenger; public class ProxyApiRegistrar extends AndroidWebkitLibraryPigeonProxyApiRegistrar { @@ -75,7 +74,6 @@ public PigeonApiWebResourceRequest getPigeonApiWebResourceRequest() { return new WebResourceRequestProxyApi(this); } - @RequiresApi(api = Build.VERSION_CODES.M) @NonNull @Override public PigeonApiWebResourceError getPigeonApiWebResourceError() { diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientProxyApi.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientProxyApi.java index 8a0d86f7b45..f86bf5bd586 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientProxyApi.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientProxyApi.java @@ -5,7 +5,6 @@ package io.flutter.plugins.webviewflutter; import android.net.Uri; -import android.os.Build; import android.os.Message; import android.view.View; import android.webkit.ConsoleMessage; @@ -20,7 +19,6 @@ import android.webkit.WebViewClient; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; import androidx.annotation.VisibleForTesting; import java.util.List; import java.util.Objects; @@ -278,7 +276,6 @@ boolean onCreateWindow( final WebViewClient windowWebViewClient = new WebViewClient() { - @RequiresApi(api = Build.VERSION_CODES.N) @Override public boolean shouldOverrideUrlLoading( @NonNull WebView windowWebView, @NonNull WebResourceRequest request) { @@ -287,16 +284,6 @@ public boolean shouldOverrideUrlLoading( } return true; } - - // Legacy codepath for < N. - @Override - @SuppressWarnings({"deprecation", "RedundantSuppression"}) - public boolean shouldOverrideUrlLoading(WebView windowWebView, String url) { - if (!webViewClient.shouldOverrideUrlLoading(view, url)) { - view.loadUrl(url); - } - return true; - } }; if (onCreateWindowWebView == null) { diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebResourceErrorProxyApi.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebResourceErrorProxyApi.java index 88baba36e16..9c258e3de2c 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebResourceErrorProxyApi.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebResourceErrorProxyApi.java @@ -4,12 +4,9 @@ package io.flutter.plugins.webviewflutter; -import android.os.Build; import android.webkit.WebResourceError; import androidx.annotation.NonNull; -import androidx.annotation.RequiresApi; -@RequiresApi(api = Build.VERSION_CODES.M) public class WebResourceErrorProxyApi extends PigeonApiWebResourceError { public WebResourceErrorProxyApi(@NonNull ProxyApiRegistrar pigeonRegistrar) { super(pigeonRegistrar); diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebResourceRequestProxyApi.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebResourceRequestProxyApi.java index 872cf27d2d1..a50a398ba8f 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebResourceRequestProxyApi.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebResourceRequestProxyApi.java @@ -4,7 +4,6 @@ package io.flutter.plugins.webviewflutter; -import android.os.Build; import android.webkit.WebResourceRequest; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -28,14 +27,10 @@ public boolean isForMainFrame(@NonNull WebResourceRequest pigeon_instance) { return pigeon_instance.isForMainFrame(); } - @Nullable + @NonNull @Override - public Boolean isRedirect(@NonNull WebResourceRequest pigeon_instance) { - if (getPigeonRegistrar().sdkIsAtLeast(Build.VERSION_CODES.N)) { - return pigeon_instance.isRedirect(); - } - - return null; + public boolean isRedirect(@NonNull WebResourceRequest pigeon_instance) { + return pigeon_instance.isRedirect(); } @NonNull diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientProxyApi.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientProxyApi.java index d5fd065ac86..e02517c138e 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientProxyApi.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientProxyApi.java @@ -5,7 +5,6 @@ package io.flutter.plugins.webviewflutter; import android.graphics.Bitmap; -import android.os.Build; import android.view.KeyEvent; import android.webkit.HttpAuthHandler; import android.webkit.WebResourceError; @@ -15,9 +14,6 @@ import android.webkit.WebViewClient; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; -import androidx.webkit.WebResourceErrorCompat; -import androidx.webkit.WebViewClientCompat; /** * Host api implementation for {@link WebViewClient}. @@ -26,7 +22,6 @@ */ public class WebViewClientProxyApi extends PigeonApiWebViewClient { /** Implementation of {@link WebViewClient} that passes arguments of callback methods to Dart. */ - @RequiresApi(Build.VERSION_CODES.N) public static class WebViewClientImpl extends WebViewClient { private final WebViewClientProxyApi api; private boolean returnValueForShouldOverrideUrlLoading = false; @@ -72,21 +67,6 @@ public void onReceivedError( () -> api.onReceivedRequestError(this, view, request, error, reply -> null)); } - // Legacy codepath for < 23; newer versions use the variant above. - @SuppressWarnings("deprecation") - @Override - public void onReceivedError( - @NonNull WebView view, - int errorCode, - @NonNull String description, - @NonNull String failingUrl) { - api.getPigeonRegistrar() - .runOnMainThread( - () -> - api.onReceivedError( - this, view, (long) errorCode, description, failingUrl, reply -> null)); - } - @Override public boolean shouldOverrideUrlLoading( @NonNull WebView view, @NonNull WebResourceRequest request) { @@ -98,15 +78,6 @@ public boolean shouldOverrideUrlLoading( return request.isForMainFrame() && returnValueForShouldOverrideUrlLoading; } - // Legacy codepath for < 24; newer versions use the variant above. - @SuppressWarnings("deprecation") - @Override - public boolean shouldOverrideUrlLoading(@NonNull WebView view, @NonNull String url) { - api.getPigeonRegistrar() - .runOnMainThread(() -> api.urlLoading(this, view, url, reply -> null)); - return returnValueForShouldOverrideUrlLoading; - } - @Override public void doUpdateVisitedHistory( @NonNull WebView view, @NonNull String url, boolean isReload) { @@ -196,174 +167,6 @@ public void setReturnValueForShouldOverrideUrlLoading(boolean value) { } } - /** - * Implementation of {@link WebViewClientCompat} that passes arguments of callback methods to - * Dart. - */ - public static class WebViewClientCompatImpl extends WebViewClientCompat { - private final WebViewClientProxyApi api; - private boolean returnValueForShouldOverrideUrlLoading = false; - - public WebViewClientCompatImpl(@NonNull WebViewClientProxyApi api) { - this.api = api; - } - - @Override - public void onPageStarted(@NonNull WebView view, @NonNull String url, @NonNull Bitmap favicon) { - api.getPigeonRegistrar() - .runOnMainThread(() -> api.onPageStarted(this, view, url, reply -> null)); - } - - @Override - public void onPageFinished(@NonNull WebView view, @NonNull String url) { - api.getPigeonRegistrar() - .runOnMainThread(() -> api.onPageFinished(this, view, url, reply -> null)); - } - - @Override - public void onReceivedHttpError( - @NonNull WebView view, - @NonNull WebResourceRequest request, - @NonNull WebResourceResponse response) { - api.getPigeonRegistrar() - .runOnMainThread( - () -> api.onReceivedHttpError(this, view, request, response, reply -> null)); - } - - @Override - public void onReceivedError( - @NonNull WebView view, - @NonNull WebResourceRequest request, - @NonNull WebResourceErrorCompat error) { - api.getPigeonRegistrar() - .runOnMainThread( - () -> api.onReceivedRequestErrorCompat(this, view, request, error, reply -> null)); - } - - // Legacy codepath for versions that don't support the variant above. - @SuppressWarnings("deprecation") - @Override - public void onReceivedError( - @NonNull WebView view, - int errorCode, - @NonNull String description, - @NonNull String failingUrl) { - api.getPigeonRegistrar() - .runOnMainThread( - () -> - api.onReceivedError( - this, view, (long) errorCode, description, failingUrl, reply -> null)); - } - - @Override - public boolean shouldOverrideUrlLoading( - @NonNull WebView view, @NonNull WebResourceRequest request) { - api.getPigeonRegistrar() - .runOnMainThread(() -> api.requestLoading(this, view, request, reply -> null)); - - // The client is only allowed to stop navigations that target the main frame because - // overridden URLs are passed to `loadUrl` and `loadUrl` cannot load a subframe. - return request.isForMainFrame() && returnValueForShouldOverrideUrlLoading; - } - - @SuppressWarnings("deprecation") - @Override - public boolean shouldOverrideUrlLoading(@NonNull WebView view, @NonNull String url) { - api.getPigeonRegistrar() - .runOnMainThread(() -> api.urlLoading(this, view, url, reply -> null)); - return returnValueForShouldOverrideUrlLoading; - } - - @Override - public void doUpdateVisitedHistory( - @NonNull WebView view, @NonNull String url, boolean isReload) { - api.getPigeonRegistrar() - .runOnMainThread( - () -> api.doUpdateVisitedHistory(this, view, url, isReload, reply -> null)); - } - - // Handles an HTTP authentication request. - // - // This callback is invoked when the WebView encounters a website requiring HTTP authentication. - // [host] and [realm] are provided for matching against stored credentials, if any. - @Override - public void onReceivedHttpAuthRequest( - @NonNull WebView view, HttpAuthHandler handler, String host, String realm) { - api.getPigeonRegistrar() - .runOnMainThread( - () -> api.onReceivedHttpAuthRequest(this, view, handler, host, realm, reply -> null)); - } - - @Override - public void onFormResubmission( - @NonNull android.webkit.WebView view, - @NonNull android.os.Message dontResend, - @NonNull android.os.Message resend) { - api.getPigeonRegistrar() - .runOnMainThread( - () -> api.onFormResubmission(this, view, dontResend, resend, reply -> null)); - } - - @Override - public void onLoadResource(@NonNull android.webkit.WebView view, @NonNull String url) { - api.getPigeonRegistrar() - .runOnMainThread(() -> api.onLoadResource(this, view, url, reply -> null)); - } - - @Override - public void onPageCommitVisible(@NonNull android.webkit.WebView view, @NonNull String url) { - api.getPigeonRegistrar() - .runOnMainThread(() -> api.onPageCommitVisible(this, view, url, reply -> null)); - } - - @Override - public void onReceivedClientCertRequest( - @NonNull android.webkit.WebView view, @NonNull android.webkit.ClientCertRequest request) { - api.getPigeonRegistrar() - .runOnMainThread( - () -> api.onReceivedClientCertRequest(this, view, request, reply -> null)); - } - - @Override - public void onReceivedLoginRequest( - @NonNull android.webkit.WebView view, - @NonNull String realm, - @Nullable String account, - @NonNull String args) { - api.getPigeonRegistrar() - .runOnMainThread( - () -> api.onReceivedLoginRequest(this, view, realm, account, args, reply -> null)); - } - - @Override - public void onReceivedSslError( - @NonNull android.webkit.WebView view, - @NonNull android.webkit.SslErrorHandler handler, - @NonNull android.net.http.SslError error) { - api.getPigeonRegistrar() - .runOnMainThread(() -> api.onReceivedSslError(this, view, handler, error, reply -> null)); - } - - @Override - public void onScaleChanged( - @NonNull android.webkit.WebView view, float oldScale, float newScale) { - api.getPigeonRegistrar() - .runOnMainThread(() -> api.onScaleChanged(this, view, oldScale, newScale, reply -> null)); - } - - @Override - public void onUnhandledKeyEvent(@NonNull WebView view, @NonNull KeyEvent event) { - // Deliberately empty. Occasionally the webview will mark events as having failed to be - // handled even though they were handled. We don't want to propagate those as they're not - // truly lost. - } - - /** Sets return value for {@link #shouldOverrideUrlLoading}. */ - public void setReturnValueForShouldOverrideUrlLoading(boolean value) { - returnValueForShouldOverrideUrlLoading = value; - } - } - /** Creates a host API that handles creating {@link WebViewClient}s. */ public WebViewClientProxyApi(@NonNull ProxyApiRegistrar pigeonRegistrar) { super(pigeonRegistrar); @@ -372,28 +175,13 @@ public WebViewClientProxyApi(@NonNull ProxyApiRegistrar pigeonRegistrar) { @NonNull @Override public WebViewClient pigeon_defaultConstructor() { - // WebViewClientCompat is used to get - // shouldOverrideUrlLoading(WebView view, WebResourceRequest request) - // invoked by the webview on older Android devices, without it pages that use iframes will - // be broken when a navigationDelegate is set on Android version earlier than N. - // - // However, this if statement attempts to avoid using WebViewClientCompat on versions >= N due - // to bug https://bugs.chromium.org/p/chromium/issues/detail?id=925887. Also, see - // https://github.com/flutter/flutter/issues/29446. - if (getPigeonRegistrar().sdkIsAtLeast(Build.VERSION_CODES.N)) { - return new WebViewClientImpl(this); - } else { - return new WebViewClientCompatImpl(this); - } + return new WebViewClientImpl(this); } @Override public void setSynchronousReturnValueForShouldOverrideUrlLoading( @NonNull WebViewClient pigeon_instance, boolean value) { - if (pigeon_instance instanceof WebViewClientCompatImpl) { - ((WebViewClientCompatImpl) pigeon_instance).setReturnValueForShouldOverrideUrlLoading(value); - } else if (getPigeonRegistrar().sdkIsAtLeast(Build.VERSION_CODES.N) - && pigeon_instance instanceof WebViewClientImpl) { + if (pigeon_instance instanceof WebViewClientImpl) { ((WebViewClientImpl) pigeon_instance).setReturnValueForShouldOverrideUrlLoading(value); } else { throw new IllegalStateException( diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientCompatTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientCompatTest.java deleted file mode 100644 index 07c1bd41f00..00000000000 --- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientCompatTest.java +++ /dev/null @@ -1,271 +0,0 @@ -// 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. - -package io.flutter.plugins.webviewflutter; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.net.http.SslError; -import android.os.Message; -import android.webkit.ClientCertRequest; -import android.webkit.HttpAuthHandler; -import android.webkit.SslErrorHandler; -import android.webkit.WebResourceRequest; -import android.webkit.WebView; -import org.junit.Test; - -public class WebViewClientCompatTest { - @Test - public void onPageStarted() { - final WebViewClientProxyApi mockApi = mock(WebViewClientProxyApi.class); - when(mockApi.getPigeonRegistrar()).thenReturn(new TestProxyApiRegistrar()); - - final WebViewClientProxyApi.WebViewClientCompatImpl instance = - new WebViewClientProxyApi.WebViewClientCompatImpl(mockApi); - final WebView webView = mock(WebView.class); - final String url = "myString"; - instance.onPageStarted(webView, url, null); - - verify(mockApi).onPageStarted(eq(instance), eq(webView), eq(url), any()); - } - - @Test - public void onReceivedError() { - final WebViewClientProxyApi mockApi = mock(WebViewClientProxyApi.class); - when(mockApi.getPigeonRegistrar()).thenReturn(new TestProxyApiRegistrar()); - - final WebViewClientProxyApi.WebViewClientCompatImpl instance = - new WebViewClientProxyApi.WebViewClientCompatImpl(mockApi); - final android.webkit.WebView webView = mock(WebView.class); - final Long errorCode = 0L; - final String description = "myString"; - final String failingUrl = "myString1"; - instance.onReceivedError(webView, errorCode.intValue(), description, failingUrl); - - verify(mockApi) - .onReceivedError( - eq(instance), eq(webView), eq(errorCode), eq(description), eq(failingUrl), any()); - } - - @Test - public void urlLoading() { - final WebViewClientProxyApi mockApi = mock(WebViewClientProxyApi.class); - when(mockApi.getPigeonRegistrar()).thenReturn(new TestProxyApiRegistrar()); - - final WebViewClientProxyApi.WebViewClientCompatImpl instance = - new WebViewClientProxyApi.WebViewClientCompatImpl(mockApi); - final android.webkit.WebView webView = mock(WebView.class); - final String url = "myString"; - instance.shouldOverrideUrlLoading(webView, url); - - verify(mockApi).urlLoading(eq(instance), eq(webView), eq(url), any()); - } - - @Test - public void urlLoadingForMainFrame() { - final WebViewClientProxyApi mockApi = mock(WebViewClientProxyApi.class); - when(mockApi.getPigeonRegistrar()).thenReturn(new TestProxyApiRegistrar()); - - final WebViewClientProxyApi.WebViewClientCompatImpl instance = - new WebViewClientProxyApi.WebViewClientCompatImpl(mockApi); - instance.setReturnValueForShouldOverrideUrlLoading(false); - final android.webkit.WebView webView = mock(WebView.class); - final String url = "myString"; - instance.shouldOverrideUrlLoading(webView, url); - - verify(mockApi).urlLoading(eq(instance), eq(webView), eq(url), any()); - } - - @Test - public void urlLoadingForMainFrameWithOverride() { - final WebViewClientProxyApi mockApi = mock(WebViewClientProxyApi.class); - when(mockApi.getPigeonRegistrar()).thenReturn(new TestProxyApiRegistrar()); - - final WebViewClientProxyApi.WebViewClientCompatImpl instance = - new WebViewClientProxyApi.WebViewClientCompatImpl(mockApi); - instance.setReturnValueForShouldOverrideUrlLoading(true); - final android.webkit.WebView webView = mock(WebView.class); - final String url = "myString"; - - assertTrue(instance.shouldOverrideUrlLoading(webView, url)); - verify(mockApi).urlLoading(eq(instance), eq(webView), eq(url), any()); - } - - @Test - public void urlLoadingNotForMainFrame() { - final WebViewClientProxyApi mockApi = mock(WebViewClientProxyApi.class); - when(mockApi.getPigeonRegistrar()).thenReturn(new TestProxyApiRegistrar()); - - final WebViewClientProxyApi.WebViewClientCompatImpl instance = - new WebViewClientProxyApi.WebViewClientCompatImpl(mockApi); - final android.webkit.WebView webView = mock(WebView.class); - final android.webkit.WebResourceRequest request = mock(WebResourceRequest.class); - when(request.isForMainFrame()).thenReturn(false); - instance.shouldOverrideUrlLoading(webView, request); - - verify(mockApi).requestLoading(eq(instance), eq(webView), eq(request), any()); - } - - @Test - public void urlLoadingNotForMainFrameWithOverride() { - final WebViewClientProxyApi mockApi = mock(WebViewClientProxyApi.class); - when(mockApi.getPigeonRegistrar()).thenReturn(new TestProxyApiRegistrar()); - - final WebViewClientProxyApi.WebViewClientCompatImpl instance = - new WebViewClientProxyApi.WebViewClientCompatImpl(mockApi); - instance.setReturnValueForShouldOverrideUrlLoading(true); - - final android.webkit.WebView webView = mock(WebView.class); - final android.webkit.WebResourceRequest request = mock(WebResourceRequest.class); - when(request.isForMainFrame()).thenReturn(false); - - assertFalse(instance.shouldOverrideUrlLoading(webView, request)); - verify(mockApi).requestLoading(eq(instance), eq(webView), eq(request), any()); - } - - @Test - public void doUpdateVisitedHistory() { - final WebViewClientProxyApi mockApi = mock(WebViewClientProxyApi.class); - when(mockApi.getPigeonRegistrar()).thenReturn(new TestProxyApiRegistrar()); - - final WebViewClientProxyApi.WebViewClientCompatImpl instance = - new WebViewClientProxyApi.WebViewClientCompatImpl(mockApi); - final android.webkit.WebView webView = mock(WebView.class); - final String url = "myString"; - final Boolean isReload = true; - instance.doUpdateVisitedHistory(webView, url, isReload); - - verify(mockApi).doUpdateVisitedHistory(eq(instance), eq(webView), eq(url), eq(isReload), any()); - } - - @Test - public void onReceivedHttpError() { - final WebViewClientProxyApi mockApi = mock(WebViewClientProxyApi.class); - when(mockApi.getPigeonRegistrar()).thenReturn(new TestProxyApiRegistrar()); - - final WebViewClientProxyApi.WebViewClientCompatImpl instance = - new WebViewClientProxyApi.WebViewClientCompatImpl(mockApi); - final android.webkit.WebView webView = mock(WebView.class); - final HttpAuthHandler handler = mock(HttpAuthHandler.class); - final String host = "myString"; - final String realm = "myString1"; - instance.onReceivedHttpAuthRequest(webView, handler, host, realm); - - verify(mockApi) - .onReceivedHttpAuthRequest( - eq(instance), eq(webView), eq(handler), eq(host), eq(realm), any()); - } - - @Test - public void onFormResubmission() { - final WebViewClientProxyApi mockApi = mock(WebViewClientProxyApi.class); - when(mockApi.getPigeonRegistrar()).thenReturn(new TestProxyApiRegistrar()); - - final WebViewClientProxyApi.WebViewClientImpl instance = - new WebViewClientProxyApi.WebViewClientImpl(mockApi); - final android.webkit.WebView view = mock(WebView.class); - final android.os.Message dontResend = mock(Message.class); - final android.os.Message resend = mock(Message.class); - instance.onFormResubmission(view, dontResend, resend); - - verify(mockApi).onFormResubmission(eq(instance), eq(view), eq(dontResend), eq(resend), any()); - } - - @Test - public void onLoadResource() { - final WebViewClientProxyApi mockApi = mock(WebViewClientProxyApi.class); - when(mockApi.getPigeonRegistrar()).thenReturn(new TestProxyApiRegistrar()); - - final WebViewClientProxyApi.WebViewClientImpl instance = - new WebViewClientProxyApi.WebViewClientImpl(mockApi); - final android.webkit.WebView view = mock(WebView.class); - final String url = "myString"; - instance.onLoadResource(view, url); - - verify(mockApi).onLoadResource(eq(instance), eq(view), eq(url), any()); - } - - @Test - public void onPageCommitVisible() { - final WebViewClientProxyApi mockApi = mock(WebViewClientProxyApi.class); - when(mockApi.getPigeonRegistrar()).thenReturn(new TestProxyApiRegistrar()); - - final WebViewClientProxyApi.WebViewClientImpl instance = - new WebViewClientProxyApi.WebViewClientImpl(mockApi); - final android.webkit.WebView view = mock(WebView.class); - final String url = "myString"; - instance.onPageCommitVisible(view, url); - - verify(mockApi).onPageCommitVisible(eq(instance), eq(view), eq(url), any()); - } - - @Test - public void onReceivedClientCertRequest() { - final WebViewClientProxyApi mockApi = mock(WebViewClientProxyApi.class); - when(mockApi.getPigeonRegistrar()).thenReturn(new TestProxyApiRegistrar()); - - final WebViewClientProxyApi.WebViewClientImpl instance = - new WebViewClientProxyApi.WebViewClientImpl(mockApi); - final android.webkit.WebView view = mock(WebView.class); - final android.webkit.ClientCertRequest request = mock(ClientCertRequest.class); - instance.onReceivedClientCertRequest(view, request); - - verify(mockApi).onReceivedClientCertRequest(eq(instance), eq(view), eq(request), any()); - } - - @Test - public void onReceivedLoginRequest() { - final WebViewClientProxyApi mockApi = mock(WebViewClientProxyApi.class); - when(mockApi.getPigeonRegistrar()).thenReturn(new TestProxyApiRegistrar()); - - final WebViewClientProxyApi.WebViewClientImpl instance = - new WebViewClientProxyApi.WebViewClientImpl(mockApi); - final android.webkit.WebView view = mock(WebView.class); - final String realm = "myString"; - final String account = "myString1"; - final String args = "myString2"; - instance.onReceivedLoginRequest(view, realm, account, args); - - verify(mockApi) - .onReceivedLoginRequest(eq(instance), eq(view), eq(realm), eq(account), eq(args), any()); - } - - @Test - public void onReceivedSslError() { - final WebViewClientProxyApi mockApi = mock(WebViewClientProxyApi.class); - when(mockApi.getPigeonRegistrar()).thenReturn(new TestProxyApiRegistrar()); - - final WebViewClientProxyApi.WebViewClientImpl instance = - new WebViewClientProxyApi.WebViewClientImpl(mockApi); - final android.webkit.WebView view = mock(WebView.class); - final android.webkit.SslErrorHandler handler = mock(SslErrorHandler.class); - final android.net.http.SslError error = mock(SslError.class); - instance.onReceivedSslError(view, handler, error); - - verify(mockApi).onReceivedSslError(eq(instance), eq(view), eq(handler), eq(error), any()); - } - - @Test - public void onScaleChanged() { - final WebViewClientProxyApi mockApi = mock(WebViewClientProxyApi.class); - when(mockApi.getPigeonRegistrar()).thenReturn(new TestProxyApiRegistrar()); - - final WebViewClientProxyApi.WebViewClientImpl instance = - new WebViewClientProxyApi.WebViewClientImpl(mockApi); - final android.webkit.WebView view = mock(WebView.class); - final float oldScale = 1.0f; - final float newScale = 2.0f; - instance.onScaleChanged(view, oldScale, newScale); - - verify(mockApi) - .onScaleChanged( - eq(instance), eq(view), eq((double) oldScale), eq((double) newScale), any()); - } -} diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java index 9f8bb4ea295..12596d8cca2 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java @@ -17,6 +17,7 @@ import android.webkit.ClientCertRequest; import android.webkit.HttpAuthHandler; import android.webkit.SslErrorHandler; +import android.webkit.WebResourceError; import android.webkit.WebResourceRequest; import android.webkit.WebView; import io.flutter.plugins.webviewflutter.WebViewClientProxyApi.WebViewClientImpl; @@ -43,14 +44,12 @@ public void onReceivedError() { final WebViewClientImpl instance = new WebViewClientImpl(mockApi); final android.webkit.WebView webView = mock(WebView.class); - final Long errorCode = 0L; - final String description = "myString"; - final String failingUrl = "myString1"; - instance.onReceivedError(webView, errorCode.intValue(), description, failingUrl); + final android.webkit.WebResourceRequest request = mock(WebResourceRequest.class); + final android.webkit.WebResourceError error = mock(WebResourceError.class); + instance.onReceivedError(webView, request, error); verify(mockApi) - .onReceivedError( - eq(instance), eq(webView), eq(errorCode), eq(description), eq(failingUrl), any()); + .onReceivedRequestError(eq(instance), eq(webView), eq(request), eq(error), any()); } @Test @@ -60,10 +59,10 @@ public void urlLoading() { final WebViewClientImpl instance = new WebViewClientImpl(mockApi); final android.webkit.WebView webView = mock(WebView.class); - final String url = "myString"; - instance.shouldOverrideUrlLoading(webView, url); + final android.webkit.WebResourceRequest request = mock(WebResourceRequest.class); + instance.shouldOverrideUrlLoading(webView, request); - verify(mockApi).urlLoading(eq(instance), eq(webView), eq(url), any()); + verify(mockApi).requestLoading(eq(instance), eq(webView), eq(request), any()); } @Test @@ -74,10 +73,11 @@ public void urlLoadingForMainFrame() { final WebViewClientImpl instance = new WebViewClientImpl(mockApi); instance.setReturnValueForShouldOverrideUrlLoading(false); final android.webkit.WebView webView = mock(WebView.class); - final String url = "myString"; - instance.shouldOverrideUrlLoading(webView, url); + final android.webkit.WebResourceRequest request = mock(WebResourceRequest.class); + when(request.isForMainFrame()).thenReturn(true); + instance.shouldOverrideUrlLoading(webView, request); - verify(mockApi).urlLoading(eq(instance), eq(webView), eq(url), any()); + verify(mockApi).requestLoading(eq(instance), eq(webView), eq(request), any()); } @Test @@ -88,10 +88,11 @@ public void urlLoadingForMainFrameWithOverride() { final WebViewClientImpl instance = new WebViewClientImpl(mockApi); instance.setReturnValueForShouldOverrideUrlLoading(true); final android.webkit.WebView webView = mock(WebView.class); - final String url = "myString"; + final android.webkit.WebResourceRequest request = mock(WebResourceRequest.class); + when(request.isForMainFrame()).thenReturn(true); - assertTrue(instance.shouldOverrideUrlLoading(webView, url)); - verify(mockApi).urlLoading(eq(instance), eq(webView), eq(url), any()); + assertTrue(instance.shouldOverrideUrlLoading(webView, request)); + verify(mockApi).requestLoading(eq(instance), eq(webView), eq(request), any()); } @Test diff --git a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart index 30231c619c4..fedcd4ca99b 100644 --- a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart @@ -139,23 +139,24 @@ Future main() async { controller: PlatformWebViewController( AndroidWebViewControllerCreationParams( androidWebViewProxy: AndroidWebViewProxy( - newWebView: ({ - void Function( - android_webkit.WebView, - int, - int, - int, - int, - )? - onScrollChanged, - }) { - final android_webkit.WebView webView = - android_webkit.WebView( - onScrollChanged: onScrollChanged, - ); - finalizer.attach(webView, webViewToken); - return webView; - }, + newWebView: + ({ + void Function( + android_webkit.WebView, + int, + int, + int, + int, + )? + onScrollChanged, + }) { + final android_webkit.WebView webView = + android_webkit.WebView( + onScrollChanged: onScrollChanged, + ); + finalizer.attach(webView, webViewToken); + return webView; + }, ), ), ), @@ -387,7 +388,8 @@ Future main() async { final String base64VideoData = base64Encode( Uint8List.view(videoData.buffer), ); - final String videoTest = ''' + final String videoTest = + ''' Video auto play