Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Media3_v2 #150

Draft
wants to merge 15 commits into
base: master
Choose a base branch
from
16 changes: 10 additions & 6 deletions packages/player/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
Expand Down Expand Up @@ -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"

}
Original file line number Diff line number Diff line change
@@ -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
15 changes: 6 additions & 9 deletions packages/player/android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="br.com.suamusica.player">
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />

<application>
<service android:name=".MediaService" android:exported="false" android:foregroundServiceType="mediaPlayback">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/>
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>

<receiver android:name=".MediaControlBroadcastReceiver" android:exported="false">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
</service>
</application>
</manifest>
</manifest>

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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<SessionResult> {
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<KeyEvent>(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
}
}
}

Loading