diff --git a/packages/player/android/build.gradle b/packages/player/android/build.gradle
index 7505b818..ab5a5768 100644
--- a/packages/player/android/build.gradle
+++ b/packages/player/android/build.gradle
@@ -4,14 +4,14 @@ version '1.0.4'
buildscript {
ext.kotlin_version = '1.9.20'
- ext.exoplayer_version = '2.19.1'
+ ext.media3_version = '1.4.0'
repositories {
google()
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.2.2'
+ classpath 'com.android.tools.build:gradle:8.1.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
@@ -44,20 +44,24 @@ android {
lintOptions {
disable 'InvalidPackage'
}
-
+ namespace = "br.com.suamusica.player"
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3'
- implementation "com.google.android.exoplayer:exoplayer:$exoplayer_version"
- implementation "com.google.android.exoplayer:extension-mediasession:$exoplayer_version"
+ implementation "androidx.media3:media3-exoplayer:$media3_version"
implementation "androidx.media:media:1.7.0"
implementation "org.jetbrains.kotlin:kotlin-reflect"
+
// Glide dependencies
implementation "com.github.bumptech.glide:glide:4.12.0"
-// implementation files('/Users/alantrope/flutter/bin/cache/artifacts/engine/android-x64/flutter.jar')
+ implementation "androidx.media3:media3-exoplayer-hls:$media3_version"
+ implementation "androidx.media3:media3-session:$media3_version"
+ implementation "androidx.media3:media3-common:$media3_version"
+ implementation "androidx.media3:media3-ui:$media3_version"
+// implementation files('/Users/suamusica/flutter/bin/cache/artifacts/engine/android-x64/flutter.jar')
kapt "com.github.bumptech.glide:compiler:4.12.0"
}
diff --git a/packages/player/android/gradle/wrapper/gradle-wrapper.properties b/packages/player/android/gradle/wrapper/gradle-wrapper.properties
index ffed3a25..fae08049 100644
--- a/packages/player/android/gradle/wrapper/gradle-wrapper.properties
+++ b/packages/player/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/packages/player/android/src/main/AndroidManifest.xml b/packages/player/android/src/main/AndroidManifest.xml
index 0d48337f..57ec7ce7 100644
--- a/packages/player/android/src/main/AndroidManifest.xml
+++ b/packages/player/android/src/main/AndroidManifest.xml
@@ -1,22 +1,19 @@
+ xmlns:tools="http://schemas.android.com/tools">
+
+
+
-
-
-
-
-
-
+
-
+
\ No newline at end of file
diff --git a/packages/player/android/src/main/kotlin/br/com/suamusica/player/FavoriteModeActionProvider.kt b/packages/player/android/src/main/kotlin/br/com/suamusica/player/FavoriteModeActionProvider.kt
deleted file mode 100644
index 31bfcd74..00000000
--- a/packages/player/android/src/main/kotlin/br/com/suamusica/player/FavoriteModeActionProvider.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package br.com.suamusica.player
-
-import android.app.PendingIntent
-import android.content.Context
-import android.content.Intent
-import android.os.Build
-import android.os.Bundle
-import android.support.v4.media.session.PlaybackStateCompat
-import android.util.Log
-import androidx.core.app.NotificationCompat
-import com.google.android.exoplayer2.Player
-import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
-import java.util.*
-
-class FavoriteModeActionProvider(private val context: Context) :
- MediaSessionConnector.CustomActionProvider {
-
- override fun onCustomAction(player: Player, action: String, extras: Bundle?) {
- PlayerSingleton.favorite(action == "Favoritar")
- }
-
- override fun getCustomAction(player: Player): PlaybackStateCompat.CustomAction? {
- if (PlayerSingleton.lastFavorite) {
- return PlaybackStateCompat.CustomAction.Builder("Desfavoritar", "Desfavoritar", R.drawable.ic_unfavorite_notification_player,).build()
- }
- return PlaybackStateCompat.CustomAction.Builder("Favoritar", "Favoritar", R.drawable.ic_favorite_notification_player,).build()
- }
-}
\ No newline at end of file
diff --git a/packages/player/android/src/main/kotlin/br/com/suamusica/player/MediaButtonEventHandler.kt b/packages/player/android/src/main/kotlin/br/com/suamusica/player/MediaButtonEventHandler.kt
index 5c43182e..43dc15b1 100644
--- a/packages/player/android/src/main/kotlin/br/com/suamusica/player/MediaButtonEventHandler.kt
+++ b/packages/player/android/src/main/kotlin/br/com/suamusica/player/MediaButtonEventHandler.kt
@@ -1,62 +1,246 @@
package br.com.suamusica.player
import android.content.Intent
+import android.os.Build
+import android.os.Bundle
import android.util.Log
import android.view.KeyEvent
-import com.google.android.exoplayer2.Player
-import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
+import androidx.media3.common.MediaItem
+import androidx.media3.common.Player
+import androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT
+import androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM
+import androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS
+import androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM
+import androidx.media3.common.util.UnstableApi
+import androidx.media3.session.CommandButton
+import androidx.media3.session.LibraryResult
+import androidx.media3.session.MediaLibraryService
+import androidx.media3.session.MediaSession
+import androidx.media3.session.R.drawable
+import androidx.media3.session.SessionCommand
+import androidx.media3.session.SessionResult
+import com.google.common.collect.ImmutableList
+import com.google.common.util.concurrent.Futures
+import com.google.common.util.concurrent.ListenableFuture
-class MediaButtonEventHandler : MediaSessionConnector.MediaButtonEventHandler {
+@UnstableApi
+class MediaButtonEventHandler(
+ private val mediaService: MediaService?,
+) : MediaLibraryService.MediaLibrarySession.Callback {
+ private val BROWSABLE_ROOT = "/"
+ private val EMPTY_ROOT = "@empty@"
+ override fun onConnect(
+ session: MediaSession,
+ controller: MediaSession.ControllerInfo
+ ): MediaSession.ConnectionResult {
+ Log.d("Player", "onConnect")
+ val sessionCommands =
+ MediaSession.ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon().apply {
+ add(SessionCommand("notification_next", Bundle.EMPTY))
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ add(SessionCommand("notification_previous", Bundle.EMPTY))
+ }
+ add(SessionCommand("notification_favoritar", Bundle.EMPTY))
+ add(SessionCommand("notification_desfavoritar", Bundle.EMPTY))
+ add(SessionCommand("seek", session.token.extras))
+ add(SessionCommand("pause", Bundle.EMPTY))
+ add(SessionCommand("stop", Bundle.EMPTY))
+ add(SessionCommand("prepare", session.token.extras))
+ add(SessionCommand("play", Bundle.EMPTY))
+ add(SessionCommand("remove_notification", Bundle.EMPTY))
+ add(SessionCommand("send_notification", session.token.extras))
+ add(SessionCommand("ads_playing", Bundle.EMPTY))
+ add(SessionCommand("onTogglePlayPause", Bundle.EMPTY))
+ }.build()
- override fun onMediaButtonEvent(player: Player, intent: Intent): Boolean {
- onMediaButtonEventHandler(intent)
- return true
+ val playerCommands =
+ MediaSession.ConnectionResult.DEFAULT_PLAYER_COMMANDS.buildUpon()
+ .remove(COMMAND_SEEK_TO_PREVIOUS)
+ .remove(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)
+ .remove(COMMAND_SEEK_TO_NEXT)
+ .remove(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)
+ .build()
+
+ return MediaSession.ConnectionResult.AcceptedResultBuilder(session)
+ .setAvailableSessionCommands(sessionCommands)
+ .setAvailablePlayerCommands(playerCommands)
+ .build()
}
- fun onMediaButtonEventHandler(intent: Intent?) {
+ override fun onCustomCommand(
+ session: MediaSession,
+ controller: MediaSession.ControllerInfo,
+ customCommand: SessionCommand,
+ args: Bundle
+ ): ListenableFuture {
+ Log.d("Player", "#MEDIA3# - onCustomCommand ${customCommand.customAction}")
+ if (customCommand.customAction == "notification_favoritar" || customCommand.customAction == "notification_desfavoritar") {
+ val isFavorite = customCommand.customAction == "notification_favoritar"
+ buildIcons(isFavorite)
+ PlayerSingleton.favorite(isFavorite)
+ }
- if (intent == null) {
- return
+ if (customCommand.customAction == "seek") {
+ mediaService?.seek(args.getLong("position"), args.getBoolean("playWhenReady"))
}
- if (Intent.ACTION_MEDIA_BUTTON == intent.action) {
- mediaButtonHandler(intent)
- } else if (intent.hasExtra(FAVORITE)) {
- PlayerSingleton.favorite(intent.getBooleanExtra(FAVORITE, false))
+ if (customCommand.customAction == "onTogglePlayPause") {
+ mediaService?.togglePlayPause()
}
- }
+ if (customCommand.customAction == "stop") {
+ mediaService?.stop()
+ }
- private fun mediaButtonHandler(intent: Intent) {
- val ke = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT)
- Log.d("Player", "Key: $ke")
+ if (customCommand.customAction == "send_notification") {
+ args.let {
+ val isFavorite = it.getBoolean(PlayerPlugin.IS_FAVORITE_ARGUMENT)
+ buildIcons(isFavorite)
- if (ke!!.action == KeyEvent.ACTION_UP) {
- return
+ }
+ }
+ if (customCommand.customAction == "play") {
+ mediaService?.play()
+ }
+ if (customCommand.customAction == "notification_previous") {
+ PlayerSingleton.previous()
+ }
+ if (customCommand.customAction == "notification_next") {
+ PlayerSingleton.next()
+ }
+ if (customCommand.customAction == "pause") {
+ mediaService?.pause()
+ }
+ if (customCommand.customAction == "ads_playing" || customCommand.customAction == "remove_notification") {
+ mediaService?.removeNotification()
}
- when (ke.keyCode) {
- KeyEvent.KEYCODE_MEDIA_PLAY -> {
- PlayerSingleton.play()
+ if (customCommand.customAction == "prepare") {
+ args.let {
+ val cookie = it.getString("cookie")!!
+ val name = it.getString("name")!!
+ val author = it.getString("author")!!
+ val url = it.getString("url")!!
+ val coverUrl = it.getString("coverUrl")!!
+ val bigCoverUrl = it.getString("bigCoverUrl")!!
+ var isFavorite: Boolean? = null;
+ if (it.containsKey(PlayerPlugin.IS_FAVORITE_ARGUMENT)) {
+ isFavorite = it.getBoolean(PlayerPlugin.IS_FAVORITE_ARGUMENT)
+ }
+ mediaService?.prepare(
+ cookie,
+ Media(name, author, url, coverUrl, bigCoverUrl, isFavorite)
+ )
+ buildIcons(isFavorite ?: false)
}
- KeyEvent.KEYCODE_MEDIA_PAUSE -> {
- PlayerSingleton.pause()
- }
- KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE -> {
- Log.d("Player", "Player: Key Code : PlayPause")
- PlayerSingleton.togglePlayPause()
+ }
+ return Futures.immediateFuture(
+ SessionResult(SessionResult.RESULT_SUCCESS)
+ )
+ }
+
+ private fun buildIcons(isFavorite: Boolean): Unit? {
+ val list = ImmutableList.of(
+ CommandButton.Builder()
+ .setDisplayName("Save to favorites")
+ .setIconResId(if (isFavorite) drawable.media3_icon_heart_filled else drawable.media3_icon_heart_unfilled)
+ .setSessionCommand(
+ SessionCommand(
+ if (isFavorite) "notification_desfavoritar" else "notification_favoritar",
+ Bundle()
+ )
+ )
+ .setEnabled(true)
+ .build(),
+ CommandButton.Builder()
+ .setDisplayName("notification_next")
+ .setIconResId(drawable.media3_icon_next)
+ .setSessionCommand(SessionCommand("notification_next", Bundle.EMPTY))
+ .setEnabled(true)
+ .build(),
+ )
+ return mediaService?.mediaSession?.setCustomLayout(
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ list.plus(
+ CommandButton.Builder()
+ .setDisplayName("notification_previous")
+ .setIconResId(drawable.media3_icon_previous)
+ .setSessionCommand(SessionCommand("notification_previous", Bundle.EMPTY))
+ .build()
+ )
+ } else {
+ list
}
- KeyEvent.KEYCODE_MEDIA_NEXT -> {
- Log.d("Player", "Player: Key Code : Next")
- PlayerSingleton.next()
+ )
+ }
+
+
+ @UnstableApi
+ override fun onMediaButtonEvent(
+ session: MediaSession,
+ controllerInfo: MediaSession.ControllerInfo,
+ intent: Intent
+ ): Boolean {
+ onMediaButtonEventHandler(intent)
+ return true
+ }
+
+ @UnstableApi
+ fun onMediaButtonEventHandler(intent: Intent?) {
+
+ if (intent == null) {
+ return
+ }
+
+ if (Intent.ACTION_MEDIA_BUTTON == intent.action) {
+ @Suppress("DEPRECATION") val ke =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT, KeyEvent::class.java)
+ } else {
+ intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT)
+ }
+ if (ke == null) {
+ return
}
- KeyEvent.KEYCODE_MEDIA_PREVIOUS -> {
- Log.d("Player", "Player: Key Code : Previous")
- PlayerSingleton.previous()
+
+ if (ke.action == KeyEvent.ACTION_UP) {
+ return
}
- KeyEvent.KEYCODE_MEDIA_STOP -> {
- PlayerSingleton.stop()
+
+ Log.d("Player", "Key: $ke")
+ Log.d("Player", "#MEDIA3# - Key: $ke")
+ when (ke.keyCode) {
+ KeyEvent.KEYCODE_MEDIA_PLAY -> {
+ PlayerSingleton.play()
+ }
+
+ KeyEvent.KEYCODE_MEDIA_PAUSE -> {
+ PlayerSingleton.pause()
+ }
+
+ KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE -> {
+ Log.d("Player", "Player: Key Code : PlayPause")
+ mediaService?.togglePlayPause()
+ }
+
+ KeyEvent.KEYCODE_MEDIA_NEXT -> {
+ Log.d("Player", "Player: Key Code : Next")
+ PlayerSingleton.next()
+ }
+
+ KeyEvent.KEYCODE_MEDIA_PREVIOUS -> {
+ Log.d("Player", "Player: Key Code : Previous")
+ PlayerSingleton.previous()
+ }
+
+ KeyEvent.KEYCODE_MEDIA_STOP -> {
+ PlayerSingleton.stop()
+ }
}
+ } else if (intent.hasExtra(PlayerPlugin.FAVORITE)) {
+ PlayerSingleton.favorite(intent.getBooleanExtra(PlayerPlugin.FAVORITE, false))
}
+ return
}
-}
\ No newline at end of file
+}
+
diff --git a/packages/player/android/src/main/kotlin/br/com/suamusica/player/MediaControlBroadcastReceiver.kt b/packages/player/android/src/main/kotlin/br/com/suamusica/player/MediaControlBroadcastReceiver.kt
deleted file mode 100644
index 3a95336e..00000000
--- a/packages/player/android/src/main/kotlin/br/com/suamusica/player/MediaControlBroadcastReceiver.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package br.com.suamusica.player
-
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-
-class MediaControlBroadcastReceiver : BroadcastReceiver() {
- override fun onReceive(context: Context?, intent: Intent?) {
- MediaButtonEventHandler().onMediaButtonEventHandler(intent)
- }
-}
\ No newline at end of file
diff --git a/packages/player/android/src/main/kotlin/br/com/suamusica/player/MediaMetadataCompatExt.kt b/packages/player/android/src/main/kotlin/br/com/suamusica/player/MediaMetadataCompatExt.kt
deleted file mode 100644
index b183dbc9..00000000
--- a/packages/player/android/src/main/kotlin/br/com/suamusica/player/MediaMetadataCompatExt.kt
+++ /dev/null
@@ -1,371 +0,0 @@
-package br.com.suamusica.player
-
-import android.graphics.Bitmap
-import android.net.Uri
-import android.support.v4.media.MediaBrowserCompat.MediaItem
-import android.support.v4.media.MediaDescriptionCompat
-import android.support.v4.media.MediaMetadataCompat
-import android.support.v4.media.MediaMetadataCompat.*
-import android.util.Log
-import androidx.collection.ArrayMap
-
-inline val MediaMetadataCompat.METADATA_KEY_IS_VERIFIED: String
- get() = "android.media.metadata.METADATA_KEY_IS_VERIFIED"
-
-inline val MediaMetadataCompat.METADATA_KEY_SHARE_URL: String
- get() = "android.media.metadata.METADATA_KEY_SHARE_URL"
-
-inline val MediaMetadataCompat.METADATA_KEY_ALBUM_ID: String
- get() = "android.media.metadata.METADATA_KEY_ALBUM_ID"
-
-inline val MediaMetadataCompat.METADATA_KEY_PLAYLIST_ID: String
- get() = "android.media.metadata.METADATA_KEY_PLAYLIST_ID"
-
-inline val MediaMetadataCompat.METADATA_KEY_ARTIST_ID: String
- get() = "android.media.metadata.METADATA_KEY_ARTIST_ID"
-
-inline val MediaMetadataCompat.id get() = getString(METADATA_KEY_MEDIA_ID) ?: ""
-
-inline val MediaMetadataCompat.title get() = getString(METADATA_KEY_TITLE) ?: ""
-
-inline val MediaMetadataCompat.artist get() = getString(METADATA_KEY_ARTIST) ?: ""
-
-inline val MediaMetadataCompat.duration
- get() = getLong(MediaMetadataCompat.METADATA_KEY_DURATION) ?: 0
-
-inline val MediaMetadataCompat.album get() = getString(METADATA_KEY_ALBUM) ?: ""
-
-inline val MediaMetadataCompat.author get() = getString(METADATA_KEY_AUTHOR) ?: ""
-
-inline val MediaMetadataCompat.writer get() = getString(METADATA_KEY_WRITER) ?: ""
-
-inline val MediaMetadataCompat.composer get() = getString(METADATA_KEY_COMPOSER) ?: ""
-
-inline val MediaMetadataCompat.compilation
- get() = getString(METADATA_KEY_COMPILATION) ?: ""
-
-inline val MediaMetadataCompat.date get() = getString(METADATA_KEY_DATE) ?: ""
-
-inline val MediaMetadataCompat.year get() = getString(METADATA_KEY_YEAR) ?: ""
-
-inline val MediaMetadataCompat.genre get() = getString(METADATA_KEY_GENRE) ?: ""
-
-inline val MediaMetadataCompat.trackNumber
- get() = getLong(METADATA_KEY_TRACK_NUMBER)
-
-inline val MediaMetadataCompat.trackCount
- get() = getLong(METADATA_KEY_NUM_TRACKS)
-
-inline val MediaMetadataCompat.discNumber
- get() = getLong(MediaMetadataCompat.METADATA_KEY_DISC_NUMBER)
-
-inline val MediaMetadataCompat.albumArtist
- get() = getString(METADATA_KEY_ALBUM_ARTIST) ?: ""
-
-inline val MediaMetadataCompat.art get(): Bitmap? = getBitmap(MediaMetadataCompat.METADATA_KEY_ART)
-
-inline val MediaMetadataCompat.artUri
- get() = Uri.parse(this.getString(METADATA_KEY_ART_URI) ?: "")
-
-inline val MediaMetadataCompat.albumArt
- get(): Bitmap? = getBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART)
-
-inline val MediaMetadataCompat.albumArtUri
- get() = Uri.parse(this.getString(METADATA_KEY_ALBUM_ART_URI) ?: "")
-
-inline val MediaMetadataCompat.userRating
- get() = getLong(MediaMetadataCompat.METADATA_KEY_USER_RATING)
-
-inline val MediaMetadataCompat.rating get() = getLong(MediaMetadataCompat.METADATA_KEY_RATING)
-
-inline val MediaMetadataCompat.displayTitle
- get() = getString(METADATA_KEY_DISPLAY_TITLE)
-
-inline val MediaMetadataCompat.displaySubtitle
- get() = getString(METADATA_KEY_DISPLAY_SUBTITLE)
-
-inline val MediaMetadataCompat.displayDescription
- get() = getString(METADATA_KEY_DISPLAY_DESCRIPTION)
-
-inline val MediaMetadataCompat.displayIcon
- get() = getBitmap(METADATA_KEY_DISPLAY_ICON)
-
-inline val MediaMetadataCompat.displayIconUri
- get() = Uri.parse(this.getString(METADATA_KEY_DISPLAY_ICON_URI) ?: "")
-
-inline val MediaMetadataCompat.mediaUri
- get() = Uri.parse(this.getString(METADATA_KEY_MEDIA_URI) ?: "")
-
-inline val MediaMetadataCompat.downloadStatus
- get() = getLong(METADATA_KEY_DOWNLOAD_STATUS)
-
-inline val MediaMetadataCompat.isVerified
- get(): Boolean = getString("METADATA_KEY_IS_VERIFIED") ?: "0" == "1"
-
-inline val MediaMetadataCompat.shareUrl
- get() = this.getString("METADATA_KEY_SHARE_URL")
-
-inline val MediaMetadataCompat.albumId
- get() = this.getString("METADATA_KEY_ALBUM_ID")
-
-inline val MediaMetadataCompat.playlistId
- get() = getString("METADATA_KEY_PLAYLIST_ID")
-
-inline val MediaMetadataCompat.artistId
- get() = getString("METADATA_KEY_ARTIST_ID")
-
-// @MediaItem.Flags
-inline val MediaMetadataCompat.flag
- get() = this.getLong(METADATA_KEY_UAMP_FLAGS).toInt()
-
-/**
- * Useful extensions for [MediaMetadataCompat.Builder].
- */
-
-// These do not have getters, so create a message for the error.
-const val NO_GET = "Property does not have a 'get'"
-
-inline var Builder.id: String
- @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
- get() = throw IllegalAccessException("Cannot get from MediaMetadataCompat.Builder")
- set(value) {
- putString(METADATA_KEY_MEDIA_ID, value)
- }
-
-inline var Builder.title: String?
- @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
- get() = throw IllegalAccessException("Cannot get from MediaMetadataCompat.Builder")
- set(value) {
- putString(METADATA_KEY_TITLE, value)
- }
-
-inline var Builder.artist: String?
- @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
- get() = throw IllegalAccessException("Cannot get from MediaMetadataCompat.Builder")
- set(value) {
- putString(METADATA_KEY_ARTIST, value)
- }
-
-inline var Builder.album: String?
- @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
- get() = throw IllegalAccessException("Cannot get from MediaMetadataCompat.Builder")
- set(value) {
- putString(METADATA_KEY_ALBUM, value)
- }
-
-inline var Builder.duration: Long
- @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
- get() = throw IllegalAccessException("Cannot get from MediaMetadataCompat.Builder")
- set(value) {
- putLong(MediaMetadataCompat.METADATA_KEY_DURATION, value)
- }
-
-inline var Builder.genre: String?
- @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
- get() = throw IllegalAccessException("Cannot get from MediaMetadataCompat.Builder")
- set(value) {
- putString(METADATA_KEY_GENRE, value)
- }
-
-inline var Builder.mediaUri: String?
- @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
- get() = throw IllegalAccessException("Cannot get from MediaMetadataCompat.Builder")
- set(value) {
- putString(METADATA_KEY_MEDIA_URI, value)
- }
-
-inline var Builder.albumArtUri: String?
- @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
- get() = throw IllegalAccessException("Cannot get from MediaMetadataCompat.Builder")
- set(value) {
- putString(METADATA_KEY_ALBUM_ART_URI, value)
- }
-
-inline var Builder.albumArt: Bitmap?
- @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
- get() = throw IllegalAccessException("Cannot get from MediaMetadataCompat.Builder")
- set(value) {
- putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, value)
- }
-
-inline var Builder.trackNumber: Long
- @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
- get() = throw IllegalAccessException("Cannot get from MediaMetadataCompat.Builder")
- set(value) {
- putLong(METADATA_KEY_TRACK_NUMBER, value)
- }
-
-inline var Builder.trackCount: Long
- @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
- get() = throw IllegalAccessException("Cannot get from MediaMetadataCompat.Builder")
- set(value) {
- putLong(METADATA_KEY_NUM_TRACKS, value)
- }
-
-inline var Builder.displayTitle: String?
- @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
- get() = throw IllegalAccessException("Cannot get from MediaMetadataCompat.Builder")
- set(value) {
- putString(METADATA_KEY_DISPLAY_TITLE, value)
- }
-
-inline var Builder.displaySubtitle: String?
- @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
- get() = throw IllegalAccessException("Cannot get from MediaMetadataCompat.Builder")
- set(value) {
- putString(METADATA_KEY_DISPLAY_SUBTITLE, value)
- }
-
-inline var Builder.displayDescription: String?
- @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
- get() = throw IllegalAccessException("Cannot get from MediaMetadataCompat.Builder")
- set(value) {
- putString(METADATA_KEY_DISPLAY_DESCRIPTION, value)
- }
-
-inline var Builder.displayIconUri: String?
- @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
- get() = throw IllegalAccessException("Cannot get from MediaMetadataCompat.Builder")
- set(value) {
- putString(METADATA_KEY_DISPLAY_ICON_URI, value)
- }
-
-inline var Builder.downloadStatus: Long
- @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
- get() = throw IllegalAccessException("Cannot get from MediaMetadataCompat.Builder")
- set(value) {
- putLong(METADATA_KEY_DOWNLOAD_STATUS, value)
- }
-
-inline var Builder.compilation: String?
- @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
- get() = throw IllegalAccessException("Cannot get from MediaMetadataCompat.Builder")
- set(value) {
- putString(METADATA_KEY_COMPILATION, value)
- }
-
-inline var Builder.isVerified: Boolean?
- @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
- get() = throw IllegalAccessException("Cannot get from MediaMetadataCompat.Builder")
- set(value) {
- putString("METADATA_KEY_IS_VERIFIED", if (value == true) "1" else "0")
- }
-
-inline var Builder.shareUrl: String?
- @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
- get() = throw IllegalAccessException("Cannot get from MediaMetadataCompat.Builder")
- set(value) {
- putString("METADATA_KEY_SHARE_URL", value)
- }
-
-inline var Builder.albumId: String?
- @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
- get() = throw IllegalAccessException("Cannot get from MediaMetadataCompat.Builder")
- set(value) {
- putString("METADATA_KEY_ALBUM_ID", value)
- }
-
-inline var Builder.playlistId: String?
- @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
- get() = throw IllegalAccessException("Cannot get from MediaMetadataCompat.Builder")
- set(value) {
- putString("METADATA_KEY_PLAYLIST_ID", value)
- }
-
-inline var Builder.artistId: String?
- @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
- get() = throw IllegalAccessException("Cannot get from MediaMetadataCompat.Builder")
- set(value) {
- putString("METADATA_KEY_ARTIST_ID", value)
- }
-
-/**
- * Custom property for storing whether a [MediaMetadataCompat] item represents an
- * item that is [MediaItem.FLAG_BROWSABLE] or [MediaItem.FLAG_PLAYABLE].
- */
-// @MediaItem.Flags
-inline var Builder.flag: Int
- @Deprecated(NO_GET, level = DeprecationLevel.ERROR)
- get() = throw IllegalAccessException("Cannot get from MediaMetadataCompat.Builder")
- set(value) {
- putLong(METADATA_KEY_UAMP_FLAGS, value.toLong())
- }
-
-/**
- * Custom property for retrieving a [MediaDescriptionCompat] which also includes
- * all of the keys from the [MediaMetadataCompat] object in its extras.
- *
- * These keys are used by the ExoPlayer MediaSession extension when announcing metadata changes.
- */
-inline val MediaMetadataCompat.fullDescription
- get() =
- description.also {
- it.extras?.putAll(bundle)
- }
-
-fun MediaMetadataCompat.toMap(): Map {
- val mutableMap = mutableMapOf()
-
- this.let { extras ->
- val keySet = extras.keySet()
- val iterator = keySet.iterator()
- val metadataTypeLong = 0
- val metadataTypeText = 1
- val metadataTypeBitmap = 2
- val metadataTypeRating = 3
-
- val metadataKeyType = ArrayMap()
- metadataKeyType[METADATA_KEY_TITLE] = metadataTypeText
- metadataKeyType[METADATA_KEY_ARTIST] = metadataTypeText
- metadataKeyType[METADATA_KEY_ALBUM] = metadataTypeText
- metadataKeyType[METADATA_KEY_AUTHOR] = metadataTypeText
- metadataKeyType[METADATA_KEY_WRITER] = metadataTypeText
- metadataKeyType[METADATA_KEY_COMPOSER] = metadataTypeText
- metadataKeyType[METADATA_KEY_COMPILATION] = metadataTypeText
- metadataKeyType[METADATA_KEY_DATE] = metadataTypeText
- metadataKeyType[METADATA_KEY_GENRE] = metadataTypeText
- metadataKeyType[METADATA_KEY_ALBUM_ARTIST] = metadataTypeText
- metadataKeyType[METADATA_KEY_ART_URI] = metadataTypeText
- metadataKeyType[METADATA_KEY_ALBUM_ART_URI] = metadataTypeText
- metadataKeyType[METADATA_KEY_DISPLAY_TITLE] = metadataTypeText
- metadataKeyType[METADATA_KEY_DISPLAY_SUBTITLE] = metadataTypeText
- metadataKeyType[METADATA_KEY_DISPLAY_DESCRIPTION] = metadataTypeText
- metadataKeyType[METADATA_KEY_DISPLAY_ICON_URI] = metadataTypeText
- metadataKeyType[METADATA_KEY_MEDIA_ID] = metadataTypeText
- metadataKeyType[METADATA_KEY_MEDIA_URI] = metadataTypeText
- metadataKeyType[METADATA_KEY_DURATION] = metadataTypeLong
- metadataKeyType[METADATA_KEY_YEAR] = metadataTypeLong
- metadataKeyType[METADATA_KEY_TRACK_NUMBER] = metadataTypeLong
- metadataKeyType[METADATA_KEY_NUM_TRACKS] = metadataTypeLong
- metadataKeyType[METADATA_KEY_DISC_NUMBER] = metadataTypeLong
- metadataKeyType[METADATA_KEY_BT_FOLDER_TYPE] = metadataTypeLong
- metadataKeyType[METADATA_KEY_ADVERTISEMENT] = metadataTypeLong
- metadataKeyType[METADATA_KEY_DOWNLOAD_STATUS] = metadataTypeLong
- metadataKeyType[METADATA_KEY_ART] = metadataTypeBitmap
- metadataKeyType[METADATA_KEY_ALBUM_ART] = metadataTypeBitmap
- metadataKeyType[METADATA_KEY_DISPLAY_ICON] = metadataTypeBitmap
- metadataKeyType[METADATA_KEY_USER_RATING] = metadataTypeRating
- metadataKeyType[METADATA_KEY_RATING] = metadataTypeRating
-
-
- while (iterator.hasNext()) {
- val key = iterator.next()
- when (metadataKeyType[key]) {
- metadataTypeLong -> mutableMap[key] = extras.getLong(key)
- metadataTypeText -> mutableMap[key] = extras.getString(key)
- metadataTypeRating -> mutableMap[key] = extras.getRating(key)
- metadataTypeBitmap -> Log.d("MediaMetadataCompat", "toMap - ignoring Bitmap.")
- else -> Log.d("MediaMetadataCompat", "toMap - ignoring ${metadataKeyType[key]}.")
- }
- }
- }
-
- return mutableMap.toMap()
-}
-
-
-/**
- * Custom property that holds whether an item is [MediaItem.FLAG_BROWSABLE] or
- * [MediaItem.FLAG_PLAYABLE].
- */
-const val METADATA_KEY_UAMP_FLAGS = "com.example.android.uamp.media.METADATA_KEY_UAMP_FLAGS"
\ No newline at end of file
diff --git a/packages/player/android/src/main/kotlin/br/com/suamusica/player/MediaService.kt b/packages/player/android/src/main/kotlin/br/com/suamusica/player/MediaService.kt
index 07f44808..79ae3e3a 100644
--- a/packages/player/android/src/main/kotlin/br/com/suamusica/player/MediaService.kt
+++ b/packages/player/android/src/main/kotlin/br/com/suamusica/player/MediaService.kt
@@ -1,10 +1,7 @@
package br.com.suamusica.player
-import android.annotation.SuppressLint
-import android.app.Notification
import android.app.PendingIntent
import android.app.Service
-import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
@@ -15,44 +12,40 @@ import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.PowerManager
-import android.support.v4.media.MediaBrowserCompat
-import android.support.v4.media.MediaDescriptionCompat
-import android.support.v4.media.MediaMetadataCompat
-import android.support.v4.media.session.MediaControllerCompat
-import android.support.v4.media.session.MediaSessionCompat
import android.support.v4.media.session.PlaybackStateCompat
import android.util.Log
-import androidx.core.app.NotificationManagerCompat
-import androidx.core.content.ContextCompat
-import androidx.media.session.MediaButtonReceiver
+import androidx.media3.common.AudioAttributes
+import androidx.media3.common.C
+import androidx.media3.common.ForwardingPlayer
+import androidx.media3.common.MediaItem
+import androidx.media3.common.MediaMetadata
+import androidx.media3.common.MediaMetadata.PICTURE_TYPE_FRONT_COVER
+import androidx.media3.common.PlaybackException
+import androidx.media3.common.PlaybackParameters
+import androidx.media3.common.Player
+import androidx.media3.common.Player.DISCONTINUITY_REASON_SEEK
+import androidx.media3.common.Timeline
+import androidx.media3.common.Tracks
+import androidx.media3.common.util.UnstableApi
+import androidx.media3.common.util.Util
+import androidx.media3.datasource.DataSource
+import androidx.media3.datasource.DataSourceBitmapLoader
+import androidx.media3.datasource.DefaultHttpDataSource
+import androidx.media3.datasource.FileDataSource
+import androidx.media3.exoplayer.ExoPlayer
+import androidx.media3.exoplayer.hls.HlsMediaSource
+import androidx.media3.exoplayer.source.ProgressiveMediaSource
+import androidx.media3.session.CacheBitmapLoader
+import androidx.media3.session.CommandButton
+import androidx.media3.session.DefaultMediaNotificationProvider
+import androidx.media3.session.MediaController
+import androidx.media3.session.MediaNotification
+import androidx.media3.session.MediaSession
+import androidx.media3.session.MediaSessionService
import br.com.suamusica.player.media.parser.SMHlsPlaylistParserFactory
-import com.bumptech.glide.Glide
-import com.bumptech.glide.load.engine.DiskCacheStrategy
-import com.bumptech.glide.request.FutureTarget
-import com.bumptech.glide.request.RequestOptions
-import com.google.android.exoplayer2.C
-import com.google.android.exoplayer2.ExoPlayer
-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.Player.DISCONTINUITY_REASON_SEEK
-import com.google.android.exoplayer2.Timeline
-import com.google.android.exoplayer2.Tracks
-import com.google.android.exoplayer2.audio.AudioAttributes
-import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
-import com.google.android.exoplayer2.ext.mediasession.TimelineQueueNavigator
-import com.google.android.exoplayer2.source.ProgressiveMediaSource
-import com.google.android.exoplayer2.source.hls.HlsMediaSource
-import com.google.android.exoplayer2.upstream.DataSource
-import com.google.android.exoplayer2.upstream.DefaultHttpDataSource
-import com.google.android.exoplayer2.upstream.FileDataSource
-import com.google.android.exoplayer2.util.Util
-import kotlinx.coroutines.DelicateCoroutinesApi
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
+import com.google.common.collect.ImmutableList
+import com.google.common.util.concurrent.ListenableFuture
+import java.io.ByteArrayOutputStream
import java.io.File
import java.io.BufferedReader
import java.io.InputStreamReader
@@ -60,28 +53,22 @@ import java.io.IOException
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicBoolean
-class MediaService : androidx.media.MediaBrowserServiceCompat() {
+@UnstableApi
+class MediaService : MediaSessionService() {
private val TAG = "MediaService"
private val userAgent =
"SuaMusica/player (Linux; Android ${Build.VERSION.SDK_INT}; ${Build.BRAND}/${Build.MODEL})"
private var packageValidator: PackageValidator? = null
-
- private var mediaSession: MediaSessionCompat? = null
- private var mediaController: MediaControllerCompat? = null
- private var mediaSessionConnector: MediaSessionConnector? = null
-
private var media: Media? = null
-
- private var notificationBuilder: NotificationBuilder? = null
- private var notificationManager: NotificationManagerCompat? = null
private var isForegroundService = false
private var wifiLock: WifiManager.WifiLock? = null
private var wakeLock: PowerManager.WakeLock? = null
+ var mediaSession: MediaSession? = null
+ private var mediaController: ListenableFuture? = null
- private val uAmpAudioAttributes = AudioAttributes.Builder()
- .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
- .setUsage(C.USAGE_MEDIA)
- .build()
+ private val uAmpAudioAttributes =
+ AudioAttributes.Builder().setContentType(C.AUDIO_CONTENT_TYPE_MUSIC).setUsage(C.USAGE_MEDIA)
+ .build()
private var player: ExoPlayer? = null
@@ -89,143 +76,105 @@ class MediaService : androidx.media.MediaBrowserServiceCompat() {
private var previousState: Int = -1
- private val BROWSABLE_ROOT = "/"
- private val EMPTY_ROOT = "@empty@"
-
- private val isSamsung = Build.MANUFACTURER.equals("samsung", ignoreCase = true)
- private val isHyperOS = !getProperty("ro.mi.os.version.name").isNullOrBlank()
-
- companion object {
- private val glideOptions = RequestOptions()
- .fallback(R.drawable.default_art)
- .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
- .timeout(5000)
-
- private const val NOTIFICATION_LARGE_ICON_SIZE = 500 // px
- private const val LOCAL_COVER_PNG = "../app_flutter/covers/0.png" // px
-
- @OptIn(DelicateCoroutinesApi::class)
- fun getArts(context: Context, artUri: String?, callback: (Bitmap?) -> Unit) {
- GlobalScope.launch(Dispatchers.IO) {
- Log.i("getArts", " artUri: $artUri")
- val glider = Glide.with(context)
- .applyDefaultRequestOptions(glideOptions)
- .asBitmap()
- val file = File(context.filesDir, LOCAL_COVER_PNG)
- var bitmap: Bitmap? = null
- val futureTarget: FutureTarget? = when {
- !artUri.isNullOrBlank() -> glider.load(artUri)
- .submit(NOTIFICATION_LARGE_ICON_SIZE, NOTIFICATION_LARGE_ICON_SIZE)
-
- file.exists() -> glider.load(Uri.fromFile(file))
- .submit(NOTIFICATION_LARGE_ICON_SIZE, NOTIFICATION_LARGE_ICON_SIZE)
-
- else -> null
- }
-
- if (futureTarget != null) {
- bitmap = try {
- futureTarget.get()
- } catch (e: Exception) {
- Log.i("getArts", "ART EXCP: $e")
- if (file.exists()) {
- BitmapFactory.decodeFile(file.absolutePath)
- } else {
- null
- }
- }
- }
- withContext(Dispatchers.Main) {
- callback(bitmap)
- }
- }
- }
- }
+ private lateinit var dataSourceBitmapLoader: DataSourceBitmapLoader
+ private lateinit var mediaButtonEventHandler: MediaButtonEventHandler
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.d(TAG, "onStartCommand")
+ super.onStartCommand(intent, flags, startId)
return Service.START_STICKY
}
override fun onCreate() {
super.onCreate()
+ mediaButtonEventHandler = MediaButtonEventHandler(this)
packageValidator = PackageValidator(applicationContext, R.xml.allowed_media_browser_callers)
- notificationBuilder = NotificationBuilder(this)
- notificationManager = NotificationManagerCompat.from(this)
- wifiLock = (applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager)
- .createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "suamusica:wifiLock")
- wakeLock = (applicationContext.getSystemService(Context.POWER_SERVICE) as PowerManager)
- .newWakeLock(
+ wifiLock =
+ (applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager).createWifiLock(
+ WifiManager.WIFI_MODE_FULL_HIGH_PERF, "suamusica:wifiLock"
+ )
+ wakeLock =
+ (applicationContext.getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK or PowerManager.ON_AFTER_RELEASE,
"suamusica:wakeLock"
)
wifiLock?.setReferenceCounted(false)
wakeLock?.setReferenceCounted(false)
- val sessionActivityPendingIntent =
- this.packageManager?.getLaunchIntentForPackage(this.packageName)?.let { sessionIntent ->
- PendingIntent.getActivity(this, 0, sessionIntent, PendingIntent.FLAG_IMMUTABLE)
- }
-
- val mediaButtonReceiver = ComponentName(this, MediaButtonReceiver::class.java)
- mediaSession = mediaSession?.let { it }
- ?: MediaSessionCompat(this, TAG, mediaButtonReceiver, null)
- .apply {
- setSessionActivity(sessionActivityPendingIntent)
- isActive = true
- }
-
- mediaSession?.setFlags(MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS)
-
player = ExoPlayer.Builder(this).build().apply {
setAudioAttributes(uAmpAudioAttributes, true)
addListener(playerEventListener())
// setWakeMode(C.WAKE_MODE_NETWORK)
setHandleAudioBecomingNoisy(true)
}
- mediaSession?.let { mediaSession ->
- val sessionToken = mediaSession.sessionToken
- // we must connect the service to the media session
- this.sessionToken = sessionToken
-
- val mediaControllerCallback = MediaControllerCallback()
-
- mediaController = MediaControllerCompat(this, sessionToken).also { mediaController ->
- mediaController.registerCallback(mediaControllerCallback)
-
- mediaSessionConnector = MediaSessionConnector(mediaSession).also { connector ->
- connector.setPlayer(player)
- connector.setPlaybackPreparer(MusicPlayerPlaybackPreparer(this))
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- if (isSamsung || isHyperOS) {
- connector.setCustomActionProviders(
- FavoriteModeActionProvider(applicationContext),
- NextActionProvider(),
- PreviousActionProvider(),
- )
- } else {
- connector.setCustomActionProviders(
- FavoriteModeActionProvider(applicationContext),
- PreviousActionProvider(),
- NextActionProvider(),
- )
- }
+
+ dataSourceBitmapLoader =
+ DataSourceBitmapLoader(applicationContext)
+ Log.d(TAG, "MEDIA3 - handleCustomCommand ${Build.VERSION_CODES.TIRAMISU}")
+ player?.let {
+ mediaSession = MediaSession.Builder(this, it)
+ .setBitmapLoader(CacheBitmapLoader(dataSourceBitmapLoader))
+ .setCallback(mediaButtonEventHandler)
+ .build()
+
+ this@MediaService.setMediaNotificationProvider(object : MediaNotification.Provider {
+ override fun createNotification(
+ mediaSession: MediaSession,
+ customLayout: ImmutableList,
+ actionFactory: MediaNotification.ActionFactory,
+ onNotificationChangedCallback: MediaNotification.Provider.Callback
+ ): MediaNotification {
+ val defaultMediaNotificationProvider =
+ DefaultMediaNotificationProvider(applicationContext)
+ .apply {
+ setSmallIcon(R.drawable.ic_notification)
+ }
+
+ val customMedia3Notification =
+ defaultMediaNotificationProvider.createNotification(
+ mediaSession,
+ mediaSession.customLayout,
+ actionFactory,
+ onNotificationChangedCallback,
+ )
+
+ val notifyIntent = Intent("SUA_MUSICA_FLUTTER_NOTIFICATION_CLICK").apply {
+ addCategory(Intent.CATEGORY_DEFAULT)
+ flags =
+ Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
}
- connector.setMediaButtonEventHandler(MediaButtonEventHandler())
- connector.setEnabledPlaybackActions(
- PlaybackStateCompat.ACTION_PLAY
- or PlaybackStateCompat.ACTION_PAUSE
- or PlaybackStateCompat.ACTION_REWIND
- or PlaybackStateCompat.ACTION_FAST_FORWARD
- or PlaybackStateCompat.ACTION_SEEK_TO
+ val NOW_PLAYING_NOTIFICATION = 0xb339
+ val notifyPendingIntent = PendingIntent.getActivity(
+ applicationContext,
+ 0,
+ notifyIntent,
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT else PendingIntent.FLAG_UPDATE_CURRENT
)
+ return MediaNotification(
+ NOW_PLAYING_NOTIFICATION,
+ customMedia3Notification.notification.apply {
+ contentIntent = notifyPendingIntent
+ })
}
- }
+
+ override fun handleCustomCommand(
+ session: MediaSession,
+ action: String,
+ extras: Bundle
+ ): Boolean {
+ Log.d(TAG, "#MEDIA3# - handleCustomCommand $action")
+ return false
+ }
+ })
}
}
- override fun onTaskRemoved(rootIntent: Intent) {
+ override fun onGetSession(
+ controllerInfo: MediaSession.ControllerInfo
+ ): MediaSession? = mediaSession
+
+ override fun onTaskRemoved(rootIntent: Intent?) {
Log.d(TAG, "onTaskRemoved")
super.onTaskRemoved(rootIntent)
@@ -243,16 +192,13 @@ class MediaService : androidx.media.MediaBrowserServiceCompat() {
override fun onDestroy() {
removeNotification()
Log.d(TAG, "onDestroy")
-// mediaController?.unregisterCallback(mediaControllerCallback)
releaseLock()
- mediaSessionConnector?.setPlayer(null)
player?.release()
stopSelf()
mediaSession?.run {
- isActive = false
- release()
- Log.d("MusicService", "onDestroy(isActive: $isActive)")
+ releaseAndPerformAndDisableTracking()
+ Log.d("MusicService", "onDestroy")
}
releasePossibleLeaks()
@@ -262,12 +208,9 @@ class MediaService : androidx.media.MediaBrowserServiceCompat() {
private fun releasePossibleLeaks() {
player?.release()
- notificationManager = null
- notificationBuilder = null
packageValidator = null
mediaSession = null
mediaController = null
- mediaSessionConnector = null
wifiLock = null
wakeLock = null
@@ -287,91 +230,28 @@ class MediaService : androidx.media.MediaBrowserServiceCompat() {
}
}
- override fun onGetRoot(
- clientPackageName: String,
- clientUid: Int,
- rootHints: Bundle?
- ): BrowserRoot? {
- val isKnowCaller = packageValidator?.isKnownCaller(clientPackageName, clientUid) ?: false
-
- return if (isKnowCaller) {
- BrowserRoot(BROWSABLE_ROOT, null)
- } else {
- BrowserRoot(EMPTY_ROOT, null)
- }
- }
-
- override fun onLoadChildren(
- parentId: String,
- result: Result>
- ) {
- result.sendResult(mutableListOf())
- }
-
fun prepare(cookie: String, media: Media) {
this.media = media
-
val dataSourceFactory = DefaultHttpDataSource.Factory()
dataSourceFactory.setReadTimeoutMs(15 * 1000)
dataSourceFactory.setConnectTimeoutMs(10 * 1000)
dataSourceFactory.setUserAgent(userAgent)
dataSourceFactory.setAllowCrossProtocolRedirects(true)
dataSourceFactory.setDefaultRequestProperties(mapOf("Cookie" to cookie))
-
- // Metadata Build
- val metadataBuilder = MediaMetadataCompat.Builder()
- val art = null
- metadataBuilder.apply {
- album = media.author
- albumArt = art
- title = media.name
- displayTitle = media.name
- putString(MediaMetadataCompat.METADATA_KEY_ARTIST, media.author)
- putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, media.name)
- putBitmap(MediaMetadataCompat.METADATA_KEY_ART, art)
- putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, art)
- }
- val metadata = metadataBuilder.build()
- mediaSession?.setMetadata(metadata)
- mediaSessionConnector?.setMediaMetadataProvider {
- return@setMediaMetadataProvider metadata
- }
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
- val timelineQueueNavigator = object : TimelineQueueNavigator(mediaSession!!) {
- override fun getSupportedQueueNavigatorActions(player: Player): Long {
- return PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS or
- PlaybackStateCompat.ACTION_SKIP_TO_NEXT or
- PlaybackStateCompat.ACTION_SEEK_TO
- }
-
- override fun getMediaDescription(
- player: Player,
- windowIndex: Int
- ): MediaDescriptionCompat {
- player.let {
- return MediaDescriptionCompat.Builder().apply {
- setTitle(media.author)
- setSubtitle(media.name)
- setIconUri(Uri.parse(media.coverUrl))
- }.build()
- }
- }
- }
- mediaSessionConnector?.setQueueNavigator(timelineQueueNavigator)
- }
+ val metadata = buildMetaData(media)
val url = media.url
Log.i(TAG, "Player: URL: $url")
val uri = if (url.startsWith("/")) Uri.fromFile(File(url)) else Uri.parse(url)
-
+ val mediaItem = MediaItem.Builder().setUri(uri).setMediaMetadata(metadata).build()
@C.ContentType val type = Util.inferContentType(uri)
- Log.i(TAG, "Player: Type: $type HLS: ${C.TYPE_HLS}")
+ Log.i(TAG, "Player: Type: $type HLS: ${C.CONTENT_TYPE_HLS}")
val source = when (type) {
- C.TYPE_HLS -> HlsMediaSource.Factory(dataSourceFactory)
+ C.CONTENT_TYPE_HLS -> HlsMediaSource.Factory(dataSourceFactory)
.setPlaylistParserFactory(SMHlsPlaylistParserFactory())
- .setAllowChunklessPreparation(true)
- .createMediaSource(MediaItem.fromUri(uri))
- C.TYPE_OTHER -> {
+ .setAllowChunklessPreparation(true).createMediaSource(mediaItem)
+
+ C.CONTENT_TYPE_OTHER -> {
Log.i(TAG, "Player: URI: $uri")
val factory: DataSource.Factory =
if (uri.scheme != null && uri.scheme?.startsWith("http") == true) {
@@ -380,8 +260,9 @@ class MediaService : androidx.media.MediaBrowserServiceCompat() {
FileDataSource.Factory()
}
- ProgressiveMediaSource.Factory(factory).createMediaSource(MediaItem.fromUri(uri))
+ ProgressiveMediaSource.Factory(factory).createMediaSource(mediaItem)
}
+
else -> {
throw IllegalStateException("Unsupported type: $type")
}
@@ -390,62 +271,49 @@ class MediaService : androidx.media.MediaBrowserServiceCompat() {
player?.prepare(source)
}
- fun play() {
- performAndEnableTracking {
- player?.play()
- }
- }
- fun adsPlaying() {
- getArts(applicationContext,null) { bitmap ->
- this.media = Media("Propaganda", "", "", "",null,null )
- val notification = buildNotification(PlaybackStateCompat.STATE_PLAYING, true, bitmap)
- notification?.let {
- notificationManager?.notify(NOW_PLAYING_NOTIFICATION, it)
- shouldStartService(it)
- }
+ private fun buildMetaData(media: Media): MediaMetadata {
+ val metadataBuilder = MediaMetadata.Builder()
+
+ if (media.isFavorite != null) {
+ mediaSession?.sessionExtras?.putBoolean(
+ PlayerPlugin.IS_FAVORITE_ARGUMENT,
+ media.isFavorite
+ )
}
- }
- fun sendCommand(type: String) {
- val extra = Bundle()
- extra.putString("type", type)
- mediaSession?.setExtras(extra)
+ val stream = ByteArrayOutputStream()
- }
+ val art = try {
+ dataSourceBitmapLoader.loadBitmap(Uri.parse(media.bigCoverUrl!!))
+ .get(5000, TimeUnit.MILLISECONDS)
+ } catch (e: Exception) {
+ BitmapFactory.decodeResource(resources, R.drawable.default_art)
+ }
- fun setFavorite(favorite: Boolean?) {
- media?.let {
- this.media = Media(it.name, it.author, it.url, it.coverUrl, it.bigCoverUrl, favorite)
- sendNotification(this.media!!, null)
+ art?.compress(Bitmap.CompressFormat.PNG, 95, stream)
+ metadataBuilder.apply {
+ setAlbumTitle(media.name)
+ setArtist(media.author)
+ setArtworkData(stream.toByteArray(), PICTURE_TYPE_FRONT_COVER)
+ setArtist(media.author)
+ setTitle(media.name)
+ setDisplayTitle(media.name)
}
+ val metadata = metadataBuilder.build()
+ return metadata
}
- fun sendNotification(media: Media, isPlayingExternal: Boolean?) {
- getArts(applicationContext, media.bigCoverUrl ?: media.coverUrl) { bitmap ->
- mediaSession?.let {
- val onGoing: Boolean = if (isPlayingExternal == null) {
- val state = player?.playbackState ?: PlaybackStateCompat.STATE_NONE
- state == PlaybackStateCompat.STATE_PLAYING || state == PlaybackStateCompat.STATE_BUFFERING
- } else {
- isPlayingExternal
- }
- this.media = media
- val notification = notificationBuilder?.buildNotification(
- it,
- media,
- onGoing,
- isPlayingExternal,
- media.isFavorite,
- player?.duration, bitmap
- )
- notification?.let {
- notificationManager?.notify(NOW_PLAYING_NOTIFICATION, notification)
- }
- }
+ fun play() {
+ performAndEnableTracking {
+ player?.play()
}
}
-
+
fun removeNotification() {
- removeNowPlayingNotification();
+ object: ForwardingPlayer(player!!) {
+ override fun getDuration(): Long {
+ return C.TIME_UNSET
+ }
+ }
}
fun seek(position: Long, playWhenReady: Boolean) {
@@ -475,20 +343,12 @@ class MediaService : androidx.media.MediaBrowserServiceCompat() {
}
}
- fun release() {
+ private fun releaseAndPerformAndDisableTracking() {
performAndDisableTracking {
player?.stop()
}
}
- private fun removeNowPlayingNotification() {
- Log.d(TAG, "removeNowPlayingNotification")
- Thread(Runnable {
- notificationManager?.cancel(NOW_PLAYING_NOTIFICATION)
- }).start()
-
- }
-
private fun notifyPositionChange() {
var position = player?.currentPosition ?: 0L
@@ -500,7 +360,7 @@ class MediaService : androidx.media.MediaBrowserServiceCompat() {
extra.putString("type", "position")
extra.putLong("position", position)
extra.putLong("duration", duration)
- mediaSession?.setExtras(extra)
+ mediaSession?.setSessionExtras(extra)
}
}
@@ -535,28 +395,6 @@ class MediaService : androidx.media.MediaBrowserServiceCompat() {
stopTrackingProgress()
}
- private fun buildNotification(
- updatedState: Int,
- onGoing: Boolean,
- art: Bitmap?
- ): Notification? {
- return if (updatedState != PlaybackStateCompat.STATE_NONE) {
- mediaSession?.let {
- notificationBuilder?.buildNotification(
- it,
- media,
- onGoing,
- null,
- media?.isFavorite,
- player?.duration,
- art
- )
- }
- } else {
- null
- }
- }
-
private fun playerEventListener(): Player.Listener {
return object : Player.Listener {
override fun onTimelineChanged(timeline: Timeline, reason: Int) {
@@ -567,16 +405,22 @@ class MediaService : androidx.media.MediaBrowserServiceCompat() {
Log.i(TAG, "onTracksChanged: ")
}
- override fun onLoadingChanged(isLoading: Boolean) {
- Log.i(TAG, "onLoadingChanged: isLoading: $isLoading")
+ override fun onPositionDiscontinuity(
+ oldPosition: Player.PositionInfo,
+ newPosition: Player.PositionInfo,
+ reason: Int
+ ) {
+ Log.i(TAG, "onPositionDiscontinuity: $reason")
+ if (reason == DISCONTINUITY_REASON_SEEK) {
+ val bundle = Bundle()
+ bundle.putString("type", "seek-end")
+ mediaSession?.setSessionExtras(bundle)
+ }
}
- override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
- Log.i(
- TAG,
- "onPlayerStateChanged: playWhenReady: $playWhenReady playbackState: $playbackState currentPlaybackState: ${player?.playbackState}"
- )
- if (playWhenReady) {
+ override fun onIsPlayingChanged(isPlaying: Boolean) {
+ super.onIsPlayingChanged(isPlaying)
+ if (isPlaying) {
val duration = player?.duration ?: 0L
acquireLock(
if (duration > 1L) duration + TimeUnit.MINUTES.toMillis(2) else TimeUnit.MINUTES.toMillis(
@@ -585,47 +429,22 @@ class MediaService : androidx.media.MediaBrowserServiceCompat() {
)
} else
releaseLock()
+ }
- if (playWhenReady && playbackState == ExoPlayer.STATE_READY) {
- //
- } else {
- if (player?.playerError != null) {
- //
+ override fun onPlaybackStateChanged(playbackState: Int) {
+ super.onPlaybackStateChanged(playbackState)
+ if (playbackState == Player.STATE_READY) {
+ if (previousState == -1) {
+ // when we define that the track shall not "playWhenReady"
+ // no position info is sent
+ // therefore, we need to "emulate" the first position notification
+ // by sending it directly
+ notifyPositionChange()
} else {
- when (playbackState) {
- ExoPlayer.STATE_IDLE -> { // 1
- //
- }
- ExoPlayer.STATE_BUFFERING -> { // 2
- //
- }
- ExoPlayer.STATE_READY -> { // 3
- val status =
- if (playWhenReady) PlayerState.PLAYING else PlayerState.PAUSED
- if (previousState == -1) {
- // when we define that the track shall not "playWhenReady"
- // no position info is sent
- // therefore, we need to "emulate" the first position notification
- // by sending it directly
- notifyPositionChange()
- } else {
- if (status == PlayerState.PAUSED) {
- stopTrackingProgressAndPerformTask {
- //
- }
- } else {
- //
- }
-
- }
- }
- ExoPlayer.STATE_ENDED -> { // 4
- stopTrackingProgressAndPerformTask {
- //
- }
- }
- }
+ stopTrackingProgressAndPerformTask {}
}
+ } else if (playbackState == Player.STATE_ENDED) {
+ stopTrackingProgressAndPerformTask {}
}
previousState = playbackState
}
@@ -648,23 +467,12 @@ class MediaService : androidx.media.MediaBrowserServiceCompat() {
.contains("Permission denied")
) "Permission denied" else error.message
)
- mediaSession?.setExtras(bundle)
- }
-
- override fun onPositionDiscontinuity(reason: Int) {
- Log.i(TAG, "onPositionDiscontinuity: $reason")
- if (reason == DISCONTINUITY_REASON_SEEK) {
- val bundle = Bundle()
- bundle.putString("type", "seek-end")
- mediaSession?.setExtras(bundle)
- }
-
+ mediaSession?.setSessionExtras(bundle)
}
override fun onPlaybackParametersChanged(playbackParameters: PlaybackParameters) {
Log.i(TAG, "onPlaybackParametersChanged: $playbackParameters")
}
-
}
}
@@ -698,28 +506,7 @@ class MediaService : androidx.media.MediaBrowserServiceCompat() {
}
}
- fun shouldStartService(notification: Notification) {
- if (!isForegroundService) {
- Log.i(TAG, "Starting Service")
- try {
- ContextCompat.startForegroundService(
- applicationContext,
- Intent(applicationContext, this@MediaService.javaClass)
- )
- startForeground(NOW_PLAYING_NOTIFICATION, notification)
- } catch (e: Exception) {
- startForeground(NOW_PLAYING_NOTIFICATION, notification)
- ContextCompat.startForegroundService(
- applicationContext,
- Intent(applicationContext, this@MediaService.javaClass)
- )
- }
- isForegroundService = true
-
- }
- }
-
- fun stopService() {
+ private fun stopService() {
if (isForegroundService) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
stopForeground(STOP_FOREGROUND_DETACH)
@@ -731,81 +518,4 @@ class MediaService : androidx.media.MediaBrowserServiceCompat() {
Log.i(TAG, "Stopping Service")
}
}
-
- private inner class MediaControllerCallback : MediaControllerCompat.Callback() {
- override fun onMetadataChanged(metadata: MediaMetadataCompat?) {
- Log.d(
- TAG,
- "onMetadataChanged: title: ${metadata?.title} duration: ${metadata?.duration}"
- )
- }
-
- override fun onPlaybackStateChanged(state: PlaybackStateCompat?) {
- Log.d(TAG, "onPlaybackStateChanged state: $state")
- updateNotification(state!!)
- }
-
- override fun onQueueChanged(queue: MutableList?) {
- Log.d(TAG, "onQueueChanged queue: $queue")
- }
-
- @SuppressLint("WakelockTimeout")
- private fun updateNotification(state: PlaybackStateCompat) {
- if (mediaController?.metadata == null || mediaSession == null) {
- return
- }
- getArts(applicationContext,media?.bigCoverUrl ?: media?.coverUrl) { bitmap ->
- val updatedState = state.state
- val onGoing =
- updatedState == PlaybackStateCompat.STATE_PLAYING || updatedState == PlaybackStateCompat.STATE_BUFFERING
- // Skip building a notification when state is "none".
- val notification = if (updatedState != PlaybackStateCompat.STATE_NONE) {
- buildNotification(updatedState, onGoing, bitmap)
- } else {
- null
- }
- Log.d(TAG, "!!! updateNotification state: $updatedState $onGoing")
-
- when (updatedState) {
- PlaybackStateCompat.STATE_BUFFERING,
- PlaybackStateCompat.STATE_PLAYING -> {
- Log.i(TAG, "updateNotification: STATE_BUFFERING or STATE_PLAYING")
- /**
- * This may look strange, but the documentation for [Service.startForeground]
- * notes that "calling this method does *not* put the service in the started
- * state itself, even though the name sounds like it."
- */
- if (notification != null) {
- notificationManager?.notify(NOW_PLAYING_NOTIFICATION, notification)
- shouldStartService(notification)
- }
- }
- else -> {
- if (isForegroundService) {
- // If playback has ended, also stop the service.
- if (updatedState == PlaybackStateCompat.STATE_NONE && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
- stopService()
- }
- if (notification != null) {
- notificationManager?.notify(NOW_PLAYING_NOTIFICATION, notification)
- } else
- removeNowPlayingNotification()
- }
- }
- }
- }
- }
- }
-
- // to get the property value from build.prop.
- private fun getProperty(property: String): String? {
- return try {
- Runtime.getRuntime().exec("getprop $property").inputStream.use { input ->
- BufferedReader(InputStreamReader(input), 1024).readLine()
- }
- } catch (e: IOException) {
- e.printStackTrace()
- null
- }
- }
}
diff --git a/packages/player/android/src/main/kotlin/br/com/suamusica/player/MediaSessionConnection.kt b/packages/player/android/src/main/kotlin/br/com/suamusica/player/MediaSessionConnection.kt
index 23e59a97..fea0cc70 100644
--- a/packages/player/android/src/main/kotlin/br/com/suamusica/player/MediaSessionConnection.kt
+++ b/packages/player/android/src/main/kotlin/br/com/suamusica/player/MediaSessionConnection.kt
@@ -51,7 +51,6 @@ class MediaSessionConnection(
bundle.putString("coverUrl", media.coverUrl)
bundle.putString("bigCoverUrl", media.bigCoverUrl)
- PlayerSingleton.lastFavorite = media.isFavorite ?: false
if (media.isFavorite != null) {
bundle.putBoolean(PlayerPlugin.IS_FAVORITE_ARGUMENT, media.isFavorite)
}
@@ -76,7 +75,7 @@ class MediaSessionConnection(
fun favorite(shouldFavorite:Boolean) {
val bundle = Bundle()
bundle.putBoolean(PlayerPlugin.IS_FAVORITE_ARGUMENT, shouldFavorite)
- sendCommand(FAVORITE, bundle)
+ sendCommand(PlayerPlugin.FAVORITE, bundle)
}
fun stop() {
@@ -101,10 +100,6 @@ class MediaSessionConnection(
bundle.putString("url", url)
bundle.putString("coverUrl", coverUrl)
bundle.putString("bigCoverUrl", bigCoverUrl)
- if (isPlaying != null) {
- bundle.putBoolean(PlayerPlugin.IS_PLAYING_ARGUMENT, isPlaying)
- }
- PlayerSingleton.lastFavorite = isFavorite ?: false
if (isFavorite != null) {
bundle.putBoolean(PlayerPlugin.IS_FAVORITE_ARGUMENT, isFavorite)
}
@@ -165,7 +160,7 @@ class MediaSessionConnection(
var lastState = PlaybackStateCompat.STATE_NONE - 1
override fun onPlaybackStateChanged(state: PlaybackStateCompat) {
if (lastState != state.state) {
- Log.i(TAG, "onPlaybackStateChanged: $state")
+ Log.i(TAG, "onPlaybackStateChanged2: $state")
lastState = state.state
playerChangeNotifier.notifyStateChange(state.state)
}
@@ -200,7 +195,7 @@ class MediaSessionConnection(
}
override fun onMetadataChanged(metadata: MediaMetadataCompat) {
- Log.i(TAG, "onMetadataChanged: $metadata duration: ${metadata.duration}")
+// Log.i(TAG, "onMetadataChanged: $metadata duration: ${metadata.duration}")
}
})
}
diff --git a/packages/player/android/src/main/kotlin/br/com/suamusica/player/MusicPlayerPlaybackPreparer.kt b/packages/player/android/src/main/kotlin/br/com/suamusica/player/MusicPlayerPlaybackPreparer.kt
deleted file mode 100644
index abb82b12..00000000
--- a/packages/player/android/src/main/kotlin/br/com/suamusica/player/MusicPlayerPlaybackPreparer.kt
+++ /dev/null
@@ -1,133 +0,0 @@
-package br.com.suamusica.player
-
-import android.net.Uri
-import android.os.Bundle
-import android.os.ResultReceiver
-import android.util.Log
-import com.google.android.exoplayer2.Player
-import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
-
-class MusicPlayerPlaybackPreparer(
- private val mediaService: MediaService,
- ) : MediaSessionConnector.PlaybackPreparer {
- val TAG = "Player"
-
- override fun onPrepareFromMediaId(mediaId: String, playWhenReady: Boolean, extras: Bundle?) {
- Log.i(TAG, "MusicPlayerPlaybackPreparer.onPrepareFromMediaId : START")
-
- Log.i(TAG, "MusicPlayerPlaybackPreparer.onPrepareFromMediaId : END")
- }
-
- override fun onPrepareFromSearch(query: String, playWhenReady: Boolean, extras: Bundle?) {
- Log.i(TAG, "MusicPlayerPlaybackPreparer.onPrepareFromSearch : START")
-
- Log.i(TAG, "MusicPlayerPlaybackPreparer.onPrepareFromSearch : END")
- }
-
- override fun onPrepareFromUri(uri: Uri, playWhenReady: Boolean, extras: Bundle?) {
- Log.i(TAG, "MusicPlayerPlaybackPreparer.onPrepareFromUri : START")
-
- Log.i(TAG, "MusicPlayerPlaybackPreparer.onPrepareFromUri : END")
- }
-
- override fun onCommand(player: Player,
- command: String, extras: Bundle?, cb: ResultReceiver?): Boolean {
- try {
- Log.i(TAG, "MusicPlayerPlaybackPreparer.onCommand : START")
-
- return when (command) {
- "prepare" -> {
- return extras?.let {
- val cookie = it.getString("cookie")!!
- val name = it.getString("name")!!
- val author = it.getString("author")!!
- val url = it.getString("url")!!
- val coverUrl = it.getString("coverUrl")!!
- val bigCoverUrl = it.getString("bigCoverUrl")
-
- var isFavorite:Boolean? = null;
- if(it.containsKey(PlayerPlugin.IS_FAVORITE_ARGUMENT)){
- isFavorite = it.getBoolean(PlayerPlugin.IS_FAVORITE_ARGUMENT)
- }
- mediaService.prepare(cookie, Media(name, author, url, coverUrl, bigCoverUrl,isFavorite))
- return@let true
- } ?: false
- }
- "play" -> {
- mediaService.play()
- true
- }
- "pause" -> {
- mediaService.pause()
- true
- }
- "stop" -> {
- mediaService.stop()
- true
- }
- "togglePlayPause" -> {
- mediaService.togglePlayPause()
- true
- }
- "release" -> {
- mediaService.release()
- true
- }
- "seek" -> {
- return extras?.let {
- val position = it.getLong("position")
- val playWhenReady = it.getBoolean("playWhenReady")
- mediaService.seek(position, playWhenReady)
- return@let true
- } ?: false
- }
- "remove_notification" -> {
- mediaService.removeNotification()
- return true
- }
- "send_notification" -> {
- return extras?.let {
- val name = it.getString("name")!!
- val author = it.getString("author")!!
- val url = it.getString("url")!!
- val coverUrl = it.getString("coverUrl")!!
- val bigCoverUrl = it.getString("bigCoverUrl")
- var isPlaying:Boolean? = null;
- var isFavorite:Boolean? = null;
- if(it.containsKey(PlayerPlugin.IS_PLAYING_ARGUMENT)){
- isPlaying = it.getBoolean(PlayerPlugin.IS_PLAYING_ARGUMENT)
- }
- if(it.containsKey(PlayerPlugin.IS_FAVORITE_ARGUMENT)){
- isFavorite = it.getBoolean(PlayerPlugin.IS_FAVORITE_ARGUMENT)
- }
- mediaService.sendNotification(Media(name, author, url, coverUrl, bigCoverUrl, isFavorite),isPlaying)
- return true
- } ?: false
- }
- "ads_playing" -> {
- mediaService.adsPlaying()
- return true
- }
- FAVORITE -> {
- return extras?.let {
- if(it.containsKey(PlayerPlugin.IS_FAVORITE_ARGUMENT)){
- mediaService.setFavorite(it.getBoolean(PlayerPlugin.IS_FAVORITE_ARGUMENT))
- }
- return@let true
- } ?: false
- }
- else -> false
- }
- } finally {
- Log.i(TAG, "MusicPlayerPlaybackPreparer.onCommand : END")
- }
- }
-
- override fun getSupportedPrepareActions(): Long {
- return 0L
- }
-
- override fun onPrepare(playWhenReady: Boolean) {
-
- }
-}
\ No newline at end of file
diff --git a/packages/player/android/src/main/kotlin/br/com/suamusica/player/NextActionProvider.kt b/packages/player/android/src/main/kotlin/br/com/suamusica/player/NextActionProvider.kt
deleted file mode 100644
index 097b75ca..00000000
--- a/packages/player/android/src/main/kotlin/br/com/suamusica/player/NextActionProvider.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package br.com.suamusica.player
-
-import android.os.Bundle
-import android.support.v4.media.session.PlaybackStateCompat
-import android.util.Log
-import com.google.android.exoplayer2.Player
-import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
-
-class NextActionProvider :
- MediaSessionConnector.CustomActionProvider {
-
- override fun onCustomAction(player: Player, action: String, extras: Bundle?) {
- PlayerSingleton.next()
- }
-
- override fun getCustomAction(player: Player): PlaybackStateCompat.CustomAction? {
- return PlaybackStateCompat.CustomAction.Builder(
- "Ir a próxima música",
- "Ir a próxima música",
- R.drawable.ic_next_notification_player,
- ).build()
- }
-}
\ No newline at end of file
diff --git a/packages/player/android/src/main/kotlin/br/com/suamusica/player/NotificationBuilder.kt b/packages/player/android/src/main/kotlin/br/com/suamusica/player/NotificationBuilder.kt
deleted file mode 100644
index 9fbb1272..00000000
--- a/packages/player/android/src/main/kotlin/br/com/suamusica/player/NotificationBuilder.kt
+++ /dev/null
@@ -1,235 +0,0 @@
-package br.com.suamusica.player
-
-import android.app.Notification
-import android.app.NotificationChannel
-import android.app.NotificationManager
-import android.app.PendingIntent
-import android.content.Context
-import android.content.Intent
-import android.graphics.Bitmap
-import android.media.MediaMetadata
-import android.os.Build
-import android.support.v4.media.MediaMetadataCompat
-import android.support.v4.media.session.MediaSessionCompat
-import android.support.v4.media.session.PlaybackStateCompat.*
-import android.util.Log
-import androidx.annotation.RequiresApi
-import androidx.core.app.NotificationCompat
-import androidx.media.app.NotificationCompat.MediaStyle
-import androidx.media.session.MediaButtonReceiver
-import com.bumptech.glide.load.engine.DiskCacheStrategy
-import com.bumptech.glide.request.RequestOptions
-import kotlinx.coroutines.*
-import java.util.*
-
-const val NOW_PLAYING_CHANNEL: String = "br.com.suamusica.media.NOW_PLAYING"
-const val NOW_PLAYING_NOTIFICATION: Int = 0xb339
-const val FAVORITE: String = "favorite"
-
-/**
- * Helper class to encapsulate code for building notifications.
- */
-class NotificationBuilder(private val context: Context) {
- private val platformNotificationManager: NotificationManager =
- context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
-
- private val skipToPreviousAction = NotificationCompat.Action(
- R.drawable.ic_prev_notification_player,
- context.getString(R.string.notification_skip_to_previous),
- MediaButtonReceiver.buildMediaButtonPendingIntent(context, ACTION_SKIP_TO_PREVIOUS)
- )
- private val playAction = NotificationCompat.Action(
- R.drawable.ic_play_notification_player,
- context.getString(R.string.notification_play),
- MediaButtonReceiver.buildMediaButtonPendingIntent(context, ACTION_PLAY)
- )
- private val pauseAction = NotificationCompat.Action(
- R.drawable.ic_pause_notification_player,
- context.getString(R.string.notification_pause),
- MediaButtonReceiver.buildMediaButtonPendingIntent(context, ACTION_PAUSE)
- )
-
- private val favoriteAction = NotificationCompat.Action(
- R.drawable.ic_favorite_notification_player,
- context.getString(R.string.notification_favorite),
- PendingIntent.getBroadcast(context,
- UUID.randomUUID().hashCode(),
- Intent(context, MediaControlBroadcastReceiver::class.java).apply {
- putExtra(FAVORITE, true)
- },
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0
- )
- )
-
- private val unFavoriteAction = NotificationCompat.Action(
- R.drawable.ic_unfavorite_notification_player,
- context.getString(R.string.notification_unfavorite),
- PendingIntent.getBroadcast(
- context,
- UUID.randomUUID().hashCode(),
- Intent(context, MediaControlBroadcastReceiver::class.java).apply {
- putExtra(FAVORITE, false)
- },
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0
- )
- )
-
- private val skipToNextAction = NotificationCompat.Action(
- R.drawable.ic_next_notification_player,
- context.getString(R.string.notification_skip_to_next),
- MediaButtonReceiver.buildMediaButtonPendingIntent(context, ACTION_SKIP_TO_NEXT)
- )
- private val stopPendingIntent =
- MediaButtonReceiver.buildMediaButtonPendingIntent(context, ACTION_STOP)
-
-
- fun buildNotification(
- mediaSession: MediaSessionCompat,
- media: Media?,
- onGoing: Boolean,
- isPlayingExternal: Boolean?,
- isFavorite: Boolean?,
- mediaDuration: Long?,
- art: Bitmap?
- ): Notification {
- if (shouldCreateNowPlayingChannel()) {
- createNowPlayingChannel()
- }
- val playbackState = mediaSession.controller.playbackState
- val builder = NotificationCompat.Builder(context, NOW_PLAYING_CHANNEL)
- val actions = if (isFavorite == null) mutableListOf(0, 1, 2) else mutableListOf(
- 0,
- 2,
- 3
- ) // favorite,play/pause,next
- val duration = mediaDuration ?: 0L
- val currentDuration =
- mediaSession.controller.metadata.getLong(MediaMetadata.METADATA_KEY_DURATION)
- val shouldUseMetadata = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
-
- isFavorite?.let {
- builder.addAction(if (it) unFavoriteAction else favoriteAction)
- }
-
- builder.addAction(skipToPreviousAction)
- when {
- isPlayingExternal != null -> {
- if (isPlayingExternal) {
- builder.addAction(pauseAction)
- } else {
- builder.addAction(playAction)
- }
- }
-
- playbackState.isPlaying -> {
- Log.i("NotificationBuilder", "Player is playing... onGoing: $onGoing")
- builder.addAction(pauseAction)
- }
-
- playbackState.isPlayEnabled -> {
- Log.i("NotificationBuilder", "Player is NOT playing... onGoing: $onGoing")
- builder.addAction(playAction)
- }
-
- else -> {
- Log.i("NotificationBuilder", "ELSE")
- builder.addAction(playAction)
- }
- }
-
- builder.addAction(skipToNextAction)
-
- val mediaStyle = MediaStyle()
- .setCancelButtonIntent(stopPendingIntent)
- .setShowActionsInCompactView(*actions.toIntArray())
- .setShowCancelButton(true)
- .setMediaSession(mediaSession.sessionToken)
-
- /// when isAds is true, the metadata should be updated to show the correct value
- val isAds = media?.name?.contains("Propaganda") ?: false
-
- if (shouldUseMetadata && (isAds || currentDuration != duration)) {
- mediaSession.setMetadata(
- MediaMetadataCompat.Builder()
- .putString(MediaMetadata.METADATA_KEY_TITLE, media?.name ?: "Propaganda")
- .putString(MediaMetadata.METADATA_KEY_ARTIST, media?.author ?: "")
- .putBitmap(
- MediaMetadata.METADATA_KEY_ALBUM_ART, art
- )
- .putLong(MediaMetadata.METADATA_KEY_DURATION, duration) // 4
- .build()
- )
- }
-
-
- val notifyIntent = Intent("SUA_MUSICA_FLUTTER_NOTIFICATION_CLICK").apply {
- addCategory(Intent.CATEGORY_DEFAULT)
- flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
- }
- val notifyPendingIntent = PendingIntent.getActivity(
- context,
- 0,
- notifyIntent,
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT else PendingIntent.FLAG_UPDATE_CURRENT
- )
- val notification = builder.apply {
- setContentIntent(notifyPendingIntent)
- setStyle(mediaStyle)
- setCategory(NotificationCompat.CATEGORY_PROGRESS)
- setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
- setShowWhen(false)
- setColorized(true)
- setOnlyAlertOnce(false)
- setAutoCancel(false)
- setOngoing(onGoing)
- setSmallIcon(R.drawable.ic_notification)
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
- setDefaults(Notification.DEFAULT_LIGHTS)
- setVibrate(longArrayOf(0))
- } else {
- setDefaults(Notification.DEFAULT_LIGHTS)
- }
- if (!shouldUseMetadata) {
- setLargeIcon(art)
- setContentTitle(media?.name ?: "Propaganda")
- setContentText(media?.author ?: "")
- }
-
- }.build()
-
- if (onGoing) {
- notification.flags += Notification.FLAG_ONGOING_EVENT
- notification.flags += Notification.FLAG_NO_CLEAR
- }
-
- Log.i("NotificationBuilder", "Sending Notification onGoing: $onGoing")
-
- return notification
- }
-
- private fun shouldCreateNowPlayingChannel() =
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !nowPlayingChannelExists()
-
- @RequiresApi(Build.VERSION_CODES.O)
- private fun nowPlayingChannelExists() =
- platformNotificationManager.getNotificationChannel(NOW_PLAYING_CHANNEL) != null
-
- @RequiresApi(Build.VERSION_CODES.O)
- private fun createNowPlayingChannel() {
- val notificationChannel = NotificationChannel(
- NOW_PLAYING_CHANNEL,
- context.getString(R.string.notification_channel),
- NotificationManager.IMPORTANCE_LOW
- )
- .apply {
- description = context.getString(R.string.notification_channel_description)
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
- this.vibrationPattern = longArrayOf(0)
- this.enableVibration(true)
- }
- }
-
- platformNotificationManager.createNotificationChannel(notificationChannel)
- }
-}
-
diff --git a/packages/player/android/src/main/kotlin/br/com/suamusica/player/PackageValidator.kt b/packages/player/android/src/main/kotlin/br/com/suamusica/player/PackageValidator.kt
index 995aa543..c9b8fa7f 100644
--- a/packages/player/android/src/main/kotlin/br/com/suamusica/player/PackageValidator.kt
+++ b/packages/player/android/src/main/kotlin/br/com/suamusica/player/PackageValidator.kt
@@ -13,7 +13,7 @@ import android.util.Base64
import android.util.Log
import androidx.annotation.XmlRes
import androidx.media.MediaBrowserServiceCompat
-import com.google.android.exoplayer2.BuildConfig
+import io.flutter.BuildConfig
import org.xmlpull.v1.XmlPullParserException
import java.io.IOException
import java.security.MessageDigest
diff --git a/packages/player/android/src/main/kotlin/br/com/suamusica/player/PlaybackStateCompatExt.kt b/packages/player/android/src/main/kotlin/br/com/suamusica/player/PlaybackStateCompatExt.kt
deleted file mode 100644
index de10d30e..00000000
--- a/packages/player/android/src/main/kotlin/br/com/suamusica/player/PlaybackStateCompatExt.kt
+++ /dev/null
@@ -1,64 +0,0 @@
-package br.com.suamusica.player
-
-import android.os.SystemClock
-import android.support.v4.media.session.PlaybackStateCompat
-
-/**
- * Useful extension methods for [PlaybackStateCompat].
- */
-//TODO: Maybe remove this one
-inline val PlaybackStateCompat.isLoading
- get() = (state == PlaybackStateCompat.STATE_BUFFERING) ||
- (state == PlaybackStateCompat.STATE_CONNECTING)
-
-inline val PlaybackStateCompat.isPrepared
- get() = (state == PlaybackStateCompat.STATE_BUFFERING) ||
- (state == PlaybackStateCompat.STATE_PLAYING) ||
- (state == PlaybackStateCompat.STATE_PAUSED)
-
-inline val PlaybackStateCompat.isPlaying
- get() = (state == PlaybackStateCompat.STATE_BUFFERING && currentPlayBackPosition > 0) ||
- (state == PlaybackStateCompat.STATE_PLAYING)
-
-inline val PlaybackStateCompat.isPlayEnabled
- get() = (actions and PlaybackStateCompat.ACTION_PLAY != 0L) ||
- ((actions and PlaybackStateCompat.ACTION_PLAY_PAUSE != 0L) &&
- (state == PlaybackStateCompat.STATE_PAUSED))
-
-inline val PlaybackStateCompat.isPauseEnabled
- get() = (actions and PlaybackStateCompat.ACTION_PAUSE != 0L) ||
- ((actions and PlaybackStateCompat.ACTION_PLAY_PAUSE != 0L) &&
- (state == PlaybackStateCompat.STATE_BUFFERING ||
- state == PlaybackStateCompat.STATE_PLAYING))
-
-inline val PlaybackStateCompat.isSkipToNextEnabled
- get() = actions and PlaybackStateCompat.ACTION_SKIP_TO_NEXT != 0L
-
-inline val PlaybackStateCompat.isSkipToPreviousEnabled
- get() = actions and PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS != 0L
-
-inline val PlaybackStateCompat.stateName
- get() = when (state) {
- PlaybackStateCompat.STATE_NONE -> "STATE_NONE"
- PlaybackStateCompat.STATE_STOPPED -> "STATE_STOPPED"
- PlaybackStateCompat.STATE_PAUSED -> "STATE_PAUSED"
- PlaybackStateCompat.STATE_PLAYING -> "STATE_PLAYING"
- PlaybackStateCompat.STATE_FAST_FORWARDING -> "STATE_FAST_FORWARDING"
- PlaybackStateCompat.STATE_REWINDING -> "STATE_REWINDING"
- PlaybackStateCompat.STATE_BUFFERING -> "STATE_BUFFERING"
- PlaybackStateCompat.STATE_ERROR -> "STATE_ERROR"
- else -> "UNKNOWN_STATE"
- }
-
-/**
- * Calculates the current playback position based on last update time along with playback
- * state and speed.
- */
-inline val PlaybackStateCompat.currentPlayBackPosition: Long
- get() = if (state == PlaybackStateCompat.STATE_PLAYING) {
- val timeDelta = SystemClock.elapsedRealtime() - lastPositionUpdateTime
- (position + (timeDelta * playbackSpeed)).toLong()
- } else {
- position
- }
-
diff --git a/packages/player/android/src/main/kotlin/br/com/suamusica/player/PlayerPlugin.kt b/packages/player/android/src/main/kotlin/br/com/suamusica/player/PlayerPlugin.kt
index 913b73fb..7590e759 100644
--- a/packages/player/android/src/main/kotlin/br/com/suamusica/player/PlayerPlugin.kt
+++ b/packages/player/android/src/main/kotlin/br/com/suamusica/player/PlayerPlugin.kt
@@ -24,6 +24,7 @@ class PlayerPlugin : MethodCallHandler, FlutterPlugin,ActivityAware {
const val LOAD_ONLY = "loadOnly"
const val RELEASE_MODE_ARGUMENT = "releaseMode"
private const val CHANNEL = "suamusica.com.br/player"
+ const val FAVORITE: String = "favorite"
// Method names
const val LOAD_METHOD = "load"
@@ -94,6 +95,7 @@ class PlayerPlugin : MethodCallHandler, FlutterPlugin,ActivityAware {
val cookie = call.argument("cookie")
PlayerSingleton.externalPlayback = call.argument("externalplayback")
Log.d(TAG, "method: ${call.method} cookie: $cookie externalPlayback: ${PlayerSingleton.externalPlayback}")
+ Log.d(TAG, "TESTE1 method: ${call.method} | ${call.argument(IS_FAVORITE_ARGUMENT)}")
when (call.method) {
LOAD_METHOD -> {
val name = call.argument(NAME_ARGUMENT)!!
diff --git a/packages/player/android/src/main/kotlin/br/com/suamusica/player/PlayerSingleton.kt b/packages/player/android/src/main/kotlin/br/com/suamusica/player/PlayerSingleton.kt
index 77979ad4..8170ecdc 100644
--- a/packages/player/android/src/main/kotlin/br/com/suamusica/player/PlayerSingleton.kt
+++ b/packages/player/android/src/main/kotlin/br/com/suamusica/player/PlayerSingleton.kt
@@ -8,7 +8,6 @@ object PlayerSingleton {
var channel: MethodChannel? = null
var mediaSessionConnection: MediaSessionConnection? = null
var externalPlayback: Boolean? = false
- var lastFavorite: Boolean=false
private const val TAG = "Player"
fun setChannel(c: MethodChannel, context: Context) {
@@ -27,14 +26,14 @@ object PlayerSingleton {
channel?.invokeMethod("commandCenter.onPlay", emptyMap())
}
}
-
- fun togglePlayPause(){
- mediaSessionConnection?.togglePlayPause()
- channel?.invokeMethod("commandCenter.onTogglePlayPause", emptyMap())
- }
- fun adsPlaying(){
- mediaSessionConnection?.adsPlaying()
- }
+//TODO(Lucas) verificar se pode retirar
+// fun togglePlayPause(){
+// mediaSessionConnection?.togglePlayPause()
+// channel?.invokeMethod("commandCenter.onTogglePlayPause", emptyMap())
+// }
+// fun adsPlaying(){
+// mediaSessionConnection?.adsPlaying()
+// }
fun pause() {
if (externalPlayback!!) {
channel?.invokeMethod("externalPlayback.pause", emptyMap())
@@ -49,6 +48,7 @@ object PlayerSingleton {
}
fun next() {
+ Log.d("Player", "#MEDIA3# - commandCenter NEXT")
channel?.invokeMethod("commandCenter.onNext", emptyMap())
}
@@ -58,10 +58,9 @@ object PlayerSingleton {
fun favorite(shouldFavorite: Boolean) {
Log.d(TAG, "Should Favorite: $shouldFavorite")
- lastFavorite = shouldFavorite
mediaSessionConnection?.favorite(shouldFavorite)
val args = mutableMapOf()
- args[FAVORITE] = shouldFavorite
+ args[PlayerPlugin.FAVORITE] = shouldFavorite
channel?.invokeMethod("commandCenter.onFavorite", args)
}
}
\ No newline at end of file
diff --git a/packages/player/android/src/main/kotlin/br/com/suamusica/player/PreviousActionProvider.kt b/packages/player/android/src/main/kotlin/br/com/suamusica/player/PreviousActionProvider.kt
deleted file mode 100644
index dc5a58a5..00000000
--- a/packages/player/android/src/main/kotlin/br/com/suamusica/player/PreviousActionProvider.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package br.com.suamusica.player
-
-import android.os.Bundle
-import android.support.v4.media.session.PlaybackStateCompat
-import android.util.Log
-import com.google.android.exoplayer2.Player
-import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
-
-class PreviousActionProvider :
- MediaSessionConnector.CustomActionProvider {
-
- override fun onCustomAction(player: Player, action: String, extras: Bundle?) {
- PlayerSingleton.previous()
- }
-
- override fun getCustomAction(player: Player): PlaybackStateCompat.CustomAction? {
- return PlaybackStateCompat.CustomAction.Builder(
- "Voltar a música anterior",
- "Voltar a música anterior",
- R.drawable.ic_prev_notification_player,
- ).build()
- }
-}
\ No newline at end of file
diff --git a/packages/player/android/src/main/kotlin/br/com/suamusica/player/media/parser/CustomHlsMasterPlaylist.java b/packages/player/android/src/main/kotlin/br/com/suamusica/player/media/parser/CustomHlsMasterPlaylist.java
index d37ad30c..36292a55 100644
--- a/packages/player/android/src/main/kotlin/br/com/suamusica/player/media/parser/CustomHlsMasterPlaylist.java
+++ b/packages/player/android/src/main/kotlin/br/com/suamusica/player/media/parser/CustomHlsMasterPlaylist.java
@@ -4,21 +4,25 @@
import androidx.annotation.Nullable;
-import com.google.android.exoplayer2.Format;
-import com.google.android.exoplayer2.drm.DrmInitData;
-import com.google.android.exoplayer2.offline.StreamKey;
-import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylist;
-import com.google.android.exoplayer2.util.MimeTypes;
+import androidx.media3.common.Format;
+import androidx.media3.common.DrmInitData;
+import androidx.media3.common.StreamKey;
+import androidx.media3.common.MimeTypes;
+import androidx.media3.common.util.UnstableApi;
+import androidx.media3.exoplayer.hls.playlist.HlsPlaylist;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-
+@UnstableApi
public final class CustomHlsMasterPlaylist extends HlsPlaylist {
- /** Represents an empty master playlist, from which no attributes can be inherited. */
+ /**
+ * Represents an empty master playlist, from which no attributes can be inherited.
+ */
+
public static final CustomHlsMasterPlaylist EMPTY =
new CustomHlsMasterPlaylist(
/* baseUri= */ "",
@@ -39,35 +43,52 @@ public final class CustomHlsMasterPlaylist extends HlsPlaylist {
public static final int GROUP_INDEX_AUDIO = 1;
public static final int GROUP_INDEX_SUBTITLE = 2;
- /** A variant (i.e. an #EXT-X-STREAM-INF tag) in a master playlist. */
+ /**
+ * A variant (i.e. an #EXT-X-STREAM-INF tag) in a master playlist.
+ */
public static final class Variant {
- /** The variant's url. */
+ /**
+ * The variant's url.
+ */
public final Uri url;
- /** Format information associated with this variant. */
+ /**
+ * Format information associated with this variant.
+ */
public final Format format;
- /** The video rendition group referenced by this variant, or {@code null}. */
+ /**
+ * The video rendition group referenced by this variant, or {@code null}.
+ */
@Nullable
public final String videoGroupId;
- /** The audio rendition group referenced by this variant, or {@code null}. */
- @Nullable public final String audioGroupId;
+ /**
+ * The audio rendition group referenced by this variant, or {@code null}.
+ */
+ @Nullable
+ public final String audioGroupId;
- /** The subtitle rendition group referenced by this variant, or {@code null}. */
- @Nullable public final String subtitleGroupId;
+ /**
+ * The subtitle rendition group referenced by this variant, or {@code null}.
+ */
+ @Nullable
+ public final String subtitleGroupId;
- /** The caption rendition group referenced by this variant, or {@code null}. */
- @Nullable public final String captionGroupId;
+ /**
+ * The caption rendition group referenced by this variant, or {@code null}.
+ */
+ @Nullable
+ public final String captionGroupId;
/**
- * @param url See {@link #url}.
- * @param format See {@link #format}.
- * @param videoGroupId See {@link #videoGroupId}.
- * @param audioGroupId See {@link #audioGroupId}.
+ * @param url See {@link #url}.
+ * @param format See {@link #format}.
+ * @param videoGroupId See {@link #videoGroupId}.
+ * @param audioGroupId See {@link #audioGroupId}.
* @param subtitleGroupId See {@link #subtitleGroupId}.
- * @param captionGroupId See {@link #captionGroupId}.
+ * @param captionGroupId See {@link #captionGroupId}.
*/
public Variant(
Uri url,
@@ -90,6 +111,7 @@ public Variant(
* @param url The media playlist url.
* @return The variant instance.
*/
+
public static CustomHlsMasterPlaylist.Variant createMediaPlaylistVariantUrl(Uri url) {
Format format =
new Format.Builder()
@@ -113,32 +135,45 @@ public static CustomHlsMasterPlaylist.Variant createMediaPlaylistVariantUrl(Uri
/* captionGroupId= */ null);
}
- /** Returns a copy of this instance with the given {@link Format}. */
+ /**
+ * Returns a copy of this instance with the given {@link Format}.
+ */
public CustomHlsMasterPlaylist.Variant copyWithFormat(Format format) {
return new CustomHlsMasterPlaylist.Variant(url, format, videoGroupId, audioGroupId, subtitleGroupId, captionGroupId);
}
}
- /** A rendition (i.e. an #EXT-X-MEDIA tag) in a master playlist. */
+ /**
+ * A rendition (i.e. an #EXT-X-MEDIA tag) in a master playlist.
+ */
public static final class Rendition {
- /** The rendition's url, or null if the tag does not have a URI attribute. */
- @Nullable public final Uri url;
+ /**
+ * The rendition's url, or null if the tag does not have a URI attribute.
+ */
+ @Nullable
+ public final Uri url;
- /** Format information associated with this rendition. */
+ /**
+ * Format information associated with this rendition.
+ */
public final Format format;
- /** The group to which this rendition belongs. */
+ /**
+ * The group to which this rendition belongs.
+ */
public final String groupId;
- /** The name of the rendition. */
+ /**
+ * The name of the rendition.
+ */
public final String name;
/**
- * @param url See {@link #url}.
- * @param format See {@link #format}.
+ * @param url See {@link #url}.
+ * @param format See {@link #format}.
* @param groupId See {@link #groupId}.
- * @param name See {@link #name}.
+ * @param name See {@link #name}.
*/
public Rendition(@Nullable Uri url, Format format, String groupId, String name) {
this.url = url;
@@ -149,17 +184,29 @@ public Rendition(@Nullable Uri url, Format format, String groupId, String name)
}
- /** All of the media playlist URLs referenced by the playlist. */
+ /**
+ * All of the media playlist URLs referenced by the playlist.
+ */
public final List mediaPlaylistUrls;
- /** The variants declared by the playlist. */
+ /**
+ * The variants declared by the playlist.
+ */
public final List variants;
- /** The video renditions declared by the playlist. */
+ /**
+ * The video renditions declared by the playlist.
+ */
public final List videos;
- /** The audio renditions declared by the playlist. */
+ /**
+ * The audio renditions declared by the playlist.
+ */
public final List audios;
- /** The subtitle renditions declared by the playlist. */
+ /**
+ * The subtitle renditions declared by the playlist.
+ */
public final List subtitles;
- /** The closed caption renditions declared by the playlist. */
+ /**
+ * The closed caption renditions declared by the playlist.
+ */
public final List closedCaptions;
/**
@@ -173,25 +220,30 @@ public Rendition(@Nullable Uri url, Format format, String groupId, String name)
* captions information.
*/
public final List muxedCaptionFormats;
- /** Contains variable definitions, as defined by the #EXT-X-DEFINE tag. */
+ /**
+ * Contains variable definitions, as defined by the #EXT-X-DEFINE tag.
+ */
public final Map variableDefinitions;
- /** DRM initialization data derived from #EXT-X-SESSION-KEY tags. */
+ /**
+ * DRM initialization data derived from #EXT-X-SESSION-KEY tags.
+ */
public final List sessionKeyDrmInitData;
/**
- * @param baseUri See {@link #baseUri}.
- * @param tags See {@link #tags}.
- * @param variants See {@link #variants}.
- * @param videos See {@link #videos}.
- * @param audios See {@link #audios}.
- * @param subtitles See {@link #subtitles}.
- * @param closedCaptions See {@link #closedCaptions}.
- * @param muxedAudioFormat See {@link #muxedAudioFormat}.
- * @param muxedCaptionFormats See {@link #muxedCaptionFormats}.
+ * @param baseUri See {@link #baseUri}.
+ * @param tags See {@link #tags}.
+ * @param variants See {@link #variants}.
+ * @param videos See {@link #videos}.
+ * @param audios See {@link #audios}.
+ * @param subtitles See {@link #subtitles}.
+ * @param closedCaptions See {@link #closedCaptions}.
+ * @param muxedAudioFormat See {@link #muxedAudioFormat}.
+ * @param muxedCaptionFormats See {@link #muxedCaptionFormats}.
* @param hasIndependentSegments See {@link #hasIndependentSegments}.
- * @param variableDefinitions See {@link #variableDefinitions}.
- * @param sessionKeyDrmInitData See {@link #sessionKeyDrmInitData}.
+ * @param variableDefinitions See {@link #variableDefinitions}.
+ * @param sessionKeyDrmInitData See {@link #sessionKeyDrmInitData}.
*/
+
public CustomHlsMasterPlaylist(
String baseUri,
List tags,
@@ -222,6 +274,7 @@ public CustomHlsMasterPlaylist(
}
@Override
+
public CustomHlsMasterPlaylist copy(List streamKeys) {
return new CustomHlsMasterPlaylist(
baseUri,
@@ -246,6 +299,7 @@ public CustomHlsMasterPlaylist copy(List streamKeys) {
* @param variantUrl The url of the single variant.
* @return A master playlist with a single variant for the provided url.
*/
+
public static CustomHlsMasterPlaylist createSingleVariantMasterPlaylist(String variantUrl) {
List variant =
Collections.singletonList(CustomHlsMasterPlaylist.Variant.createMediaPlaylistVariantUrl(Uri.parse(variantUrl)));
@@ -293,6 +347,7 @@ private static void addMediaPlaylistUrls(List
}
}
+
private static List copyStreams(
List streams, int groupIndex, List streamKeys) {
List copiedStreams = new ArrayList<>(streamKeys.size());
diff --git a/packages/player/android/src/main/kotlin/br/com/suamusica/player/media/parser/CustomHlsPlaylistParser.java b/packages/player/android/src/main/kotlin/br/com/suamusica/player/media/parser/CustomHlsPlaylistParser.java
index 531b73e4..81f20611 100644
--- a/packages/player/android/src/main/kotlin/br/com/suamusica/player/media/parser/CustomHlsPlaylistParser.java
+++ b/packages/player/android/src/main/kotlin/br/com/suamusica/player/media/parser/CustomHlsPlaylistParser.java
@@ -17,21 +17,26 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import com.google.android.exoplayer2.C;
-import com.google.android.exoplayer2.Format;
-import com.google.android.exoplayer2.ParserException;
-import com.google.android.exoplayer2.drm.DrmInitData;
-import com.google.android.exoplayer2.extractor.mp4.PsshAtomUtil;
-import com.google.android.exoplayer2.metadata.Metadata;
-import com.google.android.exoplayer2.source.UnrecognizedInputFormatException;
-import com.google.android.exoplayer2.source.hls.HlsTrackMetadataEntry;
-import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist;
-import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylist;
-import com.google.android.exoplayer2.upstream.ParsingLoadable;
-import com.google.android.exoplayer2.util.Assertions;
-import com.google.android.exoplayer2.util.MimeTypes;
-import com.google.android.exoplayer2.util.UriUtil;
-import com.google.android.exoplayer2.util.Util;
+import androidx.annotation.OptIn;
+import androidx.media3.common.C;
+import androidx.media3.common.Format;
+import androidx.media3.common.ParserException;
+import androidx.media3.common.DrmInitData;
+//import androidx.media3.exoplayer.extractor.mp4.PsshAtomUtil;
+import androidx.media3.common.Metadata;
+import androidx.media3.common.util.Assertions;
+import androidx.media3.common.util.UnstableApi;
+import androidx.media3.common.util.UriUtil;
+import androidx.media3.common.util.Util;
+import androidx.media3.exoplayer.source.UnrecognizedInputFormatException;
+import androidx.media3.exoplayer.hls.HlsTrackMetadataEntry;
+import androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist;
+import androidx.media3.exoplayer.hls.playlist.HlsPlaylist;
+import androidx.media3.exoplayer.upstream.ParsingLoadable;
+//import androidx.media3.exoplayer.util.Assertions;
+import androidx.media3.common.MimeTypes;
+//import com.google.android.exoplayer2.util.UriUtil;
+//import com.google.android.exoplayer2.util.Util;
import android.util.Log;
@@ -40,7 +45,9 @@
import android.util.Base64;
import androidx.annotation.Nullable;
+import androidx.media3.extractor.mp4.PsshAtomUtil;
+@UnstableApi
public class CustomHlsPlaylistParser implements ParsingLoadable.Parser {
private static final String PLAYLIST_HEADER = "#EXTM3U";
@@ -171,6 +178,7 @@ public CustomHlsPlaylistParser(CustomHlsMasterPlaylist masterPlaylist) {
this.masterPlaylist = masterPlaylist;
}
+ @OptIn(markerClass = UnstableApi.class)
@Override
public HlsPlaylist parse(Uri uri, InputStream inputStream) throws IOException {
Log.i("MusicService", "Player : Parser...");
@@ -211,6 +219,7 @@ public HlsPlaylist parse(Uri uri, InputStream inputStream) throws IOException {
throw ParserException.createForUnsupportedContainerFeature("Failed to parse the playlist, could not identify any tags.");
}
+
private static boolean checkPlaylistHeader(BufferedReader reader) throws IOException {
int last = reader.read();
if (last == 0xEF) {
@@ -232,6 +241,7 @@ private static boolean checkPlaylistHeader(BufferedReader reader) throws IOExcep
return Util.isLinebreak(last);
}
+
private static int skipIgnorableWhitespace(BufferedReader reader, boolean skipLinebreaks, int c)
throws IOException {
while (c != -1 && Character.isWhitespace(c) && (skipLinebreaks || !Util.isLinebreak(c))) {
@@ -240,7 +250,8 @@ private static int skipIgnorableWhitespace(BufferedReader reader, boolean skipLi
return c;
}
- private static CustomHlsMasterPlaylist parseMasterPlaylist(CustomHlsPlaylistParser.LineIterator iterator, String baseUri)
+ @UnstableApi
+ private static HlsPlaylist parseMasterPlaylist(LineIterator iterator, String baseUri)
throws IOException {
HashMap> urlToVariantInfos = new HashMap<>();
HashMap variableDefinitions = new HashMap<>();
@@ -421,7 +432,7 @@ private static CustomHlsMasterPlaylist parseMasterPlaylist(CustomHlsPlaylistPars
.setHeight(height)
.setFrameRate(frameRate)
.build();
- //.copyWithMetadata(metadata);
+ //.copyWithMetadata(metadata);
if (uri == null) {
// TODO: Remove this case and add a Rendition with a null uri to videos.
} else {
@@ -586,7 +597,7 @@ private static HlsMediaPlaylist parseMediaPlaylist(
DrmInitData cachedDrmInitData = null;
List trailingParts = new ArrayList<>();
Map renditionReports = new HashMap<>();
- HlsMediaPlaylist.ServerControl serverControl = new HlsMediaPlaylist.ServerControl(0,false,0,0,false);
+ HlsMediaPlaylist.ServerControl serverControl = new HlsMediaPlaylist.ServerControl(0, false, 0, 0, false);
List updatedParts = new ArrayList<>();
String line;
@@ -806,6 +817,7 @@ private static int parseSelectionFlags(String line) {
}
@C.RoleFlags
+
private static int parseRoleFlags(String line, Map variableDefinitions) {
String concatenatedCharacteristics =
parseOptionalStringAttr(line, REGEX_CHARACTERISTICS, variableDefinitions);
@@ -829,6 +841,7 @@ private static int parseRoleFlags(String line, Map variableDefin
return roleFlags;
}
+
private static int parseChannelsAttribute(String line, Map variableDefinitions) {
String channelsString = parseOptionalStringAttr(line, REGEX_CHANNELS, variableDefinitions);
return channelsString != null
@@ -837,6 +850,7 @@ private static int parseChannelsAttribute(String line, Map varia
}
@Nullable
+
private static DrmInitData.SchemeData parseDrmSchemeData(
String line, String keyFormat, Map variableDefinitions)
throws ParserException {
@@ -859,24 +873,29 @@ private static DrmInitData.SchemeData parseDrmSchemeData(
return null;
}
+
private static String parseEncryptionScheme(String method) {
return METHOD_SAMPLE_AES_CENC.equals(method) || METHOD_SAMPLE_AES_CTR.equals(method)
? C.CENC_TYPE_cenc
: C.CENC_TYPE_cbcs;
}
+
private static int parseIntAttr(String line, Pattern pattern) throws ParserException {
return Integer.parseInt(parseStringAttr(line, pattern, new HashMap()));
}
+
private static long parseLongAttr(String line, Pattern pattern) throws ParserException {
return Long.parseLong(parseStringAttr(line, pattern, new HashMap()));
}
+
private static double parseDoubleAttr(String line, Pattern pattern) throws ParserException {
return Double.parseDouble(parseStringAttr(line, pattern, new HashMap()));
}
+
private static String parseStringAttr(
String line, Pattern pattern, Map variableDefinitions)
throws ParserException {
diff --git a/packages/player/android/src/main/kotlin/br/com/suamusica/player/media/parser/SMHlsPlaylistParserFactory.java b/packages/player/android/src/main/kotlin/br/com/suamusica/player/media/parser/SMHlsPlaylistParserFactory.java
index 91b425aa..dd085fac 100644
--- a/packages/player/android/src/main/kotlin/br/com/suamusica/player/media/parser/SMHlsPlaylistParserFactory.java
+++ b/packages/player/android/src/main/kotlin/br/com/suamusica/player/media/parser/SMHlsPlaylistParserFactory.java
@@ -1,23 +1,28 @@
package br.com.suamusica.player.media.parser;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Collections;
import java.util.List;
-import com.google.android.exoplayer2.offline.FilteringManifestParser;
-import com.google.android.exoplayer2.offline.StreamKey;
-import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist;
-import com.google.android.exoplayer2.source.hls.playlist.HlsMultivariantPlaylist;
-import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylist;
-import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistParserFactory;
-import com.google.android.exoplayer2.upstream.ParsingLoadable;
+import androidx.media3.common.StreamKey;
+import androidx.media3.common.util.UnstableApi;
+import androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist;
+import androidx.media3.exoplayer.hls.playlist.HlsMultivariantPlaylist;
+import androidx.media3.exoplayer.hls.playlist.HlsPlaylist;
+import androidx.media3.exoplayer.hls.playlist.HlsPlaylistParserFactory;
+import androidx.media3.exoplayer.offline.FilteringManifestParser;
+import androidx.media3.exoplayer.upstream.ParsingLoadable;
+@UnstableApi
public final class SMHlsPlaylistParserFactory implements HlsPlaylistParserFactory {
private final List streamKeys;
- /** Creates an instance that does not filter any parsing results. */
+ /**
+ * Creates an instance that does not filter any parsing results.
+ */
public SMHlsPlaylistParserFactory() {
this(Collections.emptyList());
}
@@ -26,20 +31,21 @@ public SMHlsPlaylistParserFactory() {
* Creates an instance that filters the parsing results using the given {@code streamKeys}.
*
* @param streamKeys See {@link
- * FilteringManifestParser#FilteringManifestParser(ParsingLoadable.Parser, List)}.
+ * FilteringManifestParser#FilteringManifestParser(ParsingLoadable.Parser, List)}.
*/
public SMHlsPlaylistParserFactory(List streamKeys) {
this.streamKeys = streamKeys;
}
+ @NonNull
@Override
public ParsingLoadable.Parser createPlaylistParser() {
return new FilteringManifestParser<>(new CustomHlsPlaylistParser(), streamKeys);
}
@Override
- public ParsingLoadable.Parser createPlaylistParser(HlsMultivariantPlaylist multivariantPlaylist, @Nullable HlsMediaPlaylist previousMediaPlaylist) {
+ @NonNull
+ public ParsingLoadable.Parser createPlaylistParser(@Nullable HlsMultivariantPlaylist hlsMultivariantPlaylist, @Nullable HlsMediaPlaylist previousMediaPlaylist) {
return new FilteringManifestParser<>(new CustomHlsPlaylistParser(), streamKeys);
}
-
}
diff --git a/packages/player/example/.flutter-plugins-dependencies b/packages/player/example/.flutter-plugins-dependencies
index 069a4193..acf34efb 100644
--- a/packages/player/example/.flutter-plugins-dependencies
+++ b/packages/player/example/.flutter-plugins-dependencies
@@ -1 +1,134 @@
-{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"isar_flutter_libs","path":"/Users/suamusica/.pub-cache/hosted/pub.dev/isar_flutter_libs-3.1.0/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/Users/suamusica/.pub-cache/hosted/pub.dev/path_provider_foundation-2.3.2/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"smplayer","path":"/Users/suamusica/projects/suamusica/flutter_plugins/packages/player/","native_build":true,"dependencies":["isar_flutter_libs"]}],"android":[{"name":"isar_flutter_libs","path":"/Users/suamusica/.pub-cache/hosted/pub.dev/isar_flutter_libs-3.1.0/","native_build":true,"dependencies":[]},{"name":"path_provider_android","path":"/Users/suamusica/.pub-cache/hosted/pub.dev/path_provider_android-2.2.2/","native_build":true,"dependencies":[]},{"name":"smplayer","path":"/Users/suamusica/projects/suamusica/flutter_plugins/packages/player/","native_build":true,"dependencies":["isar_flutter_libs"]}],"macos":[{"name":"isar_flutter_libs","path":"/Users/suamusica/.pub-cache/hosted/pub.dev/isar_flutter_libs-3.1.0/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/Users/suamusica/.pub-cache/hosted/pub.dev/path_provider_foundation-2.3.2/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"linux":[{"name":"isar_flutter_libs","path":"/Users/suamusica/.pub-cache/hosted/pub.dev/isar_flutter_libs-3.1.0/","native_build":true,"dependencies":[]},{"name":"path_provider_linux","path":"/Users/suamusica/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[]}],"windows":[{"name":"isar_flutter_libs","path":"/Users/suamusica/.pub-cache/hosted/pub.dev/isar_flutter_libs-3.1.0/","native_build":true,"dependencies":[]},{"name":"path_provider_windows","path":"/Users/suamusica/.pub-cache/hosted/pub.dev/path_provider_windows-2.2.1/","native_build":false,"dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"isar_flutter_libs","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"smplayer","dependencies":["isar_flutter_libs","path_provider"]}],"date_created":"2024-06-24 09:57:26.814269","version":"3.22.2"}
\ No newline at end of file
+{
+ "info": "This is a generated file; do not edit or check into version control.",
+ "plugins": {
+ "ios": [
+ {
+ "name": "isar_flutter_libs",
+ "path": "/Users/suamusica/.pub-cache/hosted/pub.dev/isar_flutter_libs-3.1.0/",
+ "native_build": true,
+ "dependencies": []
+ },
+ {
+ "name": "path_provider_foundation",
+ "path": "/Users/suamusica/.pub-cache/hosted/pub.dev/path_provider_foundation-2.3.2/",
+ "shared_darwin_source": true,
+ "native_build": true,
+ "dependencies": []
+ },
+ {
+ "name": "smplayer",
+ "path": "/Users/suamusica/Documents/Dev/SM/flutter_plugins/packages/player/",
+ "native_build": true,
+ "dependencies": [
+ "isar_flutter_libs"
+ ]
+ }
+ ],
+ "android": [
+ {
+ "name": "isar_flutter_libs",
+ "path": "/Users/suamusica/.pub-cache/hosted/pub.dev/isar_flutter_libs-3.1.0/",
+ "native_build": true,
+ "dependencies": []
+ },
+ {
+ "name": "path_provider_android",
+ "path": "/Users/suamusica/.pub-cache/hosted/pub.dev/path_provider_android-2.2.4/",
+ "native_build": true,
+ "dependencies": []
+ },
+ {
+ "name": "smplayer",
+ "path": "/Users/suamusica/Documents/Dev/SM/flutter_plugins/packages/player/",
+ "native_build": true,
+ "dependencies": [
+ "isar_flutter_libs"
+ ]
+ }
+ ],
+ "macos": [
+ {
+ "name": "isar_flutter_libs",
+ "path": "/Users/suamusica/.pub-cache/hosted/pub.dev/isar_flutter_libs-3.1.0/",
+ "native_build": true,
+ "dependencies": []
+ },
+ {
+ "name": "path_provider_foundation",
+ "path": "/Users/suamusica/.pub-cache/hosted/pub.dev/path_provider_foundation-2.3.2/",
+ "shared_darwin_source": true,
+ "native_build": true,
+ "dependencies": []
+ }
+ ],
+ "linux": [
+ {
+ "name": "isar_flutter_libs",
+ "path": "/Users/suamusica/.pub-cache/hosted/pub.dev/isar_flutter_libs-3.1.0/",
+ "native_build": true,
+ "dependencies": []
+ },
+ {
+ "name": "path_provider_linux",
+ "path": "/Users/suamusica/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/",
+ "native_build": false,
+ "dependencies": []
+ }
+ ],
+ "windows": [
+ {
+ "name": "isar_flutter_libs",
+ "path": "/Users/suamusica/.pub-cache/hosted/pub.dev/isar_flutter_libs-3.1.0/",
+ "native_build": true,
+ "dependencies": []
+ },
+ {
+ "name": "path_provider_windows",
+ "path": "/Users/suamusica/.pub-cache/hosted/pub.dev/path_provider_windows-2.2.1/",
+ "native_build": false,
+ "dependencies": []
+ }
+ ],
+ "web": []
+ },
+ "dependencyGraph": [
+ {
+ "name": "isar_flutter_libs",
+ "dependencies": []
+ },
+ {
+ "name": "path_provider",
+ "dependencies": [
+ "path_provider_android",
+ "path_provider_foundation",
+ "path_provider_linux",
+ "path_provider_windows"
+ ]
+ },
+ {
+ "name": "path_provider_android",
+ "dependencies": []
+ },
+ {
+ "name": "path_provider_foundation",
+ "dependencies": []
+ },
+ {
+ "name": "path_provider_linux",
+ "dependencies": []
+ },
+ {
+ "name": "path_provider_windows",
+ "dependencies": []
+ },
+ {
+ "name": "smplayer",
+ "dependencies": [
+ "isar_flutter_libs",
+ "path_provider"
+ ]
+ }
+ ],
+ "date_created": "2024-05-09 11:03:41.030919",
+ "version": "3.19.1"
+}
\ No newline at end of file
diff --git a/packages/player/example/android/app/build.gradle b/packages/player/example/android/app/build.gradle
index 2ed9ff85..9d68c793 100644
--- a/packages/player/example/android/app/build.gradle
+++ b/packages/player/example/android/app/build.gradle
@@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
- compileSdkVersion 28
+ compileSdkVersion 34
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
@@ -39,8 +39,8 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "br.com.suamusica.suamusica_player_example"
- minSdkVersion 16
- targetSdkVersion 30
+ minSdkVersion 21
+ targetSdkVersion 34
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
@@ -54,9 +54,16 @@ android {
}
}
- compileOptions {
+
+ compileOptions {
+ coreLibraryDesugaringEnabled true
+ sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+ namespace 'br.com.suamusica.suamusica_player_example'
}
flutter {
@@ -65,7 +72,15 @@ flutter {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation 'com.android.support:multidex:1.0.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
+ testImplementation 'junit:junit:4.13.2'
+ coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
+ api ('com.google.ads.interactivemedia.v3:interactivemedia:'){
+ version {
+ strictly '3.33.0'
+ }
+ }
}
diff --git a/packages/player/example/android/app/src/main/AndroidManifest.xml b/packages/player/example/android/app/src/main/AndroidManifest.xml
index b23c5249..c9991d80 100644
--- a/packages/player/example/android/app/src/main/AndroidManifest.xml
+++ b/packages/player/example/android/app/src/main/AndroidManifest.xml
@@ -1,5 +1,5 @@
+ xmlns:tools="http://schemas.android.com/tools">
@@ -17,7 +17,8 @@
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
- android:windowSoftInputMode="adjustResize">
+ android:windowSoftInputMode="adjustResize"
+ android:exported="true">