From 92c197eac4dde0496370eef4ae6312e09689d11f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Filipovi=C4=87?= Date: Thu, 26 Jan 2023 00:29:23 +0100 Subject: [PATCH 01/15] delete unused StreamingCore class --- .../flutter/flutter_radio_player/core/StreamingCore.kt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/StreamingCore.kt diff --git a/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/StreamingCore.kt b/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/StreamingCore.kt new file mode 100644 index 0000000..e69de29 From a1ef5360ae2d1fab34bab19df845b18134e2f320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Filipovi=C4=87?= Date: Thu, 26 Jan 2023 00:30:03 +0100 Subject: [PATCH 02/15] update kotlin version --- android/build.gradle | 4 ++-- example/android/build.gradle | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 6165453..4aecbb3 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,6 +1,6 @@ buildscript { - ext.kotlin_version = '1.5.32' + ext.kotlin_version = '1.7.21' ext.exo_player_version = '2.18.5' repositories { @@ -62,4 +62,4 @@ dependencies { implementation "com.google.android.exoplayer:exoplayer:$exo_player_version" implementation "com.google.android.exoplayer:extension-mediasession:$exo_player_version" -} \ No newline at end of file +} diff --git a/example/android/build.gradle b/example/android/build.gradle index a9ddbd9..717cd8c 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.5.32' + ext.kotlin_version = '1.7.21' repositories { google() mavenCentral() From 5b02211b41904cf2374e1e7c1882d97a6736e07f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Filipovi=C4=87?= Date: Thu, 26 Jan 2023 00:53:12 +0100 Subject: [PATCH 03/15] fix check for if default source is acc --- .../flutter_radio_player/core/services/FRPCoreService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/services/FRPCoreService.kt b/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/services/FRPCoreService.kt index b85f523..6282600 100644 --- a/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/services/FRPCoreService.kt +++ b/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/services/FRPCoreService.kt @@ -196,7 +196,7 @@ class FRPCoreService : Service(), PlayerNotificationManager.NotificationListener .build() ) - if (defaultSource.isAcc!!) { + if (defaultSource.isAcc == true) { mediaBuilder.setMimeType(MimeTypes.AUDIO_AAC) Log.d(TAG, "is an AAC media source") } From b42411aa3e914452bade9f74476e75237ef393e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Filipovi=C4=87?= Date: Sat, 28 Jan 2023 11:03:28 +0100 Subject: [PATCH 04/15] upgrade example dependencies --- example/android/build.gradle | 2 +- example/android/gradle/wrapper/gradle-wrapper.properties | 2 +- example/pubspec.lock | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/example/android/build.gradle b/example/android/build.gradle index 717cd8c..ae83546 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.1.3' + classpath 'com.android.tools.build:gradle:7.4.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index 595fb86..6b66533 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip diff --git a/example/pubspec.lock b/example/pubspec.lock index 2ca2bce..3fd9998 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -45,10 +45,10 @@ packages: dependency: "direct main" description: name: cupertino_icons - sha256: "1989d917fbe8e6b39806207df5a3fdd3d816cbd090fac2ce26fb45e9a71476e5" + sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5" fake_async: dependency: transitive description: @@ -76,7 +76,7 @@ packages: path: ".." relative: true source: path - version: "2.0.0" + version: "2.0.2" flutter_test: dependency: "direct dev" description: flutter @@ -192,5 +192,5 @@ packages: source: hosted version: "2.1.4" sdks: - dart: ">=2.18.0 <3.0.0" + dart: ">=2.18.0 <4.0.0" flutter: ">=2.5.0" From 09d8e52724c70c8f172dd0f7f768e968ab0323a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Filipovi=C4=87?= Date: Sat, 28 Jan 2023 11:04:06 +0100 Subject: [PATCH 05/15] fix fetching metadata for iOs build --- ios/Classes/core/services/support/FRPPlayerEventHandler.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/Classes/core/services/support/FRPPlayerEventHandler.swift b/ios/Classes/core/services/support/FRPPlayerEventHandler.swift index af1ec0d..683f87b 100644 --- a/ios/Classes/core/services/support/FRPPlayerEventHandler.swift +++ b/ios/Classes/core/services/support/FRPPlayerEventHandler.swift @@ -15,9 +15,9 @@ class FRPPlayerEventHandler: NSObject { print("::::: EVENT HANDLER INIT ::::") } - static func handleMetaDataChanges(metaDetails: Array) { + static func handleMetaDataChanges(metaGroup: Array) { if (FRPCoreService.shared.useIcyData) { - metaDetails + metaGroup.first?.items .compactMap({ $0 as AVMetadataItem }) .forEach({ meta in print("Meta details \(meta)") From d220bd6394da64fe049fda2efe9b0c0eb4003054 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Filipovi=C4=87?= Date: Sat, 28 Jan 2023 11:05:38 +0100 Subject: [PATCH 06/15] upgrade plugin version to v2.0.3 --- example/pubspec.lock | 2 +- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 3fd9998..bb69552 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -76,7 +76,7 @@ packages: path: ".." relative: true source: path - version: "2.0.2" + version: "2.0.3" flutter_test: dependency: "direct dev" description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 3723896..d1cfbec 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_radio_player description: Online Radio Player for Flutter which enable to play streaming URL. Supports Android and iOS as well as WearOs and watchOs -version: 2.0.2 +version: 2.0.3 homepage: "https://github.com/Sithira/FlutterRadioPlayer" environment: From a0d8d03e4252039e7438aee96f5af5de7a5b995f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Filipovi=C4=87?= Date: Thu, 2 Feb 2023 01:10:01 +0100 Subject: [PATCH 07/15] upgrade Android build gradle, kotlin and dependencies versions --- android/build.gradle | 6 ++---- android/gradle.properties | 1 - android/gradle/wrapper/gradle-wrapper.properties | 2 +- example/android/build.gradle | 4 ++-- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 4aecbb3..1517c70 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,6 +1,6 @@ buildscript { - ext.kotlin_version = '1.7.21' + ext.kotlin_version = '1.8.0' ext.exo_player_version = '2.18.5' repositories { @@ -9,7 +9,6 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.1.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -29,7 +28,6 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' android { - compileSdkVersion 31 compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -56,7 +54,7 @@ dependencies { implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' implementation "org.greenrobot:eventbus:3.3.1" - implementation 'com.google.code.gson:gson:2.8.9' + implementation 'com.google.code.gson:gson:2.10.1' implementation "androidx.multidex:multidex:2.0.1" diff --git a/android/gradle.properties b/android/gradle.properties index 38c8d45..94adc3a 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,4 +1,3 @@ org.gradle.jvmargs=-Xmx1536M -android.enableR8=true android.useAndroidX=true android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index f3d0e8a..32b25ba 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip diff --git a/example/android/build.gradle b/example/android/build.gradle index ae83546..d4c3e97 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,12 +1,12 @@ buildscript { - ext.kotlin_version = '1.7.21' + ext.kotlin_version = '1.8.0' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.4.0' + classpath 'com.android.tools.build:gradle:7.4.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } From 907f6610a61a6c694575fae8dfd09a740020f3ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Filipovi=C4=87?= Date: Thu, 2 Feb 2023 01:10:48 +0100 Subject: [PATCH 08/15] move player initialization before setting media sources, disable stop button until resuming fixed, --- .../FlutterRadioPlayerPlugin.kt | 1 + .../core/services/FRPCoreService.kt | 86 ++++++++++--------- 2 files changed, 48 insertions(+), 39 deletions(-) diff --git a/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/FlutterRadioPlayerPlugin.kt b/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/FlutterRadioPlayerPlugin.kt index dfdbbeb..afcbd2a 100644 --- a/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/FlutterRadioPlayerPlugin.kt +++ b/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/FlutterRadioPlayerPlugin.kt @@ -130,6 +130,7 @@ class FlutterRadioPlayerPlugin : FlutterPlugin, ActivityAware, MethodChannel.Met if (eventSink != null) { Log.d(TAG, "FRP Event data = $event") if (event.playbackStatus != null) { + // TODO reconsider unbinding service on stop because it might be too cumbersome to rebind it when resuming if (event.playbackStatus == FRP_STOPPED) { Log.i(TAG, "Service unbind....") isBound = false diff --git a/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/services/FRPCoreService.kt b/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/services/FRPCoreService.kt index 6282600..96b9ba7 100644 --- a/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/services/FRPCoreService.kt +++ b/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/services/FRPCoreService.kt @@ -10,7 +10,6 @@ import android.support.v4.media.session.MediaSessionCompat import com.google.android.exoplayer2.* import com.google.android.exoplayer2.audio.AudioAttributes import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector -import com.google.android.exoplayer2.metadata.MetadataOutput import com.google.android.exoplayer2.source.MediaSource import com.google.android.exoplayer2.source.ProgressiveMediaSource import com.google.android.exoplayer2.source.hls.HlsMediaSource @@ -47,8 +46,6 @@ class FRPCoreService : Service(), PlayerNotificationManager.NotificationListener private val mediaSessionId = "flutter_radio_player_media_session_id" private val playbackChannelId = "flutter_radio_player_pb_channel_id" - // private var audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager - var playbackStatus = FRPPlaybackStatus.STOPPED var currentMetaData: MediaMetadata? = null var mediaSourceList: List = emptyList() @@ -66,38 +63,6 @@ class FRPCoreService : Service(), PlayerNotificationManager.NotificationListener private lateinit var mediaSession: MediaSessionCompat - override fun onCreate() { - - Log.i(TAG, "FlutterRadioPlayerService::onCreate") - - // build exoplayer - exoPlayer = exoPlayerBuilder - .setLooper(handler.looper) - .setAudioAttributes( - AudioAttributes.Builder() - .setUsage(C.USAGE_MEDIA) - .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC) - .build(), true - ) - .setLivePlaybackSpeedControl( - DefaultLivePlaybackSpeedControl.Builder() - .setFallbackMaxPlaybackSpeed(1.04f) - .build() - ) - .setHandleAudioBecomingNoisy(true) - .build() - - // exoplayer configuration - exoPlayer?.let { - it.addListener(FRPPlayerListener(this, exoPlayer, playerNotificationManager, eventBus)) - it.playWhenReady = false - } - - exoPlayer?.addAnalyticsListener(EventLogger()) - - Log.i(TAG, "::::: END FlutterRadioPlayerService::onCreate ::::") - } - override fun onDestroy() { Log.i(TAG, "::: onDestroy :::") @@ -135,14 +100,16 @@ class FRPCoreService : Service(), PlayerNotificationManager.NotificationListener // set connector and player mediaSessionConnector = MediaSessionConnector(mediaSession) - mediaSessionConnector?.setPlayer(exoPlayer) playerNotificationManager?.apply { Log.i(TAG, "Applying configurations...") // default buttons - setUseStopAction(true) + // TODO allow developer to choose actions on player init instead of hardcoding them here + // ie. move true/false to Flutter side of the plugin and apply here + // TODO fix stopping (not pausing) and resuming player before enabling stop button + setUseStopAction(false) setUsePlayPauseActions(true) // next and prev buttons @@ -157,7 +124,6 @@ class FRPCoreService : Service(), PlayerNotificationManager.NotificationListener setUseFastForwardActionInCompactView(false) setUseRewindActionInCompactView(false) - setPlayer(exoPlayer) setMediaSessionToken(mediaSession.sessionToken) } @@ -172,6 +138,47 @@ class FRPCoreService : Service(), PlayerNotificationManager.NotificationListener throw FRPException("Empty media sources") } + // TODO maybe find a better init location where the player will always be initialized correctly (onCreate wasn't called always) + // build exoplayer + if (exoPlayer == null) { + exoPlayer = exoPlayerBuilder + .setLooper(handler.looper) + .setAudioAttributes( + AudioAttributes.Builder() + .setUsage(C.USAGE_MEDIA) + .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC) + .build(), true + ) + .setLivePlaybackSpeedControl( + DefaultLivePlaybackSpeedControl.Builder() + .setFallbackMaxPlaybackSpeed(1.04f) + .build() + ) + .setRenderersFactory( + DefaultRenderersFactory(context) + .forceEnableMediaCodecAsynchronousQueueing() + ) + .setHandleAudioBecomingNoisy(true) + .build() + + // exoplayer configuration + exoPlayer?.let { + it.addListener( + FRPPlayerListener( + this, + exoPlayer, + playerNotificationManager, + eventBus + ) + ) + it.playWhenReady = false + } + + exoPlayer?.addAnalyticsListener(EventLogger()) + mediaSessionConnector?.setPlayer(exoPlayer) + playerNotificationManager?.setPlayer(exoPlayer) + } + this.mediaSourceList = sourceList.sortedByDescending { it.isPrimary } if (this.mediaSourceList.none { frpAudioSource -> frpAudioSource.isPrimary }) { @@ -329,7 +336,8 @@ class FRPCoreService : Service(), PlayerNotificationManager.NotificationListener } return when (val type = Util.inferContentType(mediaUrl)) { - C.CONTENT_TYPE_HLS -> HlsMediaSource.Factory(defaultDataSource).createMediaSource(mediaItem) + C.CONTENT_TYPE_HLS -> HlsMediaSource.Factory(defaultDataSource) + .createMediaSource(mediaItem) C.CONTENT_TYPE_OTHER -> ProgressiveMediaSource.Factory(defaultDataSource) .createMediaSource(mediaItem) else -> { From 0dac402d54dcd5b236af3d95dc1915e9129c6bf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Filipovi=C4=87?= Date: Thu, 2 Feb 2023 01:11:00 +0100 Subject: [PATCH 09/15] update plugin version --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index d1cfbec..e66d0d4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_radio_player description: Online Radio Player for Flutter which enable to play streaming URL. Supports Android and iOS as well as WearOs and watchOs -version: 2.0.3 +version: 2.1.0 homepage: "https://github.com/Sithira/FlutterRadioPlayer" environment: From 97a3a5180d45327a5e94cf94e04420142a34bd89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Filipovi=C4=87?= Date: Thu, 2 Feb 2023 12:24:52 +0100 Subject: [PATCH 10/15] android - allow removing notification when playback paused, remove notification when app terminated --- .../core/services/FRPCoreService.kt | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/services/FRPCoreService.kt b/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/services/FRPCoreService.kt index 96b9ba7..9718451 100644 --- a/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/services/FRPCoreService.kt +++ b/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/services/FRPCoreService.kt @@ -7,6 +7,7 @@ import android.content.Intent import android.net.Uri import android.os.* import android.support.v4.media.session.MediaSessionCompat +import androidx.core.app.NotificationCompat.ServiceNotificationBehavior import com.google.android.exoplayer2.* import com.google.android.exoplayer2.audio.AudioAttributes import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector @@ -71,10 +72,12 @@ class FRPCoreService : Service(), PlayerNotificationManager.NotificationListener mediaSession.release() } - if (exoPlayer != null) { - exoPlayer?.release() + exoPlayer?.release() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + stopForeground(STOP_FOREGROUND_REMOVE) + } else { + stopForeground(false) } - mediaSessionConnector?.setPlayer(null) playerNotificationManager?.setPlayer(null) @@ -260,6 +263,11 @@ class FRPCoreService : Service(), PlayerNotificationManager.NotificationListener } fun pause() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + stopForeground(STOP_FOREGROUND_DETACH) + } else { + stopForeground(false) + } exoPlayer?.pause() } From 60d8f837f761e141c1f678b37732ac56b02eca94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Filipovi=C4=87?= Date: Thu, 2 Feb 2023 20:54:38 +0100 Subject: [PATCH 11/15] reduce exoplayer dependencies to only needed ones --- android/build.gradle | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/android/build.gradle b/android/build.gradle index 1517c70..09f4c3d 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -58,6 +58,8 @@ dependencies { implementation "androidx.multidex:multidex:2.0.1" - implementation "com.google.android.exoplayer:exoplayer:$exo_player_version" + implementation "com.google.android.exoplayer:exoplayer-core:$exo_player_version" + implementation "com.google.android.exoplayer:exoplayer-ui:$exo_player_version" + implementation "com.google.android.exoplayer:exoplayer-hls:$exo_player_version" implementation "com.google.android.exoplayer:extension-mediasession:$exo_player_version" } From a30726c37d0f8f8588f9930dc88f3cc1c9f19d41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Filipovi=C4=87?= Date: Thu, 2 Feb 2023 20:56:09 +0100 Subject: [PATCH 12/15] try to make sure the service and its player are initialized on every new app start and service binding --- .../FlutterRadioPlayerPlugin.kt | 6 +- .../core/services/FRPCoreService.kt | 84 ++++++++++--------- lib/flutter_radio_player.dart | 1 + 3 files changed, 48 insertions(+), 43 deletions(-) diff --git a/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/FlutterRadioPlayerPlugin.kt b/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/FlutterRadioPlayerPlugin.kt index afcbd2a..351205e 100644 --- a/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/FlutterRadioPlayerPlugin.kt +++ b/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/FlutterRadioPlayerPlugin.kt @@ -85,7 +85,6 @@ class FlutterRadioPlayerPlugin : FlutterPlugin, ActivityAware, MethodChannel.Met Log.i(TAG, "::: Attaching to FRP to FlutterEngine :::") this.context = context frpChannel = MethodChannel(binaryMessenger, METHOD_CHANNEL_NAME) - frpChannel?.setMethodCallHandler(this) val eventChannel = EventChannel(binaryMessenger, EVENT_CHANNEL_NAME) eventChannel.setStreamHandler(object : EventChannel.StreamHandler { @@ -121,8 +120,10 @@ class FlutterRadioPlayerPlugin : FlutterPlugin, ActivityAware, MethodChannel.Met isBound = false } } + Log.i(TAG, "Binding service...") + flutterPluginBinding?.applicationContext?.bindService(serviceIntent, serviceConnection!!, Context.BIND_AUTO_CREATE) - context.bindService(serviceIntent, serviceConnection!!, Context.BIND_AUTO_CREATE) + frpChannel?.setMethodCallHandler(this) } @Subscribe(threadMode = ThreadMode.MAIN) @@ -153,6 +154,7 @@ class FlutterRadioPlayerPlugin : FlutterPlugin, ActivityAware, MethodChannel.Met } override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { + Log.i(TAG, ":::: received method call: ${call.method} ::::") when (call.method) { "init_service" -> { if (isBound) { diff --git a/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/services/FRPCoreService.kt b/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/services/FRPCoreService.kt index 9718451..5033e28 100644 --- a/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/services/FRPCoreService.kt +++ b/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/services/FRPCoreService.kt @@ -7,7 +7,6 @@ import android.content.Intent import android.net.Uri import android.os.* import android.support.v4.media.session.MediaSessionCompat -import androidx.core.app.NotificationCompat.ServiceNotificationBehavior import com.google.android.exoplayer2.* import com.google.android.exoplayer2.audio.AudioAttributes import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector @@ -47,7 +46,7 @@ class FRPCoreService : Service(), PlayerNotificationManager.NotificationListener private val mediaSessionId = "flutter_radio_player_media_session_id" private val playbackChannelId = "flutter_radio_player_pb_channel_id" - var playbackStatus = FRPPlaybackStatus.STOPPED + var playbackStatus = FRPPlaybackStatus.LOADING var currentMetaData: MediaMetadata? = null var mediaSourceList: List = emptyList() var useICYData: Boolean = false @@ -58,7 +57,6 @@ class FRPCoreService : Service(), PlayerNotificationManager.NotificationListener private val binder = LocalBinder() private var exoPlayer: ExoPlayer? = null private val eventBus: EventBus = EventBus.getDefault() - private var exoPlayerBuilder = ExoPlayer.Builder(context) private var mediaSessionConnector: MediaSessionConnector? = null private var playerNotificationManager: PlayerNotificationManager? = null @@ -73,6 +71,7 @@ class FRPCoreService : Service(), PlayerNotificationManager.NotificationListener } exoPlayer?.release() + exoPlayer = null if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { stopForeground(STOP_FOREGROUND_REMOVE) } else { @@ -106,7 +105,7 @@ class FRPCoreService : Service(), PlayerNotificationManager.NotificationListener playerNotificationManager?.apply { - Log.i(TAG, "Applying configurations...") + Log.i(TAG, ":::: Applying configurations... ::::") // default buttons // TODO allow developer to choose actions on player init instead of hardcoding them here @@ -130,7 +129,7 @@ class FRPCoreService : Service(), PlayerNotificationManager.NotificationListener setMediaSessionToken(mediaSession.sessionToken) } - Log.i(TAG, ":::: END OF SERVICE ::::") + Log.i(TAG, ":::: END OF onStartCommand IN SERVICE ::::") return START_REDELIVER_INTENT } @@ -144,7 +143,7 @@ class FRPCoreService : Service(), PlayerNotificationManager.NotificationListener // TODO maybe find a better init location where the player will always be initialized correctly (onCreate wasn't called always) // build exoplayer if (exoPlayer == null) { - exoPlayer = exoPlayerBuilder + exoPlayer = ExoPlayer.Builder(context) .setLooper(handler.looper) .setAudioAttributes( AudioAttributes.Builder() @@ -189,58 +188,56 @@ class FRPCoreService : Service(), PlayerNotificationManager.NotificationListener } Log.i(TAG, "Current PlaybackStatus $playbackStatus") + Log.i(TAG, "Current Player state ${exoPlayer?.playbackState}") val defaultSource = this.mediaSourceList.firstOrNull { frp -> frp.isPrimary } - if (playbackStatus == FRPPlaybackStatus.PAUSED || playbackStatus == FRPPlaybackStatus.STOPPED) { + if (defaultSource != null) { + Log.i(TAG, "Default media item added to exoplayer...") - if (defaultSource != null) { - Log.i(TAG, "Default media item added to exoplayer...") + val mediaUrl = Uri.parse(defaultSource.url) - val mediaUrl = Uri.parse(defaultSource.url) + val mediaBuilder = + MediaItem.Builder().setUri(mediaUrl).setLiveConfiguration( + MediaItem.LiveConfiguration.Builder() + .setMaxPlaybackSpeed(1.02f) + .build() + ) + + if (defaultSource.isAcc == true) { + mediaBuilder.setMimeType(MimeTypes.AUDIO_AAC) + Log.d(TAG, "is an AAC media source") + } + + exoPlayer?.addMediaSource(0, buildMediaSource(mediaUrl, mediaBuilder.build())) + updateCurrentPlaying(defaultSource) + } + + mediaSourceList.filter { source -> !source.isPrimary }.forEach { frp -> + run { + Log.i(TAG, "Added media source ${frp.title} with url ${frp.url}") + + val mediaUrl = Uri.parse(frp.url) - val mediaBuilder = - MediaItem.Builder().setUri(mediaUrl).setLiveConfiguration( + val mediaBuilder = MediaItem.Builder() + .setUri(mediaUrl) + .setLiveConfiguration( MediaItem.LiveConfiguration.Builder() .setMaxPlaybackSpeed(1.02f) .build() ) - if (defaultSource.isAcc == true) { + if (frp.isAcc!!) { mediaBuilder.setMimeType(MimeTypes.AUDIO_AAC) - Log.d(TAG, "is an AAC media source") } - exoPlayer?.addMediaSource(0, buildMediaSource(mediaUrl, mediaBuilder.build())) - updateCurrentPlaying(defaultSource) - } - - mediaSourceList.filter { source -> !source.isPrimary }.forEach { frp -> - run { - Log.i(TAG, "Added media source ${frp.title} with url ${frp.url}") - - val mediaUrl = Uri.parse(frp.url) - - val mediaBuilder = MediaItem.Builder() - .setUri(mediaUrl) - .setLiveConfiguration( - MediaItem.LiveConfiguration.Builder() - .setMaxPlaybackSpeed(1.02f) - .build() - ) - - if (frp.isAcc!!) { - mediaBuilder.setMimeType(MimeTypes.AUDIO_AAC) - } - - exoPlayer?.addMediaSource(buildMediaSource(mediaUrl, mediaBuilder.build())) - } + exoPlayer?.addMediaSource(buildMediaSource(mediaUrl, mediaBuilder.build())) } - - Log.i(TAG, "Preparing player...") - exoPlayer?.prepare() } + Log.i(TAG, "Preparing player...") + exoPlayer?.prepare() + if (playDefault) { Log.i(TAG, "addMediaSources with default play") exoPlayer?.playWhenReady = true @@ -302,10 +299,15 @@ class FRPCoreService : Service(), PlayerNotificationManager.NotificationListener } fun seekToMediaItem(index: Int, playIfReady: Boolean) { + Log.i(TAG, "Seeking to media item, pos: $index...") + + Log.d(TAG, "playbackState ${exoPlayer?.playbackState}") + Log.d(TAG, "playbackLooper ${exoPlayer?.playbackLooper}") exoPlayer?.seekToDefaultPosition(index) exoPlayer?.apply { playWhenReady = playIfReady } + exoPlayer?.prepare() val currentMedia = mediaSourceList[exoPlayer?.currentMediaItemIndex!!] eventBus.post(FRPPlayerEvent(currentSource = updateCurrentPlaying(currentMedia))) } diff --git a/lib/flutter_radio_player.dart b/lib/flutter_radio_player.dart index dc2ece8..8e58011 100644 --- a/lib/flutter_radio_player.dart +++ b/lib/flutter_radio_player.dart @@ -37,6 +37,7 @@ class FlutterRadioPlayer { } _eventStream ??= _eventChannel.receiveBroadcastStream().map((event) => event); + _methodChannel.invokeMethod("init_service"); if (kDebugMode) { print("Initialized Event Channels: Completed"); } From ec26e3775ca602a68e6f5b97b266b50600b0ec55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Filipovi=C4=87?= Date: Thu, 2 Feb 2023 21:05:14 +0100 Subject: [PATCH 13/15] upgrade plugin version to v2.2.0 --- example/pubspec.lock | 56 ++++++++++++++++++++++---------------------- pubspec.yaml | 2 +- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index bb69552..1d977c8 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" boolean_selector: dependency: transitive description: @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" clock: dependency: transitive description: @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.2" cupertino_icons: dependency: "direct main" description: @@ -76,20 +76,12 @@ packages: path: ".." relative: true source: path - version: "2.0.3" + version: "2.2.0" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" - js: - dependency: transitive - description: - name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" - url: "https://pub.dev" - source: hosted - version: "0.6.5" lints: dependency: transitive description: @@ -102,34 +94,34 @@ packages: dependency: transitive description: name: matcher - sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.13" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" meta: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" path: dependency: transitive description: name: path - sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.8.3" sky_engine: dependency: transitive description: flutter @@ -139,10 +131,10 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: @@ -179,10 +171,10 @@ packages: dependency: transitive description: name: test_api - sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.4.16" + version: "0.6.0" vector_math: dependency: transitive description: @@ -191,6 +183,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" sdks: - dart: ">=2.18.0 <4.0.0" + dart: ">=3.1.0-185.0.dev <4.0.0" flutter: ">=2.5.0" diff --git a/pubspec.yaml b/pubspec.yaml index e66d0d4..481b8ef 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_radio_player description: Online Radio Player for Flutter which enable to play streaming URL. Supports Android and iOS as well as WearOs and watchOs -version: 2.1.0 +version: 2.2.0 homepage: "https://github.com/Sithira/FlutterRadioPlayer" environment: From f01ca6839d982634cce48d2ec8db1303a07de348 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Filipovi=C4=87?= Date: Mon, 15 Jan 2024 11:15:53 +0100 Subject: [PATCH 14/15] Delete android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/StreamingCore.kt --- .../flutter/flutter_radio_player/core/StreamingCore.kt | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/StreamingCore.kt diff --git a/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/StreamingCore.kt b/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/StreamingCore.kt deleted file mode 100644 index e69de29..0000000 From 785d4dd9a7a710373f0f84c4ab574e0f349c0b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Filipovi=C4=87?= Date: Sun, 31 Aug 2025 13:44:25 +0200 Subject: [PATCH 15/15] upgrade android/gradle version, fix service and player lifecycle, add FVM --- .fvmrc | 4 + android/build.gradle | 20 +-- android/settings.gradle | 26 ++++ .../FlutterRadioPlayerPlugin.kt | 7 +- .../core/services/FRPCoreService.kt | 125 +++++++++--------- example/android/app/build.gradle | 19 ++- example/android/build.gradle | 15 +-- .../gradle/wrapper/gradle-wrapper.properties | 4 +- example/android/settings.gradle | 30 +++-- example/pubspec.lock | 104 +++++++++------ example/pubspec.yaml | 2 +- pubspec.yaml | 4 +- 12 files changed, 200 insertions(+), 160 deletions(-) create mode 100644 .fvmrc diff --git a/.fvmrc b/.fvmrc new file mode 100644 index 0000000..99b26a1 --- /dev/null +++ b/.fvmrc @@ -0,0 +1,4 @@ +{ + "flutter": "3.19.3", + "flavors": {} +} diff --git a/android/build.gradle b/android/build.gradle index 09f4c3d..d8b8718 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,18 +1,3 @@ -buildscript { - - ext.kotlin_version = '1.8.0' - ext.exo_player_version = '2.18.5' - - repositories { - google() - mavenCentral() - } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - group 'me.sithiramunasinghe.flutter.flutter_radio_player' version '1.0-SNAPSHOT' @@ -28,6 +13,8 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' android { + namespace 'me.sithiramunasinghe.flutter.flutter_radio_player' + compileSdkVersion 36 compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -50,7 +37,7 @@ android { dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' implementation "org.greenrobot:eventbus:3.3.1" @@ -58,6 +45,7 @@ dependencies { implementation "androidx.multidex:multidex:2.0.1" + ext.exo_player_version = '2.18.5' implementation "com.google.android.exoplayer:exoplayer-core:$exo_player_version" implementation "com.google.android.exoplayer:exoplayer-ui:$exo_player_version" implementation "com.google.android.exoplayer:exoplayer-hls:$exo_player_version" diff --git a/android/settings.gradle b/android/settings.gradle index 95ed40c..be8fa6f 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1 +1,27 @@ +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" // apply true + id "com.android.application" version "8.12.2" apply false + id "org.jetbrains.kotlin.android" version "1.8.10" apply false +} + +include ":app" + rootProject.name = 'flutter_radio_player' diff --git a/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/FlutterRadioPlayerPlugin.kt b/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/FlutterRadioPlayerPlugin.kt index 351205e..88a33a0 100644 --- a/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/FlutterRadioPlayerPlugin.kt +++ b/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/FlutterRadioPlayerPlugin.kt @@ -33,8 +33,6 @@ class FlutterRadioPlayerPlugin : FlutterPlugin, ActivityAware, MethodChannel.Met private val GSON = Gson() } - var serviceIntent: Intent? = null - private var isBound: Boolean = false private var context: Context? = null private var pluginActivity: Activity? = null @@ -56,7 +54,6 @@ class FlutterRadioPlayerPlugin : FlutterPlugin, ActivityAware, MethodChannel.Met frpChannel?.setMethodCallHandler(null) frpChannel = null eventSink = null - serviceIntent = null } override fun onAttachedToActivity(binding: ActivityPluginBinding) { @@ -82,7 +79,7 @@ class FlutterRadioPlayerPlugin : FlutterPlugin, ActivityAware, MethodChannel.Met } private fun onAttachedToEngine(context: Context, binaryMessenger: BinaryMessenger) { - Log.i(TAG, "::: Attaching to FRP to FlutterEngine :::") + Log.i(TAG, "::: Attaching FRP to FlutterEngine :::") this.context = context frpChannel = MethodChannel(binaryMessenger, METHOD_CHANNEL_NAME) @@ -99,7 +96,7 @@ class FlutterRadioPlayerPlugin : FlutterPlugin, ActivityAware, MethodChannel.Met }) // service intent - serviceIntent = Intent(context, FRPCoreService::class.java) + val serviceIntent = Intent(context, FRPCoreService::class.java) // start the background service. pluginActivity?.startService(serviceIntent) diff --git a/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/services/FRPCoreService.kt b/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/services/FRPCoreService.kt index 5033e28..aae4305 100644 --- a/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/services/FRPCoreService.kt +++ b/android/src/main/kotlin/me/sithiramunasinghe/flutter/flutter_radio_player/core/services/FRPCoreService.kt @@ -5,9 +5,17 @@ import android.app.Service import android.content.Context import android.content.Intent import android.net.Uri -import android.os.* +import android.os.Binder +import android.os.Build +import android.os.Handler +import android.os.IBinder +import android.os.Looper import android.support.v4.media.session.MediaSessionCompat -import com.google.android.exoplayer2.* +import com.google.android.exoplayer2.C +import com.google.android.exoplayer2.DefaultLivePlaybackSpeedControl +import com.google.android.exoplayer2.ExoPlayer +import com.google.android.exoplayer2.MediaItem +import com.google.android.exoplayer2.MediaMetadata import com.google.android.exoplayer2.audio.AudioAttributes import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector import com.google.android.exoplayer2.source.MediaSource @@ -51,9 +59,6 @@ class FRPCoreService : Service(), PlayerNotificationManager.NotificationListener var mediaSourceList: List = emptyList() var useICYData: Boolean = false private var currentPlayingItem: FRPCurrentSource? = null - - private var handler: Handler = Handler(Looper.getMainLooper()) - private val binder = LocalBinder() private var exoPlayer: ExoPlayer? = null private val eventBus: EventBus = EventBus.getDefault() @@ -62,28 +67,42 @@ class FRPCoreService : Service(), PlayerNotificationManager.NotificationListener private lateinit var mediaSession: MediaSessionCompat + override fun onCreate() { + Log.i(TAG, "FlutterRadioPlayerService::onCreate") + } + override fun onDestroy() { Log.i(TAG, "::: onDestroy :::") - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - mediaSession.release() + mediaSessionConnector?.setPlayer(null) + playerNotificationManager?.setPlayer(null) + + if (exoPlayer != null) { + exoPlayer?.release() + exoPlayer == null } - exoPlayer?.release() - exoPlayer = null + if (mediaSessionConnector != null) { + mediaSessionConnector = null + } + mediaSession.setActive(false) + mediaSession.release() + super.onDestroy() + } + + override fun onTaskRemoved(rootIntent: Intent?) { + Log.i(TAG, ":::: FlutterRadioPlayerService.onTaskRemoved ::::") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { stopForeground(STOP_FOREGROUND_REMOVE) } else { stopForeground(false) } - mediaSessionConnector?.setPlayer(null) - playerNotificationManager?.setPlayer(null) - - super.onDestroy() + stopSelf() + super.onTaskRemoved(rootIntent) } - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { Log.i(TAG, ":::: FlutterRadioPlayerService.onStartCommand ::::") @@ -100,12 +119,41 @@ class FRPCoreService : Service(), PlayerNotificationManager.NotificationListener mediaSession = MediaSessionCompat(this, mediaSessionId) mediaSession.isActive = true + val handler = Handler(Looper.getMainLooper()) + + // build exoplayer + exoPlayer = ExoPlayer.Builder(context) + .setLooper(handler.looper) + .setAudioAttributes( + AudioAttributes.Builder() + .setUsage(C.USAGE_MEDIA) + .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC) + .build(), true + ) + .setLivePlaybackSpeedControl( + DefaultLivePlaybackSpeedControl.Builder() + .setFallbackMaxPlaybackSpeed(1.04f) + .build() + ) + .setHandleAudioBecomingNoisy(true) + .build() + + // exoplayer configuration + exoPlayer?.let { + it.addListener(FRPPlayerListener(this, exoPlayer, playerNotificationManager, eventBus)) + it.playWhenReady = false + } + + exoPlayer?.addAnalyticsListener(EventLogger()) + + // set connector and player mediaSessionConnector = MediaSessionConnector(mediaSession) + mediaSessionConnector?.setPlayer(exoPlayer) playerNotificationManager?.apply { - Log.i(TAG, ":::: Applying configurations... ::::") + Log.i(TAG, "Applying configurations...") // default buttons // TODO allow developer to choose actions on player init instead of hardcoding them here @@ -126,6 +174,7 @@ class FRPCoreService : Service(), PlayerNotificationManager.NotificationListener setUseFastForwardActionInCompactView(false) setUseRewindActionInCompactView(false) + setPlayer(exoPlayer) setMediaSessionToken(mediaSession.sessionToken) } @@ -140,47 +189,6 @@ class FRPCoreService : Service(), PlayerNotificationManager.NotificationListener throw FRPException("Empty media sources") } - // TODO maybe find a better init location where the player will always be initialized correctly (onCreate wasn't called always) - // build exoplayer - if (exoPlayer == null) { - exoPlayer = ExoPlayer.Builder(context) - .setLooper(handler.looper) - .setAudioAttributes( - AudioAttributes.Builder() - .setUsage(C.USAGE_MEDIA) - .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC) - .build(), true - ) - .setLivePlaybackSpeedControl( - DefaultLivePlaybackSpeedControl.Builder() - .setFallbackMaxPlaybackSpeed(1.04f) - .build() - ) - .setRenderersFactory( - DefaultRenderersFactory(context) - .forceEnableMediaCodecAsynchronousQueueing() - ) - .setHandleAudioBecomingNoisy(true) - .build() - - // exoplayer configuration - exoPlayer?.let { - it.addListener( - FRPPlayerListener( - this, - exoPlayer, - playerNotificationManager, - eventBus - ) - ) - it.playWhenReady = false - } - - exoPlayer?.addAnalyticsListener(EventLogger()) - mediaSessionConnector?.setPlayer(exoPlayer) - playerNotificationManager?.setPlayer(exoPlayer) - } - this.mediaSourceList = sourceList.sortedByDescending { it.isPrimary } if (this.mediaSourceList.none { frpAudioSource -> frpAudioSource.isPrimary }) { @@ -260,11 +268,6 @@ class FRPCoreService : Service(), PlayerNotificationManager.NotificationListener } fun pause() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - stopForeground(STOP_FOREGROUND_DETACH) - } else { - stopForeground(false) - } exoPlayer?.pause() } diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 908e6f8..cef9897 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -1,3 +1,9 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) { } } -def flutterRoot = localProperties.getProperty('flutter.sdk') -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' @@ -21,12 +22,9 @@ if (flutterVersionName == null) { flutterVersionName = '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 'me.sithiramunasinghe.flutter_radio_player_example' + compileSdkVersion 36 compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -66,5 +64,4 @@ flutter { dependencies { implementation 'androidx.multidex:multidex:2.0.1' - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" } diff --git a/example/android/build.gradle b/example/android/build.gradle index d4c3e97..bc157bd 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,16 +1,3 @@ -buildscript { - ext.kotlin_version = '1.8.0' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.4.1' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - allprojects { repositories { google() @@ -26,6 +13,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index 6b66533..8e5bb06 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Jun 23 08:50:38 CEST 2017 +#Fri Aug 29 13:04:46 CEST 2025 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle index 44e62bc..3b75051 100644 --- a/example/android/settings.gradle +++ b/example/android/settings.gradle @@ -1,11 +1,25 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} -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" +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" // apply true + id "com.android.application" version '8.12.2' apply false + id "org.jetbrains.kotlin.android" version "1.8.10" apply false +} + +include ":app" \ No newline at end of file diff --git a/example/pubspec.lock b/example/pubspec.lock index 1d977c8..13a3a47 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,42 +5,42 @@ packages: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.13.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" characters: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" clock: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" collection: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.19.1" cupertino_icons: dependency: "direct main" description: @@ -53,10 +53,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.3" flutter: dependency: "direct main" description: flutter @@ -76,12 +76,36 @@ packages: path: ".." relative: true source: path - version: "2.2.0" + version: "3.0.0" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + url: "https://pub.dev" + source: hosted + version: "10.0.9" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + url: "https://pub.dev" + source: hosted + version: "3.0.9" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" lints: dependency: transitive description: @@ -94,87 +118,87 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.17" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.16.0" path: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.1" sky_engine: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_span: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.10.1" stack_trace: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.4" string_scanner: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.4.1" term_glyph: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" test_api: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.7.4" vector_math: dependency: transitive description: @@ -183,14 +207,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - web: + vm_service: dependency: transitive description: - name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + name: vm_service + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "15.0.0" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" - flutter: ">=2.5.0" + dart: ">=3.7.0-0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index c7026d1..38599ed 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -6,7 +6,7 @@ description: Demonstrates how to use the flutter_radio_player plugin. publish_to: 'none' # Remove this line if you wish to publish to pub.dev environment: - sdk: ">=2.15.1 <3.0.0" + sdk: ">=3.0.0 <4.0.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/pubspec.yaml b/pubspec.yaml index 481b8ef..e672f48 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,10 +1,10 @@ name: flutter_radio_player description: Online Radio Player for Flutter which enable to play streaming URL. Supports Android and iOS as well as WearOs and watchOs -version: 2.2.0 +version: 3.0.0 homepage: "https://github.com/Sithira/FlutterRadioPlayer" environment: - sdk: ">=2.15.1 <3.0.0" + sdk: ">=3.0.0 <4.0.0" flutter: ">=2.5.0" dependencies: