diff --git a/android/build.gradle b/android/build.gradle index d7c01a2..a5fd81e 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,52 +1,55 @@ -group 'io.flutter.plugins.videoplayer' +group 'android' version '1.0-SNAPSHOT' -def args = ["-Xlint:deprecation","-Xlint:unchecked","-Werror"] + +def args = ["-Xlint:deprecation", "-Xlint:unchecked"] buildscript { + ext.kotlin_version = '1.9.22' repositories { google() - jcenter() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:8.3.2' } } -rootProject.allprojects { +allprojects { repositories { google() - jcenter() + mavenCentral() } } -project.getTasks().withType(JavaCompile){ +tasks.withType(JavaCompile).configureEach { options.compilerArgs.addAll(args) } apply plugin: 'com.android.library' android { - compileSdkVersion 29 + namespace 'com.lazyarts.vikram.cached_video_player' + compileSdk 34 defaultConfig { - minSdkVersion 16 + minSdk 21 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } - lintOptions { - disable 'InvalidPackage' - } - android { - compileOptions { - sourceCompatibility 1.8 - targetCompatibility 1.8 - } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } - dependencies { - implementation 'com.google.android.exoplayer:exoplayer-core:2.17.1' - implementation 'com.google.android.exoplayer:exoplayer-hls:2.17.1' - implementation 'com.google.android.exoplayer:exoplayer-dash:2.17.1' - implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.17.1' + lint { + disable 'InvalidPackage' } } + +dependencies { + implementation 'com.google.android.exoplayer:exoplayer-core:2.19.1' + implementation 'com.google.android.exoplayer:exoplayer-hls:2.19.1' + implementation 'com.google.android.exoplayer:exoplayer-dash:2.19.1' + implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.19.1' +} \ No newline at end of file diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 9f96ce6..9c5194d 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Wed Oct 17 09:04:56 PDT 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip \ No newline at end of file diff --git a/android/settings.gradle b/android/settings.gradle index 077a2c3..3952bfc 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1 +1,15 @@ rootProject.name = 'cached_video_player' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +localPropertiesFile.withReader("UTF-8") { reader -> + properties.load(reader) +} + +def flutterSdkPath = properties.getProperty("flutter.sdk") +if (flutterSdkPath == null) { + throw new GradleException("flutter.sdk not set in local.properties") +} + +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" \ No newline at end of file diff --git a/android/src/main/java/com/lazyarts/vikram/cached_video_player/CacheDataSourceFactory.java b/android/src/main/java/com/lazyarts/vikram/cached_video_player/CacheDataSourceFactory.java index 470cc1c..463dc3b 100644 --- a/android/src/main/java/com/lazyarts/vikram/cached_video_player/CacheDataSourceFactory.java +++ b/android/src/main/java/com/lazyarts/vikram/cached_video_player/CacheDataSourceFactory.java @@ -2,6 +2,8 @@ import android.content.Context; +import androidx.annotation.NonNull; + import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; import com.google.android.exoplayer2.upstream.DefaultDataSource; @@ -15,10 +17,9 @@ class CacheDataSourceFactory implements DataSource.Factory { private final Context context; - private DefaultDataSource.Factory defaultDatasourceFactory; private final long maxFileSize, maxCacheSize; - private DefaultHttpDataSource.Factory defaultHttpDataSourceFactory; + private final DefaultHttpDataSource.Factory defaultHttpDataSourceFactory; CacheDataSourceFactory(Context context, long maxCacheSize, long maxFileSize) { super(); @@ -35,11 +36,12 @@ void setHeaders(Map httpHeaders) { defaultHttpDataSourceFactory.setDefaultRequestProperties(httpHeaders); } + @NonNull @Override public DataSource createDataSource() { DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter.Builder(context).build(); - defaultDatasourceFactory = new DefaultDataSource.Factory(this.context, defaultHttpDataSourceFactory); + DefaultDataSource.Factory defaultDatasourceFactory = new DefaultDataSource.Factory(this.context, defaultHttpDataSourceFactory); defaultDatasourceFactory.setTransferListener(bandwidthMeter); SimpleCache simpleCache = SimpleCacheSingleton.getInstance(context, maxCacheSize).simpleCache; diff --git a/android/src/main/java/com/lazyarts/vikram/cached_video_player/CachedVideoPlayer.java b/android/src/main/java/com/lazyarts/vikram/cached_video_player/CachedVideoPlayer.java index 768f85d..29e597f 100644 --- a/android/src/main/java/com/lazyarts/vikram/cached_video_player/CachedVideoPlayer.java +++ b/android/src/main/java/com/lazyarts/vikram/cached_video_player/CachedVideoPlayer.java @@ -18,7 +18,6 @@ import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.PlaybackException; -import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.audio.AudioAttributes; import com.google.android.exoplayer2.source.MediaSource; @@ -47,13 +46,13 @@ final class CachedVideoPlayer { private static final String FORMAT_HLS = "hls"; private static final String FORMAT_OTHER = "other"; - private ExoPlayer exoPlayer; + private final ExoPlayer exoPlayer; private Surface surface; private final TextureRegistry.SurfaceTextureEntry textureEntry; - private QueuingEventSink eventSink = new QueuingEventSink(); + private final QueuingEventSink eventSink = new QueuingEventSink(); private final EventChannel eventChannel; @@ -114,45 +113,32 @@ private MediaSource buildMediaSource( if (formatHint == null) { type = Util.inferContentType(uri.getLastPathSegment()); } else { - switch (formatHint) { - case FORMAT_SS: - type = C.TYPE_SS; - break; - case FORMAT_DASH: - type = C.TYPE_DASH; - break; - case FORMAT_HLS: - type = C.TYPE_HLS; - break; - case FORMAT_OTHER: - type = C.TYPE_OTHER; - break; - default: - type = -1; - break; - } - } - switch (type) { - case C.TYPE_SS: - return new SsMediaSource.Factory( - new DefaultSsChunkSource.Factory(mediaDataSourceFactory), - new DefaultDataSource.Factory(context, mediaDataSourceFactory)) - .createMediaSource(MediaItem.fromUri(uri)); - case C.TYPE_DASH: - return new DashMediaSource.Factory( - new DefaultDashChunkSource.Factory(mediaDataSourceFactory), - new DefaultDataSource.Factory(context, mediaDataSourceFactory)) - .createMediaSource(MediaItem.fromUri(uri)); - case C.TYPE_HLS: - return new HlsMediaSource.Factory(mediaDataSourceFactory) - .createMediaSource(MediaItem.fromUri(uri)); - case C.TYPE_OTHER: - return new ProgressiveMediaSource.Factory(mediaDataSourceFactory) - .createMediaSource(MediaItem.fromUri(uri)); - default: { - throw new IllegalStateException("Unsupported type: " + type); - } + type = switch (formatHint) { + case FORMAT_SS -> C.TYPE_SS; + case FORMAT_DASH -> C.TYPE_DASH; + case FORMAT_HLS -> C.TYPE_HLS; + case FORMAT_OTHER -> C.TYPE_OTHER; + default -> -1; + }; } + return switch (type) { + case C.TYPE_SS -> new SsMediaSource.Factory( + new DefaultSsChunkSource.Factory(mediaDataSourceFactory), + new DefaultDataSource.Factory(context, mediaDataSourceFactory)) + .createMediaSource(MediaItem.fromUri(uri)); + case C.TYPE_DASH -> new DashMediaSource.Factory( + new DefaultDashChunkSource.Factory(mediaDataSourceFactory), + new DefaultDataSource.Factory(context, mediaDataSourceFactory)) + .createMediaSource(MediaItem.fromUri(uri)); + case C.TYPE_HLS -> + new HlsMediaSource.Factory(mediaDataSourceFactory) + .createMediaSource(MediaItem.fromUri(uri)); + case C.TYPE_OTHER -> + new ProgressiveMediaSource.Factory(mediaDataSourceFactory) + .createMediaSource(MediaItem.fromUri(uri)); + default -> + throw new IllegalStateException("Unsupported type: " + type); + }; } private void setupVideoPlayer( @@ -212,9 +198,7 @@ public void onPlaybackStateChanged(final int playbackState) { @Override public void onPlayerError(@NonNull PlaybackException error) { setBuffering(false); - if (eventSink != null) { - eventSink.error("VideoError", "Video player had error " + error, null); - } + eventSink.error("VideoError", "Video player had error " + error, null); } }); } @@ -230,7 +214,7 @@ void sendBufferingUpdate() { private static void setAudioAttributes(ExoPlayer exoPlayer, boolean isMixMode) { exoPlayer.setAudioAttributes( - new AudioAttributes.Builder().setContentType(C.CONTENT_TYPE_MOVIE).build(), !isMixMode); + new AudioAttributes.Builder().setContentType(com.google.android.exoplayer2.C.AUDIO_CONTENT_TYPE_MOVIE).build(), !isMixMode); } void play() { @@ -253,9 +237,7 @@ void setVolume(double value) { void setPlaybackSpeed(double value) { // We do not need to consider pitch and skipSilence for now as we do not handle them and // therefore never diverge from the default values. - final PlaybackParameters playbackParameters = new PlaybackParameters(((float) value)); - - exoPlayer.setPlaybackParameters(playbackParameters); + exoPlayer.setPlaybackSpeed((float) value); } void seekTo(int location) { diff --git a/android/src/main/java/com/lazyarts/vikram/cached_video_player/CachedVideoPlayerPlugin.java b/android/src/main/java/com/lazyarts/vikram/cached_video_player/CachedVideoPlayerPlugin.java index 7f781d5..d6ec559 100644 --- a/android/src/main/java/com/lazyarts/vikram/cached_video_player/CachedVideoPlayerPlugin.java +++ b/android/src/main/java/com/lazyarts/vikram/cached_video_player/CachedVideoPlayerPlugin.java @@ -7,6 +7,9 @@ import android.content.Context; import android.os.Build; import android.util.LongSparseArray; + +import androidx.annotation.NonNull; + import io.flutter.FlutterInjector; import io.flutter.Log; import io.flutter.embedding.engine.plugins.FlutterPlugin; @@ -31,7 +34,7 @@ public class CachedVideoPlayerPlugin implements FlutterPlugin, VideoPlayerApi { private static final String TAG = "VideoPlayerPlugin"; private final LongSparseArray videoPlayers = new LongSparseArray<>(); private FlutterState flutterState; - private VideoPlayerOptions options = new VideoPlayerOptions(); + private final VideoPlayerOptions options = new VideoPlayerOptions(); /** Register this with the v2 embedding for the plugin to respond to lifecycle callbacks. */ public CachedVideoPlayerPlugin() {} @@ -62,20 +65,7 @@ public static void registerWith(io.flutter.plugin.common.PluginRegistry.Registra @Override public void onAttachedToEngine(FlutterPluginBinding binding) { - if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { - try { - HttpsURLConnection.setDefaultSSLSocketFactory(new CustomSSLSocketFactory()); - } catch (KeyManagementException | NoSuchAlgorithmException e) { - Log.w( - TAG, - "Failed to enable TLSv1.1 and TLSv1.2 Protocols for API level 19 and below.\n" - + "For more information about Socket Security, please consult the following link:\n" - + "https://developer.android.com/reference/javax/net/ssl/SSLSocket", - e); - } - } - - final FlutterInjector injector = FlutterInjector.instance(); + final FlutterInjector injector = FlutterInjector.instance(); this.flutterState = new FlutterState( binding.getApplicationContext(), @@ -87,7 +77,7 @@ public void onAttachedToEngine(FlutterPluginBinding binding) { } @Override - public void onDetachedFromEngine(FlutterPluginBinding binding) { + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { if (flutterState == null) { Log.wtf(TAG, "Detached from the engine before registering to it."); } @@ -142,7 +132,6 @@ public TextureMessage create(CreateMessage arg) { null, options); } else { - @SuppressWarnings("unchecked") Map httpHeaders = arg.getHttpHeaders(); player = new CachedVideoPlayer( diff --git a/android/src/main/java/com/lazyarts/vikram/cached_video_player/CustomSSLSocketFactory.java b/android/src/main/java/com/lazyarts/vikram/cached_video_player/CustomSSLSocketFactory.java index 7440c4d..bedd24c 100644 --- a/android/src/main/java/com/lazyarts/vikram/cached_video_player/CustomSSLSocketFactory.java +++ b/android/src/main/java/com/lazyarts/vikram/cached_video_player/CustomSSLSocketFactory.java @@ -14,7 +14,7 @@ import javax.net.ssl.SSLSocketFactory; public class CustomSSLSocketFactory extends SSLSocketFactory { - private SSLSocketFactory sslSocketFactory; + private final SSLSocketFactory sslSocketFactory; public CustomSSLSocketFactory() throws KeyManagementException, NoSuchAlgorithmException { SSLContext context = SSLContext.getInstance("TLS"); diff --git a/android/src/main/java/com/lazyarts/vikram/cached_video_player/Messages.java b/android/src/main/java/com/lazyarts/vikram/cached_video_player/Messages.java index 4e2d8cb..0035ee6 100644 --- a/android/src/main/java/com/lazyarts/vikram/cached_video_player/Messages.java +++ b/android/src/main/java/com/lazyarts/vikram/cached_video_player/Messages.java @@ -28,13 +28,13 @@ public void setTextureId(Long setterArg) { this.textureId = setterArg; } - HashMap toMap() { + HashMap toMap() { HashMap toMapResult = new HashMap<>(); toMapResult.put("textureId", textureId); return toMapResult; } - static TextureMessage fromMap(HashMap map) { + static TextureMessage fromMap(HashMap map) { TextureMessage fromMapResult = new TextureMessage(); Object textureId = map.get("textureId"); fromMapResult.textureId = @@ -87,17 +87,17 @@ public void setFormatHint(String setterArg) { this.formatHint = setterArg; } - private HashMap httpHeaders; + private HashMap httpHeaders; - public HashMap getHttpHeaders() { - return httpHeaders; + public HashMap getHttpHeaders() { + return castToStringMap(httpHeaders); } - public void setHttpHeaders(HashMap setterArg) { + public void setHttpHeaders(HashMap setterArg) { this.httpHeaders = setterArg; } - HashMap toMap() { + HashMap toMap() { HashMap toMapResult = new HashMap<>(); toMapResult.put("asset", asset); toMapResult.put("uri", uri); @@ -107,7 +107,7 @@ HashMap toMap() { return toMapResult; } - static CreateMessage fromMap(HashMap map) { + static CreateMessage fromMap(HashMap map) { CreateMessage fromMapResult = new CreateMessage(); Object asset = map.get("asset"); fromMapResult.asset = (String) asset; @@ -118,7 +118,7 @@ static CreateMessage fromMap(HashMap map) { Object formatHint = map.get("formatHint"); fromMapResult.formatHint = (String) formatHint; Object httpHeaders = map.get("httpHeaders"); - fromMapResult.httpHeaders = (HashMap) httpHeaders; + fromMapResult.httpHeaders = castToMap(httpHeaders); return fromMapResult; } } @@ -145,14 +145,14 @@ public void setIsLooping(Boolean setterArg) { this.isLooping = setterArg; } - HashMap toMap() { + HashMap toMap() { HashMap toMapResult = new HashMap<>(); toMapResult.put("textureId", textureId); toMapResult.put("isLooping", isLooping); return toMapResult; } - static LoopingMessage fromMap(HashMap map) { + static LoopingMessage fromMap(HashMap map) { LoopingMessage fromMapResult = new LoopingMessage(); Object textureId = map.get("textureId"); fromMapResult.textureId = @@ -187,14 +187,14 @@ public void setVolume(Double setterArg) { this.volume = setterArg; } - HashMap toMap() { + HashMap toMap() { HashMap toMapResult = new HashMap<>(); toMapResult.put("textureId", textureId); toMapResult.put("volume", volume); return toMapResult; } - static VolumeMessage fromMap(HashMap map) { + static VolumeMessage fromMap(HashMap map) { VolumeMessage fromMapResult = new VolumeMessage(); Object textureId = map.get("textureId"); fromMapResult.textureId = @@ -229,14 +229,14 @@ public void setSpeed(Double setterArg) { this.speed = setterArg; } - HashMap toMap() { + HashMap toMap() { HashMap toMapResult = new HashMap<>(); toMapResult.put("textureId", textureId); toMapResult.put("speed", speed); return toMapResult; } - static PlaybackSpeedMessage fromMap(HashMap map) { + static PlaybackSpeedMessage fromMap(HashMap map) { PlaybackSpeedMessage fromMapResult = new PlaybackSpeedMessage(); Object textureId = map.get("textureId"); fromMapResult.textureId = @@ -271,14 +271,14 @@ public void setPosition(Long setterArg) { this.position = setterArg; } - HashMap toMap() { + HashMap toMap() { HashMap toMapResult = new HashMap<>(); toMapResult.put("textureId", textureId); toMapResult.put("position", position); return toMapResult; } - static PositionMessage fromMap(HashMap map) { + static PositionMessage fromMap(HashMap map) { PositionMessage fromMapResult = new PositionMessage(); Object textureId = map.get("textureId"); fromMapResult.textureId = @@ -306,13 +306,13 @@ public void setMixWithOthers(Boolean setterArg) { this.mixWithOthers = setterArg; } - HashMap toMap() { + HashMap toMap() { HashMap toMapResult = new HashMap<>(); toMapResult.put("mixWithOthers", mixWithOthers); return toMapResult; } - static MixWithOthersMessage fromMap(HashMap map) { + static MixWithOthersMessage fromMap(HashMap map) { MixWithOthersMessage fromMapResult = new MixWithOthersMessage(); Object mixWithOthers = map.get("mixWithOthers"); fromMapResult.mixWithOthers = (Boolean) mixWithOthers; @@ -355,7 +355,7 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - HashMap wrapped = new HashMap<>(); + HashMap> wrapped = new HashMap<>(); try { api.initialize(); wrapped.put("result", null); @@ -377,10 +377,10 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - HashMap wrapped = new HashMap<>(); + HashMap> wrapped = new HashMap<>(); try { @SuppressWarnings("ConstantConditions") - CreateMessage input = CreateMessage.fromMap((HashMap) message); + CreateMessage input = CreateMessage.fromMap(castToMap(message)); TextureMessage output = api.create(input); wrapped.put("result", output.toMap()); } catch (Exception exception) { @@ -401,10 +401,10 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - HashMap wrapped = new HashMap<>(); + HashMap> wrapped = new HashMap<>(); try { @SuppressWarnings("ConstantConditions") - TextureMessage input = TextureMessage.fromMap((HashMap) message); + TextureMessage input = TextureMessage.fromMap(castToMap(message)); api.dispose(input); wrapped.put("result", null); } catch (Exception exception) { @@ -425,10 +425,10 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - HashMap wrapped = new HashMap<>(); + HashMap> wrapped = new HashMap<>(); try { @SuppressWarnings("ConstantConditions") - LoopingMessage input = LoopingMessage.fromMap((HashMap) message); + LoopingMessage input = LoopingMessage.fromMap(castToMap(message)); api.setLooping(input); wrapped.put("result", null); } catch (Exception exception) { @@ -449,10 +449,10 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - HashMap wrapped = new HashMap<>(); + HashMap> wrapped = new HashMap<>(); try { @SuppressWarnings("ConstantConditions") - VolumeMessage input = VolumeMessage.fromMap((HashMap) message); + VolumeMessage input = VolumeMessage.fromMap(castToMap(message)); api.setVolume(input); wrapped.put("result", null); } catch (Exception exception) { @@ -473,10 +473,10 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - HashMap wrapped = new HashMap<>(); + HashMap> wrapped = new HashMap<>(); try { @SuppressWarnings("ConstantConditions") - PlaybackSpeedMessage input = PlaybackSpeedMessage.fromMap((HashMap) message); + PlaybackSpeedMessage input = PlaybackSpeedMessage.fromMap(castToMap(message)); api.setPlaybackSpeed(input); wrapped.put("result", null); } catch (Exception exception) { @@ -497,10 +497,10 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - HashMap wrapped = new HashMap<>(); + HashMap> wrapped = new HashMap<>(); try { @SuppressWarnings("ConstantConditions") - TextureMessage input = TextureMessage.fromMap((HashMap) message); + TextureMessage input = TextureMessage.fromMap(castToMap(message)); api.play(input); wrapped.put("result", null); } catch (Exception exception) { @@ -521,10 +521,10 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - HashMap wrapped = new HashMap<>(); + HashMap> wrapped = new HashMap<>(); try { @SuppressWarnings("ConstantConditions") - TextureMessage input = TextureMessage.fromMap((HashMap) message); + TextureMessage input = TextureMessage.fromMap(castToMap(message)); PositionMessage output = api.position(input); wrapped.put("result", output.toMap()); } catch (Exception exception) { @@ -545,10 +545,10 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - HashMap wrapped = new HashMap<>(); + HashMap> wrapped = new HashMap<>(); try { @SuppressWarnings("ConstantConditions") - PositionMessage input = PositionMessage.fromMap((HashMap) message); + PositionMessage input = PositionMessage.fromMap(castToMap(message)); api.seekTo(input); wrapped.put("result", null); } catch (Exception exception) { @@ -569,10 +569,10 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - HashMap wrapped = new HashMap<>(); + HashMap> wrapped = new HashMap<>(); try { @SuppressWarnings("ConstantConditions") - TextureMessage input = TextureMessage.fromMap((HashMap) message); + TextureMessage input = TextureMessage.fromMap(castToMap(message)); api.pause(input); wrapped.put("result", null); } catch (Exception exception) { @@ -593,10 +593,11 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { if (api != null) { channel.setMessageHandler( (message, reply) -> { - HashMap wrapped = new HashMap<>(); + HashMap> wrapped = new HashMap<>(); try { @SuppressWarnings("ConstantConditions") - MixWithOthersMessage input = MixWithOthersMessage.fromMap((HashMap) message); + MixWithOthersMessage input = + MixWithOthersMessage.fromMap(castToMap(message)); api.setMixWithOthers(input); wrapped.put("result", null); } catch (Exception exception) { @@ -611,7 +612,30 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { } } - private static HashMap wrapError(Exception exception) { + private static HashMap castToStringMap(HashMap raw) { + HashMap result = new HashMap<>(); + for (HashMap.Entry entry : raw.entrySet()) { + if (entry.getValue() instanceof String) { + result.put(entry.getKey(), (String) entry.getValue()); + } + } + return result; + } + + private static HashMap castToMap(Object message) { + if (message instanceof HashMap rawMap) { + HashMap result = new HashMap<>(); + for (HashMap.Entry entry : rawMap.entrySet()) { + if (entry.getKey() instanceof String key) { + result.put(key, entry.getValue()); + } + } + return result; + } + throw new IllegalArgumentException("Expected Map but got: " + message.getClass().getSimpleName()); + } + + private static HashMap wrapError(Exception exception) { HashMap errorMap = new HashMap<>(); errorMap.put("message", exception.toString()); errorMap.put("code", exception.getClass().getSimpleName()); diff --git a/android/src/main/java/com/lazyarts/vikram/cached_video_player/QueuingEventSink.java b/android/src/main/java/com/lazyarts/vikram/cached_video_player/QueuingEventSink.java index c83ed3a..01cab40 100644 --- a/android/src/main/java/com/lazyarts/vikram/cached_video_player/QueuingEventSink.java +++ b/android/src/main/java/com/lazyarts/vikram/cached_video_player/QueuingEventSink.java @@ -18,7 +18,7 @@ */ final class QueuingEventSink implements EventChannel.EventSink { private EventChannel.EventSink delegate; - private ArrayList eventQueue = new ArrayList<>(); + private final ArrayList eventQueue = new ArrayList<>(); private boolean done = false; public void setDelegate(EventChannel.EventSink delegate) { @@ -59,9 +59,8 @@ private void maybeFlush() { for (Object event : eventQueue) { if (event instanceof EndOfStreamEvent) { delegate.endOfStream(); - } else if (event instanceof ErrorEvent) { - ErrorEvent errorEvent = (ErrorEvent) event; - delegate.error(errorEvent.code, errorEvent.message, errorEvent.details); + } else if (event instanceof ErrorEvent errorEvent) { + delegate.error(errorEvent.code, errorEvent.message, errorEvent.details); } else { delegate.success(event); } diff --git a/android/src/main/java/com/lazyarts/vikram/cached_video_player/SimpleCacheSingleton.java b/android/src/main/java/com/lazyarts/vikram/cached_video_player/SimpleCacheSingleton.java index c35898f..9185edc 100644 --- a/android/src/main/java/com/lazyarts/vikram/cached_video_player/SimpleCacheSingleton.java +++ b/android/src/main/java/com/lazyarts/vikram/cached_video_player/SimpleCacheSingleton.java @@ -12,7 +12,7 @@ public class SimpleCacheSingleton { LeastRecentlyUsedCacheEvictor evictor; SimpleCache simpleCache; - private static SimpleCacheSingleton instance; + private static volatile SimpleCacheSingleton instance; private SimpleCacheSingleton(Context context, long maxCacheSize) { evictor = new LeastRecentlyUsedCacheEvictor(maxCacheSize); diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..24476c5 --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,44 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/example/README.md b/example/README.md index a135626..2b3fce4 100644 --- a/example/README.md +++ b/example/README.md @@ -8,9 +8,9 @@ This project is a starting point for a Flutter application. A few resources to get you started if this is your first Flutter project: -- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, samples, guidance on mobile development, and a full API reference. diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 478a838..f8b4af2 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -11,53 +11,43 @@ if (flutterRoot == null) { throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") } -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') ?: '1' +def flutterVersionName = localProperties.getProperty('flutter.versionName') ?: '1.0' apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion flutter.compileSdkVersion + namespace 'com.example.example' + compileSdk = flutter.compileSdkVersion + ndkVersion flutter.ndkVersion + + defaultConfig { + applicationId "com.example.example" + minSdkVersion 23 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = '17' } sourceSets { main.java.srcDirs += 'src/main/kotlin' } - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.example" - minSdkVersion flutter.minSdkVersion - targetSdkVersion flutter.targetSdkVersion - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - } - buildTypes { release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. signingConfig signingConfigs.debug } } - namespace 'com.example.example' } flutter { @@ -65,5 +55,5 @@ flutter { } dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} + implementation "org.jetbrains.kotlin:kotlin-stdlib" +} \ No newline at end of file diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml index f880684..45d523a 100644 --- a/example/android/app/src/debug/AndroidManifest.xml +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -1,5 +1,7 @@ - - diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 879e8bb..e890dd8 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,8 +1,9 @@ - + diff --git a/example/android/build.gradle b/example/android/build.gradle index 2e6467a..8c08254 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,12 +1,12 @@ buildscript { - ext.kotlin_version = '1.6.21' + ext.kotlin_version = '1.9.22' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.4.1' + classpath 'com.android.tools.build:gradle:8.3.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -18,14 +18,10 @@ allprojects { } } -rootProject.buildDir = '../build' subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') + layout.buildDirectory.set(layout.projectDirectory.dir("../build/${project.name}")) } -task clean(type: Delete) { - delete rootProject.buildDir -} +tasks.register("clean", Delete) { + delete layout.buildDirectory +} \ No newline at end of file diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index 6b66533..5e6b542 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Fri Jun 23 08:50:38 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle index 44e62bc..08e3790 100644 --- a/example/android/settings.gradle +++ b/example/android/settings.gradle @@ -3,9 +3,13 @@ include ':app' def localPropertiesFile = new File(rootProject.projectDir, "local.properties") def properties = new Properties() -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } +localPropertiesFile.withReader("UTF-8") { reader -> + properties.load(reader) +} def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" +if (flutterSdkPath == null) { + throw new GradleException("flutter.sdk not set in local.properties") +} + +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" \ No newline at end of file diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index 8d4492f..7c56964 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 9.0 + 12.0 diff --git a/example/ios/Podfile b/example/ios/Podfile index 1e8c3c9..279576f 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '9.0' +# platform :ios, '12.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 4124096..0a0d84f 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -155,7 +155,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -221,10 +221,12 @@ }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( @@ -252,6 +254,7 @@ }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -339,7 +342,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -416,7 +419,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -465,7 +468,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index c87d15a..5e31d3d 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ NSAllowsArbitraryLoads + UIApplicationSupportsIndirectInputEvents + diff --git a/example/lib/main.dart b/example/lib/main.dart index d387bba..55daf13 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:cached_video_player/cached_video_player.dart'; @@ -55,6 +56,9 @@ class _MyHomePageState extends State { controller = CachedVideoPlayerController.network( "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"); controller.initialize().then((value) { + if (kIsWeb) { + controller.setVolume(0.0); + } controller.play(); setState(() {}); }); diff --git a/example/pubspec.yaml b/example/pubspec.yaml index dedc11a..3512432 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -18,7 +18,8 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=2.16.2 <3.0.0" + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.10.0" # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions diff --git a/example/web/favicon.png b/example/web/favicon.png new file mode 100644 index 0000000..8aaa46a Binary files /dev/null and b/example/web/favicon.png differ diff --git a/example/web/icons/Icon-192.png b/example/web/icons/Icon-192.png new file mode 100644 index 0000000..b749bfe Binary files /dev/null and b/example/web/icons/Icon-192.png differ diff --git a/example/web/icons/Icon-512.png b/example/web/icons/Icon-512.png new file mode 100644 index 0000000..88cfd48 Binary files /dev/null and b/example/web/icons/Icon-512.png differ diff --git a/example/web/icons/Icon-maskable-192.png b/example/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000..eb9b4d7 Binary files /dev/null and b/example/web/icons/Icon-maskable-192.png differ diff --git a/example/web/icons/Icon-maskable-512.png b/example/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000..d69c566 Binary files /dev/null and b/example/web/icons/Icon-maskable-512.png differ diff --git a/example/web/index.html b/example/web/index.html new file mode 100644 index 0000000..25c5f83 --- /dev/null +++ b/example/web/index.html @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + temp_web + + + + + + diff --git a/example/web/manifest.json b/example/web/manifest.json new file mode 100644 index 0000000..2a54bb2 --- /dev/null +++ b/example/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "temp_web", + "short_name": "temp_web", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/lib/cached_video_player.dart b/lib/cached_video_player.dart index 12cdf7f..b3fed5c 100644 --- a/lib/cached_video_player.dart +++ b/lib/cached_video_player.dart @@ -11,6 +11,8 @@ import 'package:video_player_platform_interface/video_player_platform_interface. export 'package:video_player_platform_interface/video_player_platform_interface.dart' show DurationRange, DataSourceType, VideoFormat, VideoPlayerOptions; +export 'cached_video_player_web.dart' +if (dart.library.io) 'cached_video_player.dart'; import 'src/closed_caption_file.dart'; export 'src/closed_caption_file.dart'; @@ -20,6 +22,8 @@ final VideoPlayerPlatform _videoPlayerPlatform = VideoPlayerPlatform.instance // performed. ..init(); + + /// The duration, current position, buffering state, error state and settings /// of a [CachedVideoPlayerController]. class CachedVideoPlayerValue { @@ -199,12 +203,14 @@ class CachedVideoPlayerController /// [httpHeaders] option allows to specify HTTP headers /// for the request to the [dataSource]. CachedVideoPlayerController.network( - this.dataSource, { - this.formatHint, - this.closedCaptionFile, - this.videoPlayerOptions, - this.httpHeaders = const {}, - }) : dataSourceType = DataSourceType.network, + this.dataSource, { + this.formatHint, + this.closedCaptionFile, + this.videoPlayerOptions, + Map? httpHeaders, + }) : + httpHeaders = httpHeaders != null ? httpHeaders.map((k, v) => MapEntry(k, v.toString())) : const {}, + dataSourceType = DataSourceType.network, package = null, super(CachedVideoPlayerValue(duration: Duration.zero)); diff --git a/lib/cached_video_player_web.dart b/lib/cached_video_player_web.dart new file mode 100644 index 0000000..a8dcbf4 --- /dev/null +++ b/lib/cached_video_player_web.dart @@ -0,0 +1,7 @@ +import 'package:flutter_web_plugins/flutter_web_plugins.dart'; +import 'package:video_player_web/video_player_web.dart'; + +/// Web plugin registration. +void registerWith(Registrar registrar) { + VideoPlayerPlugin.registerWith(registrar); +} \ No newline at end of file diff --git a/pigeons/messages.dart b/pigeons/messages.dart index 50d0085..274a142 100644 --- a/pigeons/messages.dart +++ b/pigeons/messages.dart @@ -2,44 +2,42 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.9 - import 'package:pigeon/pigeon_lib.dart'; class TextureMessage { - int textureId; + int? textureId; } class LoopingMessage { - int textureId; - bool isLooping; + int? textureId; + bool? isLooping; } class VolumeMessage { - int textureId; - double volume; + int? textureId; + double? volume; } class PlaybackSpeedMessage { - int textureId; - double speed; + int? textureId; + double? speed; } class PositionMessage { - int textureId; - int position; + int? textureId; + int? position; } class CreateMessage { - String asset; - String uri; - String packageName; - String formatHint; - Map httpHeaders; + String? asset; + String? uri; + String? packageName; + String? formatHint; + Map? httpHeaders; } class MixWithOthersMessage { - bool mixWithOthers; + bool? mixWithOthers; } @HostApi(dartHostTestHandler: 'TestHostVideoPlayerApi') diff --git a/pubspec.yaml b/pubspec.yaml index a7b4056..2c3243d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: cached_video_player description: A new flutter plugin that is virtually a clone of official video_player plugin except that it supports caching( Android and iOS) -version: 2.0.4 +version: 3.0.0 homepage: https://github.com/vikram25897/flutter_cached_video_player flutter: @@ -13,18 +13,12 @@ flutter: pluginClass: CachedVideoPlayerPlugin web: default_package: video_player_web + fileName: lib/cached_video_player_web.dart dependencies: meta: ^1.7.0 - video_player_platform_interface: ^6.0.1 - - # The design on https://flutter.dev/go/federated-plugins was to leave - # this constraint as "any". We cannot do it right now as it fails pub publish - # validation, so we set a ^ constraint. The exact value doesn't matter since - # the constraints on the interface pins it. - # TODO(amirh): Revisit this (either update this part in the design or the pub tool). - # https://github.com/flutter/flutter/issues/46264 - video_player_web: ^2.0.10 + video_player_platform_interface: ^5.0.2 + video_player_web: ^2.0.14 flutter: sdk: flutter @@ -37,6 +31,6 @@ dev_dependencies: pigeon: ^3.1.0 environment: - sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.10.0" + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.10.0"