Advertisement
andyshon

PlayerService

Feb 7th, 2019
130
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Kotlin 19.51 KB | None | 0 0
  1. package com.andyshon.ucanspeak.ui.catalogTab.catalog.player
  2.  
  3. import android.app.*
  4. import android.content.Context
  5. import android.content.Intent
  6. import android.graphics.BitmapFactory
  7. import android.net.Uri
  8. import android.os.Binder
  9. import android.os.Build
  10. import android.os.Handler
  11. import android.os.IBinder
  12. import android.support.v4.app.NotificationCompat
  13. import com.andyshon.ucanspeak.events.*
  14. import com.google.android.exoplayer2.*
  15. import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory
  16. import com.google.android.exoplayer2.source.ExtractorMediaSource
  17. import com.google.android.exoplayer2.source.TrackGroupArray
  18. import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
  19. import com.google.android.exoplayer2.trackselection.TrackSelectionArray
  20. import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
  21. import com.google.android.exoplayer2.util.Util
  22. import timber.log.Timber
  23. import java.util.*
  24. import android.support.v4.content.LocalBroadcastManager
  25. import android.support.v4.media.session.MediaSessionCompat
  26. import com.andyshon.ucanspeak.data.entity.LessonFilePhraseEntity
  27. import com.google.android.exoplayer2.Player
  28. import com.andyshon.ucanspeak.R
  29. import com.andyshon.ucanspeak.ui.catalogTab.CatalogTabActivity
  30. import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
  31.  
  32. private const val NOTIFY_ID = 1
  33.  
  34. class PlayerService : Service() {
  35.  
  36.     companion object {
  37.  
  38.         var isViewAttachedToBottom = false
  39.  
  40.         var rxEventBus: RxEventBus? = null
  41.         var phrases: ArrayList<LessonFilePhraseEntity> = arrayListOf()
  42.     }
  43.  
  44.     private lateinit var builder : NotificationCompat.Builder
  45.     private lateinit var notificationManager: NotificationManager
  46.  
  47.     private var myBinder = MyBinder()
  48.  
  49.     var exoPlayer: SimpleExoPlayer? = null
  50.     private var mediaSession: MediaSessionCompat? = null
  51.  
  52.  
  53.     private var audioDuration = 0L
  54.  
  55.     var isPlay = false
  56.  
  57.     private var curPos = 0
  58.     private var playerPosition: Long = 0
  59.  
  60.  
  61.     private var lessonName = ""
  62.     private var lessonDescription = ""
  63.  
  64.     var fileLink = ""
  65.  
  66.     private var isShowNotification = false
  67.  
  68.  
  69.     override fun onBind(intent: Intent): IBinder {
  70.         return myBinder
  71.     }
  72.  
  73.     internal inner class MyBinder : Binder() {
  74.         val service: PlayerService
  75.             get() = this@PlayerService
  76.     }
  77.  
  78.     override fun onCreate() {
  79.  
  80.         val context: Context = this
  81.         val renderersFactory = DefaultRenderersFactory(
  82.             context,
  83.             null,
  84.             DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF
  85.         )
  86.         val trackSelector = DefaultTrackSelector()
  87.         exoPlayer = ExoPlayerFactory.newSimpleInstance(
  88.             renderersFactory,
  89.             trackSelector
  90.         )
  91.  
  92.         exoPlayer?.addListener(object : Player.EventListener {
  93.             override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
  94.                 Timber.e("onPlayerStateChanged $playWhenReady : $playbackState")
  95.                 if (playWhenReady && playbackState == Player.STATE_READY) {
  96.                     // media actually playing
  97.  
  98.                     audioDuration = exoPlayer?.duration ?: 0
  99.                     Timber.e("DURATION = $audioDuration}")
  100.                     sendSetSeekBarMaxValue()
  101.  
  102.                     val handler = Handler()
  103.                     handler.postDelayed(onEverySecond, 100)
  104.                 }
  105.                 else {
  106.                     Timber.e("Exoplayer listener else state = $playbackState")
  107.                     if (playbackState == 4) {
  108.                         sendStopPlayer()
  109.                     }
  110.                 }
  111.             }
  112.  
  113.             override fun onPlaybackParametersChanged(playbackParameters: PlaybackParameters?) {}
  114.             override fun onSeekProcessed() {}
  115.             override fun onTracksChanged(trackGroups: TrackGroupArray?, trackSelections: TrackSelectionArray?) {}
  116.             override fun onPlayerError(error: ExoPlaybackException?) {}
  117.             override fun onLoadingChanged(isLoading: Boolean) {}
  118.             override fun onPositionDiscontinuity(reason: Int) {}
  119.             override fun onRepeatModeChanged(repeatMode: Int) {}
  120.             override fun onShuffleModeEnabledChanged(shuffleModeEnabled: Boolean) {}
  121.             override fun onTimelineChanged(timeline: Timeline?, manifest: Any?, reason: Int) {}
  122.         })
  123.     }
  124.  
  125.     override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
  126.         Timber.e("onStartCommand called intent = $intent")
  127.  
  128.         isViewAttachedToBottom = true
  129.  
  130.         if (intent?.hasExtra("lessonName") == true) {
  131.             Timber.e("lessonName")
  132.             Timber.e("has extra lessonName!")
  133.             lessonName = intent.getStringExtra("lessonName")
  134.             lessonDescription = intent.getStringExtra("lessonDescription")
  135.  
  136.             sendSetLessonName()
  137.         }
  138.  
  139.         if (intent?.hasExtra("isPlay") == true) {
  140.             Timber.e("isPlay")
  141.  
  142.             isPlay = intent.getBooleanExtra("isPlay", false)
  143.             exoPlayer?.playWhenReady = /*isPlay*/true
  144.             sendUpdatePlayButton()
  145.         }
  146.  
  147.         if (intent?.hasExtra("UPDATE_PLAYER_FLOW_BTN_PLAY") == true) {
  148.             Timber.e("UPDATE_PLAYER_FLOW_BTN_PLAY ${exoPlayer?.playWhenReady}")
  149.             sendUpdatePlayButton2()
  150.         }
  151.  
  152.         if (intent?.hasExtra("STOP_RESUME") == true) {
  153.             Timber.e("STOP_RESUME player ${exoPlayer?.playWhenReady}")
  154.             exoPlayer?.playWhenReady = exoPlayer?.playWhenReady?.not()!!
  155.             sendUpdatePlayButton2()
  156.             updateNotificationBtnPlay(exoPlayer?.playWhenReady ?: false)
  157.         }
  158.  
  159.         if (intent?.hasExtra("REWIND_NEXT") == true) {
  160.             setCurPos(++curPos)
  161.             rewindForward()
  162.         }
  163.  
  164.         if (intent?.hasExtra("REWIND_BACK") == true) {
  165.             setCurPos(--curPos)
  166.             rewindBack()
  167.         }
  168.  
  169.         if (intent?.hasExtra("SEEKBAR_CHANGE") == true) {
  170.             val progress = intent.getIntExtra("SEEKBAR_CHANGE", 0)
  171.             exoPlayer?.seekTo(progress.toLong())
  172.             updateTime()
  173.         }
  174.  
  175.         if (intent?.hasExtra("PAUSE_PLAYER") == true) {
  176.             isViewAttachedToBottom = false
  177.             exoPlayer?.playWhenReady = false
  178.             closeNotification()
  179.         }
  180.  
  181.         if (intent?.hasExtra("BLABLA") == true) {
  182.             val b = intent.getBooleanExtra("BLABLA", false)
  183.             Timber.e("BLABLA $b")
  184.             updateNotificationBtnPlay(b)
  185.             exoPlayer?.playWhenReady = b // Handler  sending message to a Handler on a dead thread
  186.         }
  187.  
  188.         if (intent?.hasExtra("CLICK") == true) {
  189.             Timber.e("Click player from notification")
  190.  
  191.             exoPlayer?.playWhenReady = exoPlayer?.playWhenReady?.not() ?: false
  192.  
  193.             val intent6 = Intent("intentKey")
  194.             intent6.putExtra("GGGG", exoPlayer?.playWhenReady)
  195.             LocalBroadcastManager.getInstance(this).sendBroadcast(intent6)
  196.  
  197.  
  198.             updateNotificationBtnPlay(exoPlayer?.playWhenReady ?: false)
  199.         }
  200.  
  201.         if (intent?.hasExtra("SET_FILE_LINK") == true) {
  202.             fileLink = intent.getStringExtra("SET_FILE_LINK")
  203.  
  204.             val userAgent = Util.getUserAgent(this, "Play Audio")
  205.             val mediaSource = ExtractorMediaSource(
  206.                 Uri.parse(fileLink), // file audio ada di folder assets
  207.                 DefaultDataSourceFactory(this, userAgent),
  208.                 DefaultExtractorsFactory(), null, null
  209.             )
  210.  
  211.             // init media session
  212.  
  213.             mediaSession = MediaSessionCompat(this, packageName)
  214.             val mediaSessionConnector = MediaSessionConnector(mediaSession)
  215.             mediaSessionConnector.setPlayer(exoPlayer, null)
  216.             mediaSession!!.isActive = true
  217.  
  218.             Timber.e("sessionToken 2 = ${mediaSession?.sessionToken}")
  219.  
  220.             exoPlayer?.prepare(mediaSource)
  221.  
  222.             exoPlayer?.playWhenReady = true
  223.  
  224.  
  225.  
  226.             notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
  227.  
  228.             createNotification(this, isPlay)
  229.         }
  230.  
  231.         if (intent?.hasExtra("CREATE_NOTIFICATION") == true) {
  232.             if (isShowNotification) {
  233.  
  234.             }
  235.             else {
  236.                 createNotification(this, /*isPlay*/false)
  237.             }
  238.         }
  239.  
  240.         return START_NOT_STICKY
  241.     }
  242.  
  243.  
  244.     private fun createNotification(context: Context, isStartPlay: Boolean) {
  245.         val id = "Default_channel_id" // default_channel_id
  246.         val title = "UCanSpeak" // Default Channel
  247.  
  248.         val acceptPlay = Intent(this, PlayerNotificationReceiver::class.java)
  249.         acceptPlay.action = "Player_play"
  250.         val pendingIntentBtnPlay = PendingIntent.getBroadcast(this, 0, acceptPlay,
  251.             PendingIntent.FLAG_UPDATE_CURRENT)
  252.  
  253.         val acceptBack = Intent(this, PlayerNotificationReceiver::class.java)
  254.         acceptBack.action = "Player_back"
  255.         val pendingIntentBtnBack = PendingIntent.getBroadcast(this, 0, acceptBack,
  256.             PendingIntent.FLAG_UPDATE_CURRENT)
  257.  
  258.         val acceptForward = Intent(this, PlayerNotificationReceiver::class.java)
  259.         acceptForward.action = "Player_forward"
  260.         val pendingIntentBtnForward = PendingIntent.getBroadcast(this, 0, acceptForward,
  261.             PendingIntent.FLAG_UPDATE_CURRENT)
  262.  
  263.  
  264.         val intent = Intent(context, CatalogTabActivity::class.java)
  265.         val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0)
  266.  
  267.         val myLogo = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)
  268.         val myLogo2 = BitmapFactory.decodeResource(resources, R.drawable.ic_logo_big_notification)
  269.  
  270.  
  271.         builder = NotificationCompat.Builder(context, id)
  272.  
  273.         builder.addAction(R.drawable.ic_player_hide_previous_btn, "Rewind back", pendingIntentBtnBack)
  274.         if (isStartPlay)
  275.             builder.addAction(R.drawable.exo_icon_pause, "Play/Resume", pendingIntentBtnPlay)
  276.         else
  277.             builder.addAction(R.drawable.exo_icon_play, "Play/Resume", pendingIntentBtnPlay)
  278.         builder.addAction(R.drawable.ic_player_hide_next_btn, "Rewind forward", pendingIntentBtnForward)
  279.             .setStyle(android.support.v4.media.app.NotificationCompat.MediaStyle().setShowActionsInCompactView(0,1,2).setMediaSession(mediaSession?.sessionToken))
  280.             .setSmallIcon(R.mipmap.ic_launcher)
  281.             .setShowWhen(false)
  282.             .setOngoing(true)
  283.             .setSound(null)
  284. //            .setSound(Uri.parse("android.resource://"+YOUR_APP_PACKAGE_NAME+"/"+R.raw.silence))   //TODO: just set silent sound
  285.             .setContentTitle(lessonName)
  286.             .setContentText(lessonDescription)
  287.             .setContentIntent(pendingIntent)
  288.  
  289.  
  290.         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
  291.             val importance = NotificationManager.IMPORTANCE_DEFAULT
  292.             var mChannel = notificationManager.getNotificationChannel(id)
  293.             if (mChannel == null) {
  294.                 mChannel = NotificationChannel(id, title, importance)
  295.                 notificationManager.createNotificationChannel(mChannel)
  296.             }
  297.             builder
  298.                 .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
  299.                 .setLargeIcon(myLogo)
  300.                 .priority = Notification.PRIORITY_MAX
  301.         }
  302.         else {
  303.             builder
  304.                 .setLargeIcon(myLogo2)
  305.                 .setTicker(lessonName)
  306. //                .priority = Notification.PRIORITY_HIGH
  307.         }
  308.         val notification = builder.build()
  309.         notificationManager.notify(NOTIFY_ID, notification)
  310.         isShowNotification = true
  311.     }
  312.  
  313.  
  314.     private fun closeNotification() {
  315.         notificationManager.cancel(NOTIFY_ID)
  316.         isShowNotification = false
  317.     }
  318.  
  319.  
  320.     private fun rewindBack() {
  321.         Timber.e("REWIND_BACK Curpos = $curPos")
  322.         if (curPos > 0) {
  323.             if (curPos == 0) {
  324.                 sendBtnBackInvisible()
  325.  
  326.             } else {
  327.                 sendBtnBackVisible()
  328.             }
  329.  
  330.             val previousPhrase = phrases[curPos - 1]
  331.             Timber.e("playerBtnBack call curPost = $curPos previousPhrase pos ${previousPhrase.position}")
  332.             exoPlayer?.seekTo(previousPhrase.position.toLong() * 1000)
  333.             sendUpdateSeekBar(exoPlayer?.currentPosition?.toInt()!!)
  334.             updateTime()
  335.         }
  336.         else {
  337.             sendBtnBackInvisible()
  338.         }
  339.     }
  340.  
  341.  
  342.     private fun rewindForward() {
  343.         Timber.e("REWIND_NEXT Curpos = $curPos")
  344.         if (curPos < phrases.size - 1) {
  345.  
  346.             if (curPos == phrases.size - 2) {
  347.                 sendBtnForwardInvisible()
  348.  
  349.             } else {
  350.                 sendBtnForwardVisible()
  351.             }
  352.  
  353.             val nextPhrase = phrases[curPos + 1]
  354.             exoPlayer?.seekTo(nextPhrase.position.toLong() * 1000)
  355.             sendUpdateSeekBar(exoPlayer?.currentPosition?.toInt()!!)
  356.             updateTime()
  357.         }
  358.         else {
  359.             sendBtnForwardInvisible()
  360.         }
  361.     }
  362.  
  363.  
  364.     fun updateNotificationBtnPlay(isPlay: Boolean) {
  365.  
  366.         //TODO: first update play button on flow player layout
  367.         sendUpdatePlayButton2()
  368.  
  369.         val acceptPlay = Intent(this, PlayerNotificationReceiver::class.java)
  370.         acceptPlay.action = "Player_play"
  371.         val pendingIntentBtnPlay = PendingIntent.getBroadcast(this, 0, acceptPlay,
  372.             PendingIntent.FLAG_UPDATE_CURRENT)
  373.  
  374.         val acceptBack = Intent(this, PlayerNotificationReceiver::class.java)
  375.         acceptBack.action = "Player_back"
  376.         val pendingIntentBtnBack = PendingIntent.getBroadcast(this, 0, acceptBack,
  377.             PendingIntent.FLAG_UPDATE_CURRENT)
  378.  
  379.         val acceptForward = Intent(this, PlayerNotificationReceiver::class.java)
  380.         acceptForward.action = "Player_forward"
  381.         val pendingIntentBtnForward = PendingIntent.getBroadcast(this, 0, acceptForward,
  382.             PendingIntent.FLAG_UPDATE_CURRENT)
  383.  
  384.         builder.mActions.clear()
  385.  
  386.         if (isPlay) {
  387.             builder.addAction(R.drawable.ic_player_hide_previous_btn, "Rewind back", pendingIntentBtnBack)
  388.             builder.addAction(R.drawable.exo_icon_pause, "Play/Resume", pendingIntentBtnPlay)
  389.             builder.addAction(R.drawable.ic_player_hide_next_btn, "Rewind forward", pendingIntentBtnForward)
  390.         }
  391.         else {
  392.             builder.addAction(R.drawable.ic_player_hide_previous_btn, "Rewind back", pendingIntentBtnBack)
  393.             builder.addAction(R.drawable.exo_icon_play, "Play/Resume", pendingIntentBtnPlay)
  394.             builder.addAction(R.drawable.ic_player_hide_next_btn, "Rewind forward", pendingIntentBtnForward)
  395.         }
  396.  
  397.         notificationManager.notify(NOTIFY_ID, builder.build())
  398.     }
  399.  
  400.     private fun updateNotificationBtnRewindBack(isVisible: Boolean) {
  401.         if (isVisible) { }
  402.         else { }
  403.     }
  404.  
  405.     private fun updateNotificationBtnRewindForward(isVisible: Boolean) {
  406.         if (isVisible) { }
  407.         else { }
  408.     }
  409.  
  410.     fun setCurPos(pos: Int) {
  411.         curPos = pos
  412.     }
  413.  
  414.     private fun sendStopPlayer() {
  415.         val intent = Intent("intentKey")
  416.         intent.putExtra("STOP_PLAYER", true)
  417.         LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
  418.     }
  419.  
  420.     private fun sendBtnBackVisible() {
  421.         val intent = Intent("intentKey")
  422.         intent.putExtra("BTN_BACK_VISIBLE", true)
  423.         LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
  424.     }
  425.  
  426.     private fun sendBtnBackInvisible() {
  427.         val intent = Intent("intentKey")
  428.         intent.putExtra("BTN_BACK_INVISIBLE", true)
  429.         LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
  430.     }
  431.  
  432.     private fun sendBtnForwardVisible() {
  433.         val intent = Intent("intentKey")
  434.         intent.putExtra("BTN_FORWARD_VISIBLE", true)
  435.         LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
  436.     }
  437.  
  438.     private fun sendBtnForwardInvisible() {
  439.         val intent = Intent("intentKey")
  440.         intent.putExtra("BTN_FORWARD_INVISIBLE", true)
  441.         LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
  442.     }
  443.  
  444.     private fun sendSetSeekBarMaxValue() {
  445.         val intent = Intent("intentKey")
  446.         intent.putExtra("SEEKBAR_MAX_VALUE", audioDuration)
  447.         LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
  448.     }
  449.  
  450.     private fun sendUpdatePlayButton2() {
  451.         val intent = Intent("intentKey")
  452.         intent.putExtra("STOP_RESUME", exoPlayer?.playWhenReady)
  453.         LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
  454.     }
  455.  
  456.     private fun sendUpdatePlayButton() {
  457.         val intent = Intent("intentKey")
  458.         intent.putExtra("yes", true)
  459.         LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
  460.     }
  461.  
  462.     private fun sendSetLessonName() {
  463.         val intent = Intent("intentKey")
  464.         intent.putExtra("lessonName", lessonName)
  465.         intent.putExtra("lessonDescription", lessonDescription)
  466.         LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
  467.     }
  468.  
  469.     private fun sendUpdateSeekBar(msg: Int) {
  470.         val intent = Intent("intentKey")
  471.         intent.putExtra("key", msg)
  472.         LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
  473.     }
  474.  
  475.  
  476.      val onEverySecond = object : Runnable {
  477.         override fun run() {
  478.             if (exoPlayer != null) {
  479.                 if (phrases.isNotEmpty()) {
  480.                     try {
  481.                         sendSetSeekBarMaxValue()
  482.  
  483.                         sendUpdateSeekBar(exoPlayer?.currentPosition?.toInt()!!)
  484.  
  485.                         val seconds = exoPlayer?.currentPosition?.toInt()!! / 1000
  486.                         if (seconds >= phrases[1].position) {
  487.                             sendBtnBackVisible()
  488.                         } else {
  489.                             sendBtnBackInvisible()
  490.                         }
  491.                         if (seconds >= phrases[phrases.size - 1].position) {
  492.                             sendBtnForwardInvisible()
  493.                         } else {
  494.                             sendBtnForwardVisible()
  495.                         }
  496.  
  497.                         updateTime()
  498.                         sendUpdateTimeText()
  499.  
  500.                         Timber.e("CUR POS EXO = ${exoPlayer?.currentPosition} : duration = $audioDuration")
  501.                         if (exoPlayer?.currentPosition ?: 0 >= audioDuration || exoPlayer?.playWhenReady == false) {
  502.                             Timber.e("Stop self")
  503. //                            stopSelf()
  504.                         }
  505.                         else {
  506.                             sendUpdatePhrase()
  507.                             Handler().postDelayed(this, 100)
  508.                         }
  509.                     } catch (e: IllegalStateException) {
  510.                         Timber.e("CATCH onEverySecond run")
  511.                     }
  512.                 }
  513.             }
  514.             else { }
  515.         }
  516.     }
  517.  
  518.     private fun updateTime() {
  519.         playerPosition = exoPlayer?.currentPosition!! / 1000
  520.     }
  521.  
  522.     private fun sendUpdatePhrase() {
  523.         val intent = Intent("intentKey")
  524.         intent.putExtra("updatePhrase", true)
  525.         LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
  526.     }
  527.  
  528.     private fun sendUpdateTimeText() {
  529.         val intent = Intent("intentKey")
  530.         intent.putExtra("current", exoPlayer?.currentPosition)
  531.         intent.putExtra("audioDuration", audioDuration)
  532.         LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
  533.     }
  534.  
  535.     override fun onDestroy() {
  536.         isViewAttachedToBottom = false
  537.         exoPlayer?.release()
  538.     }
  539. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement