diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index d0d4262d062..272901ab871 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -46b097a3070d9941a729a201b972c65414c40b37 +c3279caa127d44914069326cd287c012bb3f2783 diff --git a/CODEOWNERS b/CODEOWNERS index 6a8439a4fb9..cb9b5761722 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -22,7 +22,7 @@ packages/image_picker/** @tarrinneal packages/interactive_media_ads/** @bparrishMines packages/in_app_purchase/** @bparrishMines packages/local_auth/** @stuartmorgan-g -packages/metrics_center/** @christopherfujino +packages/metrics_center/** @bkonyi packages/multicast_dns/** @vashworth packages/path_provider/** @stuartmorgan-g packages/pigeon/** @tarrinneal @@ -32,12 +32,12 @@ packages/pointer_interceptor/** @ditman packages/quick_actions/** @bparrishMines packages/rfw/** @Hixie packages/shared_preferences/** @tarrinneal -packages/standard_message_codec/** @jonahwilliams +packages/standard_message_codec/** @stuartmorgan-g packages/two_dimensional_scrollables/** @Piinks packages/url_launcher/** @stuartmorgan-g -packages/vector_graphics/** @jonahwilliams -packages/vector_graphics_codec/** @jonahwilliams -packages/vector_graphics_compiler/** @jonahwilliams +packages/vector_graphics/** @jtmcdole +packages/vector_graphics_codec/** @jtmcdole +packages/vector_graphics_compiler/** @jtmcdole packages/video_player/** @tarrinneal packages/web_benchmarks/** @yjbanov packages/webview_flutter/** @bparrishMines @@ -52,71 +52,71 @@ third_party/packages/path_parsing/** @domesticmouse # matching entry takes precedence. # - Web -packages/camera/camera_web/** @ditman -packages/file_selector/file_selector_web/** @ditman -packages/google_maps_flutter/google_maps_flutter_web/** @ditman -packages/google_sign_in/google_sign_in_web/** @ditman -packages/image_picker/image_picker_for_web/** @ditman -packages/pointer_interceptor/pointer_interceptor_web/** @ditman -packages/shared_preferences/shared_preferences_web/** @ditman -packages/url_launcher/url_launcher_web/** @ditman -packages/video_player/video_player_web/** @ditman -packages/webview_flutter/webview_flutter_web/** @ditman +packages/camera/camera_web/** @ditman +packages/file_selector/file_selector_web/** @ditman +packages/google_maps_flutter/google_maps_flutter_web/** @ditman +packages/google_sign_in/google_sign_in_web/** @ditman +packages/image_picker/image_picker_for_web/** @ditman +packages/pointer_interceptor/pointer_interceptor_web/** @ditman +packages/shared_preferences/shared_preferences_web/** @ditman +packages/url_launcher/url_launcher_web/** @ditman +packages/video_player/video_player_web/** @ditman +packages/webview_flutter/webview_flutter_web/** @ditman # - Android -packages/camera/camera_android/** @camsim99 -packages/camera/camera_android_camerax/** @camsim99 -packages/espresso/** @jesswrd -packages/file_selector/file_selector_android/** @mboetger -packages/flutter_plugin_android_lifecycle/** @reidbaker -packages/google_maps_flutter/google_maps_flutter_android/** @reidbaker -packages/google_sign_in/google_sign_in_android/** @ash2moon -packages/image_picker/image_picker_android/** @gmackall -packages/in_app_purchase/in_app_purchase_android/** @gmackall -packages/local_auth/local_auth_android/** @mboetger -packages/path_provider/path_provider_android/** @camsim99 -packages/quick_actions/quick_actions_android/** @ash2moon -packages/shared_preferences/shared_preferences_android/** @jesswrd -packages/url_launcher/url_launcher_android/** @gmackall -packages/video_player/video_player_android/** @ash2moon +packages/camera/camera_android/** @camsim99 +packages/camera/camera_android_camerax/** @camsim99 +packages/espresso/** @jesswrd +packages/file_selector/file_selector_android/** @mboetger +packages/flutter_plugin_android_lifecycle/** @reidbaker +packages/google_maps_flutter/google_maps_flutter_android/** @reidbaker +packages/google_sign_in/google_sign_in_android/** @ash2moon +packages/image_picker/image_picker_android/** @gmackall +packages/in_app_purchase/in_app_purchase_android/** @gmackall +packages/local_auth/local_auth_android/** @mboetger +packages/path_provider/path_provider_android/** @camsim99 +packages/quick_actions/quick_actions_android/** @ash2moon +packages/shared_preferences/shared_preferences_android/** @jesswrd +packages/url_launcher/url_launcher_android/** @gmackall +packages/video_player/video_player_android/** @ash2moon # Owned by ecosystem team for now during the wrapper evaluation. -packages/webview_flutter/webview_flutter_android/** @bparrishMines +packages/webview_flutter/webview_flutter_android/** @bparrishMines # - Darwin -packages/camera/camera_avfoundation/** @hellohuanlin @vashworth -packages/file_selector/file_selector_ios/** @vashworth @cbracken -packages/file_selector/file_selector_macos/** @vashworth @cbracken -packages/google_maps_flutter/google_maps_flutter_ios/** @vashworth @cbracken -packages/google_sign_in/google_sign_in_ios/** @LongCatIsLooong @cbracken -packages/image_picker/image_picker_ios/** @vashworth @louisehsu -packages/image_picker/image_picker_macos/** @vashworth @louisehsu -packages/in_app_purchase/in_app_purchase_storekit/** @louisehsu @LongCatIsLooong -packages/local_auth/local_auth_darwin/** @cbracken @vashworth -packages/path_provider/path_provider_foundation/** @cbracken @vashworth -packages/pointer_interceptor/pointer_interceptor_ios/** @louisehsu @hellohuanlin -packages/quick_actions/quick_actions_ios/** @LongCatIsLooong @hellohuanlin +packages/camera/camera_avfoundation/** @hellohuanlin @vashworth +packages/file_selector/file_selector_ios/** @vashworth @cbracken +packages/file_selector/file_selector_macos/** @vashworth @cbracken +packages/google_maps_flutter/google_maps_flutter_ios/** @vashworth @cbracken +packages/google_sign_in/google_sign_in_ios/** @LongCatIsLooong @cbracken +packages/image_picker/image_picker_ios/** @vashworth @louisehsu +packages/image_picker/image_picker_macos/** @vashworth @louisehsu +packages/in_app_purchase/in_app_purchase_storekit/** @louisehsu @LongCatIsLooong +packages/local_auth/local_auth_darwin/** @cbracken @vashworth +packages/path_provider/path_provider_foundation/** @cbracken @vashworth +packages/pointer_interceptor/pointer_interceptor_ios/** @louisehsu @hellohuanlin +packages/quick_actions/quick_actions_ios/** @LongCatIsLooong @hellohuanlin packages/shared_preferences/shared_preferences_foundation/** @tarrinneal -packages/url_launcher/url_launcher_ios/** @hellohuanlin @LongCatIsLooong -packages/url_launcher/url_launcher_macos/** @hellohuanlin @LongCatIsLooong -packages/video_player/video_player_avfoundation/** @LongCatIsLooong @hellohuanlin -packages/webview_flutter/webview_flutter_wkwebview/** @cbracken @louisehsu +packages/url_launcher/url_launcher_ios/** @hellohuanlin @LongCatIsLooong +packages/url_launcher/url_launcher_macos/** @hellohuanlin @LongCatIsLooong +packages/video_player/video_player_avfoundation/** @LongCatIsLooong @hellohuanlin +packages/webview_flutter/webview_flutter_wkwebview/** @cbracken @louisehsu # - Linux -packages/file_selector/file_selector_linux/** @cbracken -packages/image_picker/image_picker_linux/** @cbracken -packages/path_provider/path_provider_linux/** @cbracken -packages/shared_preferences/shared_preferences_linux/** @cbracken -packages/url_launcher/url_launcher_linux/** @cbracken +packages/file_selector/file_selector_linux/** @stuartmorgan-g +packages/image_picker/image_picker_linux/** @stuartmorgan-g +packages/path_provider/path_provider_linux/** @stuartmorgan-g +packages/shared_preferences/shared_preferences_linux/** @stuartmorgan-g +packages/url_launcher/url_launcher_linux/** @stuartmorgan-g # - Windows -packages/camera/camera_windows/** @cbracken -packages/file_selector/file_selector_windows/** @cbracken -packages/image_picker/image_picker_windows/** @cbracken -packages/local_auth/local_auth_windows/** @cbracken -packages/path_provider/path_provider_windows/** @cbracken -packages/shared_preferences/shared_preferences_windows/** @cbracken -packages/url_launcher/url_launcher_windows/** @cbracken +packages/camera/camera_windows/** @stuartmorgan-g +packages/file_selector/file_selector_windows/** @stuartmorgan-g +packages/image_picker/image_picker_windows/** @stuartmorgan-g +packages/local_auth/local_auth_windows/** @stuartmorgan-g +packages/path_provider/path_provider_windows/** @stuartmorgan-g +packages/shared_preferences/shared_preferences_windows/** @stuartmorgan-g +packages/url_launcher/url_launcher_windows/** @stuartmorgan-g # - DevTools extensions # @adsonpleal is the actual maintainer of shared_preferences_tool but is not yet a committer, so can't be listed as the owner. -packages/shared_preferences/shared_preferences_tool/** @tarrinneal +packages/shared_preferences/shared_preferences_tool/** @tarrinneal diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index be098207753..4e8c96aaa21 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -1,3 +1,13 @@ +## 0.6.20 + +* Ensures that a new surface is provided every time that one is requested to render the camera preview + to fix pausing and resuming the preview. +* Updates minimum supported SDK version to Flutter 3.32.8/Dart 3.8.1. + +## 0.6.19+1 + +* Fixes incorrect camera switching by selecting a camera via its CameraInfo. + ## 0.6.19 * Changes target rotation of captured images to current default display rotation to fix captured diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraSelectorProxyApi.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraSelectorProxyApi.java index a84e16bcfc2..ba48bd6937a 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraSelectorProxyApi.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraSelectorProxyApi.java @@ -9,6 +9,7 @@ import androidx.camera.core.CameraInfo; import androidx.camera.core.CameraSelector; import androidx.camera.core.ExperimentalLensFacing; +import java.util.Collections; import java.util.List; /** @@ -24,7 +25,8 @@ class CameraSelectorProxyApi extends PigeonApiCameraSelector { @ExperimentalLensFacing @NonNull @Override - public CameraSelector pigeon_defaultConstructor(@Nullable LensFacing requireLensFacing) { + public CameraSelector pigeon_defaultConstructor( + @Nullable LensFacing requireLensFacing, @Nullable CameraInfo cameraInfoForFilter) { final CameraSelector.Builder builder = new CameraSelector.Builder(); if (requireLensFacing != null) { switch (requireLensFacing) { @@ -42,6 +44,9 @@ public CameraSelector pigeon_defaultConstructor(@Nullable LensFacing requireLens break; } } + if (cameraInfoForFilter != null) { + builder.addCameraFilter(cameraInfos -> Collections.singletonList(cameraInfoForFilter)); + } return builder.build(); } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXLibrary.g.kt b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXLibrary.g.kt index 1eeb0e98de8..71041b05add 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXLibrary.g.kt +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXLibrary.g.kt @@ -2027,7 +2027,8 @@ abstract class PigeonApiCameraSelector( open val pigeonRegistrar: CameraXLibraryPigeonProxyApiRegistrar ) { abstract fun pigeon_defaultConstructor( - requireLensFacing: LensFacing? + requireLensFacing: LensFacing?, + cameraInfoForFilter: androidx.camera.core.CameraInfo? ): androidx.camera.core.CameraSelector /** A static `CameraSelector` that selects the default back facing camera. */ @@ -2057,10 +2058,12 @@ abstract class PigeonApiCameraSelector( val args = message as List val pigeon_identifierArg = args[0] as Long val requireLensFacingArg = args[1] as LensFacing? + val cameraInfoForFilterArg = args[2] as androidx.camera.core.CameraInfo? val wrapped: List = try { api.pigeonRegistrar.instanceManager.addDartCreatedInstance( - api.pigeon_defaultConstructor(requireLensFacingArg), pigeon_identifierArg) + api.pigeon_defaultConstructor(requireLensFacingArg, cameraInfoForFilterArg), + pigeon_identifierArg) listOf(null) } catch (exception: Throwable) { CameraXLibraryPigeonUtils.wrapError(exception) diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewProxyApi.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewProxyApi.java index 5ebb5fbb739..48d94b65e10 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewProxyApi.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewProxyApi.java @@ -121,7 +121,7 @@ public void onSurfaceCleanup() { // Provide surface. surfaceProducer.setSize( request.getResolution().getWidth(), request.getResolution().getHeight()); - Surface flutterSurface = surfaceProducer.getSurface(); + Surface flutterSurface = surfaceProducer.getForcedNewSurface(); request.provideSurface( flutterSurface, Executors.newSingleThreadExecutor(), diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraSelectorTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraSelectorTest.java index 93ab0014edd..2d01c749d44 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraSelectorTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraSelectorTest.java @@ -10,6 +10,7 @@ import androidx.camera.core.CameraInfo; import androidx.camera.core.CameraSelector; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.junit.Test; @@ -18,13 +19,30 @@ public class CameraSelectorTest { @Test public void pigeon_defaultConstructor_createsCameraSelectorInstanceWithLensFacing() { final PigeonApiCameraSelector api = new TestProxyApiRegistrar().getPigeonApiCameraSelector(); - final CameraSelector selector = - api.pigeon_defaultConstructor(io.flutter.plugins.camerax.LensFacing.FRONT); + api.pigeon_defaultConstructor(io.flutter.plugins.camerax.LensFacing.FRONT, null); assertEquals(selector.getLensFacing(), (Integer) CameraSelector.LENS_FACING_FRONT); } + @Test + public void pigeon_defaultConstructor_createsCameraSelectorInstanceWithCameraInfo() { + final PigeonApiCameraSelector api = new TestProxyApiRegistrar().getPigeonApiCameraSelector(); + final androidx.camera.core.CameraInfo cameraInfo = mock(CameraInfo.class); + final androidx.camera.core.CameraInfo cameraInfoToSelect = mock(CameraInfo.class); + + final CameraSelector selector = api.pigeon_defaultConstructor(null, cameraInfoToSelect); + + final List cameraInfosList = new ArrayList<>(); + cameraInfosList.add(cameraInfo); + cameraInfosList.add(cameraInfoToSelect); + + final List filteredCameraInfosList = + selector.filter(new ArrayList<>(cameraInfosList)); + assertEquals(1, filteredCameraInfosList.size()); + assertEquals(cameraInfoToSelect, filteredCameraInfosList.get(0)); + } + @Test public void filter_callsFilterWithMethodParameters() { final PigeonApiCameraSelector api = new TestProxyApiRegistrar().getPigeonApiCameraSelector(); diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java index 5c3f3578a24..45b6be96375 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java @@ -103,7 +103,7 @@ TextureRegistry getTextureRegistry() { ArgumentCaptor.forClass(TextureRegistry.SurfaceProducer.Callback.class); when(mockSurfaceRequest.getResolution()).thenReturn(new Size(5, 6)); - when(mockSurfaceProducer.getSurface()).thenReturn(mock(Surface.class)); + when(mockSurfaceProducer.getForcedNewSurface()).thenReturn(mock(Surface.class)); final Preview.SurfaceProvider previewSurfaceProvider = api.createSurfaceProvider(mockSurfaceProducer, mockSystemServicesManager); @@ -155,7 +155,7 @@ TextureRegistry getTextureRegistry() { when(mockSurfaceRequest.getResolution()) .thenReturn(new Size(resolutionWidth, resolutionHeight)); - when(mockSurfaceProducer.getSurface()).thenReturn(mockSurface); + when(mockSurfaceProducer.getForcedNewSurface()).thenReturn(mockSurface); final ArgumentCaptor surfaceCaptor = ArgumentCaptor.forClass(Surface.class); final ArgumentCaptor> consumerCaptor = diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart index e52435b3b10..2055e87ae49 100644 --- a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart +++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart @@ -266,6 +266,9 @@ class AndroidCameraCameraX extends CameraPlatform { @visibleForTesting late bool enableRecordingAudio; + /// A map to associate a [CameraInfo] with its camera name. + final Map _savedCameras = {}; + /// Returns list of all available cameras and their descriptions. @override Future> availableCameras() async { @@ -286,12 +289,14 @@ class AndroidCameraCameraX extends CameraPlatform { // Determine the lens direction by filtering the CameraInfo // TODO(gmackall): replace this with call to CameraInfo.getLensFacing when changes containing that method are available if ((await proxy - .newCameraSelector(requireLensFacing: LensFacing.back) - .filter([cameraInfo])).isNotEmpty) { + .newCameraSelector(requireLensFacing: LensFacing.back) + .filter([cameraInfo])) + .isNotEmpty) { cameraLensDirection = CameraLensDirection.back; } else if ((await proxy - .newCameraSelector(requireLensFacing: LensFacing.front) - .filter([cameraInfo])).isNotEmpty) { + .newCameraSelector(requireLensFacing: LensFacing.front) + .filter([cameraInfo])) + .isNotEmpty) { cameraLensDirection = CameraLensDirection.front; } else { //Skip this CameraInfo as its lens direction is unknown @@ -302,6 +307,8 @@ class AndroidCameraCameraX extends CameraPlatform { cameraName = 'Camera $cameraCount'; cameraCount++; + _savedCameras[cameraName] = cameraInfo; + // TODO(camsim99): Use camera ID retrieved from Camera2CameraInfo as // camera name: https://github.com/flutter/flutter/issues/147545. cameraDescriptions.add( @@ -357,13 +364,15 @@ class AndroidCameraCameraX extends CameraPlatform { if (error != null) { throw CameraException(error.errorCode, error.description); } + // Choose CameraInfo to create CameraSelector by name associated with desired camera. + final CameraInfo? chosenCameraInfo = _savedCameras[cameraDescription.name]; // Save CameraSelector that matches cameraDescription. final LensFacing cameraSelectorLensDirection = _getCameraSelectorLensDirection(cameraDescription.lensDirection); cameraIsFrontFacing = cameraSelectorLensDirection == LensFacing.front; cameraSelector = proxy.newCameraSelector( - requireLensFacing: cameraSelectorLensDirection, + cameraInfoForFilter: chosenCameraInfo, ); // Start listening for device orientation changes preceding camera creation. unawaited( @@ -432,13 +441,13 @@ class AndroidCameraCameraX extends CameraPlatform { .toDouble(); sensorOrientationDegrees = cameraDescription.sensorOrientation.toDouble(); - _handlesCropAndRotation = - await preview!.surfaceProducerHandlesCropAndRotation(); + _handlesCropAndRotation = await preview! + .surfaceProducerHandlesCropAndRotation(); _initialDeviceOrientation = _deserializeDeviceOrientation( await deviceOrientationManager.getUiOrientation(), ); - _initialDefaultDisplayRotation = - await deviceOrientationManager.getDefaultDisplayRotation(); + _initialDefaultDisplayRotation = await deviceOrientationManager + .getDefaultDisplayRotation(); return flutterSurfaceTextureId; } @@ -469,8 +478,8 @@ class AndroidCameraCameraX extends CameraPlatform { ); } - final ResolutionInfo previewResolutionInfo = - (await preview!.getResolutionInfo())!; + final ResolutionInfo previewResolutionInfo = (await preview! + .getResolutionInfo())!; // Mark auto-focus, auto-exposure and setting points for focus & exposure // as available operations as CameraX does its best across devices to @@ -640,10 +649,9 @@ class AndroidCameraCameraX extends CameraPlatform { case FocusMode.auto: // Determine auto-focus point to restore, if any. We do not restore // default auto-focus point if set previously to lock focus. - final MeteringPoint? unLockedFocusPoint = - _defaultFocusPointLocked - ? null - : currentFocusMeteringAction!.meteringPointsAf.first; + final MeteringPoint? unLockedFocusPoint = _defaultFocusPointLocked + ? null + : currentFocusMeteringAction!.meteringPointsAf.first; _defaultFocusPointLocked = false; autoFocusPoint = unLockedFocusPoint; disableAutoCancel = false; @@ -654,10 +662,9 @@ class AndroidCameraCameraX extends CameraPlatform { if (currentFocusMeteringAction != null) { final List possibleCurrentAfPoints = currentFocusMeteringAction!.meteringPointsAf; - lockedFocusPoint = - possibleCurrentAfPoints.isEmpty - ? null - : possibleCurrentAfPoints.first; + lockedFocusPoint = possibleCurrentAfPoints.isEmpty + ? null + : possibleCurrentAfPoints.first; } // If there isn't, lock center of entire sensor area by default. @@ -1487,13 +1494,12 @@ class AndroidCameraCameraX extends CameraPlatform { ); final ResolutionFilter resolutionFilter = proxy .createWithOnePreferredSizeResolutionFilter(preferredSize: boundSize); - final AspectRatioStrategy? aspectRatioStrategy = - aspectRatio == null - ? null - : proxy.newAspectRatioStrategy( - preferredAspectRatio: aspectRatio, - fallbackRule: AspectRatioStrategyFallbackRule.auto, - ); + final AspectRatioStrategy? aspectRatioStrategy = aspectRatio == null + ? null + : proxy.newAspectRatioStrategy( + preferredAspectRatio: aspectRatio, + fallbackRule: AspectRatioStrategyFallbackRule.auto, + ); return proxy.newResolutionSelector( resolutionStrategy: resolutionStrategy, resolutionFilter: resolutionFilter, @@ -1610,17 +1616,17 @@ class AndroidCameraCameraX extends CameraPlatform { // Remove metering point with specified meteringMode from current focus // and metering action, as only one focus or exposure point may be set // at once in this plugin. - final List<(MeteringPoint, MeteringMode)> newMeteringPointInfos = - originalMeteringPoints - .where( - ((MeteringPoint, MeteringMode) meteringPointInfo) => - // meteringPointInfo may technically include points without a - // mode specified, but this logic is safe because this plugin - // only uses points that explicitly have mode - // FocusMeteringAction.flagAe or FocusMeteringAction.flagAf. - meteringPointInfo.$2 != meteringMode, - ) - .toList(); + final List<(MeteringPoint, MeteringMode)> + newMeteringPointInfos = originalMeteringPoints + .where( + ((MeteringPoint, MeteringMode) meteringPointInfo) => + // meteringPointInfo may technically include points without a + // mode specified, but this logic is safe because this plugin + // only uses points that explicitly have mode + // FocusMeteringAction.flagAe or FocusMeteringAction.flagAf. + meteringPointInfo.$2 != meteringMode, + ) + .toList(); if (newMeteringPointInfos.isEmpty) { // If no other metering points were specified, cancel any previously @@ -1657,17 +1663,16 @@ class AndroidCameraCameraX extends CameraPlatform { final Iterable<(MeteringPoint, MeteringMode)> originalMeteringPoints = _combineMeteringPoints(currentFocusMeteringAction!); - newMeteringPointInfos = - originalMeteringPoints - .where( - ((MeteringPoint, MeteringMode) meteringPointInfo) => - // meteringPointInfo may technically include points without a - // mode specified, but this logic is safe because this plugin - // only uses points that explicitly have mode - // FocusMeteringAction.flagAe or FocusMeteringAction.flagAf. - meteringPointInfo.$2 != meteringMode, - ) - .toList(); + newMeteringPointInfos = originalMeteringPoints + .where( + ((MeteringPoint, MeteringMode) meteringPointInfo) => + // meteringPointInfo may technically include points without a + // mode specified, but this logic is safe because this plugin + // only uses points that explicitly have mode + // FocusMeteringAction.flagAe or FocusMeteringAction.flagAf. + meteringPointInfo.$2 != meteringMode, + ) + .toList(); } newMeteringPointInfos.add((meteringPoint, meteringMode)); diff --git a/packages/camera/camera_android_camerax/lib/src/camerax_library.dart b/packages/camera/camera_android_camerax/lib/src/camerax_library.dart index 2c0d0a43747..e692d408fa2 100644 --- a/packages/camera/camera_android_camerax/lib/src/camerax_library.dart +++ b/packages/camera/camera_android_camerax/lib/src/camerax_library.dart @@ -37,17 +37,15 @@ void setUpGenerics({ ); camerax.CameraInfo.pigeon_setUpMessageHandlers( - pigeon_newInstance: ( - int sensorRotationDegrees, - camerax.ExposureState exposureState, - ) { - return CameraInfo.detached( - sensorRotationDegrees: sensorRotationDegrees, - exposureState: exposureState, - pigeon_binaryMessenger: pigeonBinaryMessenger, - pigeon_instanceManager: pigeonInstanceManager, - ); - }, + pigeon_newInstance: + (int sensorRotationDegrees, camerax.ExposureState exposureState) { + return CameraInfo.detached( + sensorRotationDegrees: sensorRotationDegrees, + exposureState: exposureState, + pigeon_binaryMessenger: pigeonBinaryMessenger, + pigeon_instanceManager: pigeonInstanceManager, + ); + }, ); } diff --git a/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart b/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart index 5d266c09ce7..b2f730b89c1 100644 --- a/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart +++ b/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart @@ -384,8 +384,8 @@ class PigeonInstanceManager { final PigeonInternalProxyApiBaseClass? strongInstance = _strongInstances[identifier]; if (strongInstance != null) { - final PigeonInternalProxyApiBaseClass copy = - strongInstance.pigeon_copy(); + final PigeonInternalProxyApiBaseClass copy = strongInstance + .pigeon_copy(); _identifiers[copy] = identifier; _weakInstances[identifier] = WeakReference(copy); @@ -1964,6 +1964,7 @@ class CameraSelector extends PigeonInternalProxyApiBaseClass { super.pigeon_binaryMessenger, super.pigeon_instanceManager, LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, }) { final int pigeonVar_instanceIdentifier = pigeon_instanceManager .addDartCreatedInstance(this); @@ -1979,7 +1980,11 @@ class CameraSelector extends PigeonInternalProxyApiBaseClass { binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send( - [pigeonVar_instanceIdentifier, requireLensFacing], + [ + pigeonVar_instanceIdentifier, + requireLensFacing, + cameraInfoForFilter, + ], ); () async { final List? pigeonVar_replyList = diff --git a/packages/camera/camera_android_camerax/lib/src/camerax_proxy.dart b/packages/camera/camera_android_camerax/lib/src/camerax_proxy.dart index 123a2f6804f..6ad4e335ecc 100644 --- a/packages/camera/camera_android_camerax/lib/src/camerax_proxy.dart +++ b/packages/camera/camera_android_camerax/lib/src/camerax_proxy.dart @@ -108,6 +108,7 @@ class CameraXProxy { /// Constructs [CameraSelector]. final CameraSelector Function({ LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, BinaryMessenger? pigeon_binaryMessenger, PigeonInstanceManager? pigeon_instanceManager, }) diff --git a/packages/camera/camera_android_camerax/lib/src/image_reader_rotated_preview.dart b/packages/camera/camera_android_camerax/lib/src/image_reader_rotated_preview.dart index 2cb8f44bf1a..49cf6c48509 100644 --- a/packages/camera/camera_android_camerax/lib/src/image_reader_rotated_preview.dart +++ b/packages/camera/camera_android_camerax/lib/src/image_reader_rotated_preview.dart @@ -78,8 +78,9 @@ final class _ImageReaderRotatedPreviewState late StreamSubscription deviceOrientationSubscription; Future _getCurrentDefaultDisplayRotationDegrees() async { - final int currentDefaultDisplayRotationQuarterTurns = - await widget.deviceOrientationManager.getDefaultDisplayRotation(); + final int currentDefaultDisplayRotationQuarterTurns = await widget + .deviceOrientationManager + .getDefaultDisplayRotation(); return getQuarterTurnsFromSurfaceRotationConstant( currentDefaultDisplayRotationQuarterTurns, ) * diff --git a/packages/camera/camera_android_camerax/lib/src/rotated_preview_utils.dart b/packages/camera/camera_android_camerax/lib/src/rotated_preview_utils.dart index cfebe6976d5..ae3152d7ff4 100644 --- a/packages/camera/camera_android_camerax/lib/src/rotated_preview_utils.dart +++ b/packages/camera/camera_android_camerax/lib/src/rotated_preview_utils.dart @@ -15,10 +15,9 @@ int getQuarterTurnsFromSurfaceRotationConstant(int surfaceRotationConstant) { Surface.rotation90 => 3, Surface.rotation180 => 2, Surface.rotation270 => 1, - int() => - throw ArgumentError( - '$surfaceRotationConstant is an unknown Surface rotation constant, so counter-clockwise quarter turns cannot be determined.', - ), + int() => throw ArgumentError( + '$surfaceRotationConstant is an unknown Surface rotation constant, so counter-clockwise quarter turns cannot be determined.', + ), }; } diff --git a/packages/camera/camera_android_camerax/lib/src/surface_texture_rotated_preview.dart b/packages/camera/camera_android_camerax/lib/src/surface_texture_rotated_preview.dart index bdfa3f7d2bd..a69e0359812 100644 --- a/packages/camera/camera_android_camerax/lib/src/surface_texture_rotated_preview.dart +++ b/packages/camera/camera_android_camerax/lib/src/surface_texture_rotated_preview.dart @@ -57,8 +57,9 @@ final class _SurfaceTextureRotatedPreviewState late Future defaultDisplayRotationQuarterTurns; Future _getCurrentDefaultDisplayRotationQuarterTurns() async { - final int currentDefaultDisplayRotationQuarterTurns = - await widget.deviceOrientationManager.getDefaultDisplayRotation(); + final int currentDefaultDisplayRotationQuarterTurns = await widget + .deviceOrientationManager + .getDefaultDisplayRotation(); return getQuarterTurnsFromSurfaceRotationConstant( currentDefaultDisplayRotationQuarterTurns, ); diff --git a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart index 80253e11dd9..102fcd2d288 100644 --- a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart +++ b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart @@ -290,7 +290,10 @@ enum LensFacing { ), ) abstract class CameraSelector { - CameraSelector(LensFacing? requireLensFacing); + CameraSelector( + LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, + ); /// A static `CameraSelector` that selects the default back facing camera. @static diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index 09d8688b323..fea407b822d 100644 --- a/packages/camera/camera_android_camerax/pubspec.yaml +++ b/packages/camera/camera_android_camerax/pubspec.yaml @@ -2,11 +2,11 @@ name: camera_android_camerax description: Android implementation of the camera plugin using the CameraX library. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android_camerax issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.6.19 +version: 0.6.20 environment: - sdk: ^3.7.0 - flutter: ">=3.29.0" + sdk: ^3.8.1 + flutter: ">=3.32.8" flutter: plugin: diff --git a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart index 6b2535cf437..e9b1bb7598c 100644 --- a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart +++ b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart @@ -169,6 +169,7 @@ void main() { }, newCameraSelector: ({ LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, // ignore: non_constant_identifier_names BinaryMessenger? pigeon_binaryMessenger, // ignore: non_constant_identifier_names @@ -675,6 +676,7 @@ void main() { Future.value(mockProcessCameraProvider), newCameraSelector: ({ LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, // ignore: non_constant_identifier_names BinaryMessenger? pigeon_binaryMessenger, // ignore: non_constant_identifier_names @@ -787,6 +789,7 @@ void main() { }, newCameraSelector: ({ LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, // ignore: non_constant_identifier_names BinaryMessenger? pigeon_binaryMessenger, // ignore: non_constant_identifier_names @@ -1101,6 +1104,7 @@ void main() { }, newCameraSelector: ({ LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, // ignore: non_constant_identifier_names BinaryMessenger? pigeon_binaryMessenger, // ignore: non_constant_identifier_names @@ -1929,6 +1933,371 @@ void main() { }, ); + test( + 'createCamera properly selects specific back camera by specifying a CameraInfo', + () async { + // Arrange + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + final List returnData = [ + { + 'name': 'Camera 0', + 'lensFacing': 'back', + 'sensorOrientation': 0, + }, + { + 'name': 'Camera 1', + 'lensFacing': 'back', + 'sensorOrientation': 0, + }, + { + 'name': 'Camera 2', + 'lensFacing': 'front', + 'sensorOrientation': 0, + }, + ]; + + List mockCameraInfosList = []; + final Map cameraNameToInfos = + {}; + + const int testSensorOrientation = 0; + + // Mocks for objects created by availableCameras. + final MockProcessCameraProvider mockProcessCameraProvider = + MockProcessCameraProvider(); + final MockCameraSelector mockFrontCameraSelector = MockCameraSelector(); + final MockCameraSelector mockBackCameraSelector = MockCameraSelector(); + final MockCameraSelector mockChosenCameraInfoCameraSelector = + MockCameraSelector(); + + final MockCameraInfo mockFrontCameraInfo = MockCameraInfo(); + final MockCameraInfo mockBackCameraInfoOne = MockCameraInfo(); + final MockCameraInfo mockBackCameraInfoTwo = MockCameraInfo(); + + // Mock/Detached objects for (typically attached) objects created by + // createCamera. + final MockPreview mockPreview = MockPreview(); + final MockImageCapture mockImageCapture = MockImageCapture(); + final MockImageAnalysis mockImageAnalysis = MockImageAnalysis(); + final MockRecorder mockRecorder = MockRecorder(); + final MockVideoCapture mockVideoCapture = MockVideoCapture(); + final MockCamera mockCamera = MockCamera(); + final MockCameraInfo mockCameraInfo = MockCameraInfo(); + final MockCameraControl mockCameraControl = MockCameraControl(); + final MockCamera2CameraInfo mockCamera2CameraInfo = + MockCamera2CameraInfo(); + final MockCameraCharacteristicsKey mockCameraCharacteristicsKey = + MockCameraCharacteristicsKey(); + + // Tell plugin to create mock/detached objects and stub method calls for the + // testing of availableCameras and createCamera. + camera.proxy = CameraXProxy( + setUpGenericsProxy: + ({ + BinaryMessenger? pigeonBinaryMessenger, + PigeonInstanceManager? pigeonInstanceManager, + }) {}, + getInstanceProcessCameraProvider: ({ + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + return Future.value(mockProcessCameraProvider); + }, + newCameraSelector: ({ + LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + switch (requireLensFacing) { + case LensFacing.front: + return mockFrontCameraSelector; + case LensFacing.back: + case LensFacing.external: + case LensFacing.unknown: + case null: + } + if (cameraInfoForFilter == mockBackCameraInfoOne) { + return mockChosenCameraInfoCameraSelector; + } + + return mockBackCameraSelector; + }, + newSystemServicesManager: ({ + required void Function(SystemServicesManager, String) onCameraError, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + return MockSystemServicesManager(); + }, + newPreview: ({ + int? targetRotation, + ResolutionSelector? resolutionSelector, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + return mockPreview; + }, + newImageCapture: ({ + int? targetRotation, + CameraXFlashMode? flashMode, + ResolutionSelector? resolutionSelector, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + return mockImageCapture; + }, + newRecorder: ({ + int? aspectRatio, + int? targetVideoEncodingBitRate, + QualitySelector? qualitySelector, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + return mockRecorder; + }, + withOutputVideoCapture: ({ + required VideoOutput videoOutput, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + return mockVideoCapture; + }, + newImageAnalysis: ({ + int? targetRotation, + ResolutionSelector? resolutionSelector, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + return mockImageAnalysis; + }, + newResolutionStrategy: ({ + required CameraSize boundSize, + required ResolutionStrategyFallbackRule fallbackRule, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + return MockResolutionStrategy(); + }, + newResolutionSelector: ({ + AspectRatioStrategy? aspectRatioStrategy, + ResolutionStrategy? resolutionStrategy, + ResolutionFilter? resolutionFilter, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + return MockResolutionSelector(); + }, + fromQualitySelector: ({ + required VideoQuality quality, + FallbackStrategy? fallbackStrategy, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + return MockQualitySelector(); + }, + newObserver: ({ + required void Function(Observer, T) onChanged, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + return Observer.detached( + onChanged: onChanged, + pigeon_instanceManager: PigeonInstanceManager( + onWeakReferenceRemoved: (_) {}, + ), + ); + }, + newDeviceOrientationManager: ({ + required void Function(DeviceOrientationManager, String) + onDeviceOrientationChanged, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + final MockDeviceOrientationManager manager = + MockDeviceOrientationManager(); + when(manager.getUiOrientation()).thenAnswer((_) async { + return 'PORTRAIT_UP'; + }); + return manager; + }, + newAspectRatioStrategy: ({ + required AspectRatio preferredAspectRatio, + required AspectRatioStrategyFallbackRule fallbackRule, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + return MockAspectRatioStrategy(); + }, + createWithOnePreferredSizeResolutionFilter: ({ + required CameraSize preferredSize, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + return MockResolutionFilter(); + }, + fromCamera2CameraInfo: ({ + required CameraInfo cameraInfo, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + when( + mockCamera2CameraInfo.getCameraCharacteristic( + mockCameraCharacteristicsKey, + ), + ).thenAnswer((_) async => testSensorOrientation); + return mockCamera2CameraInfo; + }, + newCameraSize: ({ + required int width, + required int height, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + return MockCameraSize(); + }, + sensorOrientationCameraCharacteristics: () { + return mockCameraCharacteristicsKey; + }, + lowerQualityOrHigherThanFallbackStrategy: ({ + required VideoQuality quality, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + return MockFallbackStrategy(); + }, + ); + + // Mock calls to native platform + when(mockProcessCameraProvider.getAvailableCameraInfos()).thenAnswer(( + _, + ) async { + mockCameraInfosList = [ + mockBackCameraInfoOne, + mockBackCameraInfoTwo, + mockFrontCameraInfo, + ]; + return [ + mockBackCameraInfoOne, + mockBackCameraInfoTwo, + mockFrontCameraInfo, + ]; + }); + when( + mockBackCameraSelector.filter([mockBackCameraInfoOne]), + ).thenAnswer((_) async => [mockBackCameraInfoOne]); + when( + mockBackCameraSelector.filter([mockBackCameraInfoTwo]), + ).thenAnswer((_) async => [mockBackCameraInfoTwo]); + when( + mockBackCameraSelector.filter([mockFrontCameraInfo]), + ).thenAnswer((_) async => []); + when( + mockFrontCameraSelector.filter([mockBackCameraInfoOne]), + ).thenAnswer((_) async => []); + when( + mockFrontCameraSelector.filter([mockBackCameraInfoTwo]), + ).thenAnswer((_) async => []); + when( + mockFrontCameraSelector.filter([mockFrontCameraInfo]), + ).thenAnswer((_) async => [mockFrontCameraInfo]); + + final List cameraDescriptions = + await camera.availableCameras(); + expect(cameraDescriptions.length, returnData.length); + + for (int i = 0; i < returnData.length; i++) { + final Map savedData = + (returnData[i] as Map).cast(); + + cameraNameToInfos[savedData['name']! as String] = + mockCameraInfosList[i]; + final CameraDescription cameraDescription = CameraDescription( + name: savedData['name']! as String, + lensDirection: + (savedData['lensFacing']! as String) == 'front' + ? CameraLensDirection.front + : CameraLensDirection.back, + sensorOrientation: savedData['sensorOrientation']! as int, + ); + expect(cameraDescriptions[i], cameraDescription); + expect(cameraNameToInfos.containsKey(cameraDescription.name), isTrue); + } + + when( + mockProcessCameraProvider.bindToLifecycle( + mockChosenCameraInfoCameraSelector, + [mockPreview, mockImageCapture, mockImageAnalysis], + ), + ).thenAnswer((_) async => mockCamera); + when(mockCamera.getCameraInfo()).thenAnswer((_) async => mockCameraInfo); + when( + mockCameraInfo.getCameraState(), + ).thenAnswer((_) async => MockLiveCameraState()); + when(mockCamera.cameraControl).thenAnswer((_) => mockCameraControl); + + camera.processCameraProvider = mockProcessCameraProvider; + + // Verify the camera name used to create camera is associated with mockBackCameraInfoOne. + expect( + cameraNameToInfos[cameraDescriptions[0].name], + mockBackCameraInfoOne, + ); + + // Creating a camera with settings using a specific camera from + // available cameras. + await camera.createCameraWithSettings( + cameraDescriptions[0], + const MediaSettings( + resolutionPreset: ResolutionPreset.low, + fps: 15, + videoBitrate: 200000, + audioBitrate: 32000, + enableAudio: true, + ), + ); + + // Verify CameraSelector is chosen based on specified cameraInfo. + expect(camera.cameraSelector, equals(mockChosenCameraInfoCameraSelector)); + }, + ); + test( 'initializeCamera throws a CameraException when createCamera has not been called before initializedCamera', () async { @@ -1987,6 +2356,7 @@ void main() { }) => Future.value(mockProcessCameraProvider), newCameraSelector: ({ LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, // ignore: non_constant_identifier_names BinaryMessenger? pigeon_binaryMessenger, // ignore: non_constant_identifier_names diff --git a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.mocks.dart b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.mocks.dart index 8a709e6beff..4a2df856f15 100644 --- a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.mocks.dart +++ b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.mocks.dart @@ -1052,6 +1052,7 @@ class MockCameraXProxy extends _i1.Mock implements _i7.CameraXProxy { @override _i2.CameraSelector Function({ + _i3.CameraInfo? cameraInfoForFilter, _i8.BinaryMessenger? pigeon_binaryMessenger, _i2.PigeonInstanceManager? pigeon_instanceManager, _i2.LensFacing? requireLensFacing, @@ -1061,6 +1062,7 @@ class MockCameraXProxy extends _i1.Mock implements _i7.CameraXProxy { Invocation.getter(#newCameraSelector), returnValue: ({ + _i3.CameraInfo? cameraInfoForFilter, _i8.BinaryMessenger? pigeon_binaryMessenger, _i2.PigeonInstanceManager? pigeon_instanceManager, _i2.LensFacing? requireLensFacing, @@ -1070,6 +1072,7 @@ class MockCameraXProxy extends _i1.Mock implements _i7.CameraXProxy { ), returnValueForMissingStub: ({ + _i3.CameraInfo? cameraInfoForFilter, _i8.BinaryMessenger? pigeon_binaryMessenger, _i2.PigeonInstanceManager? pigeon_instanceManager, _i2.LensFacing? requireLensFacing, @@ -1079,6 +1082,7 @@ class MockCameraXProxy extends _i1.Mock implements _i7.CameraXProxy { ), ) as _i2.CameraSelector Function({ + _i3.CameraInfo? cameraInfoForFilter, _i8.BinaryMessenger? pigeon_binaryMessenger, _i2.PigeonInstanceManager? pigeon_instanceManager, _i2.LensFacing? requireLensFacing, @@ -2070,16 +2074,14 @@ class MockCameraXProxy extends _i1.Mock implements _i7.CameraXProxy { _i2.CameraSelector Function() get defaultBackCameraCameraSelector => (super.noSuchMethod( Invocation.getter(#defaultBackCameraCameraSelector), - returnValue: - () => _FakeCameraSelector_14( - this, - Invocation.getter(#defaultBackCameraCameraSelector), - ), - returnValueForMissingStub: - () => _FakeCameraSelector_14( - this, - Invocation.getter(#defaultBackCameraCameraSelector), - ), + returnValue: () => _FakeCameraSelector_14( + this, + Invocation.getter(#defaultBackCameraCameraSelector), + ), + returnValueForMissingStub: () => _FakeCameraSelector_14( + this, + Invocation.getter(#defaultBackCameraCameraSelector), + ), ) as _i2.CameraSelector Function()); @@ -2087,16 +2089,14 @@ class MockCameraXProxy extends _i1.Mock implements _i7.CameraXProxy { _i2.CameraSelector Function() get defaultFrontCameraCameraSelector => (super.noSuchMethod( Invocation.getter(#defaultFrontCameraCameraSelector), - returnValue: - () => _FakeCameraSelector_14( - this, - Invocation.getter(#defaultFrontCameraCameraSelector), - ), - returnValueForMissingStub: - () => _FakeCameraSelector_14( - this, - Invocation.getter(#defaultFrontCameraCameraSelector), - ), + returnValue: () => _FakeCameraSelector_14( + this, + Invocation.getter(#defaultFrontCameraCameraSelector), + ), + returnValueForMissingStub: () => _FakeCameraSelector_14( + this, + Invocation.getter(#defaultFrontCameraCameraSelector), + ), ) as _i2.CameraSelector Function()); @@ -2105,20 +2105,14 @@ class MockCameraXProxy extends _i1.Mock implements _i7.CameraXProxy { get highestAvailableStrategyResolutionStrategy => (super.noSuchMethod( Invocation.getter(#highestAvailableStrategyResolutionStrategy), - returnValue: - () => _FakeResolutionStrategy_24( - this, - Invocation.getter( - #highestAvailableStrategyResolutionStrategy, - ), - ), - returnValueForMissingStub: - () => _FakeResolutionStrategy_24( - this, - Invocation.getter( - #highestAvailableStrategyResolutionStrategy, - ), - ), + returnValue: () => _FakeResolutionStrategy_24( + this, + Invocation.getter(#highestAvailableStrategyResolutionStrategy), + ), + returnValueForMissingStub: () => _FakeResolutionStrategy_24( + this, + Invocation.getter(#highestAvailableStrategyResolutionStrategy), + ), ) as _i2.ResolutionStrategy Function()); @@ -2129,20 +2123,18 @@ class MockCameraXProxy extends _i1.Mock implements _i7.CameraXProxy { Invocation.getter( #ratio_16_9FallbackAutoStrategyAspectRatioStrategy, ), - returnValue: - () => _FakeAspectRatioStrategy_2( - this, - Invocation.getter( - #ratio_16_9FallbackAutoStrategyAspectRatioStrategy, - ), - ), - returnValueForMissingStub: - () => _FakeAspectRatioStrategy_2( - this, - Invocation.getter( - #ratio_16_9FallbackAutoStrategyAspectRatioStrategy, - ), - ), + returnValue: () => _FakeAspectRatioStrategy_2( + this, + Invocation.getter( + #ratio_16_9FallbackAutoStrategyAspectRatioStrategy, + ), + ), + returnValueForMissingStub: () => _FakeAspectRatioStrategy_2( + this, + Invocation.getter( + #ratio_16_9FallbackAutoStrategyAspectRatioStrategy, + ), + ), ) as _i2.AspectRatioStrategy Function()); @@ -2153,20 +2145,18 @@ class MockCameraXProxy extends _i1.Mock implements _i7.CameraXProxy { Invocation.getter( #ratio_4_3FallbackAutoStrategyAspectRatioStrategy, ), - returnValue: - () => _FakeAspectRatioStrategy_2( - this, - Invocation.getter( - #ratio_4_3FallbackAutoStrategyAspectRatioStrategy, - ), - ), - returnValueForMissingStub: - () => _FakeAspectRatioStrategy_2( - this, - Invocation.getter( - #ratio_4_3FallbackAutoStrategyAspectRatioStrategy, - ), - ), + returnValue: () => _FakeAspectRatioStrategy_2( + this, + Invocation.getter( + #ratio_4_3FallbackAutoStrategyAspectRatioStrategy, + ), + ), + returnValueForMissingStub: () => _FakeAspectRatioStrategy_2( + this, + Invocation.getter( + #ratio_4_3FallbackAutoStrategyAspectRatioStrategy, + ), + ), ) as _i2.AspectRatioStrategy Function()); @@ -2174,16 +2164,14 @@ class MockCameraXProxy extends _i1.Mock implements _i7.CameraXProxy { _i2.CaptureRequestKey Function() get controlAELockCaptureRequest => (super.noSuchMethod( Invocation.getter(#controlAELockCaptureRequest), - returnValue: - () => _FakeCaptureRequestKey_34( - this, - Invocation.getter(#controlAELockCaptureRequest), - ), - returnValueForMissingStub: - () => _FakeCaptureRequestKey_34( - this, - Invocation.getter(#controlAELockCaptureRequest), - ), + returnValue: () => _FakeCaptureRequestKey_34( + this, + Invocation.getter(#controlAELockCaptureRequest), + ), + returnValueForMissingStub: () => _FakeCaptureRequestKey_34( + this, + Invocation.getter(#controlAELockCaptureRequest), + ), ) as _i2.CaptureRequestKey Function()); @@ -2192,20 +2180,18 @@ class MockCameraXProxy extends _i1.Mock implements _i7.CameraXProxy { get infoSupportedHardwareLevelCameraCharacteristics => (super.noSuchMethod( Invocation.getter(#infoSupportedHardwareLevelCameraCharacteristics), - returnValue: - () => _FakeCameraCharacteristicsKey_9( - this, - Invocation.getter( - #infoSupportedHardwareLevelCameraCharacteristics, - ), - ), - returnValueForMissingStub: - () => _FakeCameraCharacteristicsKey_9( - this, - Invocation.getter( - #infoSupportedHardwareLevelCameraCharacteristics, - ), - ), + returnValue: () => _FakeCameraCharacteristicsKey_9( + this, + Invocation.getter( + #infoSupportedHardwareLevelCameraCharacteristics, + ), + ), + returnValueForMissingStub: () => _FakeCameraCharacteristicsKey_9( + this, + Invocation.getter( + #infoSupportedHardwareLevelCameraCharacteristics, + ), + ), ) as _i2.CameraCharacteristicsKey Function()); @@ -2214,16 +2200,14 @@ class MockCameraXProxy extends _i1.Mock implements _i7.CameraXProxy { get sensorOrientationCameraCharacteristics => (super.noSuchMethod( Invocation.getter(#sensorOrientationCameraCharacteristics), - returnValue: - () => _FakeCameraCharacteristicsKey_9( - this, - Invocation.getter(#sensorOrientationCameraCharacteristics), - ), - returnValueForMissingStub: - () => _FakeCameraCharacteristicsKey_9( - this, - Invocation.getter(#sensorOrientationCameraCharacteristics), - ), + returnValue: () => _FakeCameraCharacteristicsKey_9( + this, + Invocation.getter(#sensorOrientationCameraCharacteristics), + ), + returnValueForMissingStub: () => _FakeCameraCharacteristicsKey_9( + this, + Invocation.getter(#sensorOrientationCameraCharacteristics), + ), ) as _i2.CameraCharacteristicsKey Function()); diff --git a/packages/camera/camera_android_camerax/test/preview_rotation_test.dart b/packages/camera/camera_android_camerax/test/preview_rotation_test.dart index e6921b29077..6a9eff3d11d 100644 --- a/packages/camera/camera_android_camerax/test/preview_rotation_test.dart +++ b/packages/camera/camera_android_camerax/test/preview_rotation_test.dart @@ -90,6 +90,7 @@ void main() { required MockProcessCameraProvider mockProcessCameraProvider, required CameraSelector Function({ LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, // ignore: non_constant_identifier_names BinaryMessenger? pigeon_binaryMessenger, // ignore: non_constant_identifier_names @@ -106,20 +107,21 @@ void main() { PigeonInstanceManager? pigeon_instanceManager, }) async => mockProcessCameraProvider, newCameraSelector: createCameraSelector, - newPreview: ({ - int? targetRotation, - ResolutionSelector? resolutionSelector, - // ignore: non_constant_identifier_names - BinaryMessenger? pigeon_binaryMessenger, - // ignore: non_constant_identifier_names - PigeonInstanceManager? pigeon_instanceManager, - }) { - final MockPreview preview = MockPreview(); - when( - preview.surfaceProducerHandlesCropAndRotation(), - ).thenAnswer((_) async => handlesCropAndRotation); - return preview; - }, + newPreview: + ({ + int? targetRotation, + ResolutionSelector? resolutionSelector, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + final MockPreview preview = MockPreview(); + when( + preview.surfaceProducerHandlesCropAndRotation(), + ).thenAnswer((_) async => handlesCropAndRotation); + return preview; + }, newImageCapture: ({ int? targetRotation, @@ -140,110 +142,121 @@ void main() { // ignore: non_constant_identifier_names PigeonInstanceManager? pigeon_instanceManager, }) => MockRecorder(), - withOutputVideoCapture: ({ - required VideoOutput videoOutput, - // ignore: non_constant_identifier_names - BinaryMessenger? pigeon_binaryMessenger, - // ignore: non_constant_identifier_names - PigeonInstanceManager? pigeon_instanceManager, - }) { - return MockVideoCapture(); - }, - newImageAnalysis: ({ - int? targetRotation, - ResolutionSelector? resolutionSelector, - // ignore: non_constant_identifier_names - BinaryMessenger? pigeon_binaryMessenger, - // ignore: non_constant_identifier_names - PigeonInstanceManager? pigeon_instanceManager, - }) { - return MockImageAnalysis(); - }, - newResolutionStrategy: ({ - required CameraSize boundSize, - required ResolutionStrategyFallbackRule fallbackRule, - // ignore: non_constant_identifier_names - BinaryMessenger? pigeon_binaryMessenger, - // ignore: non_constant_identifier_names - PigeonInstanceManager? pigeon_instanceManager, - }) { - return MockResolutionStrategy(); - }, - newResolutionSelector: ({ - AspectRatioStrategy? aspectRatioStrategy, - ResolutionStrategy? resolutionStrategy, - ResolutionFilter? resolutionFilter, - // ignore: non_constant_identifier_names - BinaryMessenger? pigeon_binaryMessenger, - // ignore: non_constant_identifier_names - PigeonInstanceManager? pigeon_instanceManager, - }) { - return MockResolutionSelector(); - }, - lowerQualityOrHigherThanFallbackStrategy: ({ - required VideoQuality quality, - // ignore: non_constant_identifier_names - BinaryMessenger? pigeon_binaryMessenger, - // ignore: non_constant_identifier_names - PigeonInstanceManager? pigeon_instanceManager, - }) { - return MockFallbackStrategy(); - }, - lowerQualityThanFallbackStrategy: ({ - required VideoQuality quality, - // ignore: non_constant_identifier_names - BinaryMessenger? pigeon_binaryMessenger, - // ignore: non_constant_identifier_names - PigeonInstanceManager? pigeon_instanceManager, - }) { - return MockFallbackStrategy(); - }, - fromCamera2CameraInfo: ({ - required CameraInfo cameraInfo, - // ignore: non_constant_identifier_names - BinaryMessenger? pigeon_binaryMessenger, - // ignore: non_constant_identifier_names - PigeonInstanceManager? pigeon_instanceManager, - }) { - final MockCamera2CameraInfo camera2cameraInfo = MockCamera2CameraInfo(); - when( - camera2cameraInfo.getCameraCharacteristic(any), - ).thenAnswer((_) async => 90); - return camera2cameraInfo; - }, - fromQualitySelector: ({ - required VideoQuality quality, - FallbackStrategy? fallbackStrategy, - // ignore: non_constant_identifier_names - BinaryMessenger? pigeon_binaryMessenger, - // ignore: non_constant_identifier_names - PigeonInstanceManager? pigeon_instanceManager, - }) { - return MockQualitySelector(); - }, - newObserver: ({ - required void Function(Observer, T) onChanged, - // ignore: non_constant_identifier_names - BinaryMessenger? pigeon_binaryMessenger, - // ignore: non_constant_identifier_names - PigeonInstanceManager? pigeon_instanceManager, - }) { - return Observer.detached( - onChanged: onChanged, - pigeon_instanceManager: PigeonInstanceManager( - onWeakReferenceRemoved: (_) {}, - ), - ); - }, - newSystemServicesManager: ({ - required void Function(SystemServicesManager, String) onCameraError, - // ignore: non_constant_identifier_names - BinaryMessenger? pigeon_binaryMessenger, - // ignore: non_constant_identifier_names - PigeonInstanceManager? pigeon_instanceManager, - }) { - return MockSystemServicesManager(); - }, + withOutputVideoCapture: + ({ + required VideoOutput videoOutput, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + return MockVideoCapture(); + }, + newImageAnalysis: + ({ + int? targetRotation, + ResolutionSelector? resolutionSelector, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + return MockImageAnalysis(); + }, + newResolutionStrategy: + ({ + required CameraSize boundSize, + required ResolutionStrategyFallbackRule fallbackRule, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + return MockResolutionStrategy(); + }, + newResolutionSelector: + ({ + AspectRatioStrategy? aspectRatioStrategy, + ResolutionStrategy? resolutionStrategy, + ResolutionFilter? resolutionFilter, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + return MockResolutionSelector(); + }, + lowerQualityOrHigherThanFallbackStrategy: + ({ + required VideoQuality quality, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + return MockFallbackStrategy(); + }, + lowerQualityThanFallbackStrategy: + ({ + required VideoQuality quality, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + return MockFallbackStrategy(); + }, + fromCamera2CameraInfo: + ({ + required CameraInfo cameraInfo, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + final MockCamera2CameraInfo camera2cameraInfo = + MockCamera2CameraInfo(); + when( + camera2cameraInfo.getCameraCharacteristic(any), + ).thenAnswer((_) async => 90); + return camera2cameraInfo; + }, + fromQualitySelector: + ({ + required VideoQuality quality, + FallbackStrategy? fallbackStrategy, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + return MockQualitySelector(); + }, + newObserver: + ({ + required void Function(Observer, T) onChanged, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + return Observer.detached( + onChanged: onChanged, + pigeon_instanceManager: PigeonInstanceManager( + onWeakReferenceRemoved: (_) {}, + ), + ); + }, + newSystemServicesManager: + ({ + required void Function(SystemServicesManager, String) onCameraError, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + return MockSystemServicesManager(); + }, newDeviceOrientationManager: ({ required void Function(DeviceOrientationManager, String) @@ -253,33 +266,35 @@ void main() { // ignore: non_constant_identifier_names PigeonInstanceManager? pigeon_instanceManager, }) => deviceOrientationManager, - newAspectRatioStrategy: ({ - required AspectRatio preferredAspectRatio, - required AspectRatioStrategyFallbackRule fallbackRule, - // ignore: non_constant_identifier_names - BinaryMessenger? pigeon_binaryMessenger, - // ignore: non_constant_identifier_names - PigeonInstanceManager? pigeon_instanceManager, - }) { - final MockAspectRatioStrategy mockAspectRatioStrategy = - MockAspectRatioStrategy(); - when( - mockAspectRatioStrategy.getFallbackRule(), - ).thenAnswer((_) async => fallbackRule); - when( - mockAspectRatioStrategy.getPreferredAspectRatio(), - ).thenAnswer((_) async => preferredAspectRatio); - return mockAspectRatioStrategy; - }, - createWithOnePreferredSizeResolutionFilter: ({ - required CameraSize preferredSize, - // ignore: non_constant_identifier_names - BinaryMessenger? pigeon_binaryMessenger, - // ignore: non_constant_identifier_names - PigeonInstanceManager? pigeon_instanceManager, - }) { - return MockResolutionFilter(); - }, + newAspectRatioStrategy: + ({ + required AspectRatio preferredAspectRatio, + required AspectRatioStrategyFallbackRule fallbackRule, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + final MockAspectRatioStrategy mockAspectRatioStrategy = + MockAspectRatioStrategy(); + when( + mockAspectRatioStrategy.getFallbackRule(), + ).thenAnswer((_) async => fallbackRule); + when( + mockAspectRatioStrategy.getPreferredAspectRatio(), + ).thenAnswer((_) async => preferredAspectRatio); + return mockAspectRatioStrategy; + }, + createWithOnePreferredSizeResolutionFilter: + ({ + required CameraSize preferredSize, + // ignore: non_constant_identifier_names + BinaryMessenger? pigeon_binaryMessenger, + // ignore: non_constant_identifier_names + PigeonInstanceManager? pigeon_instanceManager, + }) { + return MockResolutionFilter(); + }, ); /// Returns CameraXProxy used to mock all calls to native Android in @@ -292,6 +307,7 @@ void main() { required MockProcessCameraProvider mockProcessCameraProvider, required CameraSelector Function({ LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, // ignore: non_constant_identifier_names BinaryMessenger? pigeon_binaryMessenger, // ignore: non_constant_identifier_names @@ -321,6 +337,7 @@ void main() { /// Returns function that a CameraXProxy can use to select the front camera. MockCameraSelector Function({ LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, // ignore: non_constant_identifier_names BinaryMessenger? pigeon_binaryMessenger, // ignore: non_constant_identifier_names @@ -329,6 +346,7 @@ void main() { createCameraSelectorForFrontCamera(MockCameraSelector mockCameraSelector) { return ({ LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, // ignore: non_constant_identifier_names BinaryMessenger? pigeon_binaryMessenger, // ignore: non_constant_identifier_names @@ -349,6 +367,7 @@ void main() { /// Returns function that a CameraXProxy can use to select the back camera. MockCameraSelector Function({ LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, // ignore: non_constant_identifier_names BinaryMessenger? pigeon_binaryMessenger, // ignore: non_constant_identifier_names @@ -357,6 +376,7 @@ void main() { createCameraSelectorForBackCamera(MockCameraSelector mockCameraSelector) { return ({ LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, // ignore: non_constant_identifier_names BinaryMessenger? pigeon_binaryMessenger, // ignore: non_constant_identifier_names @@ -459,6 +479,7 @@ void main() { // ignore: non_constant_identifier_names PigeonInstanceManager? pigeon_instanceManager, LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, }) fakeCreateCameraSelector; late MediaSettings testMediaSettings; @@ -493,16 +514,15 @@ void main() { mockProcessCameraProvider: mockProcessCameraProvider, createCameraSelector: fakeCreateCameraSelector, handlesCropAndRotation: true, - getUiOrientation: - () async => - _serializeDeviceOrientation(testInitialDeviceOrientation), - getDefaultDisplayRotation: - () => Future.value(Surface.rotation0), + getUiOrientation: () async => + _serializeDeviceOrientation(testInitialDeviceOrientation), + getDefaultDisplayRotation: () => + Future.value(Surface.rotation0), ); // Get and create test camera. - final List availableCameras = - await camera.availableCameras(); + final List availableCameras = await camera + .availableCameras(); expect(availableCameras.length, 1); await camera.createCameraWithSettings( availableCameras.first, @@ -547,16 +567,15 @@ void main() { mockProcessCameraProvider: mockProcessCameraProvider, createCameraSelector: fakeCreateCameraSelector, handlesCropAndRotation: true, - getUiOrientation: - () async => - _serializeDeviceOrientation(testInitialDeviceOrientation), - getDefaultDisplayRotation: - () => Future.value(Surface.rotation90), + getUiOrientation: () async => + _serializeDeviceOrientation(testInitialDeviceOrientation), + getDefaultDisplayRotation: () => + Future.value(Surface.rotation90), ); // Get and create test camera. - final List availableCameras = - await camera.availableCameras(); + final List availableCameras = await camera + .availableCameras(); expect(availableCameras.length, 1); await camera.createCameraWithSettings( availableCameras.first, @@ -599,16 +618,15 @@ void main() { mockProcessCameraProvider: mockProcessCameraProvider, createCameraSelector: fakeCreateCameraSelector, handlesCropAndRotation: true, - getUiOrientation: - () async => - _serializeDeviceOrientation(testInitialDeviceOrientation), - getDefaultDisplayRotation: - () => Future.value(Surface.rotation180), + getUiOrientation: () async => + _serializeDeviceOrientation(testInitialDeviceOrientation), + getDefaultDisplayRotation: () => + Future.value(Surface.rotation180), ); // Get and create test camera. - final List availableCameras = - await camera.availableCameras(); + final List availableCameras = await camera + .availableCameras(); expect(availableCameras.length, 1); await camera.createCameraWithSettings( availableCameras.first, @@ -651,16 +669,15 @@ void main() { mockProcessCameraProvider: mockProcessCameraProvider, createCameraSelector: fakeCreateCameraSelector, handlesCropAndRotation: true, - getUiOrientation: - () async => - _serializeDeviceOrientation(testInitialDeviceOrientation), - getDefaultDisplayRotation: - () => Future.value(Surface.rotation270), + getUiOrientation: () async => + _serializeDeviceOrientation(testInitialDeviceOrientation), + getDefaultDisplayRotation: () => + Future.value(Surface.rotation270), ); // Get and create test camera. - final List availableCameras = - await camera.availableCameras(); + final List availableCameras = await camera + .availableCameras(); expect(availableCameras.length, 1); await camera.createCameraWithSettings( availableCameras.first, @@ -709,6 +726,7 @@ void main() { // ignore: non_constant_identifier_names PigeonInstanceManager? pigeon_instanceManager, LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, }) fakeCreateCameraSelector; late MediaSettings testMediaSettings; @@ -743,16 +761,15 @@ void main() { mockProcessCameraProvider: mockProcessCameraProvider, createCameraSelector: fakeCreateCameraSelector, handlesCropAndRotation: true, - getUiOrientation: - () async => - _serializeDeviceOrientation(DeviceOrientation.portraitUp), - getDefaultDisplayRotation: - () => Future.value(testInitialDefaultDisplayRotation), + getUiOrientation: () async => + _serializeDeviceOrientation(DeviceOrientation.portraitUp), + getDefaultDisplayRotation: () => + Future.value(testInitialDefaultDisplayRotation), ); // Get and create test camera. - final List availableCameras = - await camera.availableCameras(); + final List availableCameras = await camera + .availableCameras(); expect(availableCameras.length, 1); await camera.createCameraWithSettings( availableCameras.first, @@ -795,17 +812,15 @@ void main() { mockProcessCameraProvider: mockProcessCameraProvider, createCameraSelector: fakeCreateCameraSelector, handlesCropAndRotation: true, - getUiOrientation: - () async => _serializeDeviceOrientation( - DeviceOrientation.landscapeLeft, - ), - getDefaultDisplayRotation: - () => Future.value(testInitialDefaultDisplayRotation), + getUiOrientation: () async => + _serializeDeviceOrientation(DeviceOrientation.landscapeLeft), + getDefaultDisplayRotation: () => + Future.value(testInitialDefaultDisplayRotation), ); // Get and create test camera. - final List availableCameras = - await camera.availableCameras(); + final List availableCameras = await camera + .availableCameras(); expect(availableCameras.length, 1); await camera.createCameraWithSettings( availableCameras.first, @@ -848,16 +863,15 @@ void main() { mockProcessCameraProvider: mockProcessCameraProvider, createCameraSelector: fakeCreateCameraSelector, handlesCropAndRotation: true, - getUiOrientation: - () async => - _serializeDeviceOrientation(DeviceOrientation.portraitDown), - getDefaultDisplayRotation: - () => Future.value(testInitialDefaultDisplayRotation), + getUiOrientation: () async => + _serializeDeviceOrientation(DeviceOrientation.portraitDown), + getDefaultDisplayRotation: () => + Future.value(testInitialDefaultDisplayRotation), ); // Get and create test camera. - final List availableCameras = - await camera.availableCameras(); + final List availableCameras = await camera + .availableCameras(); expect(availableCameras.length, 1); await camera.createCameraWithSettings( availableCameras.first, @@ -900,17 +914,15 @@ void main() { mockProcessCameraProvider: mockProcessCameraProvider, createCameraSelector: fakeCreateCameraSelector, handlesCropAndRotation: true, - getUiOrientation: - () async => _serializeDeviceOrientation( - DeviceOrientation.landscapeRight, - ), - getDefaultDisplayRotation: - () => Future.value(testInitialDefaultDisplayRotation), + getUiOrientation: () async => + _serializeDeviceOrientation(DeviceOrientation.landscapeRight), + getDefaultDisplayRotation: () => + Future.value(testInitialDefaultDisplayRotation), ); // Get and create test camera. - final List availableCameras = - await camera.availableCameras(); + final List availableCameras = await camera + .availableCameras(); expect(availableCameras.length, 1); await camera.createCameraWithSettings( availableCameras.first, @@ -964,6 +976,7 @@ void main() { // ignore: non_constant_identifier_names PigeonInstanceManager? pigeon_instanceManager, LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, }) proxyCreateCameraSelectorForFrontCamera = createCameraSelectorForFrontCamera(mockFrontCameraSelector); @@ -997,8 +1010,8 @@ void main() { ); // Get and create test front camera. - final List availableCameras = - await camera.availableCameras(); + final List availableCameras = await camera + .availableCameras(); expect(availableCameras.length, 1); await camera.createCameraWithSettings( availableCameras.first, @@ -1040,10 +1053,9 @@ void main() { final RotatedBox rotatedBox = tester.widget( find.byType(RotatedBox), ); - final int clockwiseQuarterTurns = - rotatedBox.quarterTurns < 0 - ? rotatedBox.quarterTurns + 4 - : rotatedBox.quarterTurns; + final int clockwiseQuarterTurns = rotatedBox.quarterTurns < 0 + ? rotatedBox.quarterTurns + 4 + : rotatedBox.quarterTurns; expect(rotatedBox.child, isA()); expect((rotatedBox.child! as Texture).textureId, cameraId); expect( @@ -1073,6 +1085,7 @@ void main() { // ignore: non_constant_identifier_names PigeonInstanceManager? pigeon_instanceManager, LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, }) proxyCreateCameraSelectorForFrontCamera = createCameraSelectorForFrontCamera(mockFrontCameraSelector); @@ -1093,13 +1106,13 @@ void main() { getUiOrientation: /* initial device orientation is irrelevant */ () async => _serializeDeviceOrientation(DeviceOrientation.portraitUp), - getDefaultDisplayRotation: - () => Future.value(testInitialDefaultDisplayRotation), + getDefaultDisplayRotation: () => + Future.value(testInitialDefaultDisplayRotation), ); // Get and create test front camera. - final List availableCameras = - await camera.availableCameras(); + final List availableCameras = await camera + .availableCameras(); expect(availableCameras.length, 1); await camera.createCameraWithSettings( availableCameras.first, @@ -1137,10 +1150,9 @@ void main() { final RotatedBox rotatedBox = tester.widget( find.byType(RotatedBox), ); - final int clockwiseQuarterTurns = - rotatedBox.quarterTurns < 0 - ? rotatedBox.quarterTurns + 4 - : rotatedBox.quarterTurns; + final int clockwiseQuarterTurns = rotatedBox.quarterTurns < 0 + ? rotatedBox.quarterTurns + 4 + : rotatedBox.quarterTurns; expect(rotatedBox.child, isA()); expect((rotatedBox.child! as Texture).textureId, cameraId); expect( @@ -1164,6 +1176,7 @@ void main() { late MockCameraSelector mockFrontCameraSelector; late MockCameraSelector Function({ LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, // ignore: non_constant_identifier_names BinaryMessenger? pigeon_binaryMessenger, // ignore: non_constant_identifier_names @@ -1189,8 +1202,8 @@ void main() { mockCameraSelector: mockFrontCameraSelector, sensorRotationDegrees: 270, ); - proxyGetDefaultDisplayRotation = - () => Future.value(Surface.rotation0); + proxyGetDefaultDisplayRotation = () => + Future.value(Surface.rotation0); // Media settings to create camera; irrelevant for test. testMediaSettings = const MediaSettings(); @@ -1207,14 +1220,13 @@ void main() { createCameraSelector: proxyCreateCameraSelectorForFrontCamera, getDefaultDisplayRotation: proxyGetDefaultDisplayRotation, handlesCropAndRotation: false, - getUiOrientation: - () async => - _serializeDeviceOrientation(DeviceOrientation.portraitUp), + getUiOrientation: () async => + _serializeDeviceOrientation(DeviceOrientation.portraitUp), ); // Get and create test front camera. - final List availableCameras = - await camera.availableCameras(); + final List availableCameras = await camera + .availableCameras(); expect(availableCameras.length, 1); await camera.createCameraWithSettings( availableCameras.first, @@ -1267,15 +1279,13 @@ void main() { createCameraSelector: proxyCreateCameraSelectorForFrontCamera, getDefaultDisplayRotation: proxyGetDefaultDisplayRotation, handlesCropAndRotation: false, - getUiOrientation: - () async => _serializeDeviceOrientation( - DeviceOrientation.landscapeRight, - ), + getUiOrientation: () async => + _serializeDeviceOrientation(DeviceOrientation.landscapeRight), ); // Get and create test front camera. - final List availableCameras = - await camera.availableCameras(); + final List availableCameras = await camera + .availableCameras(); expect(availableCameras.length, 1); await camera.createCameraWithSettings( availableCameras.first, @@ -1328,15 +1338,13 @@ void main() { createCameraSelector: proxyCreateCameraSelectorForFrontCamera, getDefaultDisplayRotation: proxyGetDefaultDisplayRotation, handlesCropAndRotation: false, - getUiOrientation: - () async => _serializeDeviceOrientation( - DeviceOrientation.portraitDown, - ), + getUiOrientation: () async => + _serializeDeviceOrientation(DeviceOrientation.portraitDown), ); // Get and create test front camera. - final List availableCameras = - await camera.availableCameras(); + final List availableCameras = await camera + .availableCameras(); expect(availableCameras.length, 1); await camera.createCameraWithSettings( availableCameras.first, @@ -1389,15 +1397,13 @@ void main() { createCameraSelector: proxyCreateCameraSelectorForFrontCamera, getDefaultDisplayRotation: proxyGetDefaultDisplayRotation, handlesCropAndRotation: false, - getUiOrientation: - () async => _serializeDeviceOrientation( - DeviceOrientation.landscapeLeft, - ), + getUiOrientation: () async => + _serializeDeviceOrientation(DeviceOrientation.landscapeLeft), ); // Get and create test front camera. - final List availableCameras = - await camera.availableCameras(); + final List availableCameras = await camera + .availableCameras(); expect(availableCameras.length, 1); await camera.createCameraWithSettings( availableCameras.first, @@ -1455,6 +1461,7 @@ void main() { // ignore: non_constant_identifier_names PigeonInstanceManager? pigeon_instanceManager, LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, }) proxyCreateCameraSelectorForFrontCamera; late Future Function() proxyGetUiOrientation; @@ -1475,9 +1482,8 @@ void main() { mockCameraSelector: mockFrontCameraSelector, sensorRotationDegrees: 270, ); - proxyGetUiOrientation = - () async => - _serializeDeviceOrientation(DeviceOrientation.landscapeLeft); + proxyGetUiOrientation = () async => + _serializeDeviceOrientation(DeviceOrientation.landscapeLeft); // Media settings to create camera; irrelevant for test. testMediaSettings = const MediaSettings(); @@ -1492,15 +1498,15 @@ void main() { mockProcessCameraProvider: mockProcessCameraProviderForFrontCamera, createCameraSelector: proxyCreateCameraSelectorForFrontCamera, - getDefaultDisplayRotation: - () => Future.value(Surface.rotation0), + getDefaultDisplayRotation: () => + Future.value(Surface.rotation0), handlesCropAndRotation: false, getUiOrientation: proxyGetUiOrientation, ); // Get and create test front camera. - final List availableCameras = - await camera.availableCameras(); + final List availableCameras = await camera + .availableCameras(); expect(availableCameras.length, 1); await camera.createCameraWithSettings( availableCameras.first, @@ -1551,15 +1557,15 @@ void main() { mockProcessCameraProvider: mockProcessCameraProviderForFrontCamera, createCameraSelector: proxyCreateCameraSelectorForFrontCamera, - getDefaultDisplayRotation: - () => Future.value(Surface.rotation90), + getDefaultDisplayRotation: () => + Future.value(Surface.rotation90), handlesCropAndRotation: false, getUiOrientation: proxyGetUiOrientation, ); // Get and create test front camera. - final List availableCameras = - await camera.availableCameras(); + final List availableCameras = await camera + .availableCameras(); expect(availableCameras.length, 1); await camera.createCameraWithSettings( availableCameras.first, @@ -1614,15 +1620,15 @@ void main() { mockProcessCameraProvider: mockProcessCameraProviderForFrontCamera, createCameraSelector: proxyCreateCameraSelectorForFrontCamera, - getDefaultDisplayRotation: - () => Future.value(Surface.rotation180), + getDefaultDisplayRotation: () => + Future.value(Surface.rotation180), handlesCropAndRotation: false, getUiOrientation: proxyGetUiOrientation, ); // Get and create test front camera. - final List availableCameras = - await camera.availableCameras(); + final List availableCameras = await camera + .availableCameras(); expect(availableCameras.length, 1); await camera.createCameraWithSettings( availableCameras.first, @@ -1675,15 +1681,15 @@ void main() { mockProcessCameraProvider: mockProcessCameraProviderForFrontCamera, createCameraSelector: proxyCreateCameraSelectorForFrontCamera, - getDefaultDisplayRotation: - () => Future.value(Surface.rotation270), + getDefaultDisplayRotation: () => + Future.value(Surface.rotation270), handlesCropAndRotation: false, getUiOrientation: proxyGetUiOrientation, ); // Get and create test front camera. - final List availableCameras = - await camera.availableCameras(); + final List availableCameras = await camera + .availableCameras(); expect(availableCameras.length, 1); await camera.createCameraWithSettings( availableCameras.first, @@ -1749,6 +1755,7 @@ void main() { // ignore: non_constant_identifier_names PigeonInstanceManager? pigeon_instanceManager, LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, }) proxyCreateCameraSelectorForFrontCamera = createCameraSelectorForFrontCamera(mockFrontCameraSelector); @@ -1781,8 +1788,8 @@ void main() { ); // Get and create test front camera. - final List availableCameras = - await camera.availableCameras(); + final List availableCameras = await camera + .availableCameras(); expect(availableCameras.length, 1); await camera.createCameraWithSettings( availableCameras.first, @@ -1838,10 +1845,9 @@ void main() { checkXAxisIsMirrored(transformedPreviewMatrix); expect((transformedPreview.child! as Texture).textureId, cameraId); - final int clockwiseQuarterTurns = - rotatedBox.quarterTurns < 0 - ? rotatedBox.quarterTurns + 4 - : rotatedBox.quarterTurns; + final int clockwiseQuarterTurns = rotatedBox.quarterTurns < 0 + ? rotatedBox.quarterTurns + 4 + : rotatedBox.quarterTurns; expect( clockwiseQuarterTurns, expectedQuarterTurns, @@ -1864,6 +1870,7 @@ void main() { final MockCameraSelector mockFrontCameraSelector = MockCameraSelector(); final MockCameraSelector Function({ LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, // ignore: non_constant_identifier_names BinaryMessenger? pigeon_binaryMessenger, // ignore: non_constant_identifier_names @@ -1891,13 +1898,13 @@ void main() { getUiOrientation: /* initial device orientation irrelevant for test */ () async => _serializeDeviceOrientation(DeviceOrientation.landscapeLeft), - getDefaultDisplayRotation: - () => Future.value(Surface.rotation90), + getDefaultDisplayRotation: () => + Future.value(Surface.rotation90), ); // Get and create test front camera. - final List availableCameras = - await camera.availableCameras(); + final List availableCameras = await camera + .availableCameras(); expect(availableCameras.length, 1); await camera.createCameraWithSettings( availableCameras.first, @@ -1954,10 +1961,9 @@ void main() { checkYAxisIsMirrored(transformedPreviewMatrix); } expect((transformedPreview.child! as Texture).textureId, cameraId); - final int clockwiseQuarterTurns = - rotatedBox.quarterTurns < 0 - ? rotatedBox.quarterTurns + 4 - : rotatedBox.quarterTurns; + final int clockwiseQuarterTurns = rotatedBox.quarterTurns < 0 + ? rotatedBox.quarterTurns + 4 + : rotatedBox.quarterTurns; expect( clockwiseQuarterTurns, expectedQuarterTurns, @@ -1982,6 +1988,7 @@ void main() { // ignore: non_constant_identifier_names PigeonInstanceManager? pigeon_instanceManager, LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, }) proxyCreateCameraSelectorForBackCamera; late Future Function() proxyGetDefaultDisplayRotation; @@ -2000,8 +2007,8 @@ void main() { mockBackCameraSelector = MockCameraSelector(); proxyCreateCameraSelectorForBackCamera = createCameraSelectorForBackCamera(mockBackCameraSelector); - proxyGetDefaultDisplayRotation = - () => Future.value(Surface.rotation270); + proxyGetDefaultDisplayRotation = () => + Future.value(Surface.rotation270); testMediaSettings = const MediaSettings(); }); @@ -2025,14 +2032,13 @@ void main() { createCameraSelector: proxyCreateCameraSelectorForBackCamera, getDefaultDisplayRotation: proxyGetDefaultDisplayRotation, handlesCropAndRotation: false, - getUiOrientation: - () async => - _serializeDeviceOrientation(testInitialDeviceOrientation), + getUiOrientation: () async => + _serializeDeviceOrientation(testInitialDeviceOrientation), ); // Get and create test back camera. - final List availableCameras = - await camera.availableCameras(); + final List availableCameras = await camera + .availableCameras(); expect(availableCameras.length, 1); await camera.createCameraWithSettings( availableCameras.first, @@ -2084,14 +2090,13 @@ void main() { createCameraSelector: proxyCreateCameraSelectorForBackCamera, getDefaultDisplayRotation: proxyGetDefaultDisplayRotation, handlesCropAndRotation: false, - getUiOrientation: - () async => - _serializeDeviceOrientation(testInitialDeviceOrientation), + getUiOrientation: () async => + _serializeDeviceOrientation(testInitialDeviceOrientation), ); // Get and create test back camera. - final List availableCameras = - await camera.availableCameras(); + final List availableCameras = await camera + .availableCameras(); expect(availableCameras.length, 1); await camera.createCameraWithSettings( availableCameras.first, @@ -2146,8 +2151,8 @@ void main() { testSensorOrientation = 90; // Create mock for seting initial default display rotation to 180 degrees. - proxyGetDefaultDisplayRotation = - () => Future.value(Surface.rotation90); + proxyGetDefaultDisplayRotation = () => + Future.value(Surface.rotation90); // Media settings to create camera; irrelevant for test. testMediaSettings = const MediaSettings(); @@ -2171,6 +2176,7 @@ void main() { // ignore: non_constant_identifier_names PigeonInstanceManager? pigeon_instanceManager, LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, }) proxyCreateCameraSelectorForFrontCamera = createCameraSelectorForFrontCamera(mockFrontCameraSelector); @@ -2179,14 +2185,13 @@ void main() { createCameraSelector: proxyCreateCameraSelectorForFrontCamera, getDefaultDisplayRotation: proxyGetDefaultDisplayRotation, handlesCropAndRotation: false, - getUiOrientation: - () async => - _serializeDeviceOrientation(testInitialDeviceOrientation), + getUiOrientation: () async => + _serializeDeviceOrientation(testInitialDeviceOrientation), ); // Get and create test camera. - final List availableCameras = - await camera.availableCameras(); + final List availableCameras = await camera + .availableCameras(); expect(availableCameras.length, 1); await camera.createCameraWithSettings( availableCameras.first, @@ -2249,6 +2254,7 @@ void main() { // ignore: non_constant_identifier_names PigeonInstanceManager? pigeon_instanceManager, LensFacing? requireLensFacing, + CameraInfo? cameraInfoForFilter, }) proxyCreateCameraSelectorForFrontCamera = createCameraSelectorForBackCamera(mockBackCameraSelector); @@ -2257,14 +2263,13 @@ void main() { createCameraSelector: proxyCreateCameraSelectorForFrontCamera, getDefaultDisplayRotation: proxyGetDefaultDisplayRotation, handlesCropAndRotation: false, - getUiOrientation: - () async => - _serializeDeviceOrientation(testInitialDeviceOrientation), + getUiOrientation: () async => + _serializeDeviceOrientation(testInitialDeviceOrientation), ); // Get and create test camera. - final List availableCameras = - await camera.availableCameras(); + final List availableCameras = await camera + .availableCameras(); expect(availableCameras.length, 1); await camera.createCameraWithSettings( availableCameras.first, diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index bccc6346903..e98f438609f 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.1 + +* Restructures internal logic to move more code to Dart. + ## 2.8.0 * Adds platform view support for macOS. diff --git a/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m b/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m index 0dd84c55149..58ad53674d0 100644 --- a/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m +++ b/packages/video_player/video_player_avfoundation/darwin/RunnerTests/VideoPlayerTests.m @@ -168,32 +168,6 @@ - (instancetype)init { @implementation VideoPlayerTests -- (void)testCreateWithOptionsReturnsErrorForInvalidAssetPath { - NSObject *registrar = OCMProtocolMock(@protocol(FlutterPluginRegistrar)); - OCMStub([registrar lookupKeyForAsset:[OCMArg any]]).andReturn(nil); - FVPVideoPlayerPlugin *videoPlayerPlugin = - [[FVPVideoPlayerPlugin alloc] initWithRegistrar:registrar]; - - FlutterError *initializationError; - [videoPlayerPlugin initialize:&initializationError]; - XCTAssertNil(initializationError); - - FVPCreationOptions *create = - [FVPCreationOptions makeWithAsset:@"invalid/path/to/asset" - uri:nil - packageName:nil - formatHint:nil - httpHeaders:@{} - viewType:FVPPlatformVideoViewTypeTextureView]; - - FlutterError *createError; - NSNumber *playerIdentifier = [videoPlayerPlugin createWithOptions:create error:&createError]; - - XCTAssertNil(playerIdentifier); - XCTAssertNotNil(createError); - XCTAssertEqualObjects(createError.code, @"video_player"); -} - - (void)testBlankVideoBugWithEncryptedVideoStreamAndInvertedAspectRatioBugForSomeVideoStream { // This is to fix 2 bugs: 1. blank video for encrypted video streams on iOS 16 // (https://github.com/flutter/flutter/issues/111457) and 2. swapped width and height for some @@ -218,12 +192,9 @@ - (void)testBlankVideoBugWithEncryptedVideoStreamAndInvertedAspectRatioBugForSom XCTAssertNil(error); FVPCreationOptions *create = [FVPCreationOptions - makeWithAsset:nil - uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" - packageName:nil - formatHint:nil - httpHeaders:@{} - viewType:FVPPlatformVideoViewTypeTextureView]; + makeWithUri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" + httpHeaders:@{} + viewType:FVPPlatformVideoViewTypeTextureView]; NSNumber *playerIdentifier = [videoPlayerPlugin createWithOptions:create error:&error]; XCTAssertNil(error); XCTAssertNotNil(playerIdentifier); @@ -253,12 +224,9 @@ - (void)testPlayerForPlatformViewDoesNotRegisterTexture { [videoPlayerPlugin initialize:&initalizationError]; XCTAssertNil(initalizationError); FVPCreationOptions *create = [FVPCreationOptions - makeWithAsset:nil - uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8" - packageName:nil - formatHint:nil - httpHeaders:@{} - viewType:FVPPlatformVideoViewTypePlatformView]; + makeWithUri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8" + httpHeaders:@{} + viewType:FVPPlatformVideoViewTypePlatformView]; FlutterError *createError; [videoPlayerPlugin createWithOptions:create error:&createError]; @@ -284,12 +252,9 @@ - (void)testSeekToWhilePausedStartsDisplayLinkTemporarily { [videoPlayerPlugin initialize:&initalizationError]; XCTAssertNil(initalizationError); FVPCreationOptions *create = [FVPCreationOptions - makeWithAsset:nil - uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8" - packageName:nil - formatHint:nil - httpHeaders:@{} - viewType:FVPPlatformVideoViewTypeTextureView]; + makeWithUri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8" + httpHeaders:@{} + viewType:FVPPlatformVideoViewTypeTextureView]; FlutterError *createError; NSNumber *playerIdentifier = [videoPlayerPlugin createWithOptions:create error:&createError]; FVPTextureBasedVideoPlayer *player = @@ -344,12 +309,9 @@ - (void)testInitStartsDisplayLinkTemporarily { [videoPlayerPlugin initialize:&initalizationError]; XCTAssertNil(initalizationError); FVPCreationOptions *create = [FVPCreationOptions - makeWithAsset:nil - uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8" - packageName:nil - formatHint:nil - httpHeaders:@{} - viewType:FVPPlatformVideoViewTypeTextureView]; + makeWithUri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8" + httpHeaders:@{} + viewType:FVPPlatformVideoViewTypeTextureView]; FlutterError *createError; NSNumber *playerIdentifier = [videoPlayerPlugin createWithOptions:create error:&createError]; @@ -393,12 +355,9 @@ - (void)testSeekToWhilePlayingDoesNotStopDisplayLink { [videoPlayerPlugin initialize:&initalizationError]; XCTAssertNil(initalizationError); FVPCreationOptions *create = [FVPCreationOptions - makeWithAsset:nil - uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8" - packageName:nil - formatHint:nil - httpHeaders:@{} - viewType:FVPPlatformVideoViewTypeTextureView]; + makeWithUri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8" + httpHeaders:@{} + viewType:FVPPlatformVideoViewTypeTextureView]; FlutterError *createError; NSNumber *playerIdentifier = [videoPlayerPlugin createWithOptions:create error:&createError]; FVPTextureBasedVideoPlayer *player = @@ -451,12 +410,9 @@ - (void)testPauseWhileWaitingForFrameDoesNotStopDisplayLink { [videoPlayerPlugin initialize:&initalizationError]; XCTAssertNil(initalizationError); FVPCreationOptions *create = [FVPCreationOptions - makeWithAsset:nil - uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8" - packageName:nil - formatHint:nil - httpHeaders:@{} - viewType:FVPPlatformVideoViewTypeTextureView]; + makeWithUri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8" + httpHeaders:@{} + viewType:FVPPlatformVideoViewTypeTextureView]; FlutterError *createError; NSNumber *playerIdentifier = [videoPlayerPlugin createWithOptions:create error:&createError]; FVPTextureBasedVideoPlayer *player = @@ -481,12 +437,9 @@ - (void)testDeregistersFromPlayer { XCTAssertNil(error); FVPCreationOptions *create = [FVPCreationOptions - makeWithAsset:nil - uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" - packageName:nil - formatHint:nil - httpHeaders:@{} - viewType:FVPPlatformVideoViewTypeTextureView]; + makeWithUri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" + httpHeaders:@{} + viewType:FVPPlatformVideoViewTypeTextureView]; NSNumber *playerIdentifier = [videoPlayerPlugin createWithOptions:create error:&error]; XCTAssertNil(error); XCTAssertNotNil(playerIdentifier); @@ -513,12 +466,9 @@ - (void)testBufferingStateFromPlayer { XCTAssertNil(error); FVPCreationOptions *create = [FVPCreationOptions - makeWithAsset:nil - uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" - packageName:nil - formatHint:nil - httpHeaders:@{} - viewType:FVPPlatformVideoViewTypeTextureView]; + makeWithUri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" + httpHeaders:@{} + viewType:FVPPlatformVideoViewTypeTextureView]; NSNumber *playerIdentifier = [videoPlayerPlugin createWithOptions:create error:&error]; XCTAssertNil(error); XCTAssertNotNil(playerIdentifier); @@ -709,12 +659,9 @@ - (void)testDoesNotCrashOnRateObservationAfterDisposal { XCTAssertNil(error); FVPCreationOptions *create = [FVPCreationOptions - makeWithAsset:nil - uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" - packageName:nil - formatHint:nil - httpHeaders:@{} - viewType:FVPPlatformVideoViewTypeTextureView]; + makeWithUri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" + httpHeaders:@{} + viewType:FVPPlatformVideoViewTypeTextureView]; NSNumber *playerIdentifier = [videoPlayerPlugin createWithOptions:create error:&error]; XCTAssertNil(error); XCTAssertNotNil(playerIdentifier); @@ -763,12 +710,9 @@ - (void)testHotReloadDoesNotCrash { XCTAssertNil(error); FVPCreationOptions *create = [FVPCreationOptions - makeWithAsset:nil - uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" - packageName:nil - formatHint:nil - httpHeaders:@{} - viewType:FVPPlatformVideoViewTypeTextureView]; + makeWithUri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" + httpHeaders:@{} + viewType:FVPPlatformVideoViewTypeTextureView]; NSNumber *playerIdentifier = [videoPlayerPlugin createWithOptions:create error:&error]; XCTAssertNil(error); XCTAssertNotNil(playerIdentifier); @@ -830,13 +774,9 @@ - (void)testFailedToLoadVideoEventShouldBeAlwaysSent { [videoPlayerPlugin initialize:&error]; - FVPCreationOptions *create = - [FVPCreationOptions makeWithAsset:nil - uri:@"" - packageName:nil - formatHint:nil - httpHeaders:@{} - viewType:FVPPlatformVideoViewTypeTextureView]; + FVPCreationOptions *create = [FVPCreationOptions makeWithUri:@"" + httpHeaders:@{} + viewType:FVPPlatformVideoViewTypeTextureView]; NSNumber *playerIdentifier = [videoPlayerPlugin createWithOptions:create error:&error]; FVPVideoPlayer *player = videoPlayerPlugin.playersByIdentifier[playerIdentifier]; XCTAssertNotNil(player); @@ -896,12 +836,9 @@ - (void)testPlayerShouldNotDropEverySecondFrame { [videoPlayerPlugin initialize:&error]; XCTAssertNil(error); FVPCreationOptions *create = [FVPCreationOptions - makeWithAsset:nil - uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" - packageName:nil - formatHint:nil - httpHeaders:@{} - viewType:FVPPlatformVideoViewTypeTextureView]; + makeWithUri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" + httpHeaders:@{} + viewType:FVPPlatformVideoViewTypeTextureView]; NSNumber *playerIdentifier = [videoPlayerPlugin createWithOptions:create error:&error]; FVPTextureBasedVideoPlayer *player = (FVPTextureBasedVideoPlayer *)videoPlayerPlugin.playersByIdentifier[playerIdentifier]; diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPTextureBasedVideoPlayer.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPTextureBasedVideoPlayer.m index 071518e36f2..d8ef40c456e 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPTextureBasedVideoPlayer.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPTextureBasedVideoPlayer.m @@ -31,18 +31,6 @@ @interface FVPTextureBasedVideoPlayer () @end @implementation FVPTextureBasedVideoPlayer -- (instancetype)initWithAsset:(NSString *)asset - frameUpdater:(FVPFrameUpdater *)frameUpdater - displayLink:(NSObject *)displayLink - avFactory:(id)avFactory - viewProvider:(NSObject *)viewProvider { - return [self initWithURL:[NSURL fileURLWithPath:[FVPVideoPlayer absolutePathForAssetName:asset]] - frameUpdater:frameUpdater - displayLink:displayLink - httpHeaders:@{} - avFactory:avFactory - viewProvider:viewProvider]; -} - (instancetype)initWithURL:(NSURL *)url frameUpdater:(FVPFrameUpdater *)frameUpdater diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayer.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayer.m index 77a7195c873..acf5bf60805 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayer.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayer.m @@ -17,14 +17,6 @@ static void *rateContext = &rateContext; @implementation FVPVideoPlayer -- (instancetype)initWithAsset:(NSString *)asset - avFactory:(id)avFactory - viewProvider:(NSObject *)viewProvider { - return [self initWithURL:[NSURL fileURLWithPath:[FVPVideoPlayer absolutePathForAssetName:asset]] - httpHeaders:@{} - avFactory:avFactory - viewProvider:viewProvider]; -} - (instancetype)initWithURL:(NSURL *)url httpHeaders:(nonnull NSDictionary *)headers @@ -122,19 +114,6 @@ - (void)dispose { [_eventChannel setStreamHandler:nil]; } -+ (NSString *)absolutePathForAssetName:(NSString *)assetName { - NSString *path = [[NSBundle mainBundle] pathForResource:assetName ofType:nil]; -#if TARGET_OS_OSX - // See https://github.com/flutter/flutter/issues/135302 - // TODO(stuartmorgan): Remove this if the asset APIs are adjusted to work better for macOS. - if (!path) { - path = [NSURL URLWithString:assetName relativeToURL:NSBundle.mainBundle.bundleURL].path; - } -#endif - - return path; -} - - (void)addObserversForItem:(AVPlayerItem *)item player:(AVPlayer *)player { [item addObserver:self forKeyPath:@"loadedTimeRanges" @@ -250,8 +229,10 @@ - (void)observeValueForKeyPath:(NSString *)path NSMutableArray *> *values = [[NSMutableArray alloc] init]; for (NSValue *rangeValue in [object loadedTimeRanges]) { CMTimeRange range = [rangeValue CMTimeRangeValue]; - int64_t start = FVPCMTimeToMillis(range.start); - [values addObject:@[ @(start), @(start + FVPCMTimeToMillis(range.duration)) ]]; + [values addObject:@[ + @(FVPCMTimeToMillis(range.start)), + @(FVPCMTimeToMillis(range.duration)), + ]]; } _eventSink(@{@"event" : @"bufferingUpdate", @"values" : values}); } diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerPlugin.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerPlugin.m index 114bba8552a..4c53923cd42 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerPlugin.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/FVPVideoPlayerPlugin.m @@ -43,8 +43,6 @@ @implementation FVPDefaultDisplayLinkFactory #pragma mark - @interface FVPVideoPlayerPlugin () -@property(readonly, weak, nonatomic) NSObject *registry; -@property(readonly, weak, nonatomic) NSObject *messenger; @property(readonly, strong, nonatomic) NSObject *registrar; @property(nonatomic, strong) id displayLinkFactory; @property(nonatomic, strong) id avFactory; @@ -79,8 +77,6 @@ - (instancetype)initWithAVFactory:(id)avFactory registrar:(NSObject *)registrar { self = [super init]; NSAssert(self, @"super init cannot be nil"); - _registry = [registrar textures]; - _messenger = [registrar messenger]; _registrar = registrar; _viewProvider = viewProvider; _displayLinkFactory = displayLinkFactory ?: [[FVPDefaultDisplayLinkFactory alloc] init]; @@ -108,13 +104,13 @@ - (int64_t)onPlayerSetup:(FVPVideoPlayer *)player { int64_t playerIdentifier; if (textureBasedPlayer) { - playerIdentifier = [self.registry registerTexture:textureBasedPlayer]; + playerIdentifier = [self.registrar.textures registerTexture:textureBasedPlayer]; [textureBasedPlayer setTextureIdentifier:playerIdentifier]; } else { playerIdentifier = self.nextNonTexturePlayerIdentifier--; } - NSObject *messenger = self.messenger; + NSObject *messenger = self.registrar.messenger; NSString *channelSuffix = [NSString stringWithFormat:@"%lld", playerIdentifier]; // Set up the player-specific API handler, and its onDispose unregistration. SetUpFVPVideoPlayerInstanceApiWithSuffix(messenger, player, channelSuffix); @@ -123,7 +119,7 @@ - (int64_t)onPlayerSetup:(FVPVideoPlayer *)player { player.onDisposed = ^() { SetUpFVPVideoPlayerInstanceApiWithSuffix(messenger, nil, channelSuffix); if (isTextureBased) { - [weakSelf.registry unregisterTexture:playerIdentifier]; + [weakSelf.registrar.textures unregisterTexture:playerIdentifier]; } }; // Set up the event channel. @@ -196,12 +192,6 @@ - (nullable NSNumber *)createWithOptions:(nonnull FVPCreationOptions *)options @try { FVPVideoPlayer *player = textureBased ? [self texturePlayerWithOptions:options] : [self platformViewPlayerWithOptions:options]; - - if (player == nil) { - *error = [FlutterError errorWithCode:@"video_player" message:@"not implemented" details:nil]; - return nil; - } - return @([self onPlayerSetup:player]); } @catch (NSException *exception) { *error = [FlutterError errorWithCode:@"video_player" message:exception.reason details:nil]; @@ -209,59 +199,30 @@ - (nullable NSNumber *)createWithOptions:(nonnull FVPCreationOptions *)options } } -- (nullable FVPTextureBasedVideoPlayer *)texturePlayerWithOptions: +- (nonnull FVPTextureBasedVideoPlayer *)texturePlayerWithOptions: (nonnull FVPCreationOptions *)options { - FVPFrameUpdater *frameUpdater = [[FVPFrameUpdater alloc] initWithRegistry:_registry]; + FVPFrameUpdater *frameUpdater = + [[FVPFrameUpdater alloc] initWithRegistry:self.registrar.textures]; NSObject *displayLink = [self.displayLinkFactory displayLinkWithRegistrar:_registrar callback:^() { [frameUpdater displayLinkFired]; }]; - if (options.asset) { - NSString *assetPath = [self assetPathFromCreationOptions:options]; - return [[FVPTextureBasedVideoPlayer alloc] initWithAsset:assetPath - frameUpdater:frameUpdater - displayLink:displayLink - avFactory:self.avFactory - viewProvider:self.viewProvider]; - } else if (options.uri) { - return [[FVPTextureBasedVideoPlayer alloc] initWithURL:[NSURL URLWithString:options.uri] - frameUpdater:frameUpdater - displayLink:displayLink - httpHeaders:options.httpHeaders - avFactory:self.avFactory - viewProvider:self.viewProvider]; - } - - return nil; + return [[FVPTextureBasedVideoPlayer alloc] initWithURL:[NSURL URLWithString:options.uri] + frameUpdater:frameUpdater + displayLink:displayLink + httpHeaders:options.httpHeaders + avFactory:self.avFactory + viewProvider:self.viewProvider]; } -- (nullable FVPVideoPlayer *)platformViewPlayerWithOptions:(nonnull FVPCreationOptions *)options { +- (nonnull FVPVideoPlayer *)platformViewPlayerWithOptions:(nonnull FVPCreationOptions *)options { // FVPVideoPlayer contains all required logic for platform views. - if (options.asset) { - NSString *assetPath = [self assetPathFromCreationOptions:options]; - return [[FVPVideoPlayer alloc] initWithAsset:assetPath - avFactory:self.avFactory - viewProvider:self.viewProvider]; - } else if (options.uri) { - return [[FVPVideoPlayer alloc] initWithURL:[NSURL URLWithString:options.uri] - httpHeaders:options.httpHeaders - avFactory:self.avFactory - viewProvider:self.viewProvider]; - } - - return nil; -} - -- (NSString *)assetPathFromCreationOptions:(nonnull FVPCreationOptions *)options { - NSString *assetPath; - if (options.packageName) { - assetPath = [self.registrar lookupKeyForAsset:options.asset fromPackage:options.packageName]; - } else { - assetPath = [self.registrar lookupKeyForAsset:options.asset]; - } - return assetPath; + return [[FVPVideoPlayer alloc] initWithURL:[NSURL URLWithString:options.uri] + httpHeaders:options.httpHeaders + avFactory:self.avFactory + viewProvider:self.viewProvider]; } - (void)disposePlayer:(NSInteger)playerIdentifier error:(FlutterError **)error { @@ -286,4 +247,26 @@ - (void)setMixWithOthers:(BOOL)mixWithOthers #endif } +- (nullable NSString *)fileURLForAssetWithName:(NSString *)asset + package:(nullable NSString *)package + error:(FlutterError *_Nullable *_Nonnull)error { + NSString *resource = package == nil + ? [self.registrar lookupKeyForAsset:asset] + : [self.registrar lookupKeyForAsset:asset fromPackage:package]; + + NSString *path = [[NSBundle mainBundle] pathForResource:resource ofType:nil]; +#if TARGET_OS_OSX + // See https://github.com/flutter/flutter/issues/135302 + // TODO(stuartmorgan): Remove this if the asset APIs are adjusted to work better for macOS. + if (!path) { + path = [NSURL URLWithString:resource relativeToURL:NSBundle.mainBundle.bundleURL].path; + } +#endif + + if (!path) { + return nil; + } + return [NSURL fileURLWithPath:path].absoluteString; +} + @end diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPTextureBasedVideoPlayer.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPTextureBasedVideoPlayer.h index 229fd78ca24..bae9105039e 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPTextureBasedVideoPlayer.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPTextureBasedVideoPlayer.h @@ -23,14 +23,6 @@ NS_ASSUME_NONNULL_BEGIN avFactory:(id)avFactory viewProvider:(NSObject *)viewProvider; -/// Initializes a new instance of FVPTextureBasedVideoPlayer with the given asset, frame updater, -/// display link, AV factory, and registrar. -- (instancetype)initWithAsset:(NSString *)asset - frameUpdater:(FVPFrameUpdater *)frameUpdater - displayLink:(NSObject *)displayLink - avFactory:(id)avFactory - viewProvider:(NSObject *)viewProvider; - /// Sets the texture Identifier for the frame updater. This method should be called once the texture /// identifier is obtained from the texture registry. - (void)setTextureIdentifier:(int64_t)textureIdentifier; diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h index 9a18197ee24..469dda24a61 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer.h @@ -35,12 +35,6 @@ NS_ASSUME_NONNULL_BEGIN /// A block that will be called when dispose is called. @property(nonatomic, nullable, copy) void (^onDisposed)(void); -/// Initializes a new instance of FVPVideoPlayer with the given asset, AV factory, and view -/// provider. -- (instancetype)initWithAsset:(NSString *)asset - avFactory:(id)avFactory - viewProvider:(NSObject *)viewProvider; - /// Initializes a new instance of FVPVideoPlayer with the given URL, HTTP headers, AV factory, and /// view provider. - (instancetype)initWithURL:(NSURL *)url diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer_Internal.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer_Internal.h index 7b282be4c06..c03736e62f5 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer_Internal.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/FVPVideoPlayer_Internal.h @@ -40,10 +40,6 @@ NS_ASSUME_NONNULL_BEGIN /// Updates the playing state of the video player. - (void)updatePlayingState; - -/// Returns the absolute file path for a given asset name. -/// This method attempts to locate the specified asset within the app bundle. -+ (NSString *)absolutePathForAssetName:(NSString *)assetName; @end NS_ASSUME_NONNULL_END diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/messages.g.h b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/messages.g.h index 1f3966d6e77..b895043931e 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/messages.g.h +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/include/video_player_avfoundation/messages.g.h @@ -39,16 +39,10 @@ typedef NS_ENUM(NSUInteger, FVPPlatformVideoViewType) { @interface FVPCreationOptions : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithAsset:(nullable NSString *)asset - uri:(nullable NSString *)uri - packageName:(nullable NSString *)packageName - formatHint:(nullable NSString *)formatHint - httpHeaders:(NSDictionary *)httpHeaders - viewType:(FVPPlatformVideoViewType)viewType; -@property(nonatomic, copy, nullable) NSString *asset; -@property(nonatomic, copy, nullable) NSString *uri; -@property(nonatomic, copy, nullable) NSString *packageName; -@property(nonatomic, copy, nullable) NSString *formatHint; ++ (instancetype)makeWithUri:(NSString *)uri + httpHeaders:(NSDictionary *)httpHeaders + viewType:(FVPPlatformVideoViewType)viewType; +@property(nonatomic, copy) NSString *uri; @property(nonatomic, copy) NSDictionary *httpHeaders; @property(nonatomic, assign) FVPPlatformVideoViewType viewType; @end @@ -63,6 +57,9 @@ NSObject *FVPGetMessagesCodec(void); error:(FlutterError *_Nullable *_Nonnull)error; - (void)disposePlayer:(NSInteger)playerId error:(FlutterError *_Nullable *_Nonnull)error; - (void)setMixWithOthers:(BOOL)mixWithOthers error:(FlutterError *_Nullable *_Nonnull)error; +- (nullable NSString *)fileURLForAssetWithName:(NSString *)asset + package:(nullable NSString *)package + error:(FlutterError *_Nullable *_Nonnull)error; @end extern void SetUpFVPAVFoundationVideoPlayerApi( diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/messages.g.m b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/messages.g.m index 90519755c2e..13076be0e91 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/messages.g.m +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation/Sources/video_player_avfoundation/messages.g.m @@ -77,29 +77,20 @@ + (nullable FVPPlatformVideoViewCreationParams *)nullableFromList:(NSArray * @end @implementation FVPCreationOptions -+ (instancetype)makeWithAsset:(nullable NSString *)asset - uri:(nullable NSString *)uri - packageName:(nullable NSString *)packageName - formatHint:(nullable NSString *)formatHint - httpHeaders:(NSDictionary *)httpHeaders - viewType:(FVPPlatformVideoViewType)viewType { ++ (instancetype)makeWithUri:(NSString *)uri + httpHeaders:(NSDictionary *)httpHeaders + viewType:(FVPPlatformVideoViewType)viewType { FVPCreationOptions *pigeonResult = [[FVPCreationOptions alloc] init]; - pigeonResult.asset = asset; pigeonResult.uri = uri; - pigeonResult.packageName = packageName; - pigeonResult.formatHint = formatHint; pigeonResult.httpHeaders = httpHeaders; pigeonResult.viewType = viewType; return pigeonResult; } + (FVPCreationOptions *)fromList:(NSArray *)list { FVPCreationOptions *pigeonResult = [[FVPCreationOptions alloc] init]; - pigeonResult.asset = GetNullableObjectAtIndex(list, 0); - pigeonResult.uri = GetNullableObjectAtIndex(list, 1); - pigeonResult.packageName = GetNullableObjectAtIndex(list, 2); - pigeonResult.formatHint = GetNullableObjectAtIndex(list, 3); - pigeonResult.httpHeaders = GetNullableObjectAtIndex(list, 4); - FVPPlatformVideoViewTypeBox *boxedFVPPlatformVideoViewType = GetNullableObjectAtIndex(list, 5); + pigeonResult.uri = GetNullableObjectAtIndex(list, 0); + pigeonResult.httpHeaders = GetNullableObjectAtIndex(list, 1); + FVPPlatformVideoViewTypeBox *boxedFVPPlatformVideoViewType = GetNullableObjectAtIndex(list, 2); pigeonResult.viewType = boxedFVPPlatformVideoViewType.value; return pigeonResult; } @@ -108,10 +99,7 @@ + (nullable FVPCreationOptions *)nullableFromList:(NSArray *)list { } - (NSArray *)toList { return @[ - self.asset ?: [NSNull null], self.uri ?: [NSNull null], - self.packageName ?: [NSNull null], - self.formatHint ?: [NSNull null], self.httpHeaders ?: [NSNull null], [[FVPPlatformVideoViewTypeBox alloc] initWithValue:self.viewType], ]; @@ -284,6 +272,31 @@ void SetUpFVPAVFoundationVideoPlayerApiWithSuffix(id bin [channel setMessageHandler:nil]; } } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.video_player_avfoundation." + @"AVFoundationVideoPlayerApi.getAssetUrl", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FVPGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(fileURLForAssetWithName:package:error:)], + @"FVPAVFoundationVideoPlayerApi api (%@) doesn't respond to " + @"@selector(fileURLForAssetWithName:package:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSString *arg_asset = GetNullableObjectAtIndex(args, 0); + NSString *arg_package = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + NSString *output = [api fileURLForAssetWithName:arg_asset package:arg_package error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } } void SetUpFVPVideoPlayerInstanceApi(id binaryMessenger, NSObject *api) { diff --git a/packages/video_player/video_player_avfoundation/example/lib/main.dart b/packages/video_player/video_player_avfoundation/example/lib/main.dart index f98516b2003..c3b61debf30 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/main.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/main.dart @@ -269,9 +269,9 @@ class _BumbleBeeEncryptedLiveStreamState _controller.addListener(() { setState(() {}); }); - _controller.initialize(); - - _controller.play(); + _controller.initialize().then((_) { + _controller.play(); + }); } @override diff --git a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart index bd89408eeb7..3558dccd67c 100644 --- a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart +++ b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart @@ -74,30 +74,38 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { final DataSource dataSource = options.dataSource; final VideoViewType viewType = options.viewType; - String? asset; - String? packageName; String? uri; - String? formatHint; - Map httpHeaders = {}; switch (dataSource.sourceType) { case DataSourceType.asset: - asset = dataSource.asset; - packageName = dataSource.package; + final String? asset = dataSource.asset; + if (asset == null) { + throw ArgumentError( + '"asset" must be non-null for an asset data source', + ); + } + uri = await _api.getAssetUrl( + asset, + dataSource.package, + ); + if (uri == null) { + // Throw a platform exception for compatibility with the previous + // implementation, which threw on the native side. + throw PlatformException( + code: 'video_player', + message: + 'Asset $asset not found in package ${dataSource.package}.'); + } case DataSourceType.network: - uri = dataSource.uri; - formatHint = _videoFormatStringMap[dataSource.formatHint]; - httpHeaders = dataSource.httpHeaders; case DataSourceType.file: - uri = dataSource.uri; case DataSourceType.contentUri: uri = dataSource.uri; } + if (uri == null) { + throw ArgumentError('Unable to construct a video asset from $options'); + } final CreationOptions pigeonCreationOptions = CreationOptions( - asset: asset, - packageName: packageName, uri: uri, - httpHeaders: httpHeaders, - formatHint: formatHint, + httpHeaders: dataSource.httpHeaders, viewType: _platformVideoViewTypeFromVideoViewType(viewType), ); @@ -166,37 +174,33 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { .receiveBroadcastStream() .map((dynamic event) { final Map map = event as Map; - switch (map['event']) { - case 'initialized': - return VideoEvent( + return switch (map['event']) { + 'initialized' => VideoEvent( eventType: VideoEventType.initialized, duration: Duration(milliseconds: map['duration'] as int), - size: Size((map['width'] as num?)?.toDouble() ?? 0.0, - (map['height'] as num?)?.toDouble() ?? 0.0), - ); - case 'completed': - return VideoEvent( + size: Size( + (map['width'] as num?)?.toDouble() ?? 0.0, + (map['height'] as num?)?.toDouble() ?? 0.0, + ), + ), + 'completed' => VideoEvent( eventType: VideoEventType.completed, - ); - case 'bufferingUpdate': - final List values = map['values'] as List; - - return VideoEvent( - buffered: values.map(_toDurationRange).toList(), + ), + 'bufferingUpdate' => VideoEvent( + buffered: (map['values'] as List) + .map(_toDurationRange) + .toList(), eventType: VideoEventType.bufferingUpdate, - ); - case 'bufferingStart': - return VideoEvent(eventType: VideoEventType.bufferingStart); - case 'bufferingEnd': - return VideoEvent(eventType: VideoEventType.bufferingEnd); - case 'isPlayingStateUpdate': - return VideoEvent( + ), + 'bufferingStart' => + VideoEvent(eventType: VideoEventType.bufferingStart), + 'bufferingEnd' => VideoEvent(eventType: VideoEventType.bufferingEnd), + 'isPlayingStateUpdate' => VideoEvent( eventType: VideoEventType.isPlayingStateUpdate, isPlaying: map['isPlaying'] as bool, - ); - default: - return VideoEvent(eventType: VideoEventType.unknown); - } + ), + _ => VideoEvent(eventType: VideoEventType.unknown), + }; }); } @@ -250,19 +254,13 @@ class AVFoundationVideoPlayer extends VideoPlayerPlatform { return player ?? (throw StateError('No active player with ID $id.')); } - static const Map _videoFormatStringMap = - { - VideoFormat.ss: 'ss', - VideoFormat.hls: 'hls', - VideoFormat.dash: 'dash', - VideoFormat.other: 'other', - }; - DurationRange _toDurationRange(dynamic value) { final List pair = value as List; + final int startMilliseconds = pair[0] as int; + final int durationMilliseconds = pair[1] as int; return DurationRange( - Duration(milliseconds: pair[0] as int), - Duration(milliseconds: pair[1] as int), + Duration(milliseconds: startMilliseconds), + Duration(milliseconds: startMilliseconds + durationMilliseconds), ); } } diff --git a/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart b/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart index cc184ae7db0..ae5ec1d9f6c 100644 --- a/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart +++ b/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart @@ -84,21 +84,12 @@ class PlatformVideoViewCreationParams { class CreationOptions { CreationOptions({ - this.asset, - this.uri, - this.packageName, - this.formatHint, + required this.uri, required this.httpHeaders, required this.viewType, }); - String? asset; - - String? uri; - - String? packageName; - - String? formatHint; + String uri; Map httpHeaders; @@ -106,10 +97,7 @@ class CreationOptions { List _toList() { return [ - asset, uri, - packageName, - formatHint, httpHeaders, viewType, ]; @@ -122,13 +110,10 @@ class CreationOptions { static CreationOptions decode(Object result) { result as List; return CreationOptions( - asset: result[0] as String?, - uri: result[1] as String?, - packageName: result[2] as String?, - formatHint: result[3] as String?, + uri: result[0]! as String, httpHeaders: - (result[4] as Map?)!.cast(), - viewType: result[5]! as PlatformVideoViewType, + (result[1] as Map?)!.cast(), + viewType: result[2]! as PlatformVideoViewType, ); } @@ -308,6 +293,32 @@ class AVFoundationVideoPlayerApi { return; } } + + Future getAssetUrl(String asset, String? package) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_avfoundation.AVFoundationVideoPlayerApi.getAssetUrl$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([asset, package]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return (pigeonVar_replyList[0] as String?); + } + } } class VideoPlayerInstanceApi { diff --git a/packages/video_player/video_player_avfoundation/pigeons/messages.dart b/packages/video_player/video_player_avfoundation/pigeons/messages.dart index c01fc094f1f..4b336cca5e6 100644 --- a/packages/video_player/video_player_avfoundation/pigeons/messages.dart +++ b/packages/video_player/video_player_avfoundation/pigeons/messages.dart @@ -34,14 +34,12 @@ class PlatformVideoViewCreationParams { class CreationOptions { CreationOptions({ + required this.uri, required this.httpHeaders, required this.viewType, }); - String? asset; - String? uri; - String? packageName; - String? formatHint; + String uri; Map httpHeaders; PlatformVideoViewType viewType; } @@ -57,6 +55,8 @@ abstract class AVFoundationVideoPlayerApi { void dispose(int playerId); @ObjCSelector('setMixWithOthers:') void setMixWithOthers(bool mixWithOthers); + @ObjCSelector('fileURLForAssetWithName:package:') + String? getAssetUrl(String asset, String? package); } @HostApi() diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index 1482e8a5457..29e56655cac 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avfoundation description: iOS and macOS implementation of the video_player plugin. repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.8.0 +version: 2.8.1 environment: sdk: ^3.6.0 diff --git a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart index 6862e999616..fb367310ec1 100644 --- a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart +++ b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart @@ -75,6 +75,11 @@ void main() { const String asset = 'someAsset'; const String package = 'somePackage'; + const String assetUrl = 'file:///some/asset/path'; + when( + api.getAssetUrl(asset, package), + ).thenAnswer((_) async => assetUrl); + final int? playerId = await player.create( DataSource( sourceType: DataSourceType.asset, @@ -86,13 +91,37 @@ void main() { final VerificationResult verification = verify(api.create(captureAny)); final CreationOptions creationOptions = verification.captured[0] as CreationOptions; - expect(creationOptions.asset, asset); - expect(creationOptions.packageName, package); + expect(creationOptions.uri, assetUrl); expect(playerId, newPlayerId); expect(player.playerViewStates[newPlayerId], const VideoPlayerTextureViewState(textureId: newPlayerId)); }); + test('create with asset throws PlatformException for missing asset', + () async { + final ( + AVFoundationVideoPlayer player, + MockAVFoundationVideoPlayerApi api, + _, + ) = setUpMockPlayer(playerId: 1); + + const String asset = 'someAsset'; + const String package = 'somePackage'; + when( + api.getAssetUrl(asset, package), + ).thenAnswer((_) async => null); + + expect( + player.create( + DataSource( + sourceType: DataSourceType.asset, + asset: asset, + package: package, + ), + ), + throwsA(isA())); + }); + test('create with network', () async { final ( AVFoundationVideoPlayer player, @@ -114,10 +143,7 @@ void main() { final VerificationResult verification = verify(api.create(captureAny)); final CreationOptions creationOptions = verification.captured[0] as CreationOptions; - expect(creationOptions.asset, null); expect(creationOptions.uri, uri); - expect(creationOptions.packageName, null); - expect(creationOptions.formatHint, 'dash'); expect(creationOptions.httpHeaders, {}); expect(playerId, newPlayerId); expect(player.playerViewStates[newPlayerId], @@ -181,6 +207,10 @@ void main() { const String asset = 'someAsset'; const String package = 'somePackage'; + const String assetUrl = 'file:///some/asset/path'; + when( + api.getAssetUrl(asset, package), + ).thenAnswer((_) async => assetUrl); final int? playerId = await player.createWithOptions( VideoCreationOptions( dataSource: DataSource( @@ -195,8 +225,7 @@ void main() { final VerificationResult verification = verify(api.create(captureAny)); final CreationOptions creationOptions = verification.captured[0] as CreationOptions; - expect(creationOptions.asset, asset); - expect(creationOptions.packageName, package); + expect(creationOptions.uri, assetUrl); expect(playerId, newPlayerId); expect(player.playerViewStates[newPlayerId], const VideoPlayerTextureViewState(textureId: newPlayerId)); @@ -226,10 +255,7 @@ void main() { final VerificationResult verification = verify(api.create(captureAny)); final CreationOptions creationOptions = verification.captured[0] as CreationOptions; - expect(creationOptions.asset, null); expect(creationOptions.uri, uri); - expect(creationOptions.packageName, null); - expect(creationOptions.formatHint, 'dash'); expect(creationOptions.httpHeaders, {}); expect(playerId, newPlayerId); expect(player.playerViewStates[newPlayerId], @@ -550,7 +576,7 @@ void main() { ), DurationRange( const Duration(milliseconds: 1235), - const Duration(milliseconds: 4000), + const Duration(milliseconds: 1235 + 4000), ), ]), VideoEvent(eventType: VideoEventType.bufferingStart), diff --git a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.mocks.dart b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.mocks.dart index 1e9968cf0d7..76199b56c02 100644 --- a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.mocks.dart +++ b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.mocks.dart @@ -81,6 +81,23 @@ class MockAVFoundationVideoPlayerApi extends _i1.Mock returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + + @override + _i4.Future getAssetUrl( + String? asset, + String? package, + ) => + (super.noSuchMethod( + Invocation.method( + #getAssetUrl, + [ + asset, + package, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); } /// A class which mocks [VideoPlayerInstanceApi]. diff --git a/script/tool/lib/src/common/core.dart b/script/tool/lib/src/common/core.dart index 484d864aecf..f4503693ad3 100644 --- a/script/tool/lib/src/common/core.dart +++ b/script/tool/lib/src/common/core.dart @@ -91,6 +91,7 @@ final Map _dartSdkForFlutterSdk = { Version(3, 29, 0): Version(3, 7, 0), Version(3, 29, 3): Version(3, 7, 2), Version(3, 32, 0): Version(3, 8, 0), + Version(3, 32, 8): Version(3, 8, 1), }; /// Returns the version of the Dart SDK that shipped with the given Flutter