ultrasonic-app-subsonic-and.../ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt

1208 lines
47 KiB
Kotlin
Raw Normal View History

/*
* PlayerFragment.kt
* Copyright (C) 2009-2021 Ultrasonic developers
*
* Distributed under terms of the GNU GPLv3 license.
*/
2021-06-19 20:05:38 +02:00
package org.moire.ultrasonic.fragment
2021-06-19 20:42:03 +02:00
import android.annotation.SuppressLint
2021-06-19 20:05:38 +02:00
import android.app.AlertDialog
import android.graphics.Point
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.os.Handler
import android.view.ContextMenu
import android.view.ContextMenu.ContextMenuInfo
import android.view.GestureDetector
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.view.animation.AnimationUtils
import android.widget.AdapterView.AdapterContextMenuInfo
import android.widget.EditText
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.SeekBar
import android.widget.SeekBar.OnSeekBarChangeListener
import android.widget.TextView
import android.widget.ViewFlipper
import androidx.core.view.isVisible
2021-06-19 20:05:38 +02:00
import androidx.fragment.app.Fragment
import androidx.navigation.Navigation
import com.mobeta.android.dslv.DragSortListView
import com.mobeta.android.dslv.DragSortListView.DragSortListener
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.ArrayList
import java.util.Date
import java.util.LinkedList
import java.util.Locale
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit
import kotlin.math.abs
import kotlin.math.max
import org.koin.android.ext.android.inject
import org.koin.core.component.KoinComponent
import org.koin.core.component.get
2021-06-19 20:05:38 +02:00
import org.moire.ultrasonic.R
import org.moire.ultrasonic.audiofx.EqualizerController
import org.moire.ultrasonic.audiofx.VisualizerController
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.domain.PlayerState
import org.moire.ultrasonic.domain.RepeatMode
import org.moire.ultrasonic.featureflags.Feature
import org.moire.ultrasonic.featureflags.FeatureStorage
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
import org.moire.ultrasonic.service.DownloadFile
import org.moire.ultrasonic.service.LocalMediaPlayer
2021-06-19 20:05:38 +02:00
import org.moire.ultrasonic.service.MediaPlayerController
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
import org.moire.ultrasonic.subsonic.ImageLoaderProvider
import org.moire.ultrasonic.subsonic.NetworkAndStorageChecker
import org.moire.ultrasonic.subsonic.ShareHandler
import org.moire.ultrasonic.util.CancellationToken
import org.moire.ultrasonic.util.Constants
import org.moire.ultrasonic.util.Settings
import org.moire.ultrasonic.util.SilentBackgroundTask
2021-06-19 20:05:38 +02:00
import org.moire.ultrasonic.util.Util
import org.moire.ultrasonic.view.AutoRepeatButton
import org.moire.ultrasonic.view.SongListAdapter
import org.moire.ultrasonic.view.VisualizerView
import timber.log.Timber
2021-02-05 21:45:50 +01:00
/**
* Contains the Music Player screen of Ultrasonic with playback controls and the playlist
*
* TODO: This class was more or less straight converted from Java legacy code.
* There are many places where further cleanup would be nice.
* The usage of threads and SilentBackgroundTask can be replaced with Coroutines.
*/
2021-06-19 20:42:03 +02:00
@Suppress("LargeClass", "TooManyFunctions", "MagicNumber")
class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinComponent {
// Settings
2021-06-19 20:05:38 +02:00
private var currentRevision: Long = 0
private var swipeDistance = 0
private var swipeVelocity = 0
private var jukeboxAvailable = false
private var useFiveStarRating = false
private var isEqualizerAvailable = false
private var isVisualizerAvailable = false
2021-06-19 20:42:03 +02:00
// Detectors & Callbacks
private lateinit var gestureScanner: GestureDetector
private lateinit var cancellationToken: CancellationToken
// Data & Services
private val networkAndStorageChecker: NetworkAndStorageChecker by inject()
private val mediaPlayerController: MediaPlayerController by inject()
private val localMediaPlayer: LocalMediaPlayer by inject()
2021-06-19 20:42:03 +02:00
private val shareHandler: ShareHandler by inject()
private val imageLoaderProvider: ImageLoaderProvider by inject()
private lateinit var executorService: ScheduledExecutorService
private var currentPlaying: DownloadFile? = null
private var currentSong: MusicDirectory.Entry? = null
private var onProgressChangedTask: SilentBackgroundTask<Void?>? = null
// Views and UI Elements
private lateinit var visualizerViewLayout: LinearLayout
private lateinit var visualizerView: VisualizerView
private lateinit var playlistNameView: EditText
private lateinit var starMenuItem: MenuItem
private lateinit var fiveStar1ImageView: ImageView
private lateinit var fiveStar2ImageView: ImageView
private lateinit var fiveStar3ImageView: ImageView
private lateinit var fiveStar4ImageView: ImageView
private lateinit var fiveStar5ImageView: ImageView
private lateinit var playlistFlipper: ViewFlipper
private lateinit var emptyTextView: TextView
private lateinit var songTitleTextView: TextView
private lateinit var albumTextView: TextView
private lateinit var artistTextView: TextView
private lateinit var albumArtImageView: ImageView
private lateinit var playlistView: DragSortListView
private lateinit var positionTextView: TextView
private lateinit var downloadTrackTextView: TextView
private lateinit var downloadTotalDurationTextView: TextView
private lateinit var durationTextView: TextView
private lateinit var pauseButton: View
private lateinit var stopButton: View
private lateinit var startButton: View
private lateinit var repeatButton: ImageView
private lateinit var hollowStar: Drawable
private lateinit var fullStar: Drawable
private lateinit var progressBar: SeekBar
2021-06-19 20:05:38 +02:00
override fun onCreate(savedInstanceState: Bundle?) {
Util.applyTheme(this.context)
super.onCreate(savedInstanceState)
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
2021-06-19 20:05:38 +02:00
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.current_playing, container, false)
2021-02-05 21:45:50 +01:00
}
private fun findViews(view: View) {
2021-06-19 20:05:38 +02:00
playlistFlipper = view.findViewById(R.id.current_playing_playlist_flipper)
emptyTextView = view.findViewById(R.id.playlist_empty)
songTitleTextView = view.findViewById(R.id.current_playing_song)
albumTextView = view.findViewById(R.id.current_playing_album)
artistTextView = view.findViewById(R.id.current_playing_artist)
albumArtImageView = view.findViewById(R.id.current_playing_album_art_image)
positionTextView = view.findViewById(R.id.current_playing_position)
downloadTrackTextView = view.findViewById(R.id.current_playing_track)
downloadTotalDurationTextView = view.findViewById(R.id.current_total_duration)
durationTextView = view.findViewById(R.id.current_playing_duration)
progressBar = view.findViewById(R.id.current_playing_progress_bar)
playlistView = view.findViewById(R.id.playlist_view)
2021-06-19 20:42:03 +02:00
2021-06-19 20:05:38 +02:00
pauseButton = view.findViewById(R.id.button_pause)
stopButton = view.findViewById(R.id.button_stop)
startButton = view.findViewById(R.id.button_start)
repeatButton = view.findViewById(R.id.button_repeat)
visualizerViewLayout = view.findViewById(R.id.current_playing_visualizer_layout)
fiveStar1ImageView = view.findViewById(R.id.song_five_star_1)
fiveStar2ImageView = view.findViewById(R.id.song_five_star_2)
fiveStar3ImageView = view.findViewById(R.id.song_five_star_3)
fiveStar4ImageView = view.findViewById(R.id.song_five_star_4)
fiveStar5ImageView = view.findViewById(R.id.song_five_star_5)
2021-06-19 20:42:03 +02:00
}
@Suppress("LongMethod")
@SuppressLint("ClickableViewAccessibility")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
cancellationToken = CancellationToken()
setTitle(this, R.string.common_appname)
val windowManager = requireActivity().windowManager
val display = windowManager.defaultDisplay
val size = Point()
display.getSize(size)
val width = size.x
val height = size.y
setHasOptionsMenu(true)
useFiveStarRating = get<FeatureStorage>().isFeatureEnabled(Feature.FIVE_STAR_RATING)
swipeDistance = (width + height) * PERCENTAGE_OF_SCREEN_FOR_SWIPE / 100
swipeVelocity = swipeDistance
gestureScanner = GestureDetector(context, this)
// The secondary progress is an indicator of how far the song is cached.
localMediaPlayer.secondaryProgress.observe(
viewLifecycleOwner,
{
progressBar.secondaryProgress = it
}
)
2021-06-19 20:42:03 +02:00
findViews(view)
val previousButton: AutoRepeatButton = view.findViewById(R.id.button_previous)
val nextButton: AutoRepeatButton = view.findViewById(R.id.button_next)
val shuffleButton = view.findViewById<View>(R.id.button_shuffle)
val ratingLinearLayout = view.findViewById<LinearLayout>(R.id.song_rating)
if (!useFiveStarRating) ratingLinearLayout.isVisible = false
2021-06-19 20:05:38 +02:00
hollowStar = Util.getDrawableFromAttribute(view.context, R.attr.star_hollow)
fullStar = Util.getDrawableFromAttribute(context, R.attr.star_full)
2021-06-19 20:42:03 +02:00
fiveStar1ImageView.setOnClickListener { setSongRating(1) }
fiveStar2ImageView.setOnClickListener { setSongRating(2) }
fiveStar3ImageView.setOnClickListener { setSongRating(3) }
fiveStar4ImageView.setOnClickListener { setSongRating(4) }
fiveStar5ImageView.setOnClickListener { setSongRating(5) }
albumArtImageView.setOnTouchListener { _, me ->
gestureScanner.onTouchEvent(me)
}
albumArtImageView.setOnClickListener {
toggleFullScreenAlbumArt()
}
2021-06-19 20:05:38 +02:00
previousButton.setOnClickListener {
2021-06-19 20:42:03 +02:00
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
2021-06-19 20:05:38 +02:00
object : SilentBackgroundTask<Void?>(activity) {
2021-06-19 20:42:03 +02:00
override fun doInBackground(): Void? {
mediaPlayerController.previous()
2021-06-19 20:05:38 +02:00
return null
}
2021-02-05 21:45:50 +01:00
2021-06-19 20:42:03 +02:00
override fun done(result: Void?) {
2021-06-19 20:05:38 +02:00
onCurrentChanged()
onSliderProgressChanged()
}
}.execute()
}
2021-06-19 20:42:03 +02:00
2021-06-19 20:05:38 +02:00
previousButton.setOnRepeatListener {
val incrementTime = Settings.incrementTime
2021-06-19 20:05:38 +02:00
changeProgress(-incrementTime)
}
2021-06-19 20:42:03 +02:00
2021-06-19 20:05:38 +02:00
nextButton.setOnClickListener {
2021-06-19 20:42:03 +02:00
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
2021-06-19 20:05:38 +02:00
object : SilentBackgroundTask<Boolean?>(activity) {
override fun doInBackground(): Boolean {
2021-06-19 20:42:03 +02:00
mediaPlayerController.next()
2021-06-19 20:05:38 +02:00
return true
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:42:03 +02:00
override fun done(result: Boolean?) {
if (result == true) {
2021-06-19 20:05:38 +02:00
onCurrentChanged()
onSliderProgressChanged()
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
}
}.execute()
}
2021-06-19 20:42:03 +02:00
2021-06-19 20:05:38 +02:00
nextButton.setOnRepeatListener {
val incrementTime = Settings.incrementTime
2021-06-19 20:05:38 +02:00
changeProgress(incrementTime)
}
2021-06-19 20:42:03 +02:00
pauseButton.setOnClickListener {
2021-06-19 20:05:38 +02:00
object : SilentBackgroundTask<Void?>(activity) {
2021-06-19 20:42:03 +02:00
override fun doInBackground(): Void? {
mediaPlayerController.pause()
2021-06-19 20:05:38 +02:00
return null
}
2021-02-05 21:45:50 +01:00
2021-06-19 20:42:03 +02:00
override fun done(result: Void?) {
2021-06-19 20:05:38 +02:00
onCurrentChanged()
onSliderProgressChanged()
}
}.execute()
2021-06-19 20:42:03 +02:00
}
stopButton.setOnClickListener {
2021-06-19 20:05:38 +02:00
object : SilentBackgroundTask<Void?>(activity) {
2021-06-19 20:42:03 +02:00
override fun doInBackground(): Void? {
mediaPlayerController.reset()
2021-06-19 20:05:38 +02:00
return null
}
2021-02-05 21:45:50 +01:00
2021-06-19 20:42:03 +02:00
override fun done(result: Void?) {
2021-06-19 20:05:38 +02:00
onCurrentChanged()
onSliderProgressChanged()
}
}.execute()
2021-06-19 20:42:03 +02:00
}
startButton.setOnClickListener {
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
2021-06-19 20:05:38 +02:00
object : SilentBackgroundTask<Void?>(activity) {
2021-06-19 20:42:03 +02:00
override fun doInBackground(): Void? {
2021-06-19 20:05:38 +02:00
start()
return null
}
2021-02-05 21:45:50 +01:00
2021-06-19 20:42:03 +02:00
override fun done(result: Void?) {
2021-06-19 20:05:38 +02:00
onCurrentChanged()
onSliderProgressChanged()
}
}.execute()
2021-06-19 20:42:03 +02:00
}
2021-06-19 20:05:38 +02:00
shuffleButton.setOnClickListener {
2021-06-19 20:42:03 +02:00
mediaPlayerController.shuffle()
2021-06-19 20:05:38 +02:00
Util.toast(activity, R.string.download_menu_shuffle_notification)
}
2021-06-19 20:42:03 +02:00
repeatButton.setOnClickListener {
val repeatMode = mediaPlayerController.repeatMode.next()
2021-06-19 20:42:03 +02:00
mediaPlayerController.repeatMode = repeatMode
2021-10-13 20:59:28 +02:00
onPlaylistChanged()
2021-06-19 20:05:38 +02:00
when (repeatMode) {
RepeatMode.OFF -> Util.toast(
context, R.string.download_repeat_off
)
RepeatMode.ALL -> Util.toast(
context, R.string.download_repeat_all
)
RepeatMode.SINGLE -> Util.toast(
context, R.string.download_repeat_single
)
else -> {
}
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:42:03 +02:00
}
progressBar.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
2021-06-19 20:05:38 +02:00
override fun onStopTrackingTouch(seekBar: SeekBar) {
object : SilentBackgroundTask<Void?>(activity) {
2021-06-19 20:42:03 +02:00
override fun doInBackground(): Void? {
mediaPlayerController.seekTo(progressBar.progress)
2021-06-19 20:05:38 +02:00
return null
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:42:03 +02:00
override fun done(result: Void?) {
2021-06-19 20:05:38 +02:00
onSliderProgressChanged()
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
}.execute()
}
override fun onStartTrackingTouch(seekBar: SeekBar) {}
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {}
})
2021-06-19 20:42:03 +02:00
playlistView.setOnItemClickListener { _, _, position, _ ->
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
2021-06-19 20:05:38 +02:00
object : SilentBackgroundTask<Void?>(activity) {
2021-06-19 20:42:03 +02:00
override fun doInBackground(): Void? {
mediaPlayerController.play(position)
2021-06-19 20:05:38 +02:00
return null
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:42:03 +02:00
override fun done(result: Void?) {
2021-06-19 20:05:38 +02:00
onCurrentChanged()
onSliderProgressChanged()
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
}.execute()
2021-06-19 20:42:03 +02:00
}
2021-06-19 20:05:38 +02:00
registerForContextMenu(playlistView)
2021-06-19 20:42:03 +02:00
if (arguments != null && requireArguments().getBoolean(
Constants.INTENT_EXTRA_NAME_SHUFFLE,
false
)
2021-06-19 20:05:38 +02:00
) {
2021-06-19 20:42:03 +02:00
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
2021-06-19 20:05:38 +02:00
mediaPlayerController.isShufflePlayEnabled = true
}
2021-06-19 20:42:03 +02:00
visualizerViewLayout.isVisible = false
VisualizerController.get().observe(
requireActivity(),
{ visualizerController ->
if (visualizerController != null) {
Timber.d("VisualizerController Observer.onChanged received controller")
visualizerView = VisualizerView(context)
visualizerViewLayout.addView(
visualizerView,
LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT
)
2021-06-19 20:05:38 +02:00
)
visualizerViewLayout.isVisible = visualizerView.isActive
visualizerView.setOnTouchListener { _, _ ->
visualizerView.isActive = !visualizerView.isActive
mediaPlayerController.showVisualization = visualizerView.isActive
true
}
isVisualizerAvailable = true
2021-06-19 20:05:38 +02:00
} else {
Timber.d("VisualizerController Observer.onChanged has no controller")
visualizerViewLayout.isVisible = false
isVisualizerAvailable = false
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
}
)
2021-06-19 20:42:03 +02:00
EqualizerController.get().observe(
requireActivity(),
{ equalizerController ->
isEqualizerAvailable = if (equalizerController != null) {
Timber.d("EqualizerController Observer.onChanged received controller")
true
} else {
Timber.d("EqualizerController Observer.onChanged has no controller")
false
}
2021-06-19 20:05:38 +02:00
}
)
2021-06-19 20:05:38 +02:00
Thread {
try {
2021-06-19 20:42:03 +02:00
jukeboxAvailable = mediaPlayerController.isJukeboxAvailable
} catch (all: Exception) {
Timber.e(all)
2021-06-19 20:05:38 +02:00
}
}.start()
2021-06-19 20:42:03 +02:00
view.setOnTouchListener { _, event -> gestureScanner.onTouchEvent(event) }
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
override fun onResume() {
super.onResume()
2021-06-19 20:42:03 +02:00
if (mediaPlayerController.currentPlaying == null) {
playlistFlipper.displayedChild = 1
2021-06-19 20:05:38 +02:00
} else {
2021-02-05 21:45:50 +01:00
// Download list and Album art must be updated when Resumed
2021-10-13 20:59:28 +02:00
onPlaylistChanged()
2021-06-19 20:05:38 +02:00
onCurrentChanged()
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
val handler = Handler()
val runnable = Runnable { handler.post { update(cancellationToken) } }
executorService = Executors.newSingleThreadScheduledExecutor()
executorService.scheduleWithFixedDelay(runnable, 0L, 250L, TimeUnit.MILLISECONDS)
2021-06-19 20:42:03 +02:00
if (mediaPlayerController.keepScreenOn) {
requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
2021-06-19 20:05:38 +02:00
} else {
2021-06-19 20:42:03 +02:00
requireActivity().window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:42:03 +02:00
if (::visualizerView.isInitialized) {
visualizerView.isActive = mediaPlayerController.showVisualization
}
2021-06-19 20:42:03 +02:00
requireActivity().invalidateOptionsMenu()
2021-02-05 21:45:50 +01:00
}
// Scroll to current playing.
2021-06-19 20:05:38 +02:00
private fun scrollToCurrent() {
2021-06-19 20:42:03 +02:00
val adapter = playlistView.adapter
2021-06-19 20:05:38 +02:00
if (adapter != null) {
val count = adapter.count
for (i in 0 until count) {
2021-06-19 20:42:03 +02:00
if (currentPlaying == playlistView.getItemAtPosition(i)) {
playlistView.smoothScrollToPositionFromTop(i, 40)
2021-06-19 20:05:38 +02:00
return
2021-02-05 21:45:50 +01:00
}
}
}
}
2021-06-19 20:05:38 +02:00
override fun onPause() {
super.onPause()
2021-06-19 20:42:03 +02:00
executorService.shutdown()
if (::visualizerView.isInitialized) {
visualizerView.isActive = mediaPlayerController.showVisualization
}
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
override fun onDestroyView() {
2021-06-19 20:42:03 +02:00
cancellationToken.cancel()
2021-06-19 20:05:38 +02:00
super.onDestroyView()
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.nowplaying, menu)
super.onCreateOptionsMenu(menu, inflater)
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:42:03 +02:00
@Suppress("ComplexMethod", "LongMethod", "NestedBlockDepth")
2021-06-19 20:05:38 +02:00
override fun onPrepareOptionsMenu(menu: Menu) {
super.onPrepareOptionsMenu(menu)
val screenOption = menu.findItem(R.id.menu_item_screen_on_off)
val jukeboxOption = menu.findItem(R.id.menu_item_jukebox)
val equalizerMenuItem = menu.findItem(R.id.menu_item_equalizer)
val visualizerMenuItem = menu.findItem(R.id.menu_item_visualizer)
val shareMenuItem = menu.findItem(R.id.menu_item_share)
val shareSongMenuItem = menu.findItem(R.id.menu_item_share_song)
2021-06-19 20:05:38 +02:00
starMenuItem = menu.findItem(R.id.menu_item_star)
val bookmarkMenuItem = menu.findItem(R.id.menu_item_bookmark_set)
val bookmarkRemoveMenuItem = menu.findItem(R.id.menu_item_bookmark_delete)
2021-06-19 20:42:03 +02:00
2021-06-19 20:05:38 +02:00
if (isOffline()) {
if (shareMenuItem != null) {
shareMenuItem.isVisible = false
}
2021-06-19 20:42:03 +02:00
starMenuItem.isVisible = false
2021-06-19 20:05:38 +02:00
if (bookmarkMenuItem != null) {
bookmarkMenuItem.isVisible = false
}
if (bookmarkRemoveMenuItem != null) {
bookmarkRemoveMenuItem.isVisible = false
2021-02-05 21:45:50 +01:00
}
}
2021-06-19 20:05:38 +02:00
if (equalizerMenuItem != null) {
equalizerMenuItem.isEnabled = isEqualizerAvailable
equalizerMenuItem.isVisible = isEqualizerAvailable
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
if (visualizerMenuItem != null) {
visualizerMenuItem.isEnabled = isVisualizerAvailable
visualizerMenuItem.isVisible = isVisualizerAvailable
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:42:03 +02:00
val mediaPlayerController = mediaPlayerController
val downloadFile = mediaPlayerController.currentPlaying
2021-06-19 20:42:03 +02:00
if (downloadFile != null) {
currentSong = downloadFile.song
}
2021-06-19 20:42:03 +02:00
if (useFiveStarRating) starMenuItem.isVisible = false
2021-06-19 20:42:03 +02:00
if (currentSong != null) {
starMenuItem.icon = if (currentSong!!.starred) fullStar else hollowStar
shareSongMenuItem.isVisible = true
2021-06-19 20:42:03 +02:00
} else {
starMenuItem.icon = hollowStar
shareSongMenuItem.isVisible = false
2021-06-19 20:42:03 +02:00
}
2021-06-19 20:42:03 +02:00
if (mediaPlayerController.keepScreenOn) {
screenOption?.setTitle(R.string.download_menu_screen_off)
} else {
screenOption?.setTitle(R.string.download_menu_screen_on)
}
2021-06-19 20:42:03 +02:00
if (jukeboxOption != null) {
jukeboxOption.isEnabled = jukeboxAvailable
jukeboxOption.isVisible = jukeboxAvailable
if (mediaPlayerController.isJukeboxEnabled) {
jukeboxOption.setTitle(R.string.download_menu_jukebox_off)
2021-06-19 20:05:38 +02:00
} else {
2021-06-19 20:42:03 +02:00
jukeboxOption.setTitle(R.string.download_menu_jukebox_on)
2021-02-05 21:45:50 +01:00
}
}
}
2021-06-19 20:05:38 +02:00
override fun onCreateContextMenu(menu: ContextMenu, view: View, menuInfo: ContextMenuInfo?) {
super.onCreateContextMenu(menu, view, menuInfo)
if (view === playlistView) {
val info = menuInfo as AdapterContextMenuInfo?
2021-06-19 20:42:03 +02:00
val downloadFile = playlistView.getItemAtPosition(info!!.position) as DownloadFile
val menuInflater = requireActivity().menuInflater
2021-06-19 20:05:38 +02:00
menuInflater.inflate(R.menu.nowplaying_context, menu)
2021-06-19 20:42:03 +02:00
val song: MusicDirectory.Entry?
song = downloadFile.song
if (song.parent == null) {
2021-06-19 20:05:38 +02:00
val menuItem = menu.findItem(R.id.menu_show_album)
if (menuItem != null) {
menuItem.isVisible = false
2021-02-05 21:45:50 +01:00
}
}
2021-06-19 20:42:03 +02:00
if (isOffline() || !Settings.shouldUseId3Tags) {
2021-06-19 20:42:03 +02:00
menu.findItem(R.id.menu_show_artist)?.isVisible = false
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:42:03 +02:00
2021-06-19 20:05:38 +02:00
if (isOffline()) {
2021-06-19 20:42:03 +02:00
menu.findItem(R.id.menu_lyrics)?.isVisible = false
2021-02-05 21:45:50 +01:00
}
}
}
2021-06-19 20:05:38 +02:00
override fun onContextItemSelected(menuItem: MenuItem): Boolean {
val info = menuItem.menuInfo as AdapterContextMenuInfo
2021-06-19 20:42:03 +02:00
val downloadFile = playlistView.getItemAtPosition(info.position) as DownloadFile
2021-06-19 20:05:38 +02:00
return menuItemSelected(menuItem.itemId, downloadFile) || super.onContextItemSelected(
menuItem
)
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return menuItemSelected(item.itemId, null) || super.onOptionsItemSelected(item)
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:42:03 +02:00
@Suppress("ComplexMethod", "LongMethod", "ReturnCount")
2021-06-19 20:05:38 +02:00
private fun menuItemSelected(menuItemId: Int, song: DownloadFile?): Boolean {
var entry: MusicDirectory.Entry? = null
val bundle: Bundle
if (song != null) {
entry = song.song
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:42:03 +02:00
when (menuItemId) {
R.id.menu_show_artist -> {
if (entry == null) return false
if (Settings.shouldUseId3Tags) {
2021-06-19 20:42:03 +02:00
bundle = Bundle()
bundle.putString(Constants.INTENT_EXTRA_NAME_ID, entry.artistId)
bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.artist)
bundle.putString(Constants.INTENT_EXTRA_NAME_PARENT_ID, entry.artistId)
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true)
Navigation.findNavController(requireView())
.navigate(R.id.playerToSelectAlbum, bundle)
2021-06-19 20:42:03 +02:00
}
return true
}
2021-06-19 20:42:03 +02:00
R.id.menu_show_album -> {
if (entry == null) return false
val albumId = if (Settings.shouldUseId3Tags) entry.albumId else entry.parent
2021-06-19 20:05:38 +02:00
bundle = Bundle()
2021-06-19 20:42:03 +02:00
bundle.putString(Constants.INTENT_EXTRA_NAME_ID, albumId)
bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.album)
bundle.putString(Constants.INTENT_EXTRA_NAME_PARENT_ID, entry.parent)
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_IS_ALBUM, true)
Navigation.findNavController(requireView())
.navigate(R.id.playerToSelectAlbum, bundle)
2021-06-19 20:42:03 +02:00
return true
2021-06-19 20:05:38 +02:00
}
2021-06-19 20:42:03 +02:00
R.id.menu_lyrics -> {
if (entry == null) return false
2021-06-19 20:42:03 +02:00
bundle = Bundle()
bundle.putString(Constants.INTENT_EXTRA_NAME_ARTIST, entry.artist)
bundle.putString(Constants.INTENT_EXTRA_NAME_TITLE, entry.title)
Navigation.findNavController(requireView()).navigate(R.id.playerToLyrics, bundle)
2021-06-19 20:42:03 +02:00
return true
2021-06-19 20:05:38 +02:00
}
2021-06-19 20:42:03 +02:00
R.id.menu_remove -> {
mediaPlayerController.removeFromPlaylist(song!!)
2021-10-13 20:59:28 +02:00
onPlaylistChanged()
2021-06-19 20:42:03 +02:00
return true
}
2021-06-19 20:42:03 +02:00
R.id.menu_item_screen_on_off -> {
val window = requireActivity().window
2021-06-19 20:42:03 +02:00
if (mediaPlayerController.keepScreenOn) {
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
2021-06-19 20:42:03 +02:00
mediaPlayerController.keepScreenOn = false
} else {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
2021-06-19 20:42:03 +02:00
mediaPlayerController.keepScreenOn = true
}
return true
}
2021-06-19 20:42:03 +02:00
R.id.menu_shuffle -> {
mediaPlayerController.shuffle()
Util.toast(context, R.string.download_menu_shuffle_notification)
return true
2021-06-19 20:05:38 +02:00
}
2021-06-19 20:42:03 +02:00
R.id.menu_item_equalizer -> {
Navigation.findNavController(requireView()).navigate(R.id.playerToEqualizer)
2021-06-19 20:42:03 +02:00
return true
}
2021-06-19 20:42:03 +02:00
R.id.menu_item_visualizer -> {
val active = !visualizerView.isActive
visualizerView.isActive = active
visualizerViewLayout.isVisible = visualizerView.isActive
2021-06-19 20:42:03 +02:00
mediaPlayerController.showVisualization = visualizerView.isActive
Util.toast(
context,
if (active) R.string.download_visualizer_on
else R.string.download_visualizer_off
2021-06-19 20:42:03 +02:00
)
2021-06-19 20:05:38 +02:00
return true
}
2021-06-19 20:42:03 +02:00
R.id.menu_item_jukebox -> {
val jukeboxEnabled = !mediaPlayerController.isJukeboxEnabled
mediaPlayerController.isJukeboxEnabled = jukeboxEnabled
Util.toast(
context,
if (jukeboxEnabled) R.string.download_jukebox_on
else R.string.download_jukebox_off,
2021-06-19 20:42:03 +02:00
false
)
return true
2021-06-19 20:05:38 +02:00
}
2021-06-19 20:42:03 +02:00
R.id.menu_item_toggle_list -> {
toggleFullScreenAlbumArt()
2021-06-19 20:05:38 +02:00
return true
}
2021-06-19 20:42:03 +02:00
R.id.menu_item_clear_playlist -> {
mediaPlayerController.isShufflePlayEnabled = false
mediaPlayerController.clear()
2021-10-13 20:59:28 +02:00
onPlaylistChanged()
2021-06-19 20:42:03 +02:00
return true
}
R.id.menu_item_save_playlist -> {
if (mediaPlayerController.playlistSize > 0) {
showSavePlaylistDialog()
}
2021-06-19 20:05:38 +02:00
return true
}
2021-06-19 20:42:03 +02:00
R.id.menu_item_star -> {
if (currentSong == null) return true
2021-06-19 20:42:03 +02:00
val isStarred = currentSong!!.starred
val id = currentSong!!.id
if (isStarred) {
starMenuItem.icon = hollowStar
currentSong!!.starred = false
} else {
starMenuItem.icon = fullStar
currentSong!!.starred = true
}
Thread {
val musicService = getMusicService()
try {
if (isStarred) {
musicService.unstar(id, null, null)
} else {
musicService.star(id, null, null)
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:42:03 +02:00
} catch (all: Exception) {
Timber.e(all)
}
}.start()
return true
}
R.id.menu_item_bookmark_set -> {
if (currentSong == null) return true
2021-06-19 20:42:03 +02:00
val songId = currentSong!!.id
val playerPosition = mediaPlayerController.playerPosition
currentSong!!.bookmarkPosition = playerPosition
val bookmarkTime = Util.formatTotalDuration(playerPosition.toLong(), true)
Thread {
val musicService = getMusicService()
try {
musicService.createBookmark(songId, playerPosition)
} catch (all: Exception) {
Timber.e(all)
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:42:03 +02:00
}.start()
val msg = resources.getString(
R.string.download_bookmark_set_at_position,
bookmarkTime
)
2021-06-19 20:42:03 +02:00
Util.toast(context, msg)
return true
}
R.id.menu_item_bookmark_delete -> {
if (currentSong == null) return true
2021-06-19 20:42:03 +02:00
val bookmarkSongId = currentSong!!.id
currentSong!!.bookmarkPosition = 0
Thread {
val musicService = getMusicService()
try {
musicService.deleteBookmark(bookmarkSongId)
} catch (all: Exception) {
Timber.e(all)
}
}.start()
Util.toast(context, R.string.download_bookmark_removed)
return true
}
2021-06-19 20:42:03 +02:00
R.id.menu_item_share -> {
val mediaPlayerController = mediaPlayerController
val entries: MutableList<MusicDirectory.Entry?> = ArrayList()
val downloadServiceSongs = mediaPlayerController.playList
for (downloadFile in downloadServiceSongs) {
val playlistEntry = downloadFile.song
entries.add(playlistEntry)
}
shareHandler.createShare(this, entries, null, cancellationToken)
return true
}
R.id.menu_item_share_song -> {
if (currentSong == null) return true
val entries: MutableList<MusicDirectory.Entry?> = ArrayList()
entries.add(currentSong)
shareHandler.createShare(this, entries, null, cancellationToken)
return true
}
2021-06-19 20:42:03 +02:00
else -> return false
2021-02-05 21:45:50 +01:00
}
}
2021-06-19 20:05:38 +02:00
private fun update(cancel: CancellationToken?) {
if (cancel!!.isCancellationRequested) return
2021-06-19 20:42:03 +02:00
val mediaPlayerController = mediaPlayerController
2021-06-19 20:05:38 +02:00
if (currentRevision != mediaPlayerController.playListUpdateRevision) {
2021-10-13 20:59:28 +02:00
onPlaylistChanged()
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
if (currentPlaying != mediaPlayerController.currentPlaying) {
onCurrentChanged()
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
onSliderProgressChanged()
2021-06-19 20:42:03 +02:00
requireActivity().invalidateOptionsMenu()
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
private fun savePlaylistInBackground(playlistName: String) {
Util.toast(context, resources.getString(R.string.download_playlist_saving, playlistName))
2021-06-19 20:42:03 +02:00
mediaPlayerController.suggestedPlaylistName = playlistName
2021-06-19 20:05:38 +02:00
object : SilentBackgroundTask<Void?>(activity) {
@Throws(Throwable::class)
2021-06-19 20:42:03 +02:00
override fun doInBackground(): Void? {
2021-06-19 20:05:38 +02:00
val entries: MutableList<MusicDirectory.Entry> = LinkedList()
2021-06-19 20:42:03 +02:00
for (downloadFile in mediaPlayerController.playList) {
2021-06-19 20:05:38 +02:00
entries.add(downloadFile.song)
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
val musicService = getMusicService()
musicService.createPlaylist(null, playlistName, entries)
return null
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:42:03 +02:00
override fun done(result: Void?) {
2021-06-19 20:05:38 +02:00
Util.toast(context, R.string.download_playlist_done)
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
override fun error(error: Throwable) {
Timber.e(error, "Exception has occurred in savePlaylistInBackground")
val msg = String.format(
Locale.ROOT,
2021-06-19 20:05:38 +02:00
"%s %s",
resources.getString(R.string.download_playlist_error),
getErrorMessage(error)
)
Util.toast(context, msg)
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
}.execute()
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
private fun toggleFullScreenAlbumArt() {
2021-06-19 20:42:03 +02:00
if (playlistFlipper.displayedChild == 1) {
playlistFlipper.inAnimation =
2021-06-19 20:05:38 +02:00
AnimationUtils.loadAnimation(context, R.anim.push_down_in)
2021-06-19 20:42:03 +02:00
playlistFlipper.outAnimation =
2021-06-19 20:05:38 +02:00
AnimationUtils.loadAnimation(context, R.anim.push_down_out)
2021-06-19 20:42:03 +02:00
playlistFlipper.displayedChild = 0
2021-06-19 20:05:38 +02:00
} else {
2021-06-19 20:42:03 +02:00
playlistFlipper.inAnimation =
2021-06-19 20:05:38 +02:00
AnimationUtils.loadAnimation(context, R.anim.push_up_in)
2021-06-19 20:42:03 +02:00
playlistFlipper.outAnimation =
2021-06-19 20:05:38 +02:00
AnimationUtils.loadAnimation(context, R.anim.push_up_out)
2021-06-19 20:42:03 +02:00
playlistFlipper.displayedChild = 1
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
scrollToCurrent()
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
private fun start() {
2021-06-19 20:42:03 +02:00
val service = mediaPlayerController
2021-06-19 20:05:38 +02:00
val state = service.playerState
if (state === PlayerState.PAUSED ||
state === PlayerState.COMPLETED || state === PlayerState.STOPPED
) {
2021-06-19 20:05:38 +02:00
service.start()
} else if (state === PlayerState.IDLE) {
2021-06-19 20:42:03 +02:00
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
val current = mediaPlayerController.currentPlayingNumberOnPlaylist
2021-06-19 20:05:38 +02:00
if (current == -1) {
service.play(0)
} else {
service.play(current)
2021-02-05 21:45:50 +01:00
}
}
}
2021-10-13 20:59:28 +02:00
private fun onPlaylistChanged() {
2021-06-19 20:42:03 +02:00
val mediaPlayerController = mediaPlayerController
2021-06-19 20:05:38 +02:00
val list = mediaPlayerController.playList
2021-06-19 20:42:03 +02:00
emptyTextView.setText(R.string.download_empty)
2021-06-19 20:05:38 +02:00
val adapter = SongListAdapter(context, list)
2021-06-19 20:42:03 +02:00
playlistView.adapter = adapter
playlistView.setDragSortListener(object : DragSortListener {
2021-06-19 20:05:38 +02:00
override fun drop(from: Int, to: Int) {
if (from != to) {
val item = adapter.getItem(from)
adapter.remove(item)
adapter.notifyDataSetChanged()
adapter.insert(item, to)
adapter.notifyDataSetChanged()
2021-02-05 21:45:50 +01:00
}
}
2021-06-19 20:05:38 +02:00
override fun drag(from: Int, to: Int) {}
override fun remove(which: Int) {
2021-06-19 20:42:03 +02:00
val item = adapter.getItem(which) ?: return
2021-06-19 20:05:38 +02:00
val currentPlaying = mediaPlayerController.currentPlaying
if (currentPlaying == item) {
2021-06-19 20:42:03 +02:00
mediaPlayerController.next()
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
adapter.remove(item)
adapter.notifyDataSetChanged()
val songRemoved = String.format(
resources.getString(R.string.download_song_removed),
item.song.title
)
Util.toast(context, songRemoved)
2021-10-13 20:59:28 +02:00
onPlaylistChanged()
2021-06-19 20:05:38 +02:00
onCurrentChanged()
}
})
emptyTextView.isVisible = list.isEmpty()
2021-06-19 20:05:38 +02:00
currentRevision = mediaPlayerController.playListUpdateRevision
when (mediaPlayerController.repeatMode) {
2021-06-19 20:42:03 +02:00
RepeatMode.OFF -> repeatButton.setImageDrawable(
2021-06-19 20:05:38 +02:00
Util.getDrawableFromAttribute(
context, R.attr.media_repeat_off
)
)
2021-06-19 20:42:03 +02:00
RepeatMode.ALL -> repeatButton.setImageDrawable(
2021-06-19 20:05:38 +02:00
Util.getDrawableFromAttribute(
context, R.attr.media_repeat_all
)
)
2021-06-19 20:42:03 +02:00
RepeatMode.SINGLE -> repeatButton.setImageDrawable(
2021-06-19 20:05:38 +02:00
Util.getDrawableFromAttribute(
context, R.attr.media_repeat_single
)
)
else -> {
2021-02-05 21:45:50 +01:00
}
}
}
2021-06-19 20:05:38 +02:00
private fun onCurrentChanged() {
currentPlaying = mediaPlayerController.currentPlaying
scrollToCurrent()
val totalDuration = mediaPlayerController.playListDuration
val totalSongs = mediaPlayerController.playlistSize.toLong()
val currentSongIndex = mediaPlayerController.currentPlayingNumberOnPlaylist + 1
val duration = Util.formatTotalDuration(totalDuration)
val trackFormat =
String.format(Locale.getDefault(), "%d / %d", currentSongIndex, totalSongs)
if (currentPlaying != null) {
currentSong = currentPlaying!!.song
2021-06-19 20:42:03 +02:00
songTitleTextView.text = currentSong!!.title
albumTextView.text = currentSong!!.album
artistTextView.text = currentSong!!.artist
downloadTrackTextView.text = trackFormat
downloadTotalDurationTextView.text = duration
imageLoaderProvider.getImageLoader()
2021-06-19 20:05:38 +02:00
.loadImage(albumArtImageView, currentSong, true, 0)
displaySongRating()
} else {
currentSong = null
2021-06-19 20:42:03 +02:00
songTitleTextView.text = null
albumTextView.text = null
artistTextView.text = null
downloadTrackTextView.text = null
downloadTotalDurationTextView.text = null
imageLoaderProvider.getImageLoader()
.loadImage(albumArtImageView, null, true, 0)
2021-02-05 21:45:50 +01:00
}
}
2021-06-19 20:05:38 +02:00
private fun onSliderProgressChanged() {
2021-06-19 20:42:03 +02:00
if (onProgressChangedTask != null) {
2021-06-19 20:05:38 +02:00
return
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
onProgressChangedTask = object : SilentBackgroundTask<Void?>(activity) {
var isJukeboxEnabled = false
var millisPlayed = 0
var duration: Int? = null
var playerState: PlayerState? = null
2021-06-19 20:42:03 +02:00
override fun doInBackground(): Void? {
isJukeboxEnabled = mediaPlayerController.isJukeboxEnabled
millisPlayed = max(0, mediaPlayerController.playerPosition)
duration = mediaPlayerController.playerDuration
playerState = mediaPlayerController.playerState
2021-06-19 20:05:38 +02:00
return null
}
2021-06-19 20:42:03 +02:00
@Suppress("LongMethod")
override fun done(result: Void?) {
if (cancellationToken.isCancellationRequested) return
2021-06-19 20:05:38 +02:00
if (currentPlaying != null) {
val millisTotal = if (duration == null) 0 else duration!!
2021-06-19 20:42:03 +02:00
positionTextView.text = Util.formatTotalDuration(millisPlayed.toLong(), true)
durationTextView.text = Util.formatTotalDuration(millisTotal.toLong(), true)
progressBar.max =
2021-06-19 20:05:38 +02:00
if (millisTotal == 0) 100 else millisTotal // Work-around for apparent bug.
progressBar.progress = millisPlayed
progressBar.isEnabled = currentPlaying!!.isWorkDone || isJukeboxEnabled
2021-06-19 20:05:38 +02:00
} else {
2021-06-19 20:42:03 +02:00
positionTextView.setText(R.string.util_zero_time)
durationTextView.setText(R.string.util_no_time)
progressBar.progress = 0
progressBar.max = 0
progressBar.isEnabled = false
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:42:03 +02:00
2021-06-19 20:05:38 +02:00
when (playerState) {
PlayerState.DOWNLOADING -> {
val progress =
if (currentPlaying != null) currentPlaying!!.progress.value!! else 0
val downloadStatus = resources.getString(
R.string.download_playerstate_downloading,
Util.formatPercentage(progress)
)
setTitle(this@PlayerFragment, downloadStatus)
}
PlayerState.PREPARING -> setTitle(
this@PlayerFragment,
R.string.download_playerstate_buffering
)
PlayerState.STARTED -> {
2021-06-19 20:42:03 +02:00
if (mediaPlayerController.isShufflePlayEnabled) {
2021-06-19 20:05:38 +02:00
setTitle(
this@PlayerFragment,
R.string.download_playerstate_playing_shuffle
)
} else {
setTitle(this@PlayerFragment, R.string.common_appname)
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
}
2021-06-19 20:42:03 +02:00
PlayerState.IDLE,
PlayerState.PREPARED,
PlayerState.STOPPED,
PlayerState.PAUSED,
PlayerState.COMPLETED -> {
2021-06-19 20:05:38 +02:00
}
else -> setTitle(this@PlayerFragment, R.string.common_appname)
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:42:03 +02:00
2021-06-19 20:05:38 +02:00
when (playerState) {
PlayerState.STARTED -> {
pauseButton.isVisible = true
stopButton.isVisible = false
startButton.isVisible = false
2021-06-19 20:05:38 +02:00
}
PlayerState.DOWNLOADING, PlayerState.PREPARING -> {
pauseButton.isVisible = false
stopButton.isVisible = true
startButton.isVisible = false
2021-06-19 20:05:38 +02:00
}
else -> {
pauseButton.isVisible = false
stopButton.isVisible = false
startButton.isVisible = true
2021-06-19 20:05:38 +02:00
}
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:42:03 +02:00
// TODO: It would be a lot nicer if MediaPlayerController would send an event
// when this is necessary instead of updating every time
2021-06-19 20:05:38 +02:00
displaySongRating()
onProgressChangedTask = null
2021-02-05 21:45:50 +01:00
}
}
2021-06-19 20:42:03 +02:00
onProgressChangedTask!!.execute()
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
private fun changeProgress(ms: Int) {
object : SilentBackgroundTask<Void?>(activity) {
var msPlayed = 0
var duration: Int? = null
var seekTo = 0
2021-06-19 20:42:03 +02:00
override fun doInBackground(): Void? {
msPlayed = max(0, mediaPlayerController.playerPosition)
2021-06-19 20:05:38 +02:00
duration = mediaPlayerController.playerDuration
val msTotal = duration!!
2021-06-19 20:42:03 +02:00
seekTo = (msPlayed + ms).coerceAtMost(msTotal)
2021-06-19 20:05:38 +02:00
mediaPlayerController.seekTo(seekTo)
return null
}
2021-06-19 20:42:03 +02:00
override fun done(result: Void?) {
progressBar.progress = seekTo
2021-06-19 20:05:38 +02:00
}
}.execute()
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
override fun onDown(me: MotionEvent): Boolean {
return false
}
2021-02-05 21:45:50 +01:00
2021-06-19 20:42:03 +02:00
@Suppress("ReturnCount")
2021-06-19 20:05:38 +02:00
override fun onFling(
e1: MotionEvent,
e2: MotionEvent,
velocityX: Float,
velocityY: Float
): Boolean {
val e1X = e1.x
val e2X = e2.x
val e1Y = e1.y
val e2Y = e2.y
2021-06-19 20:42:03 +02:00
val absX = abs(velocityX)
val absY = abs(velocityY)
2021-02-05 21:45:50 +01:00
// Right to Left swipe
2021-06-19 20:05:38 +02:00
if (e1X - e2X > swipeDistance && absX > swipeVelocity) {
2021-06-19 20:42:03 +02:00
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
2021-06-19 20:05:38 +02:00
mediaPlayerController.next()
onCurrentChanged()
onSliderProgressChanged()
return true
2021-02-05 21:45:50 +01:00
}
// Left to Right swipe
2021-06-19 20:05:38 +02:00
if (e2X - e1X > swipeDistance && absX > swipeVelocity) {
2021-06-19 20:42:03 +02:00
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
2021-06-19 20:05:38 +02:00
mediaPlayerController.previous()
onCurrentChanged()
onSliderProgressChanged()
return true
2021-02-05 21:45:50 +01:00
}
// Top to Bottom swipe
2021-06-19 20:05:38 +02:00
if (e2Y - e1Y > swipeDistance && absY > swipeVelocity) {
2021-06-19 20:42:03 +02:00
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
2021-06-19 20:05:38 +02:00
mediaPlayerController.seekTo(mediaPlayerController.playerPosition + 30000)
onSliderProgressChanged()
return true
2021-02-05 21:45:50 +01:00
}
// Bottom to Top swipe
2021-06-19 20:05:38 +02:00
if (e1Y - e2Y > swipeDistance && absY > swipeVelocity) {
2021-06-19 20:42:03 +02:00
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
2021-06-19 20:05:38 +02:00
mediaPlayerController.seekTo(mediaPlayerController.playerPosition - 8000)
onSliderProgressChanged()
return true
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
return false
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
override fun onLongPress(e: MotionEvent) {}
override fun onScroll(
e1: MotionEvent,
e2: MotionEvent,
distanceX: Float,
distanceY: Float
): Boolean {
return false
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
override fun onShowPress(e: MotionEvent) {}
override fun onSingleTapUp(e: MotionEvent): Boolean {
return false
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
private fun displaySongRating() {
var rating = 0
if (currentSong?.userRating != null) {
rating = currentSong!!.userRating!!
}
2021-06-19 20:42:03 +02:00
fiveStar1ImageView.setImageDrawable(if (rating > 0) fullStar else hollowStar)
fiveStar2ImageView.setImageDrawable(if (rating > 1) fullStar else hollowStar)
fiveStar3ImageView.setImageDrawable(if (rating > 2) fullStar else hollowStar)
fiveStar4ImageView.setImageDrawable(if (rating > 3) fullStar else hollowStar)
fiveStar5ImageView.setImageDrawable(if (rating > 4) fullStar else hollowStar)
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
private fun setSongRating(rating: Int) {
if (currentSong == null) return
displaySongRating()
2021-06-19 20:42:03 +02:00
mediaPlayerController.setSongRating(rating)
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
private fun showSavePlaylistDialog() {
val layout = LayoutInflater.from(this.context).inflate(R.layout.save_playlist, null)
playlistNameView = layout.findViewById(R.id.save_playlist_name)
2021-06-19 20:42:03 +02:00
val builder: AlertDialog.Builder = AlertDialog.Builder(context)
2021-06-19 20:05:38 +02:00
builder.setTitle(R.string.download_playlist_title)
builder.setMessage(R.string.download_playlist_name)
2021-06-19 20:42:03 +02:00
builder.setPositiveButton(R.string.common_save) { _, _ ->
2021-06-19 20:05:38 +02:00
savePlaylistInBackground(
2021-06-19 20:42:03 +02:00
playlistNameView.text.toString()
2021-06-19 20:05:38 +02:00
)
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:42:03 +02:00
builder.setNegativeButton(R.string.common_cancel) { dialog, _ -> dialog.cancel() }
2021-06-19 20:05:38 +02:00
builder.setView(layout)
builder.setCancelable(true)
val dialog = builder.create()
2021-06-19 20:42:03 +02:00
val playlistName = mediaPlayerController.suggestedPlaylistName
2021-06-19 20:05:38 +02:00
if (playlistName != null) {
2021-06-19 20:42:03 +02:00
playlistNameView.setText(playlistName)
2021-06-19 20:05:38 +02:00
} else {
val dateFormat: DateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
2021-06-19 20:42:03 +02:00
playlistNameView.setText(dateFormat.format(Date()))
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:05:38 +02:00
dialog.show()
}
2021-02-05 21:45:50 +01:00
2021-06-19 20:05:38 +02:00
companion object {
private const val PERCENTAGE_OF_SCREEN_FOR_SWIPE = 5
2021-02-05 21:45:50 +01:00
}
2021-06-19 20:42:03 +02:00
}