I am trying to play audio using exoplayer.
I have multiple files in a local directory and I want to play them sequentially. I am trying to add multiple to URIs to MediaSource, but I couldn't find the solution.
Here, the first file is playing correctly but none of the rest. I am using the ExoPlayer version 2.9.2
Note: In my case the files in the "Articles" folder will be updated dynamically while player is running.
Please suggest a solution. My code:
class AudioPlayerService : Service() {
private var player: SimpleExoPlayer? = null
private var playerNotificationManager: PlayerNotificationManager? = null
private var mediaSession: MediaSessionCompat? = null
private var mediaSessionConnector: MediaSessionConnector? = null
private var dataSourceFactory: DataSource.Factory? = null
private var extractorsFactory: ExtractorsFactory? = null
private var dynamicConcatenatingMediaSource: ConcatenatingMediaSource? = null
override fun onCreate() {
super.onCreate()
val context: Context = this
player = ExoPlayerFactory.newSimpleInstance(this)
dataSourceFactory = DefaultDataSourceFactory(context, BuildConfig.APPLICATION_ID)
extractorsFactory = DefaultExtractorsFactory()
var file: File = File(Environment.getExternalStorageDirectory().absolutePath + "/Articles")
/*for (i in file.listFiles()){
Log.e("","" +file.listFiles())
}*/
var uri =
Uri.parse(Environment.getExternalStorageDirectory().absolutePath + "/Articles" + "/article_tts" + 0 + ".wav")
//if (dynamicConcatenatingMediaSource == null)
dynamicConcatenatingMediaSource = ConcatenatingMediaSource()
val mediaSource: MediaSource =
ExtractorMediaSource.Factory(dataSourceFactory).createMediaSource(uri)
dynamicConcatenatingMediaSource?.addMediaSource(mediaSource)
player?.prepare(dynamicConcatenatingMediaSource)
player?.setPlayWhenReady(true)
player?.addListener(object : Player.EventListener {
override fun onTracksChanged(
trackGroups: TrackGroupArray?,
trackSelections: TrackSelectionArray?
) {
}
override fun onPlayerError(error: ExoPlaybackException?) {
stopSelf()
}
override fun onLoadingChanged(isLoading: Boolean) {
}
override fun onPositionDiscontinuity(reason: Int) {
}
override fun onRepeatModeChanged(repeatMode: Int) {
}
override fun onShuffleModeEnabledChanged(shuffleModeEnabled: Boolean) {
}
override fun onTimelineChanged(timeline: Timeline?, manifest: Any?, reason: Int) {
}
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
if (playbackState == Player.STATE_ENDED) {
player?.stop()
stopSelf()
}
}
override fun onSeekProcessed() {
}
override fun onPlaybackParametersChanged(playbackParameters: PlaybackParameters?) {
}
})
playerNotificationManager = PlayerNotificationManager.createWithNotificationChannel(
context,
"playback_channel",
R.string.playback_channel_name,
7001,
object : MediaDescriptionAdapter {
override fun getCurrentContentTitle(player: Player): String {
return "Playlist"
}
override fun createCurrentContentIntent(player: Player): PendingIntent? {
return null
}
override fun getCurrentContentText(player: Player): String? {
return "Playlist Demo"
}
override fun getCurrentLargeIcon(
player: Player,
callback: BitmapCallback
): Bitmap? {
return getBitmap(context, R.drawable.ic_vikatan_logo_new)
}
}
)
playerNotificationManager?.setNotificationListener(object :
PlayerNotificationManager.NotificationListener {
override fun onNotificationStarted(
notificationId: Int,
notification: Notification
) {
startForeground(notificationId, notification)
}
override fun onNotificationCancelled(notificationId: Int) {
stopSelf()
}
})
playerNotificationManager?.setPriority(NotificationCompat.PRIORITY_HIGH);
playerNotificationManager?.setUseNavigationActions(false)
playerNotificationManager?.setFastForwardIncrementMs(0)
playerNotificationManager?.setRewindIncrementMs(0)
playerNotificationManager?.setUsePlayPauseActions(true);
playerNotificationManager?.setPlayer(player)
mediaSession = MediaSessionCompat(context, "audio")
mediaSession?.setActive(true)
playerNotificationManager?.setMediaSessionToken(mediaSession?.getSessionToken())
mediaSessionConnector = MediaSessionConnector(mediaSession)
mediaSessionConnector?.setQueueNavigator(object : TimelineQueueNavigator(mediaSession) {
override fun getMediaDescription(
player: Player,
windowIndex: Int
): MediaDescriptionCompat {
return getMediaDescription(context)
}
})
mediaSessionConnector?.setPlayer(player, null)
}
override fun onDestroy() {
mediaSession!!.release()
mediaSessionConnector!!.setPlayer(null, null)
playerNotificationManager!!.setPlayer(null)
player!!.release()
player = null
super.onDestroy()
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return START_STICKY
}
fun getBitmap(context: Context, #DrawableRes bitmapResource: Int): Bitmap {
return (context.resources.getDrawable(bitmapResource) as BitmapDrawable).bitmap
}
fun getMediaDescription(context: Context?): MediaDescriptionCompat {
val extras = Bundle()
val bitmap: Bitmap = getBitmap(context!!, R.drawable.ic_vikatan_logo_new)
extras.putParcelable(
MediaMetadataCompat.METADATA_KEY_ALBUM_ART,
bitmap
)
extras.putParcelable(
MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON,
bitmap
)
return MediaDescriptionCompat.Builder()
.setMediaId("audio_id")
.setIconBitmap(bitmap)
.setTitle("Playlist")
.setDescription("Playlist Demo")
.setExtras(extras)
.build()
}
}
Use the concatenating media source.
private val concatenatingMediaSource = ConcatenatingMediaSource()
Then create separate mediasource for each audio file and add to concatenatingMediaSource by using
concatenatingMediaSource.addMediaSource(your_media_source_object)
Related
I am a noob in Kotlin.
I've made a streaming music player using Exoplayer2.
For running this music player in background, I tried to bind activity to service.
But it is not working.
I dont know why this player is stopped outside the app even though i add the foreground service.
Can anybody help me? :(
MusicService.kt
var mExoPlayer: SimpleExoPlayer? = null
class MusicService : MediaBrowserServiceCompat() {
private var mMediaSession: MediaSessionCompat? = null
private lateinit var mStateBuilder: PlaybackStateCompat.Builder
private var playbackPosition = 0L
private var currentWindow = 0
private var oldUri: Uri? = null
private val mMediaSessionCallback = object : MediaSessionCompat.Callback() {
override fun onPlayFromUri(uri: Uri?, extras: Bundle?) {
super.onPlayFromUri(uri, extras)
uri?.let {
val mediaSource = extractMediaSourceFromUri(uri)
if (uri != oldUri)
play(mediaSource)
else play()
oldUri = uri
}
}
override fun onPause() {
super.onPause()
pause()
}
override fun onStop() {
super.onStop()
stop()
}
}
override fun onCreate() {
super.onCreate()
initializePlayer()
initializeExtractor()
initializeAttributes()
mMediaSession = MediaSessionCompat(baseContext, "tag for debugging").apply {
setFlags(
MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or
MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS )
mStateBuilder = PlaybackStateCompat.Builder()
.setActions(PlaybackStateCompat.ACTION_PLAY or PlaybackStateCompat.ACTION_PLAY_PAUSE)
setPlaybackState(mStateBuilder.build())
setCallback(mMediaSessionCallback)
setSessionToken(sessionToken)
isActive = true
}
}
private var mAttrs: AudioAttributes? = null
private fun play(mediaSource: MediaSource) {
if (mExoPlayer == null) initializePlayer()
mExoPlayer?.apply {
mAttrs?.let { initializeAttributes() }
mAttrs?.let { setAudioAttributes(it, true) }
prepare(mediaSource)
seekTo(currentWindow, playbackPosition)
playWhenReady = true
}
}
private fun play() {
mExoPlayer?.apply {
mExoPlayer?.playWhenReady = true
updatePlaybackState(PlaybackStateCompat.STATE_PLAYING)
mMediaSession?.isActive = true
}
}
private fun initializePlayer() {
mExoPlayer = ExoPlayerFactory.newSimpleInstance(
this, DefaultRenderersFactory(baseContext)
, DefaultTrackSelector(),
DefaultLoadControl()
)
}
private fun pause() {
mExoPlayer?.apply {
playWhenReady = false
if (playbackState == PlaybackStateCompat.STATE_PLAYING) {
updatePlaybackState(PlaybackStateCompat.STATE_PAUSED)
}
}
}
private fun stop() {
mExoPlayer?.playWhenReady = false
mExoPlayer?.release()
mExoPlayer = null
updatePlaybackState(PlaybackStateCompat.STATE_NONE)
mMediaSession?.isActive = false
mMediaSession?.release()
}
override fun onTaskRemoved(rootIntent: Intent?) {
super.onTaskRemoved(rootIntent)
stopSelf()
}
override fun onDestroy() {
super.onDestroy()
stop()
}
private fun updatePlaybackState(state: Int) {
mMediaSession?.setPlaybackState(
PlaybackStateCompat.Builder().setState(
state
, 0L
, 1.0f // Speed playing
).build()
)
}
private fun initializeAttributes() {
mAttrs = AudioAttributes.Builder().setUsage(C.USAGE_MEDIA)
.setContentType(C.CONTENT_TYPE_MUSIC)
.build()
}
private lateinit var mExtractorFactory: ExtractorMediaSource.Factory
private fun initializeExtractor() {
val userAgent = Util.getUserAgent(baseContext, "Application Name")
mExtractorFactory = ExtractorMediaSource.Factory(DefaultDataSourceFactory(this, userAgent))
.setExtractorsFactory(DefaultExtractorsFactory())
}
private fun extractMediaSourceFromUri(uri: Uri): MediaSource {
return mExtractorFactory.createMediaSource(uri)
}
override fun onLoadChildren(parentId: String, result: Result<MutableList<MediaBrowserCompat.MediaItem>>) {
}
override fun onGetRoot(clientPackageName: String, clientUid: Int, rootHints: Bundle?): BrowserRoot? {
return BrowserRoot("", null)
}
playerAct.kt
var audioUrlPass = ""
class playerAct: AppCompatActivity() { private val songUrl: String = audioUrlPass
private lateinit var mMediaBrowserCompat: MediaBrowserCompat
private val connectionCallback: MediaBrowserCompat.ConnectionCallback = object : MediaBrowserCompat.ConnectionCallback() {
override fun onConnected() {
super.onConnected()
mMediaBrowserCompat.sessionToken.also { token ->
val mediaController = MediaControllerCompat(this#playerAct, token)
MediaControllerCompat.setMediaController(this#playerAct, mediaController)
}
playPauseBuild()
}
override fun onConnectionFailed() {
super.onConnectionFailed()
}
}
private val mControllerCallback = object : MediaControllerCompat.Callback() {
}
fun playPauseBuild() {
val mediaController = MediaControllerCompat.getMediaController(this#playerAct)
main_pcv.showTimeoutMs = 0
exo_play.setOnClickListener {
val state = mediaController.playbackState.state
if (state == PlaybackStateCompat.STATE_PAUSED ||
state == PlaybackStateCompat.STATE_STOPPED ||
state == PlaybackStateCompat.STATE_NONE
) {
mediaController.transportControls.playFromUri(Uri.parse(songUrl), null)
}
else if (state == PlaybackStateCompat.STATE_PLAYING ||
state == PlaybackStateCompat.STATE_BUFFERING ||
state == PlaybackStateCompat.STATE_CONNECTING
) {
mediaController.transportControls.pause()
}
}
mediaController.registerCallback(mControllerCallback)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.player_layout)
val componentName = ComponentName(this, MusicService::class.java)
mMediaBrowserCompat = MediaBrowserCompat(
this, componentName, //Identifier for the service
connectionCallback,
null
)
menuButton.setOnClickListener {
super.onBackPressed()
}
btnTime1.setOnClickListener {
val intent = Intent(this, TimerActivity::class.java)
startActivity(intent)
}
playerSeekBar.setOnSeekBarChangeListener(
object : SeekBar.OnSeekBarChangeListener{
override fun onProgressChanged(seekbar: SeekBar?, progress: Int, fromUser: Boolean) {
if (fromUser){
val volumeNum = progress / 100.0f
mExoPlayer!!.setVolume(volumeNum)
}
}
override fun onStartTrackingTouch(seekbar: SeekBar?) { }
override fun onStopTrackingTouch(seekbar: SeekBar?) { }
})
}
override fun onStart() {
super.onStart()controller
Intent(this, MusicService::class.java).also {
mMediaBrowserCompat.connect()
}
}
override fun onStop() {
super.onStop()
val controllerCompat = MediaControllerCompat.getMediaController(this)
controllerCompat?.unregisterCallback(mControllerCallback)
mMediaBrowserCompat.disconnect()
}
}
In order to start the foreground service, you will need a notification shown within 5 seconds of starting it. Check the following guide in the android documentation: https://developer.android.com/guide/topics/media-apps/audio-app/building-a-mediabrowserservice#mediastyle-notifications
I'm trying to get context in service. I know that Service extends Context and I can get it at onCreate method. So I tried it and got error. I don't understand other example uses the context at onCreate and works, but not to me. Also I registered my service. the error is occurred at this line in service.
player = ExoPlayerFactory.newSimpleInstance(mContext, DefaultTrackSelector())
Why the context is null even though I initialized the context in onCreate? I saw the similar problem at here Android getContext on a background Service
error
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Context android.content.Context.getApplicationContext()' on a null object reference
activity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val musicPlayer = binding.musicPlayer
musicPlayer.player = audioService.getplayerInstance()
musicPlayer.player.playWhenReady = true
mIntent = Intent(this,AudioService::class.java)
Util.startForegroundService(this,mIntent)
bindService(mIntent, mConnection, Context.BIND_AUTO_CREATE);
initializePlayer();
}
Service
class AudioService : Service() {
private val mBinder: IBinder =Binder()
private var player: SimpleExoPlayer? = null
private var playerNotificationManager: PlayerNotificationManager? = null
private var cacheDataSourceFactory : CacheDataSourceFactory?=null
var mContext: Context? = null
override fun onDestroy() {
releasePlayer()
super.onDestroy()
}
override fun onCreate() {
super.onCreate()
mContext = this
if (player == null) {
startPlayer()
}
}
private fun releasePlayer() {
if (player != null) {
playerNotificationManager!!.setPlayer(null)
player!!.release()
player = null
}
}
override fun onBind(intent: Intent?): IBinder {
return mBinder
}
fun getplayerInstance(): SimpleExoPlayer? {
if (player == null) {
startPlayer()
}
return player
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
return START_STICKY
}
private fun startPlayer() {
val uri: Uri = Uri.parse("https://storage.googleapis.com/exoplayer-test-media-0/Jazz_In_Paris.mp3")
player = ExoPlayerFactory.newSimpleInstance(mContext, DefaultTrackSelector())
cacheDataSourceFactory = getCacheDataSourceFactory(mContext!!)
val mediaSource: MediaSource = ProgressiveMediaSource.Factory(cacheDataSourceFactory)
.createMediaSource(uri)
player!!.prepare(mediaSource)
player!!.setPlayWhenReady(true)
playerNotificationManager = PlayerNotificationManager.createWithNotificationChannel(
mContext,"channel_id", com.haii.exoplayerdemo.R.string.channelName,com.haii.exoplayerdemo.R.string.channelDescription,11,
object : PlayerNotificationManager.MediaDescriptionAdapter{
override fun createCurrentContentIntent(player: Player): PendingIntent? {
val intent = Intent(mContext,MainActivity::class.java)
return PendingIntent.getActivity(mContext,0,intent,PendingIntent.FLAG_UPDATE_CURRENT)
}
override fun getCurrentContentText(player: Player): String? {
return "description"
}
override fun getCurrentContentTitle(player: Player): String {
return "title"
}
override fun getCurrentLargeIcon(
player: Player,
callback: PlayerNotificationManager.BitmapCallback
): Bitmap? {
return null
}
} ,
object : PlayerNotificationManager.NotificationListener {
override fun onNotificationStarted(notificationId: Int, notification: Notification) {
Log.d("TAG","onNotificationStarted")
startForeground(notificationId,notification)
}
override fun onNotificationPosted(notificationId: Int, notification: Notification, ongoing: Boolean) {
Log.d("TAG","onNotificationPosted")
// startForeground(notificationId,notification)
}
override fun onNotificationCancelled(notificationId: Int, dismissedByUser: Boolean) {
Log.e("TAG","onNotificationCancelled 2")
stopSelf()
}
override fun onNotificationCancelled(notificationId: Int) {
Log.e("TAG","onNotificationCancelled 1")
// stopSelf()
}
}
)
playerNotificationManager!!.setPlayer(player)
}
fun getCacheDataSourceFactory(context : Context) : com.haii.exoplayerdemo.CacheDataSourceFactory?{
if(cacheDataSourceFactory==null){
cacheDataSourceFactory = com.haii.exoplayerdemo.CacheDataSourceFactory(
context,
DefaultDataSourceFactory(context, "ua"),
MyExoPlayer.MAX_CACHE_VALUE, MyExoPlayer.MAX_CACHE_FILE_VALUE
)
}
return cacheDataSourceFactory
}
}
I`m refering from here https://developer.android.com/guide/components/services.
The thing that you are doing wrong is calling getPlayerInstance() before onCreate is called. Your oncreate is called when you either bind or start the service. Good luck
Following is my service class
class LocalAudioService : Service() {
var player: SimpleExoPlayer? = null
private var playerNotificationManager: PlayerNotificationManager? = null
private var mediaSession: MediaSessionCompat? = null
private var mediaSessionConnector: MediaSessionConnector? = null
private var allInOneApplication: AllInOneApplication? = null
private val binder = LocalBinder()
override fun onDestroy() {
mediaSession?.release()
mediaSessionConnector?.setPlayer(null, null)
playerNotificationManager?.setPlayer(null)
player?.release()
player = null
super.onDestroy()
}
override fun onBind(p0: Intent?): IBinder? {
return binder
}
fun getPlayerInstance(): SimpleExoPlayer? {
if (player == null) {
startPlayer()
}
return player
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if(player == null){
startPlayer()
}
return START_STICKY
}
private fun startPlayer(){
val videoTrackSelectionFactory = AdaptiveTrackSelection.Factory()
val trackSelector = DefaultTrackSelector(this, videoTrackSelectionFactory)
allInOneApplication = AllInOneApplication.getInstance()
val renderersFactory = (application as AllInOneApplication).buildRenderersFactory(false)
player =
SimpleExoPlayer.Builder(this, renderersFactory).setTrackSelector(trackSelector).build()
val dataSourceFactory = DefaultDataSourceFactory(
this,
Util.getUserAgent(this, getString(R.string.app_name))
)
// val cacheDataSourceFactory = CacheDataSourceFactory(
// DownloadUtil.getCache(this),
// dataSourceFactory,
// CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR
// )
val concatenatingMediaSource = ConcatenatingMediaSource()
allInOneApplication?.localAudios?.map {
val mediaSource = ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(it?.audioUri)
concatenatingMediaSource.addMediaSource(mediaSource)
}
player?.prepare(concatenatingMediaSource)
player?.playWhenReady = true
playerNotificationManager = PlayerNotificationManager.createWithNotificationChannel(
this,
AUDIO_CHANNEL_ID,
R.string.audio_channel_name,
R.string.audio_channel_name,
NOTIFICATION_ID,
object : MediaDescriptionAdapter {
override fun createCurrentContentIntent(player: Player): PendingIntent? {
val intent = Intent(this#LocalAudioService, LocalAudioPlayer::class.java)
return PendingIntent.getActivity(
this#LocalAudioService,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT
)
}
override fun getCurrentContentText(player: Player): CharSequence? {
return allInOneApplication?.localAudios?.get(player.currentWindowIndex)?.title
}
override fun getCurrentContentTitle(player: Player): CharSequence {
return allInOneApplication?.localAudios?.get(player.currentWindowIndex)?.title.toString()
}
override fun getCurrentLargeIcon(
player: Player,
callback: PlayerNotificationManager.BitmapCallback
): Bitmap? {
return BitmapFactory.decodeResource(resources, R.drawable.audio)
}
}, object : PlayerNotificationManager.NotificationListener {
override fun onNotificationPosted(
notificationId: Int,
notification: Notification,
ongoing: Boolean
) {
startForeground(notificationId, notification)
}
override fun onNotificationCancelled(
notificationId: Int,
dismissedByUser: Boolean
) {
stopSelf()
}
}
)
playerNotificationManager?.setPlayer(player)
mediaSession = MediaSessionCompat(this, MEDIA_SESSION_TAG)
mediaSession?.isActive = true
mediaSession?.sessionToken?.let { playerNotificationManager?.setMediaSessionToken(it) }
mediaSessionConnector = MediaSessionConnector(mediaSession)
mediaSessionConnector?.setQueueNavigator(object : TimelineQueueNavigator(mediaSession) {
override fun getMediaDescription(
player: Player?,
windowIndex: Int
): MediaDescriptionCompat? {
return allInOneApplication?.getMediaDescription(
this#LocalAudioService,
allInOneApplication?.localAudios?.get(windowIndex)
)
}
})
mediaSessionConnector?.setPlayer(player, null)
}
inner class LocalBinder : Binder() {
val service: LocalAudioService
get() = this#LocalAudioService
}
}
Following is my Activity class
class LocalAudioPlayer : AppCompatActivity() {
private var localAudioService: LocalAudioService? = null
private var mBound = false
private val mConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(componentName: ComponentName, iBinder: IBinder) {
val binder: LocalAudioService.LocalBinder = iBinder as LocalAudioService.LocalBinder
localAudioService = binder.service
mBound = true
initializePlayer()
}
override fun onServiceDisconnected(componentName: ComponentName) {
mBound = false
}
}
private fun initializePlayer() {
if (mBound) {
val player: SimpleExoPlayer? = localAudioService?.getPlayerInstance()
exoplayer_local_audio_player.player = player
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_local_audio_player)
Intent(this,LocalAudioService::class.java).also {
Util.startForegroundService(this,it)
exoplayer_local_audio_player.useController = true
exoplayer_local_audio_player.showController()
exoplayer_local_audio_player.controllerAutoShow = true
exoplayer_local_audio_player.controllerHideOnTouch = false
}
}
override fun onStart() {
super.onStart()
bindService(intent,mConnection, Context.BIND_AUTO_CREATE)
initializePlayer()
//TODO do any UI setup
}
override fun onStop() {
unbindService(mConnection)
mBound = false
super.onStop()
}
}
I tried following this question Binding PlayerView with SimpleExoPlayer from a service but the sole does not works for me
Try this:
override fun onStart() {
super.onStart()
Intent(this, LocalAudioService::class.java).also { intent ->
bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
}
}
initializePlayer() should not be called here. At this moment the Service is not bound yet. When the service is bound it will called in the onServiceConnected callback.
bindService(intent,mConnection, Context.BIND_AUTO_CREATE)
intent is null here . Do Asure you are getting correct and non null intent.It should be global declared as in reference example
intent = new Intent(this, AudioPlayerService.class);
here intent is globally declared.
I develop a Flutter plugin, the problem is FlutterPluginBinding.flutterEngine to get FlutterEngine is deprecated. Full code is below:
class AMPlugin : FlutterPlugin, MethodCallHandler, ActivityAware, PluginRegistry.ActivityResultListener {
private lateinit var channel: MethodChannel
private var activity: Activity? = null
override fun onAttachedToEngine(#NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
val engine = flutterPluginBinding.flutterEngine
channel = MethodChannel(engine.dartExecutor, "aM")
channel.setMethodCallHandler(this)
}
override fun onMethodCall(#NonNull call: MethodCall, #NonNull result: Result) {
when (call.method) {
else -> result.notImplemented()
}
}
override fun onDetachedFromEngine(#NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean {
return true
}
override fun onDetachedFromActivity() {
activity = null
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
activity = binding.activity
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activity = binding.activity
}
override fun onDetachedFromActivityForConfigChanges() {
activity = null
}
companion object {
#Suppress("UNUSED")
#JvmStatic
fun registerWith(registrar: Registrar) {
val channel = MethodChannel(registrar.messenger(), "aM")
channel.setMethodCallHandler(AMPlugin())
}
}
}
But I can't understand how to get FlutterEngine or initialize MethodChannel in another way. I tried as val engine = FlutterEngine(binding.applicationContext) but it leads to the crash, seems it requires Activity context.
How to replace this deprecated API?
replace:
channel = MethodChannel(engine.dartExecutor, "aM")
with
channel = MethodChannel(flutterPluginBinding.getBinaryMessenger(), "aM")
I am trying to set the notification icon in my player which is using exoplayer. I have a playlist and am using concatenatingMediaSource. I have the url of the album art for every song but I dont know how I can set it as the notification icon. I read some answers which suggested using a AsyncTask and creating the notification onPostExecute() but I don't see how I can do in exoplayer playerNotificationManager.
Here is my Audio Service class:-
class AudioPlayerService: Service() {
private var player: SimpleExoPlayer? = null
private var playerNotificationManager: PlayerNotificationManager? = null
private var mediaSession: MediaSessionCompat? = null
private var mediaSessionConnector: MediaSessionConnector? = null
private var songList: ArrayList<MetaData>? = null
private var context: Context? = null
override fun onCreate() {
super.onCreate()
context = this
val descriptionAdapter = object : PlayerNotificationManager.MediaDescriptionAdapter {
override fun getCurrentContentTitle(player: Player?): String {
return songList!![player!!.currentWindowIndex].name
}
override fun getCurrentContentText(player: Player?): String? {
return songList!![player!!.currentWindowIndex].artist
}
override fun getCurrentLargeIcon(player: Player?, callback: PlayerNotificationManager.BitmapCallback?): Bitmap? {
return null
}
override fun createCurrentContentIntent(player: Player?): PendingIntent? {
val intent = Intent(context, MainActivity::class.java)
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
}
}
playerNotificationManager = PlayerNotificationManager.createWithNotificationChannel(
this, "1", R.string.notification_name, 1, descriptionAdapter
)
val notificationListener = object : PlayerNotificationManager.NotificationListener {
override fun onNotificationStarted(notificationId: Int, notification: Notification?) {
startForeground(notificationId, notification)
}
override fun onNotificationCancelled(notificationId: Int) {
stopSelf()
}
}
playerNotificationManager!!.setNotificationListener(
notificationListener
)
mediaSession = MediaSessionCompat(context, "Test")
mediaSession!!.isActive = true
playerNotificationManager!!.setMediaSessionToken(mediaSession!!.sessionToken)
mediaSessionConnector = MediaSessionConnector(mediaSession)
val timelineQueueNavigator = object: TimelineQueueNavigator(mediaSession) {
override fun getMediaDescription(player: Player?, windowIndex: Int): MediaDescriptionCompat {
return getMediaDescription(songList!![windowIndex])
}
}
mediaSessionConnector!!.setQueueNavigator(timelineQueueNavigator)
}
override fun onDestroy() {
super.onDestroy()
mediaSession!!.release()
mediaSessionConnector!!.setPlayer(null, null)
playerNotificationManager!!.setPlayer(null)
player!!.release()
player = null
}
override fun onBind(p0: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
songList = intent!!.getParcelableArrayListExtra<MetaData>("list")
context = this
player = ExoPlayerFactory.newSimpleInstance(this, DefaultTrackSelector())
val dataSource = DefaultDataSourceFactory(
this, Util.getUserAgent(this, getString(R.string.app_name)))
val concatenatingMediaSource = ConcatenatingMediaSource()
for(item in songList!!){
val mediaSource = ExtractorMediaSource.Factory(dataSource)
.createMediaSource(Uri.parse(item.url))
concatenatingMediaSource.addMediaSource(mediaSource)
}
player!!.prepare(concatenatingMediaSource)
player!!.playWhenReady = true
playerNotificationManager!!.setPlayer(player)
mediaSessionConnector!!.setPlayer(player, null)
return START_STICKY
}
private fun getMediaDescription(item: MetaData): MediaDescriptionCompat {
return MediaDescriptionCompat.Builder()
.setMediaId(item.id.toString())
.setTitle(item.name)
.setDescription(item.artist)
.setMediaUri(Uri.parse(item.url))
.build()
}
}
Thanks in advance.
You can use Glide for it in overrided function getCurrentLargeIcon
override fun getCurrentLargeIcon(
player: Player?,
callback: PlayerNotificationManager.BitmapCallback?
): Bitmap? {
loadBitmap(currentStation?.cover, callback)
return null //or stub image
}
private fun loadBitmap(url: String, callback: PlayerNotificationManager.BitmapCallback?) {
Glide.with(this)
.asBitmap()
.load(url)
.into(object : CustomTarget<Bitmap>() {
override fun onResourceReady(
resource: Bitmap,
transition: Transition<in Bitmap>?
) {
callback?.onBitmap(resource)
}
})
}
You need to override it like this
#Nullable
#Override
public Bitmap getCurrentLargeIcon(Player player, PlayerNotificationManager.BitmapCallback callback) {
Glide.with(this)
.asBitmap()
.load(uri)
.into(new CustomTarget<Bitmap>() {
#Override
public void onResourceReady(#NonNull Bitmap resource, #Nullable Transition<? super Bitmap> transition) {
callback.onBitmap(resource);
}
#Override
public void onLoadCleared(#Nullable Drawable placeholder) {
}
});
return null;
}