diff --git a/app/src/main/java/com/github/libretube/Globals.kt b/app/src/main/java/com/github/libretube/Globals.kt index 9ea6b35f6..3dac425a9 100644 --- a/app/src/main/java/com/github/libretube/Globals.kt +++ b/app/src/main/java/com/github/libretube/Globals.kt @@ -1,7 +1,5 @@ package com.github.libretube -import android.content.Intent - /** * Global variables can be stored here */ @@ -15,7 +13,4 @@ object Globals { // for downloads var IS_DOWNLOAD_RUNNING = false - - // background mode intent - var backgroundModeIntent: Intent? = null } diff --git a/app/src/main/java/com/github/libretube/dialogs/VideoOptionsDialog.kt b/app/src/main/java/com/github/libretube/dialogs/VideoOptionsDialog.kt index b56df0d94..a2cabca84 100644 --- a/app/src/main/java/com/github/libretube/dialogs/VideoOptionsDialog.kt +++ b/app/src/main/java/com/github/libretube/dialogs/VideoOptionsDialog.kt @@ -2,15 +2,13 @@ package com.github.libretube.dialogs import android.app.Dialog import android.content.Context -import android.content.Intent import android.os.Bundle import android.widget.ArrayAdapter import android.widget.Toast import androidx.fragment.app.DialogFragment -import com.github.libretube.Globals import com.github.libretube.R import com.github.libretube.preferences.PreferenceHelper -import com.github.libretube.services.BackgroundMode +import com.github.libretube.util.BackgroundHelper import com.google.android.material.dialog.MaterialAlertDialogBuilder /** @@ -48,13 +46,7 @@ class VideoOptionsDialog(private val videoId: String, context: Context) : Dialog when (optionsList[which]) { // Start the background mode context?.getString(R.string.playOnBackground) -> { - if (Globals.backgroundModeIntent != null) { - activity?.stopService(Globals.backgroundModeIntent) - } - val intent = Intent(context, BackgroundMode::class.java) - intent.putExtra("videoId", videoId) - Globals.backgroundModeIntent = intent - activity?.startService(intent) + BackgroundHelper.playOnBackground(requireContext(), videoId) } // Add Video to Playlist Dialog context?.getString(R.string.addToPlaylist) -> { diff --git a/app/src/main/java/com/github/libretube/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/fragments/PlayerFragment.kt index eca462cf9..94d702010 100644 --- a/app/src/main/java/com/github/libretube/fragments/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/fragments/PlayerFragment.kt @@ -58,7 +58,7 @@ import com.github.libretube.obj.Streams import com.github.libretube.obj.Subscribe import com.github.libretube.preferences.PreferenceHelper import com.github.libretube.preferences.PreferenceKeys -import com.github.libretube.services.BackgroundMode +import com.github.libretube.util.BackgroundHelper import com.github.libretube.util.ConnectionHelper import com.github.libretube.util.CronetHelper import com.github.libretube.util.DescriptionAdapter @@ -528,13 +528,7 @@ class PlayerFragment : Fragment() { exoPlayer.pause() // start the background mode - if (Globals.backgroundModeIntent != null) { - activity?.stopService(Globals.backgroundModeIntent) - } - val intent = Intent(context, BackgroundMode::class.java) - intent.putExtra("videoId", videoId) - Globals.backgroundModeIntent = intent - activity?.startService(intent) + BackgroundHelper.playOnBackground(requireContext(), videoId!!) } binding.playerScrollView.viewTreeObserver diff --git a/app/src/main/java/com/github/libretube/fragments/SearchFragment.kt b/app/src/main/java/com/github/libretube/fragments/SearchFragment.kt index 29d7d2815..f1e670039 100644 --- a/app/src/main/java/com/github/libretube/fragments/SearchFragment.kt +++ b/app/src/main/java/com/github/libretube/fragments/SearchFragment.kt @@ -66,6 +66,7 @@ class SearchFragment : Fragment() { binding.clearSearchImageView.setOnClickListener { binding.autoCompleteTextView.text.clear() + showHistory() } binding.filterMenuImageView.setOnClickListener { diff --git a/app/src/main/java/com/github/libretube/services/BackgroundMode.kt b/app/src/main/java/com/github/libretube/services/BackgroundMode.kt index aa50ccaf1..b40e52011 100644 --- a/app/src/main/java/com/github/libretube/services/BackgroundMode.kt +++ b/app/src/main/java/com/github/libretube/services/BackgroundMode.kt @@ -1,11 +1,15 @@ package com.github.libretube.services +import android.app.Notification +import android.app.NotificationChannel import android.app.NotificationManager import android.app.Service import android.content.Context import android.content.Intent +import android.os.Build import android.os.IBinder import android.support.v4.media.session.MediaSessionCompat +import com.github.libretube.R import com.github.libretube.obj.Streams import com.github.libretube.preferences.PreferenceHelper import com.github.libretube.preferences.PreferenceKeys @@ -20,7 +24,6 @@ import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector import com.google.android.exoplayer2.ui.PlayerNotificationManager import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -import java.lang.Exception /** * Loads the selected videos audio in background mode with a notification area. @@ -57,27 +60,44 @@ class BackgroundMode : Service() { */ private lateinit var audioAttributes: AudioAttributes + override fun onCreate() { + super.onCreate() + if (Build.VERSION.SDK_INT >= 26) { + val channelId = "background service" + val channel = NotificationChannel( + channelId, + "BackgroundPlay Service", + NotificationManager.IMPORTANCE_DEFAULT + ) + val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager + notificationManager.createNotificationChannel(channel) + val notification: Notification = Notification.Builder(this, channelId) + .setContentTitle(getString(R.string.app_name)) + .setContentText(getString(R.string.playingOnBackground)).build() + startForeground(1, notification) + } + } + /** * Initializes the [player] with the [MediaItem]. */ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - try { - val videoId = intent?.getStringExtra("videoId")!! - val seekToPosition = intent.getLongExtra("seekToPosition", 0L) - playOnBackgroundMode(this, videoId, seekToPosition) - } catch (e: Exception) { - try { - stopService(intent) - } catch (e: Exception) {} - } + // destroy the old player + destroyPlayer() + + // get the intent arguments + val videoId = intent?.getStringExtra("videoId")!! + val position = intent.getLongExtra("position", 0L) + + // play the audio in the background + playAudio(videoId, position) return super.onStartCommand(intent, flags, startId) } /** * Gets the video data and prepares the [player]. */ - private fun playOnBackgroundMode( - c: Context, + private fun playAudio( videoId: String, seekToPosition: Long = 0 ) { @@ -88,14 +108,15 @@ class BackgroundMode : Service() { // Wait until the job is done, to load correctly later in the player job.join() - initializePlayer(c) - initializePlayerNotification(c) + initializePlayer() + initializePlayerNotification() player?.apply { playWhenReady = playWhenReadyPlayer prepare() } + // seek to the previous position if available if (seekToPosition != 0L) player?.seekTo(seekToPosition) } } @@ -103,14 +124,14 @@ class BackgroundMode : Service() { /** * create the player */ - private fun initializePlayer(c: Context) { + private fun initializePlayer() { audioAttributes = AudioAttributes.Builder() .setUsage(C.USAGE_MEDIA) .setContentType(C.CONTENT_TYPE_MUSIC) .build() if (player == null) { - player = ExoPlayer.Builder(c) + player = ExoPlayer.Builder(this) .setAudioAttributes(audioAttributes, true) .build() } @@ -123,49 +144,43 @@ class BackgroundMode : Service() { override fun onPlaybackStateChanged(@Player.State state: Int) { val autoplay = PreferenceHelper.getBoolean(PreferenceKeys.AUTO_PLAY, false) if (state == Player.STATE_ENDED) { - if (autoplay) playNextVideo(c) + if (autoplay) playNextVideo() } } }) - setMediaItem(c) + setMediaItem() } /** * Plays the first related video to the current (used when the playback of the current video ended) */ - private fun playNextVideo(c: Context) { + private fun playNextVideo() { if (response!!.relatedStreams!!.isNotEmpty()) { val videoId = response!! .relatedStreams!![0].url!! .replace("/watch?v=", "") - // destroy old player and its notification - playerNotification = null - player = null - - // kill old notification - val notificationManager = c.getSystemService(Context.NOTIFICATION_SERVICE) - as NotificationManager - notificationManager.cancel(1) + // destroy previous notification and player + destroyPlayer() // play new video on background - playOnBackgroundMode(c, videoId) + playAudio(videoId) } } /** * Initializes the [playerNotification] attached to the [player] and shows it. */ - private fun initializePlayerNotification(c: Context) { + private fun initializePlayerNotification() { playerNotification = PlayerNotificationManager - .Builder(c, 1, "background_mode") + .Builder(this, 1, "background_mode") // set the description of the notification .setMediaDescriptionAdapter( DescriptionAdapter( response?.title!!, response?.uploader!!, response?.thumbnailUrl!!, - c + this ) ) .build() @@ -183,19 +198,30 @@ class BackgroundMode : Service() { * Sets the [MediaItem] with the [response] into the [player]. Also creates a [MediaSessionConnector] * with the [mediaSession] and attach it to the [player]. */ - private fun setMediaItem(c: Context) { + private fun setMediaItem() { response?.let { val mediaItem = MediaItem.Builder().setUri(it.hls!!).build() player?.setMediaItem(mediaItem) } - mediaSession = MediaSessionCompat(c, this.javaClass.name) + mediaSession = MediaSessionCompat(this, this.javaClass.name) mediaSession.isActive = true mediaSessionConnector = MediaSessionConnector(mediaSession) mediaSessionConnector.setPlayer(player) } + private fun destroyPlayer() { + // clear old player and its notification + playerNotification = null + player = null + + // kill old notification + val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) + as NotificationManager + notificationManager.cancel(1) + } + override fun onBind(p0: Intent?): IBinder? { TODO("Not yet implemented") } diff --git a/app/src/main/java/com/github/libretube/util/BackgroundHelper.kt b/app/src/main/java/com/github/libretube/util/BackgroundHelper.kt new file mode 100644 index 000000000..824c58c67 --- /dev/null +++ b/app/src/main/java/com/github/libretube/util/BackgroundHelper.kt @@ -0,0 +1,18 @@ +package com.github.libretube.util + +import android.content.Context +import android.content.Intent +import com.github.libretube.services.BackgroundMode + +object BackgroundHelper { + fun playOnBackground( + context: Context, + videoId: String, + position: Int? = null + ) { + val intent = Intent(context, BackgroundMode::class.java) + intent.putExtra("videoId", videoId) + if (position != null) intent.putExtra("position", position) + context.startForegroundService(intent) + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f5e701163..7bdd874f0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -257,4 +257,5 @@ Preview the video by seeking to the position when scrubbing the seekbar. General Language, region + Playing on background … \ No newline at end of file