From e6906e4d6e797b613289f1f035ea2b8e63a67b18 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 17 Sep 2025 10:20:14 -0400 Subject: [PATCH] [video_player] Separate texture ID on Android Cleans up the tech debt of having two different methods of generating player IDs on Andoird, as was recently done on iOS. The current code was a result of adding non-texture-based players without refactoring the Dart<->Java communication, and relied on knowing that the engine assigned texture IDs by increasing an incrementing value. This fully separates texture IDs from player IDs, so that the plugin can fully control player ID management without relying on engine internals. This also brings the Android and iOS implementations into better alignment, so that they don't have differences that aren't related to the platforms themselves. Follow-up to work done for https://github.com/flutter/flutter/issues/86613 Prep for https://github.com/flutter/flutter/issues/172763 --- .../video_player_android/CHANGELOG.md | 4 + .../flutter/plugins/videoplayer/Messages.java | 208 +++++++++++----- .../videoplayer/VideoPlayerPlugin.java | 95 ++++---- .../videoplayer/VideoPlayerPluginTest.java | 22 +- .../lib/src/android_video_player.dart | 72 +++--- .../lib/src/messages.g.dart | 116 +++++++-- .../pigeons/messages.dart | 21 +- .../video_player_android/pubspec.yaml | 2 +- .../test/android_video_player_test.dart | 229 +++++++++++------- .../test/android_video_player_test.mocks.dart | 47 +++- 10 files changed, 527 insertions(+), 289 deletions(-) diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index 52d6dca43664..e87cb3296b87 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.14 + +* Restructures internal logic for player creation and tracking. + ## 2.8.13 * Bumps com.android.tools.build:gradle to 8.12.1. diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java index ffd89e6137e0..eca51f7c2e95 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java @@ -65,18 +65,6 @@ protected static ArrayList wrapError(@NonNull Throwable exception) { @Retention(CLASS) @interface CanIgnoreReturnValue {} - /** Pigeon equivalent of VideoViewType. */ - public enum PlatformVideoViewType { - TEXTURE_VIEW(0), - PLATFORM_VIEW(1); - - final int index; - - PlatformVideoViewType(final int index) { - this.index = index; - } - } - /** Pigeon equivalent of video_platform_interface's VideoFormat. */ public enum PlatformVideoFormat { DASH(0), @@ -163,7 +151,7 @@ ArrayList toList() { } /** Generated class from Pigeon that represents data sent in messages. */ - public static final class CreateMessage { + public static final class CreationOptions { private @NonNull String uri; public @NonNull String getUri() { @@ -210,18 +198,8 @@ public void setUserAgent(@Nullable String setterArg) { this.userAgent = setterArg; } - private @Nullable PlatformVideoViewType viewType; - - public @Nullable PlatformVideoViewType getViewType() { - return viewType; - } - - public void setViewType(@Nullable PlatformVideoViewType setterArg) { - this.viewType = setterArg; - } - /** Constructor is non-public to enforce null safety; use Builder. */ - CreateMessage() {} + CreationOptions() {} @Override public boolean equals(Object o) { @@ -231,17 +209,16 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } - CreateMessage that = (CreateMessage) o; + CreationOptions that = (CreationOptions) o; return uri.equals(that.uri) && Objects.equals(formatHint, that.formatHint) && httpHeaders.equals(that.httpHeaders) - && Objects.equals(userAgent, that.userAgent) - && Objects.equals(viewType, that.viewType); + && Objects.equals(userAgent, that.userAgent); } @Override public int hashCode() { - return Objects.hash(uri, formatHint, httpHeaders, userAgent, viewType); + return Objects.hash(uri, formatHint, httpHeaders, userAgent); } public static final class Builder { @@ -278,38 +255,28 @@ public static final class Builder { return this; } - private @Nullable PlatformVideoViewType viewType; - - @CanIgnoreReturnValue - public @NonNull Builder setViewType(@Nullable PlatformVideoViewType setterArg) { - this.viewType = setterArg; - return this; - } - - public @NonNull CreateMessage build() { - CreateMessage pigeonReturn = new CreateMessage(); + public @NonNull CreationOptions build() { + CreationOptions pigeonReturn = new CreationOptions(); pigeonReturn.setUri(uri); pigeonReturn.setFormatHint(formatHint); pigeonReturn.setHttpHeaders(httpHeaders); pigeonReturn.setUserAgent(userAgent); - pigeonReturn.setViewType(viewType); return pigeonReturn; } } @NonNull ArrayList toList() { - ArrayList toListResult = new ArrayList<>(5); + ArrayList toListResult = new ArrayList<>(4); toListResult.add(uri); toListResult.add(formatHint); toListResult.add(httpHeaders); toListResult.add(userAgent); - toListResult.add(viewType); return toListResult; } - static @NonNull CreateMessage fromList(@NonNull ArrayList pigeonVar_list) { - CreateMessage pigeonResult = new CreateMessage(); + static @NonNull CreationOptions fromList(@NonNull ArrayList pigeonVar_list) { + CreationOptions pigeonResult = new CreationOptions(); Object uri = pigeonVar_list.get(0); pigeonResult.setUri((String) uri); Object formatHint = pigeonVar_list.get(1); @@ -318,8 +285,98 @@ ArrayList toList() { pigeonResult.setHttpHeaders((Map) httpHeaders); Object userAgent = pigeonVar_list.get(3); pigeonResult.setUserAgent((String) userAgent); - Object viewType = pigeonVar_list.get(4); - pigeonResult.setViewType((PlatformVideoViewType) viewType); + return pigeonResult; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class TexturePlayerIds { + private @NonNull Long playerId; + + public @NonNull Long getPlayerId() { + return playerId; + } + + public void setPlayerId(@NonNull Long setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"playerId\" is null."); + } + this.playerId = setterArg; + } + + private @NonNull Long textureId; + + public @NonNull Long getTextureId() { + return textureId; + } + + public void setTextureId(@NonNull Long setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"textureId\" is null."); + } + this.textureId = setterArg; + } + + /** Constructor is non-public to enforce null safety; use Builder. */ + TexturePlayerIds() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TexturePlayerIds that = (TexturePlayerIds) o; + return playerId.equals(that.playerId) && textureId.equals(that.textureId); + } + + @Override + public int hashCode() { + return Objects.hash(playerId, textureId); + } + + public static final class Builder { + + private @Nullable Long playerId; + + @CanIgnoreReturnValue + public @NonNull Builder setPlayerId(@NonNull Long setterArg) { + this.playerId = setterArg; + return this; + } + + private @Nullable Long textureId; + + @CanIgnoreReturnValue + public @NonNull Builder setTextureId(@NonNull Long setterArg) { + this.textureId = setterArg; + return this; + } + + public @NonNull TexturePlayerIds build() { + TexturePlayerIds pigeonReturn = new TexturePlayerIds(); + pigeonReturn.setPlayerId(playerId); + pigeonReturn.setTextureId(textureId); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList<>(2); + toListResult.add(playerId); + toListResult.add(textureId); + return toListResult; + } + + static @NonNull TexturePlayerIds fromList(@NonNull ArrayList pigeonVar_list) { + TexturePlayerIds pigeonResult = new TexturePlayerIds(); + Object playerId = pigeonVar_list.get(0); + pigeonResult.setPlayerId((Long) playerId); + Object textureId = pigeonVar_list.get(1); + pigeonResult.setTextureId((Long) textureId); return pigeonResult; } } @@ -427,19 +484,16 @@ private PigeonCodec() {} protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { switch (type) { case (byte) 129: - { - Object value = readValue(buffer); - return value == null ? null : PlatformVideoViewType.values()[((Long) value).intValue()]; - } - case (byte) 130: { Object value = readValue(buffer); return value == null ? null : PlatformVideoFormat.values()[((Long) value).intValue()]; } - case (byte) 131: + case (byte) 130: return PlatformVideoViewCreationParams.fromList((ArrayList) readValue(buffer)); + case (byte) 131: + return CreationOptions.fromList((ArrayList) readValue(buffer)); case (byte) 132: - return CreateMessage.fromList((ArrayList) readValue(buffer)); + return TexturePlayerIds.fromList((ArrayList) readValue(buffer)); case (byte) 133: return PlaybackState.fromList((ArrayList) readValue(buffer)); default: @@ -449,18 +503,18 @@ protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { @Override protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { - if (value instanceof PlatformVideoViewType) { + if (value instanceof PlatformVideoFormat) { stream.write(129); - writeValue(stream, value == null ? null : ((PlatformVideoViewType) value).index); - } else if (value instanceof PlatformVideoFormat) { - stream.write(130); writeValue(stream, value == null ? null : ((PlatformVideoFormat) value).index); } else if (value instanceof PlatformVideoViewCreationParams) { - stream.write(131); + stream.write(130); writeValue(stream, ((PlatformVideoViewCreationParams) value).toList()); - } else if (value instanceof CreateMessage) { + } else if (value instanceof CreationOptions) { + stream.write(131); + writeValue(stream, ((CreationOptions) value).toList()); + } else if (value instanceof TexturePlayerIds) { stream.write(132); - writeValue(stream, ((CreateMessage) value).toList()); + writeValue(stream, ((TexturePlayerIds) value).toList()); } else if (value instanceof PlaybackState) { stream.write(133); writeValue(stream, ((PlaybackState) value).toList()); @@ -476,7 +530,10 @@ public interface AndroidVideoPlayerApi { void initialize(); @NonNull - Long create(@NonNull CreateMessage msg); + Long createForPlatformView(@NonNull CreationOptions options); + + @NonNull + TexturePlayerIds createForTextureView(@NonNull CreationOptions options); void dispose(@NonNull Long playerId); @@ -530,7 +587,32 @@ static void setUp( BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.create" + "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.createForPlatformView" + + messageChannelSuffix, + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList<>(); + ArrayList args = (ArrayList) message; + CreationOptions optionsArg = (CreationOptions) args.get(0); + try { + Long output = api.createForPlatformView(optionsArg); + wrapped.add(0, output); + } catch (Throwable exception) { + wrapped = wrapError(exception); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.createForTextureView" + messageChannelSuffix, getCodec()); if (api != null) { @@ -538,9 +620,9 @@ static void setUp( (message, reply) -> { ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; - CreateMessage msgArg = (CreateMessage) args.get(0); + CreationOptions optionsArg = (CreationOptions) args.get(0); try { - Long output = api.create(msgArg); + TexturePlayerIds output = api.createForTextureView(optionsArg); wrapped.add(0, output); } catch (Throwable exception) { wrapped = wrapError(exception); diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java index 672c297c5a04..cf3295528f32 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java @@ -14,8 +14,9 @@ import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.EventChannel; import io.flutter.plugins.videoplayer.Messages.AndroidVideoPlayerApi; -import io.flutter.plugins.videoplayer.Messages.CreateMessage; +import io.flutter.plugins.videoplayer.Messages.CreationOptions; import io.flutter.plugins.videoplayer.Messages.PlatformVideoFormat; +import io.flutter.plugins.videoplayer.Messages.TexturePlayerIds; import io.flutter.plugins.videoplayer.Messages.VideoPlayerInstanceApi; import io.flutter.plugins.videoplayer.platformview.PlatformVideoViewFactory; import io.flutter.plugins.videoplayer.platformview.PlatformViewVideoPlayer; @@ -27,14 +28,8 @@ public class VideoPlayerPlugin implements FlutterPlugin, AndroidVideoPlayerApi { private static final String TAG = "VideoPlayerPlugin"; private final LongSparseArray videoPlayers = new LongSparseArray<>(); private FlutterState flutterState; - private final VideoPlayerOptions options = new VideoPlayerOptions(); - - // TODO(stuartmorgan): Decouple identifiers for platform views and texture views. - /** - * The next non-texture player ID, initialized to a high number to avoid collisions with texture - * IDs (which are generated separately). - */ - private Long nextPlatformViewPlayerId = Long.MAX_VALUE; + private final VideoPlayerOptions sharedOptions = new VideoPlayerOptions(); + private long nextPlayerIdentifier = 1; /** Register this with the v2 embedding for the plugin to respond to lifecycle callbacks. */ public VideoPlayerPlugin() {} @@ -90,16 +85,48 @@ public void initialize() { } @Override - public @NonNull Long create(@NonNull CreateMessage arg) { - final @NonNull String uri = arg.getUri(); - final VideoAsset videoAsset; + public @NonNull Long createForPlatformView(@NonNull CreationOptions options) { + final VideoAsset videoAsset = videoAssetWithOptions(options); + + long id = nextPlayerIdentifier++; + VideoPlayer videoPlayer = + PlatformViewVideoPlayer.create( + flutterState.applicationContext, + VideoPlayerEventCallbacks.bindTo(createEventChannel(id)), + videoAsset, + sharedOptions); + + registerPlayerInstance(videoPlayer, id); + return id; + } + + @Override + public @NonNull TexturePlayerIds createForTextureView(@NonNull CreationOptions options) { + final VideoAsset videoAsset = videoAssetWithOptions(options); + + long id = nextPlayerIdentifier++; + TextureRegistry.SurfaceProducer handle = flutterState.textureRegistry.createSurfaceProducer(); + VideoPlayer videoPlayer = + TextureVideoPlayer.create( + flutterState.applicationContext, + VideoPlayerEventCallbacks.bindTo(createEventChannel(id)), + handle, + videoAsset, + sharedOptions); + + registerPlayerInstance(videoPlayer, id); + return new TexturePlayerIds.Builder().setPlayerId(id).setTextureId(handle.id()).build(); + } + + private @NonNull VideoAsset videoAssetWithOptions(@NonNull CreationOptions options) { + final @NonNull String uri = options.getUri(); if (uri.startsWith("asset:")) { - videoAsset = VideoAsset.fromAssetUrl(uri); + return VideoAsset.fromAssetUrl(uri); } else if (uri.startsWith("rtsp:")) { - videoAsset = VideoAsset.fromRtspUrl(uri); + return VideoAsset.fromRtspUrl(uri); } else { VideoAsset.StreamingFormat streamingFormat = VideoAsset.StreamingFormat.UNKNOWN; - PlatformVideoFormat formatHint = arg.getFormatHint(); + PlatformVideoFormat formatHint = options.getFormatHint(); if (formatHint != null) { switch (formatHint) { case SS: @@ -113,42 +140,20 @@ public void initialize() { break; } } - videoAsset = - VideoAsset.fromRemoteUrl(uri, streamingFormat, arg.getHttpHeaders(), arg.getUserAgent()); - } - - long id; - VideoPlayer videoPlayer; - if (arg.getViewType() == Messages.PlatformVideoViewType.PLATFORM_VIEW) { - id = nextPlatformViewPlayerId--; - videoPlayer = - PlatformViewVideoPlayer.create( - flutterState.applicationContext, - VideoPlayerEventCallbacks.bindTo(createEventChannel(id)), - videoAsset, - options); - } else { - TextureRegistry.SurfaceProducer handle = flutterState.textureRegistry.createSurfaceProducer(); - id = handle.id(); - videoPlayer = - TextureVideoPlayer.create( - flutterState.applicationContext, - VideoPlayerEventCallbacks.bindTo(createEventChannel(id)), - handle, - videoAsset, - options); + return VideoAsset.fromRemoteUrl( + uri, streamingFormat, options.getHttpHeaders(), options.getUserAgent()); } + } + private void registerPlayerInstance(VideoPlayer player, long id) { // Set up the instance-specific API handler, and make sure it is removed when the player is // disposed. BinaryMessenger messenger = flutterState.binaryMessenger; final String channelSuffix = Long.toString(id); - VideoPlayerInstanceApi.setUp(messenger, channelSuffix, videoPlayer); - videoPlayer.setDisposeHandler( - () -> VideoPlayerInstanceApi.setUp(messenger, channelSuffix, null)); + VideoPlayerInstanceApi.setUp(messenger, channelSuffix, player); + player.setDisposeHandler(() -> VideoPlayerInstanceApi.setUp(messenger, channelSuffix, null)); - videoPlayers.put(id, videoPlayer); - return id; + videoPlayers.put(id, player); } @NonNull @@ -182,7 +187,7 @@ public void dispose(@NonNull Long playerId) { @Override public void setMixWithOthers(@NonNull Boolean mixWithOthers) { - options.mixWithOthers = mixWithOthers; + sharedOptions.mixWithOthers = mixWithOthers; } @Override diff --git a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerPluginTest.java b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerPluginTest.java index 4a5e9135ca31..d7781175eed3 100644 --- a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerPluginTest.java +++ b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerPluginTest.java @@ -11,8 +11,8 @@ import android.util.LongSparseArray; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.plugin.platform.PlatformViewRegistry; -import io.flutter.plugins.videoplayer.Messages.CreateMessage; -import io.flutter.plugins.videoplayer.Messages.PlatformVideoViewType; +import io.flutter.plugins.videoplayer.Messages.CreationOptions; +import io.flutter.plugins.videoplayer.Messages.TexturePlayerIds; import io.flutter.plugins.videoplayer.platformview.PlatformVideoViewFactory; import io.flutter.plugins.videoplayer.platformview.PlatformViewVideoPlayer; import io.flutter.plugins.videoplayer.texture.TextureVideoPlayer; @@ -78,17 +78,16 @@ public void createsPlatformViewVideoPlayer() throws Exception { .when(() -> PlatformViewVideoPlayer.create(any(), any(), any(), any())) .thenReturn(mock(PlatformViewVideoPlayer.class)); - final CreateMessage createMessage = - new CreateMessage.Builder() - .setViewType(PlatformVideoViewType.PLATFORM_VIEW) + final CreationOptions options = + new CreationOptions.Builder() .setUri("https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4") .setHttpHeaders(new HashMap<>()) .build(); - final long playerId = plugin.create(createMessage); + final long playerId = plugin.createForPlatformView(options); final LongSparseArray videoPlayers = getVideoPlayers(); - assertNotNull(videoPlayers.get(playerId)); + assertTrue(videoPlayers.get(playerId) instanceof PlatformViewVideoPlayer); } } @@ -100,17 +99,16 @@ public void createsTextureVideoPlayer() throws Exception { .when(() -> TextureVideoPlayer.create(any(), any(), any(), any(), any())) .thenReturn(mock(TextureVideoPlayer.class)); - final CreateMessage createMessage = - new CreateMessage.Builder() - .setViewType(PlatformVideoViewType.TEXTURE_VIEW) + final CreationOptions options = + new CreationOptions.Builder() .setUri("https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4") .setHttpHeaders(new HashMap<>()) .build(); - final long playerId = plugin.create(createMessage); + final TexturePlayerIds ids = plugin.createForTextureView(options); final LongSparseArray videoPlayers = getVideoPlayers(); - assertTrue(videoPlayers.get(playerId) instanceof TextureVideoPlayer); + assertTrue(videoPlayers.get(ids.getPlayerId()) instanceof TextureVideoPlayer); } } } diff --git a/packages/video_player/video_player_android/lib/src/android_video_player.dart b/packages/video_player/video_player_android/lib/src/android_video_player.dart index a3f147c31de7..bccdf020fa9a 100644 --- a/packages/video_player/video_player_android/lib/src/android_video_player.dart +++ b/packages/video_player/video_player_android/lib/src/android_video_player.dart @@ -29,14 +29,14 @@ class AndroidVideoPlayer extends VideoPlayerPlatform { AndroidVideoPlayer({ @visibleForTesting AndroidVideoPlayerApi? pluginApi, @visibleForTesting - VideoPlayerInstanceApi Function(int playerId)? playerProvider, + VideoPlayerInstanceApi Function(int playerId)? playerApiProvider, }) : _api = pluginApi ?? AndroidVideoPlayerApi(), - _playerProvider = playerProvider ?? _productionApiProvider; + _playerApiProvider = playerApiProvider ?? _productionApiProvider; final AndroidVideoPlayerApi _api; // A method to create VideoPlayerInstanceApi instances, which can be //overridden for testing. - final VideoPlayerInstanceApi Function(int playerId) _playerProvider; + final VideoPlayerInstanceApi Function(int playerId) _playerApiProvider; final Map _players = {}; @@ -100,16 +100,27 @@ class AndroidVideoPlayer extends VideoPlayerPlatform { if (uri == null) { throw ArgumentError('Unable to construct a video asset from $options'); } - final CreateMessage message = CreateMessage( + final CreationOptions pigeonCreationOptions = CreationOptions( uri: uri, httpHeaders: httpHeaders, userAgent: userAgent, formatHint: formatHint, - viewType: _platformVideoViewTypeFromVideoViewType(options.viewType), ); - final int playerId = await _api.create(message); - ensureApiInitialized(playerId, options.viewType); + final int playerId; + final VideoPlayerViewState state; + switch (options.viewType) { + case VideoViewType.textureView: + final TexturePlayerIds ids = await _api.createForTextureView( + pigeonCreationOptions, + ); + playerId = ids.playerId; + state = VideoPlayerTextureViewState(textureId: ids.textureId); + case VideoViewType.platformView: + playerId = await _api.createForPlatformView(pigeonCreationOptions); + state = const VideoPlayerPlatformViewState(); + } + ensurePlayerInitialized(playerId, state); return playerId; } @@ -130,18 +141,11 @@ class AndroidVideoPlayer extends VideoPlayerPlatform { /// Returns the player instance for [playerId], creating it if it doesn't /// already exist. @visibleForTesting - void ensureApiInitialized(int playerId, VideoViewType viewType) { + void ensurePlayerInitialized(int playerId, VideoPlayerViewState viewState) { _players.putIfAbsent(playerId, () { - final _VideoPlayerViewState viewState = switch (viewType) { - // playerId is also the textureId when using texture view. - VideoViewType.textureView => _VideoPlayerTextureViewState( - textureId: playerId, - ), - VideoViewType.platformView => const _VideoPlayerPlatformViewState(), - }; final String eventChannelName = '$_videoEventChannelNameBase$playerId'; return _PlayerInstance( - _playerProvider(playerId), + _playerApiProvider(playerId), viewState, eventChannelName: eventChannelName, ); @@ -198,13 +202,13 @@ class AndroidVideoPlayer extends VideoPlayerPlatform { @override Widget buildViewWithOptions(VideoViewOptions options) { final int playerId = options.playerId; - final _VideoPlayerViewState viewState = _playerWith(id: playerId).viewState; + final VideoPlayerViewState viewState = _playerWith(id: playerId).viewState; return switch (viewState) { - _VideoPlayerTextureViewState(:final int textureId) => Texture( + VideoPlayerTextureViewState(:final int textureId) => Texture( textureId: textureId, ), - _VideoPlayerPlatformViewState() => PlatformViewPlayer(playerId: playerId), + VideoPlayerPlatformViewState() => PlatformViewPlayer(playerId: playerId), }; } @@ -233,15 +237,6 @@ class AndroidVideoPlayer extends VideoPlayerPlatform { } } -PlatformVideoViewType _platformVideoViewTypeFromVideoViewType( - VideoViewType viewType, -) { - return switch (viewType) { - VideoViewType.textureView => PlatformVideoViewType.textureView, - VideoViewType.platformView => PlatformVideoViewType.platformView, - }; -} - /// An instance of a video player, corresponding to a single player ID in /// [AndroidVideoPlayer]. class _PlayerInstance { @@ -268,7 +263,7 @@ class _PlayerInstance { late final StreamSubscription _eventSubscription; int _lastBufferPosition = -1; - final _VideoPlayerViewState viewState; + final VideoPlayerViewState viewState; Future setLooping(bool looping) { return _api.setLooping(looping); @@ -367,22 +362,25 @@ class _PlayerInstance { } /// Base class representing the state of a video player view. +@visibleForTesting @immutable -sealed class _VideoPlayerViewState { - const _VideoPlayerViewState(); +sealed class VideoPlayerViewState { + const VideoPlayerViewState(); } /// Represents the state of a video player view that uses a texture. -final class _VideoPlayerTextureViewState extends _VideoPlayerViewState { - /// Creates a new instance of [_VideoPlayerTextureViewState]. - const _VideoPlayerTextureViewState({required this.textureId}); +@visibleForTesting +final class VideoPlayerTextureViewState extends VideoPlayerViewState { + /// Creates a new instance of [VideoPlayerTextureViewState]. + const VideoPlayerTextureViewState({required this.textureId}); /// The ID of the texture used by the video player. final int textureId; } /// Represents the state of a video player view that uses a platform view. -final class _VideoPlayerPlatformViewState extends _VideoPlayerViewState { - /// Creates a new instance of [_VideoPlayerPlatformViewState]. - const _VideoPlayerPlatformViewState(); +@visibleForTesting +final class VideoPlayerPlatformViewState extends VideoPlayerViewState { + /// Creates a new instance of [VideoPlayerPlatformViewState]. + const VideoPlayerPlatformViewState(); } diff --git a/packages/video_player/video_player_android/lib/src/messages.g.dart b/packages/video_player/video_player_android/lib/src/messages.g.dart index e576b0336a48..b05ea72e6d59 100644 --- a/packages/video_player/video_player_android/lib/src/messages.g.dart +++ b/packages/video_player/video_player_android/lib/src/messages.g.dart @@ -36,9 +36,6 @@ bool _deepEquals(Object? a, Object? b) { return a == b; } -/// Pigeon equivalent of VideoViewType. -enum PlatformVideoViewType { textureView, platformView } - /// Pigeon equivalent of video_platform_interface's VideoFormat. enum PlatformVideoFormat { dash, hls, ss } @@ -79,13 +76,12 @@ class PlatformVideoViewCreationParams { int get hashCode => Object.hashAll(_toList()); } -class CreateMessage { - CreateMessage({ +class CreationOptions { + CreationOptions({ required this.uri, this.formatHint, required this.httpHeaders, this.userAgent, - this.viewType, }); String uri; @@ -96,32 +92,69 @@ class CreateMessage { String? userAgent; - PlatformVideoViewType? viewType; - List _toList() { - return [uri, formatHint, httpHeaders, userAgent, viewType]; + return [uri, formatHint, httpHeaders, userAgent]; } Object encode() { return _toList(); } - static CreateMessage decode(Object result) { + static CreationOptions decode(Object result) { result as List; - return CreateMessage( + return CreationOptions( uri: result[0]! as String, formatHint: result[1] as PlatformVideoFormat?, httpHeaders: (result[2] as Map?)!.cast(), userAgent: result[3] as String?, - viewType: result[4] as PlatformVideoViewType?, ); } @override // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(Object other) { - if (other is! CreateMessage || other.runtimeType != runtimeType) { + if (other is! CreationOptions || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()); +} + +class TexturePlayerIds { + TexturePlayerIds({required this.playerId, required this.textureId}); + + int playerId; + + int textureId; + + List _toList() { + return [playerId, textureId]; + } + + Object encode() { + return _toList(); + } + + static TexturePlayerIds decode(Object result) { + result as List; + return TexturePlayerIds( + playerId: result[0]! as int, + textureId: result[1]! as int, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! TexturePlayerIds || other.runtimeType != runtimeType) { return false; } if (identical(this, other)) { @@ -184,16 +217,16 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is PlatformVideoViewType) { - buffer.putUint8(129); - writeValue(buffer, value.index); } else if (value is PlatformVideoFormat) { - buffer.putUint8(130); + buffer.putUint8(129); writeValue(buffer, value.index); } else if (value is PlatformVideoViewCreationParams) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is CreationOptions) { buffer.putUint8(131); writeValue(buffer, value.encode()); - } else if (value is CreateMessage) { + } else if (value is TexturePlayerIds) { buffer.putUint8(132); writeValue(buffer, value.encode()); } else if (value is PlaybackState) { @@ -208,15 +241,14 @@ class _PigeonCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 129: - final int? value = readValue(buffer) as int?; - return value == null ? null : PlatformVideoViewType.values[value]; - case 130: final int? value = readValue(buffer) as int?; return value == null ? null : PlatformVideoFormat.values[value]; - case 131: + case 130: return PlatformVideoViewCreationParams.decode(readValue(buffer)!); + case 131: + return CreationOptions.decode(readValue(buffer)!); case 132: - return CreateMessage.decode(readValue(buffer)!); + return TexturePlayerIds.decode(readValue(buffer)!); case 133: return PlaybackState.decode(readValue(buffer)!); default: @@ -266,9 +298,9 @@ class AndroidVideoPlayerApi { } } - Future create(CreateMessage msg) async { + Future createForPlatformView(CreationOptions options) async { final String pigeonVar_channelName = - 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.create$pigeonVar_messageChannelSuffix'; + 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.createForPlatformView$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, @@ -276,7 +308,7 @@ class AndroidVideoPlayerApi { binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send( - [msg], + [options], ); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; @@ -298,6 +330,38 @@ class AndroidVideoPlayerApi { } } + Future createForTextureView(CreationOptions options) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.createForTextureView$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [options], + ); + 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 if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as TexturePlayerIds?)!; + } + } + Future dispose(int playerId) async { final String pigeonVar_channelName = 'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.dispose$pigeonVar_messageChannelSuffix'; diff --git a/packages/video_player/video_player_android/pigeons/messages.dart b/packages/video_player/video_player_android/pigeons/messages.dart index b2246ec6d339..ddb72d66a372 100644 --- a/packages/video_player/video_player_android/pigeons/messages.dart +++ b/packages/video_player/video_player_android/pigeons/messages.dart @@ -13,9 +13,6 @@ import 'package:pigeon/pigeon.dart'; copyrightHeader: 'pigeons/copyright.txt', ), ) -/// Pigeon equivalent of VideoViewType. -enum PlatformVideoViewType { textureView, platformView } - /// Pigeon equivalent of video_platform_interface's VideoFormat. enum PlatformVideoFormat { dash, hls, ss } @@ -26,13 +23,19 @@ class PlatformVideoViewCreationParams { final int playerId; } -class CreateMessage { - CreateMessage({required this.uri, required this.httpHeaders}); +class CreationOptions { + CreationOptions({required this.uri, required this.httpHeaders}); String uri; PlatformVideoFormat? formatHint; Map httpHeaders; String? userAgent; - PlatformVideoViewType? viewType; +} + +class TexturePlayerIds { + TexturePlayerIds({required this.playerId, required this.textureId}); + + final int playerId; + final int textureId; } class PlaybackState { @@ -48,7 +51,11 @@ class PlaybackState { @HostApi() abstract class AndroidVideoPlayerApi { void initialize(); - int create(CreateMessage msg); + // Creates a new player using a platform view for rendering and returns its + // ID. + int createForPlatformView(CreationOptions options); + // Creates a new player using a texture for rendering and returns its IDs. + TexturePlayerIds createForTextureView(CreationOptions options); void dispose(int playerId); void setMixWithOthers(bool mixWithOthers); String getLookupKeyForAsset(String asset, String? packageName); diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index 00129de08bbb..ac12f3df83b0 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_android description: Android implementation of the video_player plugin. repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.8.13 +version: 2.8.14 environment: sdk: ^3.7.0 diff --git a/packages/video_player/video_player_android/test/android_video_player_test.dart b/packages/video_player/video_player_android/test/android_video_player_test.dart index ac32c50496d1..0dcaefebe9b3 100644 --- a/packages/video_player/video_player_android/test/android_video_player_test.dart +++ b/packages/video_player/video_player_android/test/android_video_player_test.dart @@ -22,14 +22,19 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); (AndroidVideoPlayer, MockAndroidVideoPlayerApi, MockVideoPlayerInstanceApi) - setUpMockPlayer({required int playerId}) { + setUpMockPlayer({required int playerId, int? textureId}) { final MockAndroidVideoPlayerApi pluginApi = MockAndroidVideoPlayerApi(); final MockVideoPlayerInstanceApi instanceApi = MockVideoPlayerInstanceApi(); final AndroidVideoPlayer player = AndroidVideoPlayer( pluginApi: pluginApi, - playerProvider: (_) => instanceApi, + playerApiProvider: (_) => instanceApi, + ); + player.ensurePlayerInitialized( + playerId, + textureId == null + ? const VideoPlayerPlatformViewState() + : VideoPlayerTextureViewState(textureId: textureId), ); - player.ensureApiInitialized(playerId, VideoViewType.platformView); return (player, pluginApi, instanceApi); } @@ -66,9 +71,11 @@ void main() { AndroidVideoPlayer player, MockAndroidVideoPlayerApi api, _, - ) = setUpMockPlayer(playerId: 1); + ) = setUpMockPlayer(playerId: 1, textureId: 100); const int newPlayerId = 2; - when(api.create(any)).thenAnswer((_) async => newPlayerId); + when(api.createForTextureView(any)).thenAnswer( + (_) async => TexturePlayerIds(playerId: newPlayerId, textureId: 100), + ); const String asset = 'someAsset'; const String package = 'somePackage'; @@ -85,10 +92,12 @@ void main() { ), ); - final VerificationResult verification = verify(api.create(captureAny)); - final CreateMessage createMessage = - verification.captured[0] as CreateMessage; - expect(createMessage.uri, 'asset:///$assetKey'); + final VerificationResult verification = verify( + api.createForTextureView(captureAny), + ); + final CreationOptions creationOptions = + verification.captured[0] as CreationOptions; + expect(creationOptions.uri, 'asset:///$assetKey'); expect(playerId, newPlayerId); expect( player.buildViewWithOptions(VideoViewOptions(playerId: playerId!)), @@ -101,9 +110,11 @@ void main() { AndroidVideoPlayer player, MockAndroidVideoPlayerApi api, _, - ) = setUpMockPlayer(playerId: 1); + ) = setUpMockPlayer(playerId: 1, textureId: 100); const int newPlayerId = 2; - when(api.create(any)).thenAnswer((_) async => newPlayerId); + when(api.createForTextureView(any)).thenAnswer( + (_) async => TexturePlayerIds(playerId: newPlayerId, textureId: 100), + ); const String uri = 'https://example.com'; final int? playerId = await player.create( @@ -114,12 +125,14 @@ void main() { ), ); - final VerificationResult verification = verify(api.create(captureAny)); - final CreateMessage createMessage = - verification.captured[0] as CreateMessage; - expect(createMessage.uri, uri); - expect(createMessage.formatHint, PlatformVideoFormat.dash); - expect(createMessage.httpHeaders, {}); + final VerificationResult verification = verify( + api.createForTextureView(captureAny), + ); + final CreationOptions creationOptions = + verification.captured[0] as CreationOptions; + expect(creationOptions.uri, uri); + expect(creationOptions.formatHint, PlatformVideoFormat.dash); + expect(creationOptions.httpHeaders, {}); expect(playerId, newPlayerId); expect( player.buildViewWithOptions(VideoViewOptions(playerId: playerId!)), @@ -132,8 +145,10 @@ void main() { AndroidVideoPlayer player, MockAndroidVideoPlayerApi api, _, - ) = setUpMockPlayer(playerId: 1); - when(api.create(any)).thenAnswer((_) async => 2); + ) = setUpMockPlayer(playerId: 1, textureId: 100); + when( + api.createForTextureView(any), + ).thenAnswer((_) async => TexturePlayerIds(playerId: 2, textureId: 100)); const Map headers = { 'Authorization': 'Bearer token', @@ -145,10 +160,12 @@ void main() { httpHeaders: headers, ), ); - final VerificationResult verification = verify(api.create(captureAny)); - final CreateMessage createMessage = - verification.captured[0] as CreateMessage; - expect(createMessage.httpHeaders, headers); + final VerificationResult verification = verify( + api.createForTextureView(captureAny), + ); + final CreationOptions creationOptions = + verification.captured[0] as CreationOptions; + expect(creationOptions.httpHeaders, headers); }); test('create with network sets a default user agent', () async { @@ -156,8 +173,10 @@ void main() { AndroidVideoPlayer player, MockAndroidVideoPlayerApi api, _, - ) = setUpMockPlayer(playerId: 1); - when(api.create(any)).thenAnswer((_) async => 2); + ) = setUpMockPlayer(playerId: 1, textureId: 100); + when( + api.createForTextureView(any), + ).thenAnswer((_) async => TexturePlayerIds(playerId: 2, textureId: 100)); await player.create( DataSource( @@ -166,10 +185,12 @@ void main() { httpHeaders: {}, ), ); - final VerificationResult verification = verify(api.create(captureAny)); - final CreateMessage createMessage = - verification.captured[0] as CreateMessage; - expect(createMessage.userAgent, 'ExoPlayer'); + final VerificationResult verification = verify( + api.createForTextureView(captureAny), + ); + final CreationOptions creationOptions = + verification.captured[0] as CreationOptions; + expect(creationOptions.userAgent, 'ExoPlayer'); }); test('create with network uses user agent from headers', () async { @@ -177,8 +198,10 @@ void main() { AndroidVideoPlayer player, MockAndroidVideoPlayerApi api, _, - ) = setUpMockPlayer(playerId: 1); - when(api.create(any)).thenAnswer((_) async => 2); + ) = setUpMockPlayer(playerId: 1, textureId: 100); + when( + api.createForTextureView(any), + ).thenAnswer((_) async => TexturePlayerIds(playerId: 2, textureId: 100)); const String userAgent = 'Test User Agent'; const Map headers = { @@ -191,10 +214,12 @@ void main() { httpHeaders: headers, ), ); - final VerificationResult verification = verify(api.create(captureAny)); - final CreateMessage createMessage = - verification.captured[0] as CreateMessage; - expect(createMessage.userAgent, userAgent); + final VerificationResult verification = verify( + api.createForTextureView(captureAny), + ); + final CreationOptions creationOptions = + verification.captured[0] as CreationOptions; + expect(creationOptions.userAgent, userAgent); }); test('create with file', () async { @@ -202,17 +227,21 @@ void main() { AndroidVideoPlayer player, MockAndroidVideoPlayerApi api, _, - ) = setUpMockPlayer(playerId: 1); - when(api.create(any)).thenAnswer((_) async => 2); + ) = setUpMockPlayer(playerId: 1, textureId: 100); + when( + api.createForTextureView(any), + ).thenAnswer((_) async => TexturePlayerIds(playerId: 2, textureId: 100)); const String fileUri = 'file:///foo/bar'; final int? playerId = await player.create( DataSource(sourceType: DataSourceType.file, uri: fileUri), ); - final VerificationResult verification = verify(api.create(captureAny)); - final CreateMessage createMessage = - verification.captured[0] as CreateMessage; - expect(createMessage.uri, fileUri); + final VerificationResult verification = verify( + api.createForTextureView(captureAny), + ); + final CreationOptions creationOptions = + verification.captured[0] as CreationOptions; + expect(creationOptions.uri, fileUri); expect( player.buildViewWithOptions(VideoViewOptions(playerId: playerId!)), isA(), @@ -224,8 +253,10 @@ void main() { AndroidVideoPlayer player, MockAndroidVideoPlayerApi api, _, - ) = setUpMockPlayer(playerId: 1); - when(api.create(any)).thenAnswer((_) async => 2); + ) = setUpMockPlayer(playerId: 1, textureId: 100); + when( + api.createForTextureView(any), + ).thenAnswer((_) async => TexturePlayerIds(playerId: 2, textureId: 100)); const String fileUri = 'file:///foo/bar'; const Map headers = { @@ -238,10 +269,12 @@ void main() { httpHeaders: headers, ), ); - final VerificationResult verification = verify(api.create(captureAny)); - final CreateMessage createMessage = - verification.captured[0] as CreateMessage; - expect(createMessage.httpHeaders, headers); + final VerificationResult verification = verify( + api.createForTextureView(captureAny), + ); + final CreationOptions creationOptions = + verification.captured[0] as CreationOptions; + expect(creationOptions.httpHeaders, headers); }); test('createWithOptions with asset', () async { @@ -249,9 +282,11 @@ void main() { AndroidVideoPlayer player, MockAndroidVideoPlayerApi api, _, - ) = setUpMockPlayer(playerId: 1); + ) = setUpMockPlayer(playerId: 1, textureId: 100); const int newPlayerId = 2; - when(api.create(any)).thenAnswer((_) async => newPlayerId); + when(api.createForTextureView(any)).thenAnswer( + (_) async => TexturePlayerIds(playerId: newPlayerId, textureId: 100), + ); const String asset = 'someAsset'; const String package = 'somePackage'; @@ -271,10 +306,12 @@ void main() { ), ); - final VerificationResult verification = verify(api.create(captureAny)); - final CreateMessage createMessage = - verification.captured[0] as CreateMessage; - expect(createMessage.uri, 'asset:///$assetKey'); + final VerificationResult verification = verify( + api.createForTextureView(captureAny), + ); + final CreationOptions creationOptions = + verification.captured[0] as CreationOptions; + expect(creationOptions.uri, 'asset:///$assetKey'); expect(playerId, newPlayerId); expect( player.buildViewWithOptions(VideoViewOptions(playerId: playerId!)), @@ -287,9 +324,11 @@ void main() { AndroidVideoPlayer player, MockAndroidVideoPlayerApi api, _, - ) = setUpMockPlayer(playerId: 1); + ) = setUpMockPlayer(playerId: 1, textureId: 100); const int newPlayerId = 2; - when(api.create(any)).thenAnswer((_) async => newPlayerId); + when(api.createForTextureView(any)).thenAnswer( + (_) async => TexturePlayerIds(playerId: newPlayerId, textureId: 100), + ); const String uri = 'https://example.com'; final int? playerId = await player.createWithOptions( @@ -303,12 +342,14 @@ void main() { ), ); - final VerificationResult verification = verify(api.create(captureAny)); - final CreateMessage createMessage = - verification.captured[0] as CreateMessage; - expect(createMessage.uri, uri); - expect(createMessage.formatHint, PlatformVideoFormat.dash); - expect(createMessage.httpHeaders, {}); + final VerificationResult verification = verify( + api.createForTextureView(captureAny), + ); + final CreationOptions creationOptions = + verification.captured[0] as CreationOptions; + expect(creationOptions.uri, uri); + expect(creationOptions.formatHint, PlatformVideoFormat.dash); + expect(creationOptions.httpHeaders, {}); expect(playerId, newPlayerId); expect( player.buildViewWithOptions(VideoViewOptions(playerId: playerId!)), @@ -321,9 +362,11 @@ void main() { AndroidVideoPlayer player, MockAndroidVideoPlayerApi api, _, - ) = setUpMockPlayer(playerId: 1); + ) = setUpMockPlayer(playerId: 1, textureId: 100); const int newPlayerId = 2; - when(api.create(any)).thenAnswer((_) async => newPlayerId); + when(api.createForTextureView(any)).thenAnswer( + (_) async => TexturePlayerIds(playerId: newPlayerId, textureId: 100), + ); const Map headers = { 'Authorization': 'Bearer token', @@ -339,10 +382,12 @@ void main() { ), ); - final VerificationResult verification = verify(api.create(captureAny)); - final CreateMessage createMessage = - verification.captured[0] as CreateMessage; - expect(createMessage.httpHeaders, headers); + final VerificationResult verification = verify( + api.createForTextureView(captureAny), + ); + final CreationOptions creationOptions = + verification.captured[0] as CreationOptions; + expect(creationOptions.httpHeaders, headers); expect(playerId, newPlayerId); }); @@ -351,9 +396,11 @@ void main() { AndroidVideoPlayer player, MockAndroidVideoPlayerApi api, _, - ) = setUpMockPlayer(playerId: 1); + ) = setUpMockPlayer(playerId: 1, textureId: 100); const int newPlayerId = 2; - when(api.create(any)).thenAnswer((_) async => newPlayerId); + when(api.createForTextureView(any)).thenAnswer( + (_) async => TexturePlayerIds(playerId: newPlayerId, textureId: 100), + ); const String fileUri = 'file:///foo/bar'; final int? playerId = await player.createWithOptions( @@ -363,10 +410,12 @@ void main() { ), ); - final VerificationResult verification = verify(api.create(captureAny)); - final CreateMessage createMessage = - verification.captured[0] as CreateMessage; - expect(createMessage.uri, fileUri); + final VerificationResult verification = verify( + api.createForTextureView(captureAny), + ); + final CreationOptions creationOptions = + verification.captured[0] as CreationOptions; + expect(creationOptions.uri, fileUri); expect(playerId, newPlayerId); expect( player.buildViewWithOptions(VideoViewOptions(playerId: playerId!)), @@ -379,8 +428,10 @@ void main() { AndroidVideoPlayer player, MockAndroidVideoPlayerApi api, _, - ) = setUpMockPlayer(playerId: 1); - when(api.create(any)).thenAnswer((_) async => 2); + ) = setUpMockPlayer(playerId: 1, textureId: 100); + when( + api.createForTextureView(any), + ).thenAnswer((_) async => TexturePlayerIds(playerId: 2, textureId: 100)); const String fileUri = 'file:///foo/bar'; const Map headers = { @@ -397,10 +448,12 @@ void main() { ), ); - final VerificationResult verification = verify(api.create(captureAny)); - final CreateMessage createMessage = - verification.captured[0] as CreateMessage; - expect(createMessage.httpHeaders, headers); + final VerificationResult verification = verify( + api.createForTextureView(captureAny), + ); + final CreationOptions creationOptions = + verification.captured[0] as CreationOptions; + expect(creationOptions.httpHeaders, headers); }); test('createWithOptions with platform view', () async { @@ -410,22 +463,22 @@ void main() { _, ) = setUpMockPlayer(playerId: 1); const int newPlayerId = 2; - when(api.create(any)).thenAnswer((_) async => newPlayerId); + when(api.createForPlatformView(any)).thenAnswer((_) async => newPlayerId); + const String uri = 'file:///foo/bar'; final int? playerId = await player.createWithOptions( VideoCreationOptions( - dataSource: DataSource( - sourceType: DataSourceType.file, - uri: 'file:///foo/bar', - ), + dataSource: DataSource(sourceType: DataSourceType.file, uri: uri), viewType: VideoViewType.platformView, ), ); - final VerificationResult verification = verify(api.create(captureAny)); - final CreateMessage createMessage = - verification.captured[0] as CreateMessage; - expect(createMessage.viewType, PlatformVideoViewType.platformView); + final VerificationResult verification = verify( + api.createForPlatformView(captureAny), + ); + final CreationOptions creationOptions = + verification.captured[0] as CreationOptions; + expect(creationOptions.uri, uri); expect(playerId, newPlayerId); expect( player.buildViewWithOptions(VideoViewOptions(playerId: playerId!)), diff --git a/packages/video_player/video_player_android/test/android_video_player_test.mocks.dart b/packages/video_player/video_player_android/test/android_video_player_test.mocks.dart index 4d7f49d18deb..4bc36448aa45 100644 --- a/packages/video_player/video_player_android/test/android_video_player_test.mocks.dart +++ b/packages/video_player/video_player_android/test/android_video_player_test.mocks.dart @@ -23,8 +23,14 @@ import 'package:video_player_android/src/messages.g.dart' as _i2; // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class -class _FakePlaybackState_0 extends _i1.SmartFake implements _i2.PlaybackState { - _FakePlaybackState_0(Object parent, Invocation parentInvocation) +class _FakeTexturePlayerIds_0 extends _i1.SmartFake + implements _i2.TexturePlayerIds { + _FakeTexturePlayerIds_0(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} + +class _FakePlaybackState_1 extends _i1.SmartFake implements _i2.PlaybackState { + _FakePlaybackState_1(Object parent, Invocation parentInvocation) : super(parent, parentInvocation); } @@ -58,14 +64,35 @@ class MockAndroidVideoPlayerApi extends _i1.Mock as _i4.Future); @override - _i4.Future create(_i2.CreateMessage? msg) => + _i4.Future createForPlatformView(_i2.CreationOptions? options) => (super.noSuchMethod( - Invocation.method(#create, [msg]), + Invocation.method(#createForPlatformView, [options]), returnValue: _i4.Future.value(0), returnValueForMissingStub: _i4.Future.value(0), ) as _i4.Future); + @override + _i4.Future<_i2.TexturePlayerIds> createForTextureView( + _i2.CreationOptions? options, + ) => + (super.noSuchMethod( + Invocation.method(#createForTextureView, [options]), + returnValue: _i4.Future<_i2.TexturePlayerIds>.value( + _FakeTexturePlayerIds_0( + this, + Invocation.method(#createForTextureView, [options]), + ), + ), + returnValueForMissingStub: _i4.Future<_i2.TexturePlayerIds>.value( + _FakeTexturePlayerIds_0( + this, + Invocation.method(#createForTextureView, [options]), + ), + ), + ) + as _i4.Future<_i2.TexturePlayerIds>); + @override _i4.Future dispose(int? playerId) => (super.noSuchMethod( @@ -161,18 +188,18 @@ class MockVideoPlayerInstanceApi extends _i1.Mock as _i4.Future); @override - _i4.Future seekTo(int? position) => + _i4.Future pause() => (super.noSuchMethod( - Invocation.method(#seekTo, [position]), + Invocation.method(#pause, []), returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); @override - _i4.Future pause() => + _i4.Future seekTo(int? position) => (super.noSuchMethod( - Invocation.method(#pause, []), + Invocation.method(#seekTo, [position]), returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) @@ -183,13 +210,13 @@ class MockVideoPlayerInstanceApi extends _i1.Mock (super.noSuchMethod( Invocation.method(#getPlaybackState, []), returnValue: _i4.Future<_i2.PlaybackState>.value( - _FakePlaybackState_0( + _FakePlaybackState_1( this, Invocation.method(#getPlaybackState, []), ), ), returnValueForMissingStub: _i4.Future<_i2.PlaybackState>.value( - _FakePlaybackState_0( + _FakePlaybackState_1( this, Invocation.method(#getPlaybackState, []), ),