My initial commit
Removed Google play dependencies
This commit is contained in:
parent
fd582fff69
commit
301ac10570
430 changed files with 2210 additions and 3137 deletions
|
@ -0,0 +1,20 @@
|
|||
package io.github.muntashirakon.music.fragments
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import io.github.muntashirakon.music.R
|
||||
|
||||
|
||||
enum class AlbumCoverStyle(
|
||||
@StringRes val titleRes: Int,
|
||||
@DrawableRes val drawableResId: Int,
|
||||
val id: Int
|
||||
) {
|
||||
Card(R.string.card, R.drawable.np_blur_card, 4),
|
||||
Circle(R.string.circular, R.drawable.np_circle, 2),
|
||||
Flat(R.string.flat, R.drawable.np_flat, 1),
|
||||
FullCard(R.string.full_card, R.drawable.np_adaptive, 6),
|
||||
Full(R.string.full, R.drawable.np_full, 5),
|
||||
Material(R.string.material, R.drawable.np_material, 3),
|
||||
Normal(R.string.normal, R.drawable.np_normal, 0),
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
package io.github.muntashirakon.music.fragments
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import io.github.muntashirakon.music.fragments.ReloadType.*
|
||||
import io.github.muntashirakon.music.interfaces.MusicServiceEventListener
|
||||
import io.github.muntashirakon.music.model.*
|
||||
import io.github.muntashirakon.music.providers.RepositoryImpl
|
||||
import io.github.muntashirakon.music.providers.interfaces.Repository
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class LibraryViewModel(application: Application) :
|
||||
AndroidViewModel(application), MusicServiceEventListener {
|
||||
|
||||
private val _repository: Repository = RepositoryImpl(application.applicationContext)
|
||||
private val _albums = MutableLiveData<List<Album>>()
|
||||
private val _songs = MutableLiveData<List<Song>>()
|
||||
private val _artists = MutableLiveData<List<Artist>>()
|
||||
private val _playlist = MutableLiveData<List<Playlist>>()
|
||||
private val _genre = MutableLiveData<List<Genre>>()
|
||||
private val _homeSections = MutableLiveData<List<Home>>()
|
||||
|
||||
fun homeSections(): LiveData<List<Home>> = _homeSections
|
||||
fun allAlbums(): LiveData<List<Album>> = _albums
|
||||
fun allSongs(): LiveData<List<Song>> = _songs
|
||||
fun allArtists(): LiveData<List<Artist>> = _artists
|
||||
fun allPlaylisits(): LiveData<List<Playlist>> = _playlist
|
||||
fun allGenres(): LiveData<List<Genre>> = _genre
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
loadLibraryContent()
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadLibraryContent() = viewModelScope.launch {
|
||||
_songs.value = loadSongs.await()
|
||||
_albums.value = loadAlbums.await()
|
||||
_artists.value = loadArtists.await()
|
||||
_playlist.value = loadPlaylists.await()
|
||||
_genre.value = loadGenres.await()
|
||||
loadHomeSections()
|
||||
}
|
||||
|
||||
private fun loadHomeSections() = viewModelScope.launch {
|
||||
val list = mutableListOf<Home>()
|
||||
val result = listOf(
|
||||
_repository.topArtists(),
|
||||
_repository.topAlbums(),
|
||||
_repository.recentArtists(),
|
||||
_repository.recentAlbums(),
|
||||
_repository.favoritePlaylist()
|
||||
)
|
||||
for (r in result) {
|
||||
if (r != null) {
|
||||
list.add(r)
|
||||
}
|
||||
}
|
||||
_homeSections.value = list
|
||||
}
|
||||
|
||||
private val loadSongs: Deferred<List<Song>>
|
||||
get() = viewModelScope.async(IO) {
|
||||
_repository.allSongs()
|
||||
}
|
||||
|
||||
private val loadAlbums: Deferred<List<Album>>
|
||||
get() = viewModelScope.async(IO) {
|
||||
_repository.allAlbums()
|
||||
}
|
||||
|
||||
private val loadArtists: Deferred<List<Artist>>
|
||||
get() = viewModelScope.async(IO) {
|
||||
_repository.allArtists()
|
||||
}
|
||||
|
||||
private val loadPlaylists: Deferred<List<Playlist>>
|
||||
get() = viewModelScope.async(IO) {
|
||||
_repository.allPlaylists()
|
||||
}
|
||||
|
||||
private val loadGenres: Deferred<List<Genre>>
|
||||
get() = viewModelScope.async(IO) {
|
||||
_repository.allGenres()
|
||||
}
|
||||
|
||||
fun forceReload(reloadType: ReloadType) = viewModelScope.launch {
|
||||
when (reloadType) {
|
||||
Songs -> _songs.value = loadSongs.await()
|
||||
Albums -> _albums.value = loadAlbums.await()
|
||||
Artists -> _artists.value = loadArtists.await()
|
||||
HomeSections -> _songs.value = loadSongs.await()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMediaStoreChanged() {
|
||||
loadLibraryContent()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {}
|
||||
override fun onServiceDisconnected() {}
|
||||
override fun onQueueChanged() {}
|
||||
override fun onPlayingMetaChanged() {}
|
||||
override fun onPlayStateChanged() {}
|
||||
override fun onRepeatModeChanged() {}
|
||||
override fun onShuffleModeChanged() {}
|
||||
}
|
||||
|
||||
enum class ReloadType {
|
||||
Songs,
|
||||
Albums,
|
||||
Artists,
|
||||
HomeSections
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
package io.github.muntashirakon.music.fragments
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.text.SpannableString
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.view.*
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.extensions.show
|
||||
import io.github.muntashirakon.music.extensions.textColorPrimary
|
||||
import io.github.muntashirakon.music.extensions.textColorSecondary
|
||||
import io.github.muntashirakon.music.fragments.base.AbsMusicServiceFragment
|
||||
import io.github.muntashirakon.music.glide.SongGlideRequest
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper
|
||||
import io.github.muntashirakon.music.helper.PlayPauseButtonOnClickHandler
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
import io.github.muntashirakon.music.util.RetroUtil
|
||||
import io.github.muntashirakon.music.util.ViewUtil
|
||||
import com.bumptech.glide.Glide
|
||||
import kotlinx.android.synthetic.main.fragment_mini_player.*
|
||||
import kotlin.math.abs
|
||||
|
||||
open class MiniPlayerFragment : AbsMusicServiceFragment(), MusicProgressViewUpdateHelper.Callback,
|
||||
View.OnClickListener {
|
||||
|
||||
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_mini_player, container, false)
|
||||
}
|
||||
|
||||
override fun onClick(view: View) {
|
||||
when (view.id) {
|
||||
R.id.actionNext -> MusicPlayerRemote.playNextSong()
|
||||
R.id.actionPrevious -> MusicPlayerRemote.back()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
view.setOnTouchListener(FlingPlayBackController(requireContext()))
|
||||
setUpMiniPlayer()
|
||||
|
||||
if (RetroUtil.isTablet()) {
|
||||
actionNext.show()
|
||||
actionPrevious.show()
|
||||
actionNext?.show()
|
||||
actionPrevious?.show()
|
||||
|
||||
} else {
|
||||
actionNext.visibility =
|
||||
if (PreferenceUtil.isExtraControls) View.VISIBLE else View.GONE
|
||||
actionPrevious.visibility =
|
||||
if (PreferenceUtil.isExtraControls) View.VISIBLE else View.GONE
|
||||
}
|
||||
actionNext.setOnClickListener(this)
|
||||
actionPrevious.setOnClickListener(this)
|
||||
actionNext?.setOnClickListener(this)
|
||||
actionPrevious?.setOnClickListener(this)
|
||||
}
|
||||
|
||||
private fun setUpMiniPlayer() {
|
||||
setUpPlayPauseButton()
|
||||
ViewUtil.setProgressDrawable(progressBar, ThemeStore.accentColor(requireContext()))
|
||||
}
|
||||
|
||||
private fun setUpPlayPauseButton() {
|
||||
miniPlayerPlayPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
||||
}
|
||||
|
||||
private fun updateSongTitle() {
|
||||
val builder = SpannableStringBuilder()
|
||||
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
val title = SpannableString(song.title)
|
||||
title.setSpan(ForegroundColorSpan(textColorPrimary()), 0, title.length, 0)
|
||||
|
||||
val text = SpannableString(song.artistName)
|
||||
text.setSpan(ForegroundColorSpan(textColorSecondary()), 0, text.length, 0)
|
||||
|
||||
builder.append(title).append(" • ").append(text)
|
||||
|
||||
miniPlayerTitle.isSelected = true
|
||||
miniPlayerTitle.text = builder
|
||||
|
||||
if (RetroUtil.isTablet()) {
|
||||
image?.let {
|
||||
SongGlideRequest.Builder.from(
|
||||
Glide.with(requireContext()),
|
||||
MusicPlayerRemote.currentSong
|
||||
).checkIgnoreMediaStore(requireContext())
|
||||
.ignoreMediaStore(PreferenceUtil.isAllowedToDownloadMetadata())
|
||||
.asBitmap()
|
||||
.build()
|
||||
.into(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
updateSongTitle()
|
||||
updatePlayPauseDrawableState()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
updateSongTitle()
|
||||
}
|
||||
|
||||
override fun onPlayStateChanged() {
|
||||
updatePlayPauseDrawableState()
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
progressBar.max = total
|
||||
val animator = ObjectAnimator.ofInt(progressBar, "progress", progress)
|
||||
animator.duration = 1000
|
||||
animator.interpolator = DecelerateInterpolator()
|
||||
animator.start()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
progressViewUpdateHelper.start()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
progressViewUpdateHelper.stop()
|
||||
}
|
||||
|
||||
protected fun updatePlayPauseDrawableState() {
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
miniPlayerPlayPauseButton!!.setImageResource(R.drawable.ic_pause_white_24dp)
|
||||
} else {
|
||||
miniPlayerPlayPauseButton!!.setImageResource(R.drawable.ic_play_arrow_white_24dp)
|
||||
}
|
||||
}
|
||||
|
||||
class FlingPlayBackController(context: Context) : View.OnTouchListener {
|
||||
|
||||
private var flingPlayBackController: GestureDetector
|
||||
|
||||
init {
|
||||
flingPlayBackController = GestureDetector(context,
|
||||
object : GestureDetector.SimpleOnGestureListener() {
|
||||
override fun onFling(
|
||||
e1: MotionEvent, e2: MotionEvent, velocityX: Float,
|
||||
velocityY: Float
|
||||
): Boolean {
|
||||
if (abs(velocityX) > abs(velocityY)) {
|
||||
if (velocityX < 0) {
|
||||
MusicPlayerRemote.playNextSong()
|
||||
return true
|
||||
} else if (velocityX > 0) {
|
||||
MusicPlayerRemote.playPreviousSong()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onTouch(v: View, event: MotionEvent): Boolean {
|
||||
return flingPlayBackController.onTouchEvent(event)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package io.github.muntashirakon.music.fragments
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import io.github.muntashirakon.music.R
|
||||
|
||||
enum class NowPlayingScreen constructor(
|
||||
@param:StringRes @field:StringRes
|
||||
val titleRes: Int,
|
||||
@param:DrawableRes @field:DrawableRes val drawableResId: Int,
|
||||
val id: Int
|
||||
) {
|
||||
|
||||
Adaptive(R.string.adaptive, R.drawable.np_adaptive, 10),
|
||||
Blur(R.string.blur, R.drawable.np_blur, 4),
|
||||
BlurCard(R.string.blur_card, R.drawable.np_blur_card, 9),
|
||||
Card(R.string.card, R.drawable.np_card, 6),
|
||||
Circle(R.string.circle, R.drawable.np_minimalistic_circle, 15),
|
||||
Classic(R.string.classic, R.drawable.np_classic, 16),
|
||||
Color(R.string.color, R.drawable.np_color, 5),
|
||||
Fit(R.string.fit, R.drawable.np_fit, 12),
|
||||
Flat(R.string.flat, R.drawable.np_flat, 1),
|
||||
Full(R.string.full, R.drawable.np_full, 2),
|
||||
Gradient(R.string.gradient, R.drawable.np_gradient, 17),
|
||||
Material(R.string.material, R.drawable.np_material, 11),
|
||||
Normal(R.string.normal, R.drawable.np_normal, 0),
|
||||
Peak(R.string.peak, R.drawable.np_peak, 14),
|
||||
Plain(R.string.plain, R.drawable.np_plain, 3),
|
||||
Simple(R.string.simple, R.drawable.np_simple, 8),
|
||||
Tiny(R.string.tiny, R.drawable.np_tiny, 7),
|
||||
}
|
133
app/src/main/java/io/github/muntashirakon/music/fragments/VolumeFragment.kt
Executable file
133
app/src/main/java/io/github/muntashirakon/music/fragments/VolumeFragment.kt
Executable file
|
@ -0,0 +1,133 @@
|
|||
package io.github.muntashirakon.music.fragments
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.PorterDuff
|
||||
import android.media.AudioManager
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.SeekBar
|
||||
import androidx.fragment.app.Fragment
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.extensions.applyColor
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
import io.github.muntashirakon.music.volume.AudioVolumeObserver
|
||||
import io.github.muntashirakon.music.volume.OnAudioVolumeChangedListener
|
||||
import kotlinx.android.synthetic.main.fragment_volume.*
|
||||
|
||||
class VolumeFragment : Fragment(), SeekBar.OnSeekBarChangeListener, OnAudioVolumeChangedListener,
|
||||
View.OnClickListener {
|
||||
|
||||
private var audioVolumeObserver: AudioVolumeObserver? = null
|
||||
|
||||
private val audioManager: AudioManager?
|
||||
get() = requireContext().getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_volume, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setTintable(ThemeStore.accentColor(requireContext()))
|
||||
volumeDown.setOnClickListener(this)
|
||||
volumeUp.setOnClickListener(this)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (audioVolumeObserver == null) {
|
||||
audioVolumeObserver = AudioVolumeObserver(requireActivity())
|
||||
}
|
||||
audioVolumeObserver?.register(AudioManager.STREAM_MUSIC, this)
|
||||
|
||||
val audioManager = audioManager
|
||||
if (audioManager != null) {
|
||||
volumeSeekBar.max = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
|
||||
volumeSeekBar.progress = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
|
||||
}
|
||||
volumeSeekBar.setOnSeekBarChangeListener(this)
|
||||
}
|
||||
|
||||
override fun onAudioVolumeChanged(currentVolume: Int, maxVolume: Int) {
|
||||
if (volumeSeekBar == null) {
|
||||
return
|
||||
}
|
||||
|
||||
volumeSeekBar.max = maxVolume
|
||||
volumeSeekBar.progress = currentVolume
|
||||
volumeDown.setImageResource(if (currentVolume == 0) R.drawable.ic_volume_off_white_24dp else R.drawable.ic_volume_down_white_24dp)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
audioVolumeObserver?.unregister()
|
||||
}
|
||||
|
||||
override fun onProgressChanged(seekBar: SeekBar, i: Int, b: Boolean) {
|
||||
val audioManager = audioManager
|
||||
audioManager?.setStreamVolume(AudioManager.STREAM_MUSIC, i, 0)
|
||||
setPauseWhenZeroVolume(i < 1)
|
||||
volumeDown?.setImageResource(if (i == 0) R.drawable.ic_volume_off_white_24dp else R.drawable.ic_volume_down_white_24dp)
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) {
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {
|
||||
}
|
||||
|
||||
override fun onClick(view: View) {
|
||||
val audioManager = audioManager
|
||||
when (view.id) {
|
||||
R.id.volumeDown -> audioManager?.adjustStreamVolume(
|
||||
AudioManager.STREAM_MUSIC, AudioManager.ADJUST_LOWER, 0
|
||||
)
|
||||
R.id.volumeUp -> audioManager?.adjustStreamVolume(
|
||||
AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun tintWhiteColor() {
|
||||
val color = Color.WHITE
|
||||
volumeDown.setColorFilter(color, PorterDuff.Mode.SRC_IN)
|
||||
volumeUp.setColorFilter(color, PorterDuff.Mode.SRC_IN)
|
||||
volumeSeekBar.applyColor(color)
|
||||
}
|
||||
|
||||
fun setTintable(color: Int) {
|
||||
volumeSeekBar.applyColor(color)
|
||||
}
|
||||
|
||||
fun removeThumb() {
|
||||
volumeSeekBar.thumb = null
|
||||
}
|
||||
|
||||
private fun setPauseWhenZeroVolume(pauseWhenZeroVolume: Boolean) {
|
||||
if (PreferenceUtil.isPauseOnZeroVolume)
|
||||
if (MusicPlayerRemote.isPlaying && pauseWhenZeroVolume)
|
||||
MusicPlayerRemote.pauseSong()
|
||||
|
||||
}
|
||||
|
||||
fun setTintableColor(color: Int) {
|
||||
volumeDown.setColorFilter(color, PorterDuff.Mode.SRC_IN)
|
||||
volumeUp.setColorFilter(color, PorterDuff.Mode.SRC_IN)
|
||||
//TintHelper.setTint(volumeSeekBar, color, false)
|
||||
volumeSeekBar.applyColor(color)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun newInstance(): VolumeFragment {
|
||||
return VolumeFragment()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
package io.github.muntashirakon.music.fragments.albums
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.adapter.album.AlbumAdapter
|
||||
import io.github.muntashirakon.music.fragments.ReloadType
|
||||
import io.github.muntashirakon.music.fragments.base.AbsLibraryPagerRecyclerViewCustomGridSizeFragment
|
||||
import io.github.muntashirakon.music.interfaces.MainActivityFragmentCallbacks
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
|
||||
class AlbumsFragment :
|
||||
AbsLibraryPagerRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridLayoutManager>(),
|
||||
MainActivityFragmentCallbacks {
|
||||
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
mainActivity.libraryViewModel.allAlbums()
|
||||
.observe(viewLifecycleOwner, Observer { albums ->
|
||||
if (albums.isNotEmpty())
|
||||
adapter?.swapDataSet(albums)
|
||||
else
|
||||
adapter?.swapDataSet(listOf())
|
||||
})
|
||||
}
|
||||
|
||||
override val emptyMessage: Int
|
||||
get() = R.string.no_albums
|
||||
|
||||
override fun createLayoutManager(): GridLayoutManager {
|
||||
return GridLayoutManager(requireActivity(), getGridSize())
|
||||
}
|
||||
|
||||
override fun createAdapter(): AlbumAdapter {
|
||||
val dataSet = if (adapter == null) ArrayList() else adapter!!.dataSet
|
||||
return AlbumAdapter(
|
||||
mainActivity,
|
||||
dataSet,
|
||||
itemLayoutRes(),
|
||||
mainActivity
|
||||
)
|
||||
}
|
||||
|
||||
override fun setGridSize(gridSize: Int) {
|
||||
layoutManager?.spanCount = gridSize
|
||||
adapter?.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun loadSortOrder(): String {
|
||||
return PreferenceUtil.albumSortOrder
|
||||
}
|
||||
|
||||
override fun saveSortOrder(sortOrder: String) {
|
||||
PreferenceUtil.albumSortOrder = sortOrder
|
||||
}
|
||||
|
||||
override fun loadGridSize(): Int {
|
||||
return PreferenceUtil.albumGridSize
|
||||
}
|
||||
|
||||
override fun saveGridSize(gridColumns: Int) {
|
||||
PreferenceUtil.albumGridSize = gridColumns
|
||||
}
|
||||
|
||||
override fun loadGridSizeLand(): Int {
|
||||
return PreferenceUtil.albumGridSizeLand
|
||||
}
|
||||
|
||||
override fun saveGridSizeLand(gridColumns: Int) {
|
||||
PreferenceUtil.albumGridSizeLand = gridColumns
|
||||
}
|
||||
|
||||
override fun setSortOrder(sortOrder: String) {
|
||||
mainActivity.libraryViewModel.forceReload(ReloadType.Albums)
|
||||
}
|
||||
|
||||
override fun loadLayoutRes(): Int {
|
||||
return PreferenceUtil.albumGridStyle
|
||||
}
|
||||
|
||||
override fun saveLayoutRes(layoutRes: Int) {
|
||||
PreferenceUtil.albumGridStyle = layoutRes
|
||||
}
|
||||
|
||||
override fun handleBackPress(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
var TAG: String = AlbumsFragment::class.java.simpleName
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance(): AlbumsFragment {
|
||||
return AlbumsFragment()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
package io.github.muntashirakon.music.fragments.artists
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.adapter.artist.ArtistAdapter
|
||||
import io.github.muntashirakon.music.fragments.ReloadType
|
||||
import io.github.muntashirakon.music.fragments.base.AbsLibraryPagerRecyclerViewCustomGridSizeFragment
|
||||
import io.github.muntashirakon.music.interfaces.MainActivityFragmentCallbacks
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
|
||||
class ArtistsFragment :
|
||||
AbsLibraryPagerRecyclerViewCustomGridSizeFragment<ArtistAdapter, GridLayoutManager>(),
|
||||
MainActivityFragmentCallbacks {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
mainActivity.libraryViewModel.allArtists().observe(
|
||||
viewLifecycleOwner, Observer { artists ->
|
||||
if (artists.isNotEmpty()) {
|
||||
adapter?.swapDataSet(artists)
|
||||
} else {
|
||||
adapter?.swapDataSet(listOf())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun handleBackPress(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override val emptyMessage: Int
|
||||
get() = R.string.no_artists
|
||||
|
||||
override fun setSortOrder(sortOrder: String) {
|
||||
mainActivity.libraryViewModel.forceReload(ReloadType.Artists)
|
||||
}
|
||||
|
||||
override fun createLayoutManager(): GridLayoutManager {
|
||||
return GridLayoutManager(requireActivity(), getGridSize())
|
||||
}
|
||||
|
||||
override fun createAdapter(): ArtistAdapter {
|
||||
val dataSet = if (adapter == null) ArrayList() else adapter!!.dataSet
|
||||
return ArtistAdapter(
|
||||
mainActivity,
|
||||
dataSet,
|
||||
itemLayoutRes(),
|
||||
mainActivity
|
||||
)
|
||||
}
|
||||
|
||||
override fun loadGridSize(): Int {
|
||||
return PreferenceUtil.artistGridSize
|
||||
}
|
||||
|
||||
override fun saveGridSize(gridColumns: Int) {
|
||||
PreferenceUtil.artistGridSize = gridColumns
|
||||
}
|
||||
|
||||
override fun loadGridSizeLand(): Int {
|
||||
return PreferenceUtil.artistGridSizeLand
|
||||
}
|
||||
|
||||
override fun saveGridSizeLand(gridColumns: Int) {
|
||||
PreferenceUtil.artistGridSizeLand = gridColumns
|
||||
}
|
||||
|
||||
override fun setGridSize(gridSize: Int) {
|
||||
layoutManager?.spanCount = gridSize
|
||||
adapter?.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun loadSortOrder(): String {
|
||||
return PreferenceUtil.artistSortOrder
|
||||
}
|
||||
|
||||
override fun saveSortOrder(sortOrder: String) {
|
||||
PreferenceUtil.artistSortOrder = sortOrder
|
||||
}
|
||||
|
||||
override fun loadLayoutRes(): Int {
|
||||
return PreferenceUtil.artistGridStyle
|
||||
}
|
||||
|
||||
override fun saveLayoutRes(layoutRes: Int) {
|
||||
PreferenceUtil.artistGridStyle = layoutRes
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val TAG: String = ArtistsFragment::class.java.simpleName
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance(): ArtistsFragment {
|
||||
return ArtistsFragment()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package io.github.muntashirakon.music.fragments.base
|
||||
|
||||
import android.os.Bundle
|
||||
import io.github.muntashirakon.music.activities.MainActivity
|
||||
|
||||
open class AbsLibraryPagerFragment : AbsMusicServiceFragment() {
|
||||
|
||||
val mainActivity: MainActivity
|
||||
get() = requireActivity() as MainActivity
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
package io.github.muntashirakon.music.fragments.base
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.util.RetroUtil
|
||||
|
||||
abstract class AbsLibraryPagerRecyclerViewCustomGridSizeFragment<A : RecyclerView.Adapter<*>, LM : RecyclerView.LayoutManager> :
|
||||
AbsLibraryPagerRecyclerViewFragment<A, LM>() {
|
||||
|
||||
private var gridSize: Int = 0
|
||||
private var sortOrder: String? = null
|
||||
private var currentLayoutRes: Int = 0
|
||||
private val isLandscape: Boolean
|
||||
get() = RetroUtil.isLandscape()
|
||||
|
||||
val maxGridSize: Int
|
||||
get() = if (isLandscape) {
|
||||
resources.getInteger(R.integer.max_columns_land)
|
||||
} else {
|
||||
resources.getInteger(R.integer.max_columns)
|
||||
}
|
||||
|
||||
fun itemLayoutRes(): Int {
|
||||
return if (getGridSize() > maxGridSizeForList) {
|
||||
loadLayoutRes()
|
||||
} else R.layout.item_list
|
||||
}
|
||||
|
||||
|
||||
fun setAndSaveLayoutRes(layoutRes: Int) {
|
||||
saveLayoutRes(layoutRes)
|
||||
invalidateAdapter()
|
||||
}
|
||||
|
||||
private val maxGridSizeForList: Int
|
||||
get() = if (isLandscape) {
|
||||
resources.getInteger(R.integer.default_list_columns_land)
|
||||
} else resources.getInteger(R.integer.default_list_columns)
|
||||
|
||||
fun getGridSize(): Int {
|
||||
if (gridSize == 0) {
|
||||
gridSize = if (isLandscape) {
|
||||
loadGridSizeLand()
|
||||
} else {
|
||||
loadGridSize()
|
||||
}
|
||||
}
|
||||
return gridSize
|
||||
}
|
||||
|
||||
|
||||
fun getSortOrder(): String? {
|
||||
if (sortOrder == null) {
|
||||
sortOrder = loadSortOrder()
|
||||
}
|
||||
return sortOrder
|
||||
}
|
||||
|
||||
|
||||
fun setAndSaveSortOrder(sortOrder: String) {
|
||||
this.sortOrder = sortOrder
|
||||
saveSortOrder(sortOrder)
|
||||
setSortOrder(sortOrder)
|
||||
}
|
||||
|
||||
fun setAndSaveGridSize(gridSize: Int) {
|
||||
val oldLayoutRes = itemLayoutRes()
|
||||
this.gridSize = gridSize
|
||||
if (isLandscape) {
|
||||
saveGridSizeLand(gridSize)
|
||||
} else {
|
||||
saveGridSize(gridSize)
|
||||
}
|
||||
invalidateLayoutManager()
|
||||
// only recreate the adapter and layout manager if the layout currentLayoutRes has changed
|
||||
if (oldLayoutRes != itemLayoutRes()) {
|
||||
invalidateAdapter()
|
||||
} else {
|
||||
setGridSize(gridSize)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun notifyLayoutResChanged(@LayoutRes res: Int) {
|
||||
this.currentLayoutRes = res
|
||||
val recyclerView = recyclerView()
|
||||
applyRecyclerViewPaddingForLayoutRes(recyclerView, currentLayoutRes)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
applyRecyclerViewPaddingForLayoutRes(recyclerView(), currentLayoutRes)
|
||||
}
|
||||
|
||||
private fun applyRecyclerViewPaddingForLayoutRes(recyclerView: RecyclerView, res: Int) {
|
||||
val padding: Int = if (res == R.layout.item_grid) {
|
||||
(resources.displayMetrics.density * 2).toInt()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
recyclerView.setPadding(padding, padding, padding, padding)
|
||||
}
|
||||
|
||||
protected abstract fun setGridSize(gridSize: Int)
|
||||
|
||||
protected abstract fun setSortOrder(sortOrder: String)
|
||||
|
||||
protected abstract fun loadSortOrder(): String
|
||||
|
||||
protected abstract fun saveSortOrder(sortOrder: String)
|
||||
|
||||
protected abstract fun loadGridSize(): Int
|
||||
|
||||
protected abstract fun saveGridSize(gridColumns: Int)
|
||||
|
||||
protected abstract fun loadGridSizeLand(): Int
|
||||
|
||||
protected abstract fun saveGridSizeLand(gridColumns: Int)
|
||||
|
||||
protected abstract fun loadLayoutRes(): Int
|
||||
|
||||
protected abstract fun saveLayoutRes(layoutRes: Int)
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
package io.github.muntashirakon.music.fragments.base
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.NonNull
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.util.DensityUtil
|
||||
import io.github.muntashirakon.music.util.ThemedFastScroller.create
|
||||
import io.github.muntashirakon.music.views.ScrollingViewOnApplyWindowInsetsListener
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import kotlinx.android.synthetic.main.fragment_main_activity_recycler_view.*
|
||||
import me.zhanghai.android.fastscroll.FastScroller
|
||||
import me.zhanghai.android.fastscroll.FastScrollerBuilder
|
||||
|
||||
abstract class AbsLibraryPagerRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : RecyclerView.LayoutManager> :
|
||||
AbsLibraryPagerFragment(), AppBarLayout.OnOffsetChangedListener {
|
||||
|
||||
|
||||
protected var adapter: A? = null
|
||||
protected var layoutManager: LM? = null
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_main_activity_recycler_view, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
mainActivity.addOnAppBarOffsetChangedListener(this)
|
||||
initLayoutManager()
|
||||
initAdapter()
|
||||
setUpRecyclerView()
|
||||
}
|
||||
|
||||
private fun setUpRecyclerView() {
|
||||
recyclerView.layoutManager = layoutManager
|
||||
recyclerView.adapter = adapter
|
||||
val fastScroller = create(recyclerView)
|
||||
recyclerView.setOnApplyWindowInsetsListener(
|
||||
ScrollingViewOnApplyWindowInsetsListener(
|
||||
recyclerView,
|
||||
fastScroller
|
||||
)
|
||||
)
|
||||
checkForPadding()
|
||||
}
|
||||
|
||||
protected open fun createFastScroller(recyclerView: RecyclerView): FastScroller {
|
||||
return FastScrollerBuilder(recyclerView).useMd2Style().build()
|
||||
}
|
||||
|
||||
private fun initAdapter() {
|
||||
adapter = createAdapter()
|
||||
adapter?.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onChanged() {
|
||||
super.onChanged()
|
||||
checkIsEmpty()
|
||||
checkForPadding()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
protected open val emptyMessage: Int
|
||||
@StringRes get() = R.string.empty
|
||||
|
||||
private fun getEmojiByUnicode(unicode: Int): String {
|
||||
return String(Character.toChars(unicode))
|
||||
}
|
||||
|
||||
private fun checkIsEmpty() {
|
||||
emptyEmoji.text = getEmojiByUnicode(0x1F631)
|
||||
emptyText.setText(emptyMessage)
|
||||
empty.visibility = if (adapter!!.itemCount == 0) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
private fun checkForPadding() {
|
||||
val itemCount: Int = adapter?.itemCount ?: 0
|
||||
val params = container.layoutParams as ViewGroup.MarginLayoutParams
|
||||
if (itemCount > 0 && MusicPlayerRemote.playingQueue.isNotEmpty()) {
|
||||
val height = DensityUtil.dip2px(requireContext(), 104f)
|
||||
params.bottomMargin = height
|
||||
} else {
|
||||
val height = DensityUtil.dip2px(requireContext(), 52f)
|
||||
params.bottomMargin = height
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private fun initLayoutManager() {
|
||||
layoutManager = createLayoutManager()
|
||||
}
|
||||
|
||||
protected abstract fun createLayoutManager(): LM
|
||||
|
||||
@NonNull
|
||||
protected abstract fun createAdapter(): A
|
||||
|
||||
override fun onOffsetChanged(p0: AppBarLayout?, i: Int) {
|
||||
container.setPadding(
|
||||
container.paddingLeft,
|
||||
container.paddingTop,
|
||||
container.paddingRight,
|
||||
mainActivity.getTotalAppBarScrollingRange() + i
|
||||
)
|
||||
}
|
||||
|
||||
override fun onQueueChanged() {
|
||||
super.onQueueChanged()
|
||||
checkForPadding()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
checkForPadding()
|
||||
}
|
||||
|
||||
protected fun invalidateLayoutManager() {
|
||||
initLayoutManager()
|
||||
recyclerView.layoutManager = layoutManager
|
||||
}
|
||||
|
||||
protected fun invalidateAdapter() {
|
||||
initAdapter()
|
||||
checkIsEmpty()
|
||||
recyclerView.adapter = adapter
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
mainActivity.removeOnAppBarOffsetChangedListener(this)
|
||||
}
|
||||
|
||||
fun recyclerView(): RecyclerView {
|
||||
return recyclerView
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package io.github.muntashirakon.music.fragments.base
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.activities.MainActivity
|
||||
|
||||
abstract class AbsMainActivityFragment : AbsMusicServiceFragment() {
|
||||
|
||||
val mainActivity: MainActivity
|
||||
get() = activity as MainActivity
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
mainActivity.setNavigationbarColorAuto()
|
||||
mainActivity.setLightNavigationBar(true)
|
||||
mainActivity.setTaskDescriptionColorAuto()
|
||||
mainActivity.hideStatusBar()
|
||||
}
|
||||
|
||||
private fun setStatusBarColor(view: View, color: Int) {
|
||||
val statusBar = view.findViewById<View>(R.id.status_bar)
|
||||
if (statusBar != null) {
|
||||
if (VersionUtils.hasMarshmallow()) {
|
||||
statusBar.setBackgroundColor(color)
|
||||
mainActivity.setLightStatusbarAuto(color)
|
||||
} else {
|
||||
statusBar.setBackgroundColor(color)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setStatusBarColorAuto(view: View) {
|
||||
val colorPrimary = ATHUtil.resolveColor(requireContext(), R.attr.colorSurface)
|
||||
// we don't want to use statusbar color because we are doing the color darkening on our own to support KitKat
|
||||
if (VersionUtils.hasMarshmallow()) {
|
||||
setStatusBarColor(view, colorPrimary)
|
||||
} else {
|
||||
setStatusBarColor(view, ColorUtil.darkenColor(colorPrimary))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
package io.github.muntashirakon.music.fragments.base
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.webkit.MimeTypeMap
|
||||
import androidx.fragment.app.Fragment
|
||||
import io.github.muntashirakon.music.activities.base.AbsMusicServiceActivity
|
||||
import io.github.muntashirakon.music.interfaces.MusicServiceEventListener
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
import io.github.muntashirakon.music.util.RetroUtil
|
||||
import org.jaudiotagger.audio.AudioFileIO
|
||||
import java.io.File
|
||||
import java.net.URLEncoder
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by hemanths on 18/08/17.
|
||||
*/
|
||||
|
||||
open class AbsMusicServiceFragment : Fragment(), MusicServiceEventListener {
|
||||
|
||||
var playerActivity: AbsMusicServiceActivity? = null
|
||||
private set
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
try {
|
||||
playerActivity = context as AbsMusicServiceActivity?
|
||||
} catch (e: ClassCastException) {
|
||||
throw RuntimeException(context.javaClass.simpleName + " must be an instance of " + AbsMusicServiceActivity::class.java.simpleName)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetach() {
|
||||
super.onDetach()
|
||||
playerActivity = null
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
playerActivity?.addMusicServiceEventListener(this)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
playerActivity?.removeMusicServiceEventListener(this)
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected() {
|
||||
}
|
||||
|
||||
override fun onQueueChanged() {
|
||||
}
|
||||
|
||||
override fun onPlayStateChanged() {
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged() {
|
||||
}
|
||||
|
||||
override fun onShuffleModeChanged() {
|
||||
}
|
||||
|
||||
override fun onMediaStoreChanged() {
|
||||
}
|
||||
|
||||
fun getSongInfo(song: Song): String {
|
||||
val file = File(song.data)
|
||||
if (file.exists()) {
|
||||
return try {
|
||||
val audioHeader = AudioFileIO.read(File(song.data)).audioHeader
|
||||
val string: StringBuilder = StringBuilder()
|
||||
val uriFile = Uri.fromFile(file)
|
||||
string.append(getMimeType(uriFile.toString())).append(" • ")
|
||||
string.append(audioHeader.bitRate).append(" kb/s").append(" • ")
|
||||
string.append(RetroUtil.frequencyCount(audioHeader.sampleRate.toInt()))
|
||||
.append(" kHz")
|
||||
string.toString()
|
||||
} catch (er: Exception) {
|
||||
" - "
|
||||
}
|
||||
}
|
||||
return "-"
|
||||
}
|
||||
|
||||
private fun getMimeType(url: String): String? {
|
||||
var type: String? = MimeTypeMap.getFileExtensionFromUrl(
|
||||
URLEncoder.encode(url, "utf-8")
|
||||
).toUpperCase(Locale.getDefault())
|
||||
if (type == null) {
|
||||
type = url.substring(url.lastIndexOf(".") + 1)
|
||||
}
|
||||
return type
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package io.github.muntashirakon.music.fragments.base
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.animation.AccelerateInterpolator
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.fragments.VolumeFragment
|
||||
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper
|
||||
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
|
||||
/**
|
||||
* Created by hemanths on 24/09/17.
|
||||
*/
|
||||
|
||||
abstract class AbsPlayerControlsFragment : AbsMusicServiceFragment(),
|
||||
MusicProgressViewUpdateHelper.Callback {
|
||||
|
||||
protected abstract fun show()
|
||||
|
||||
protected abstract fun hide()
|
||||
|
||||
protected abstract fun updateShuffleState()
|
||||
|
||||
protected abstract fun updateRepeatState()
|
||||
|
||||
protected abstract fun setUpProgressSlider()
|
||||
|
||||
abstract fun setColor(color: MediaNotificationProcessor)
|
||||
|
||||
fun showBonceAnimation(view: View) {
|
||||
view.apply {
|
||||
clearAnimation()
|
||||
scaleX = 0.9f
|
||||
scaleY = 0.9f
|
||||
visibility = View.VISIBLE
|
||||
pivotX = (view.width / 2).toFloat()
|
||||
pivotY = (view.height / 2).toFloat()
|
||||
|
||||
animate().setDuration(200)
|
||||
.setInterpolator(DecelerateInterpolator())
|
||||
.scaleX(1.1f)
|
||||
.scaleY(1.1f)
|
||||
.withEndAction {
|
||||
animate().setDuration(200)
|
||||
.setInterpolator(AccelerateInterpolator())
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.alpha(1f)
|
||||
.start()
|
||||
}
|
||||
.start()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
hideVolumeIfAvailable()
|
||||
}
|
||||
|
||||
protected var volumeFragment: VolumeFragment? = null
|
||||
|
||||
private fun hideVolumeIfAvailable() {
|
||||
if (PreferenceUtil.isVolumeVisibilityMode) {
|
||||
childFragmentManager.beginTransaction()
|
||||
.replace(R.id.volumeFragmentContainer, VolumeFragment()).commit()
|
||||
childFragmentManager.executePendingTransactions()
|
||||
volumeFragment =
|
||||
childFragmentManager.findFragmentById(R.id.volumeFragmentContainer) as VolumeFragment?
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val SLIDER_ANIMATION_TIME: Long = 400
|
||||
}
|
||||
}
|
|
@ -0,0 +1,289 @@
|
|||
package io.github.muntashirakon.music.fragments.base
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ContentUris
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.media.MediaMetadataRetriever
|
||||
import android.os.AsyncTask
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import android.text.TextUtils
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.activities.tageditor.AbsTagEditorActivity
|
||||
import io.github.muntashirakon.music.activities.tageditor.SongTagEditorActivity
|
||||
import io.github.muntashirakon.music.dialogs.*
|
||||
import io.github.muntashirakon.music.extensions.hide
|
||||
import io.github.muntashirakon.music.fragments.player.PlayerAlbumCoverFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.interfaces.PaletteColorHolder
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
import io.github.muntashirakon.music.model.lyrics.Lyrics
|
||||
import io.github.muntashirakon.music.util.*
|
||||
import kotlinx.android.synthetic.main.shadow_statusbar_toolbar.*
|
||||
import java.io.FileNotFoundException
|
||||
|
||||
abstract class AbsPlayerFragment : AbsMusicServiceFragment(),
|
||||
Toolbar.OnMenuItemClickListener,
|
||||
PaletteColorHolder,
|
||||
PlayerAlbumCoverFragment.Callbacks {
|
||||
|
||||
var callbacks: Callbacks? = null
|
||||
private set
|
||||
private var updateIsFavoriteTask: AsyncTask<*, *, *>? = null
|
||||
private var updateLyricsAsyncTask: AsyncTask<*, *, *>? = null
|
||||
private var playerAlbumCoverFragment: PlayerAlbumCoverFragment? = null
|
||||
|
||||
override fun onAttach(
|
||||
context: Context
|
||||
) {
|
||||
super.onAttach(context)
|
||||
try {
|
||||
callbacks = context as Callbacks?
|
||||
} catch (e: ClassCastException) {
|
||||
throw RuntimeException(context.javaClass.simpleName + " must implement " + Callbacks::class.java.simpleName)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetach() {
|
||||
super.onDetach()
|
||||
callbacks = null
|
||||
}
|
||||
|
||||
override fun onMenuItemClick(
|
||||
item: MenuItem
|
||||
): Boolean {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
when (item.itemId) {
|
||||
R.id.action_toggle_favorite -> {
|
||||
toggleFavorite(song)
|
||||
return true
|
||||
}
|
||||
R.id.action_share -> {
|
||||
SongShareDialog.create(song).show(childFragmentManager, "SHARE_SONG")
|
||||
return true
|
||||
}
|
||||
R.id.action_go_to_drive_mode -> {
|
||||
NavigationUtil.gotoDriveMode(requireActivity())
|
||||
return true
|
||||
}
|
||||
R.id.action_delete_from_device -> {
|
||||
DeleteSongsDialog.create(song).show(childFragmentManager, "DELETE_SONGS")
|
||||
return true
|
||||
}
|
||||
R.id.action_add_to_playlist -> {
|
||||
AddToPlaylistDialog.create(song).show(childFragmentManager, "ADD_PLAYLIST")
|
||||
return true
|
||||
}
|
||||
R.id.action_clear_playing_queue -> {
|
||||
MusicPlayerRemote.clearQueue()
|
||||
return true
|
||||
}
|
||||
R.id.action_save_playing_queue -> {
|
||||
CreatePlaylistDialog.create(ArrayList(MusicPlayerRemote.playingQueue))
|
||||
.show(childFragmentManager, "ADD_TO_PLAYLIST")
|
||||
return true
|
||||
}
|
||||
R.id.action_tag_editor -> {
|
||||
val intent = Intent(activity, SongTagEditorActivity::class.java)
|
||||
intent.putExtra(AbsTagEditorActivity.EXTRA_ID, song.id)
|
||||
startActivity(intent)
|
||||
return true
|
||||
}
|
||||
R.id.action_details -> {
|
||||
SongDetailDialog.create(song).show(childFragmentManager, "SONG_DETAIL")
|
||||
return true
|
||||
}
|
||||
R.id.action_go_to_album -> {
|
||||
NavigationUtil.goToAlbum(requireActivity(), song.albumId)
|
||||
return true
|
||||
}
|
||||
R.id.action_go_to_artist -> {
|
||||
NavigationUtil.goToArtist(requireActivity(), song.artistId)
|
||||
return true
|
||||
}
|
||||
R.id.now_playing -> {
|
||||
NavigationUtil.goToPlayingQueue(requireActivity())
|
||||
return true
|
||||
}
|
||||
R.id.action_show_lyrics -> {
|
||||
NavigationUtil.goToLyrics(requireActivity())
|
||||
return true
|
||||
}
|
||||
R.id.action_equalizer -> {
|
||||
NavigationUtil.openEqualizer(requireActivity())
|
||||
return true
|
||||
}
|
||||
R.id.action_sleep_timer -> {
|
||||
SleepTimerDialog().show(parentFragmentManager, TAG)
|
||||
return true
|
||||
}
|
||||
R.id.action_set_as_ringtone -> {
|
||||
if (RingtoneManager.requiresDialog(requireActivity())) {
|
||||
RingtoneManager.getDialog(requireActivity())
|
||||
}
|
||||
val ringtoneManager = RingtoneManager(requireActivity())
|
||||
ringtoneManager.setRingtone(song)
|
||||
return true
|
||||
}
|
||||
R.id.action_go_to_genre -> {
|
||||
val retriever = MediaMetadataRetriever()
|
||||
val trackUri =
|
||||
ContentUris.withAppendedId(
|
||||
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
|
||||
song.id.toLong()
|
||||
)
|
||||
retriever.setDataSource(activity, trackUri)
|
||||
var genre: String? =
|
||||
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_GENRE)
|
||||
if (genre == null) {
|
||||
genre = "Not Specified"
|
||||
}
|
||||
Toast.makeText(context, genre, Toast.LENGTH_SHORT).show()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
protected open fun toggleFavorite(song: Song) {
|
||||
MusicUtil.toggleFavorite(requireActivity(), song)
|
||||
}
|
||||
|
||||
abstract fun playerToolbar(): Toolbar?
|
||||
|
||||
abstract fun onShow()
|
||||
|
||||
abstract fun onHide()
|
||||
|
||||
abstract fun onBackPressed(): Boolean
|
||||
|
||||
abstract fun toolbarIconColor(): Int
|
||||
|
||||
override fun onServiceConnected() {
|
||||
updateIsFavorite()
|
||||
updateLyrics()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
updateIsFavorite()
|
||||
updateLyrics()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
if (updateIsFavoriteTask != null && !updateIsFavoriteTask!!.isCancelled) {
|
||||
updateIsFavoriteTask!!.cancel(true)
|
||||
}
|
||||
if (updateLyricsAsyncTask != null && !updateLyricsAsyncTask!!.isCancelled) {
|
||||
updateLyricsAsyncTask!!.cancel(true)
|
||||
}
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
fun updateIsFavorite() {
|
||||
if (updateIsFavoriteTask != null) {
|
||||
updateIsFavoriteTask!!.cancel(false)
|
||||
}
|
||||
updateIsFavoriteTask = object : AsyncTask<Song, Void, Boolean>() {
|
||||
override fun doInBackground(vararg params: Song): Boolean {
|
||||
return MusicUtil.isFavorite(requireActivity(), params[0])
|
||||
}
|
||||
|
||||
override fun onPostExecute(isFavorite: Boolean) {
|
||||
val res = if (isFavorite)
|
||||
R.drawable.ic_favorite_white_24dp
|
||||
else
|
||||
R.drawable.ic_favorite_border_white_24dp
|
||||
|
||||
val drawable =
|
||||
RetroUtil.getTintedVectorDrawable(requireContext(), res, toolbarIconColor())
|
||||
if (playerToolbar() != null && playerToolbar()!!.menu.findItem(R.id.action_toggle_favorite) != null)
|
||||
playerToolbar()!!.menu.findItem(R.id.action_toggle_favorite).setIcon(drawable)
|
||||
.title =
|
||||
if (isFavorite) getString(R.string.action_remove_from_favorites) else getString(
|
||||
R.string.action_add_to_favorites
|
||||
)
|
||||
}
|
||||
}.execute(MusicPlayerRemote.currentSong)
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private fun updateLyrics() {
|
||||
if (updateLyricsAsyncTask != null) updateLyricsAsyncTask!!.cancel(false)
|
||||
|
||||
updateLyricsAsyncTask = object : AsyncTask<Song, Void, Lyrics>() {
|
||||
override fun onPreExecute() {
|
||||
super.onPreExecute()
|
||||
setLyrics(null)
|
||||
}
|
||||
|
||||
override fun doInBackground(vararg params: Song): Lyrics? {
|
||||
try {
|
||||
var data: String? =
|
||||
LyricUtil.getStringFromFile(params[0].title, params[0].artistName)
|
||||
return if (TextUtils.isEmpty(data)) {
|
||||
data = MusicUtil.getLyrics(params[0])
|
||||
return if (TextUtils.isEmpty(data)) {
|
||||
null
|
||||
} else {
|
||||
Lyrics.parse(params[0], data)
|
||||
}
|
||||
} else Lyrics.parse(params[0], data!!)
|
||||
} catch (err: FileNotFoundException) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPostExecute(l: Lyrics?) {
|
||||
setLyrics(l)
|
||||
}
|
||||
|
||||
override fun onCancelled(s: Lyrics?) {
|
||||
onPostExecute(null)
|
||||
}
|
||||
}.execute(MusicPlayerRemote.currentSong)
|
||||
}
|
||||
|
||||
open fun setLyrics(l: Lyrics?) {
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
if (PreferenceUtil.isFullScreenMode &&
|
||||
view.findViewById<View>(R.id.status_bar) != null
|
||||
) {
|
||||
view.findViewById<View>(R.id.status_bar).visibility = View.GONE
|
||||
}
|
||||
playerAlbumCoverFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment?
|
||||
playerAlbumCoverFragment?.setCallbacks(this)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
||||
statusBarShadow?.hide()
|
||||
}
|
||||
|
||||
interface Callbacks {
|
||||
|
||||
fun onPaletteColorChanged()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG: String = AbsPlayerFragment::class.java.simpleName
|
||||
const val VISIBILITY_ANIM_DURATION: Long = 300
|
||||
}
|
||||
|
||||
protected fun getUpNextAndQueueTime(): String {
|
||||
val duration = MusicPlayerRemote.getQueueDurationMillis(MusicPlayerRemote.position)
|
||||
|
||||
return MusicUtil.buildInfoString(
|
||||
resources.getString(R.string.up_next),
|
||||
MusicUtil.getReadableDurationString(duration)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package io.github.muntashirakon.music.fragments.genres
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import io.github.muntashirakon.music.App
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.adapter.GenreAdapter
|
||||
import io.github.muntashirakon.music.fragments.base.AbsLibraryPagerRecyclerViewFragment
|
||||
import io.github.muntashirakon.music.interfaces.MainActivityFragmentCallbacks
|
||||
|
||||
class GenresFragment : AbsLibraryPagerRecyclerViewFragment<GenreAdapter, LinearLayoutManager>(),
|
||||
MainActivityFragmentCallbacks {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
App.musicComponent.inject(this)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
mainActivity.libraryViewModel.allGenres().observe(
|
||||
viewLifecycleOwner, Observer { genres ->
|
||||
if (genres.isNotEmpty()) {
|
||||
adapter?.swapDataSet(genres)
|
||||
} else {
|
||||
adapter?.swapDataSet(listOf())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun handleBackPress(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun createLayoutManager(): LinearLayoutManager {
|
||||
return LinearLayoutManager(activity)
|
||||
}
|
||||
|
||||
override fun createAdapter(): GenreAdapter {
|
||||
val dataSet = if (adapter == null) ArrayList() else adapter!!.dataSet
|
||||
return GenreAdapter(mainActivity, dataSet, R.layout.item_list_no_image)
|
||||
}
|
||||
|
||||
override val emptyMessage: Int
|
||||
get() = R.string.no_genres
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val TAG: String = GenresFragment::class.java.simpleName
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance(): GenresFragment {
|
||||
return GenresFragment()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package io.github.muntashirakon.music.fragments.home
|
||||
|
||||
import android.app.ActivityOptions
|
||||
import android.os.Bundle
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.adapter.HomeAdapter
|
||||
import io.github.muntashirakon.music.fragments.base.AbsMainActivityFragment
|
||||
import io.github.muntashirakon.music.glide.ProfileBannerGlideRequest
|
||||
import io.github.muntashirakon.music.glide.UserProfileGlideRequest
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.interfaces.MainActivityFragmentCallbacks
|
||||
import io.github.muntashirakon.music.loaders.SongLoader
|
||||
import io.github.muntashirakon.music.model.smartplaylist.HistoryPlaylist
|
||||
import io.github.muntashirakon.music.model.smartplaylist.LastAddedPlaylist
|
||||
import io.github.muntashirakon.music.model.smartplaylist.MyTopTracksPlaylist
|
||||
import io.github.muntashirakon.music.util.NavigationUtil
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
import com.bumptech.glide.Glide
|
||||
import kotlinx.android.synthetic.main.abs_playlists.*
|
||||
import kotlinx.android.synthetic.main.fragment_banner_home.*
|
||||
import kotlinx.android.synthetic.main.home_content.*
|
||||
|
||||
class BannerHomeFragment : AbsMainActivityFragment(), MainActivityFragmentCallbacks {
|
||||
private lateinit var homeAdapter: HomeAdapter
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
viewGroup: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(
|
||||
if (PreferenceUtil.isHomeBanner) R.layout.fragment_banner_home else R.layout.fragment_home,
|
||||
viewGroup,
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
private fun loadImageFromStorage() {
|
||||
UserProfileGlideRequest.Builder.from(
|
||||
Glide.with(requireActivity()),
|
||||
UserProfileGlideRequest.getUserModel()
|
||||
).build().into(userImage)
|
||||
}
|
||||
|
||||
private val displayMetrics: DisplayMetrics
|
||||
get() {
|
||||
val display = mainActivity.windowManager.defaultDisplay
|
||||
val metrics = DisplayMetrics()
|
||||
display.getMetrics(metrics)
|
||||
return metrics
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
setStatusBarColorAuto(view)
|
||||
|
||||
bannerImage?.setOnClickListener {
|
||||
val options = ActivityOptions.makeSceneTransitionAnimation(
|
||||
mainActivity,
|
||||
userImage,
|
||||
getString(R.string.transition_user_image)
|
||||
)
|
||||
NavigationUtil.goToUserInfo(requireActivity(), options)
|
||||
}
|
||||
|
||||
lastAdded.setOnClickListener {
|
||||
NavigationUtil.goToPlaylistNew(requireActivity(), LastAddedPlaylist(requireActivity()))
|
||||
}
|
||||
|
||||
topPlayed.setOnClickListener {
|
||||
NavigationUtil.goToPlaylistNew(
|
||||
requireActivity(),
|
||||
MyTopTracksPlaylist(requireActivity())
|
||||
)
|
||||
}
|
||||
|
||||
actionShuffle.setOnClickListener {
|
||||
MusicPlayerRemote.openAndShuffleQueue(SongLoader.getAllSongs(requireActivity()), true)
|
||||
}
|
||||
|
||||
history.setOnClickListener {
|
||||
NavigationUtil.goToPlaylistNew(requireActivity(), HistoryPlaylist(requireActivity()))
|
||||
}
|
||||
|
||||
userImage.setOnClickListener {
|
||||
val options = ActivityOptions.makeSceneTransitionAnimation(
|
||||
mainActivity,
|
||||
userImage,
|
||||
getString(R.string.transition_user_image)
|
||||
)
|
||||
NavigationUtil.goToUserInfo(requireActivity(), options)
|
||||
}
|
||||
titleWelcome?.text =
|
||||
String.format("%s", PreferenceUtil.userName)
|
||||
|
||||
homeAdapter = HomeAdapter(mainActivity, displayMetrics)
|
||||
recyclerView.apply {
|
||||
layoutManager = LinearLayoutManager(mainActivity)
|
||||
adapter = homeAdapter
|
||||
}
|
||||
|
||||
mainActivity.libraryViewModel.homeSections()
|
||||
.observe(viewLifecycleOwner, Observer { sections ->
|
||||
homeAdapter.swapData(sections)
|
||||
})
|
||||
|
||||
loadProfile()
|
||||
}
|
||||
|
||||
override fun handleBackPress(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
private fun loadProfile() {
|
||||
bannerImage?.let {
|
||||
ProfileBannerGlideRequest.Builder.from(
|
||||
Glide.with(requireContext()),
|
||||
ProfileBannerGlideRequest.getBannerModel()
|
||||
).build().into(it)
|
||||
}
|
||||
UserProfileGlideRequest.Builder.from(
|
||||
Glide.with(requireActivity()),
|
||||
UserProfileGlideRequest.getUserModel()
|
||||
).build().into(userImage)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val TAG: String = "BannerHomeFragment"
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance(): BannerHomeFragment {
|
||||
return BannerHomeFragment()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,737 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package io.github.muntashirakon.music.fragments.mainactivity;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.media.MediaScannerConnection;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.text.Html;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewGroup.MarginLayoutParams;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.loader.content.Loader;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.afollestad.materialcab.MaterialCab;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import code.name.monkey.appthemehelper.ThemeStore;
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil;
|
||||
import io.github.muntashirakon.music.R;
|
||||
import io.github.muntashirakon.music.adapter.SongFileAdapter;
|
||||
import io.github.muntashirakon.music.fragments.base.AbsMainActivityFragment;
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote;
|
||||
import io.github.muntashirakon.music.helper.menu.SongMenuHelper;
|
||||
import io.github.muntashirakon.music.helper.menu.SongsMenuHelper;
|
||||
import io.github.muntashirakon.music.interfaces.CabHolder;
|
||||
import io.github.muntashirakon.music.interfaces.MainActivityFragmentCallbacks;
|
||||
import io.github.muntashirakon.music.misc.DialogAsyncTask;
|
||||
import io.github.muntashirakon.music.misc.UpdateToastMediaScannerCompletionListener;
|
||||
import io.github.muntashirakon.music.misc.WrappedAsyncTaskLoader;
|
||||
import io.github.muntashirakon.music.model.Song;
|
||||
import io.github.muntashirakon.music.util.DensityUtil;
|
||||
import io.github.muntashirakon.music.util.FileUtil;
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil;
|
||||
import io.github.muntashirakon.music.util.RetroColorUtil;
|
||||
import io.github.muntashirakon.music.util.ThemedFastScroller;
|
||||
import io.github.muntashirakon.music.views.BreadCrumbLayout;
|
||||
import io.github.muntashirakon.music.views.ScrollingViewOnApplyWindowInsetsListener;
|
||||
import me.zhanghai.android.fastscroll.FastScroller;
|
||||
|
||||
public class FoldersFragment extends AbsMainActivityFragment implements
|
||||
MainActivityFragmentCallbacks,
|
||||
CabHolder, BreadCrumbLayout.SelectionCallback, SongFileAdapter.Callbacks,
|
||||
LoaderManager.LoaderCallbacks<List<File>> {
|
||||
|
||||
public static final String TAG = FoldersFragment.class.getSimpleName();
|
||||
public static final FileFilter AUDIO_FILE_FILTER = file -> !file.isHidden() && (file.isDirectory() ||
|
||||
FileUtil.fileIsMimeType(file, "audio/*", MimeTypeMap.getSingleton()) ||
|
||||
FileUtil.fileIsMimeType(file, "application/opus", MimeTypeMap.getSingleton()) ||
|
||||
FileUtil.fileIsMimeType(file, "application/ogg", MimeTypeMap.getSingleton()));
|
||||
private static final String PATH = "path";
|
||||
private static final String CRUMBS = "crumbs";
|
||||
private static final int LOADER_ID = 5;
|
||||
private SongFileAdapter adapter;
|
||||
private BreadCrumbLayout breadCrumbs;
|
||||
private MaterialCab cab;
|
||||
private View coordinatorLayout;
|
||||
private View empty;
|
||||
private TextView emojiText;
|
||||
private Comparator<File> fileComparator = (lhs, rhs) -> {
|
||||
if (lhs.isDirectory() && !rhs.isDirectory()) {
|
||||
return -1;
|
||||
} else if (!lhs.isDirectory() && rhs.isDirectory()) {
|
||||
return 1;
|
||||
} else {
|
||||
return lhs.getName().compareToIgnoreCase
|
||||
(rhs.getName());
|
||||
}
|
||||
};
|
||||
private RecyclerView recyclerView;
|
||||
|
||||
public FoldersFragment() {
|
||||
}
|
||||
|
||||
public static File getDefaultStartDirectory() {
|
||||
File musicDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
|
||||
File startFolder;
|
||||
if (musicDir.exists() && musicDir.isDirectory()) {
|
||||
startFolder = musicDir;
|
||||
} else {
|
||||
File externalStorage = Environment.getExternalStorageDirectory();
|
||||
if (externalStorage.exists() && externalStorage.isDirectory()) {
|
||||
startFolder = externalStorage;
|
||||
} else {
|
||||
startFolder = new File("/"); // root
|
||||
}
|
||||
}
|
||||
return startFolder;
|
||||
}
|
||||
|
||||
public static FoldersFragment newInstance(File directory) {
|
||||
FoldersFragment frag = new FoldersFragment();
|
||||
Bundle b = new Bundle();
|
||||
b.putSerializable(PATH, directory);
|
||||
frag.setArguments(b);
|
||||
return frag;
|
||||
}
|
||||
|
||||
public static FoldersFragment newInstance(Context context) {
|
||||
return newInstance(PreferenceUtil.INSTANCE.getStartDirectory());
|
||||
}
|
||||
|
||||
private static File tryGetCanonicalFile(File file) {
|
||||
try {
|
||||
return file.getCanonicalFile();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_folder, container, false);
|
||||
initViews(view);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
setStatusBarColorAuto(view);
|
||||
setUpAppbarColor();
|
||||
setUpBreadCrumbs();
|
||||
setUpRecyclerView();
|
||||
setUpAdapter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
//noinspection ConstantConditions
|
||||
setCrumb(new BreadCrumbLayout.Crumb(
|
||||
FileUtil.safeGetCanonicalFile((File) getArguments().getSerializable(PATH))), true);
|
||||
} else {
|
||||
breadCrumbs.restoreFromStateWrapper(savedInstanceState.getParcelable(CRUMBS));
|
||||
getLoaderManager().initLoader(LOADER_ID, null, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
saveScrollPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
if (breadCrumbs != null) {
|
||||
outState.putParcelable(CRUMBS, breadCrumbs.getStateWrapper());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleBackPress() {
|
||||
if (cab != null && cab.isActive()) {
|
||||
cab.finish();
|
||||
return true;
|
||||
}
|
||||
if (breadCrumbs != null && breadCrumbs.popHistory()) {
|
||||
setCrumb(breadCrumbs.lastHistory(), false);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Loader<List<File>> onCreateLoader(int id, Bundle args) {
|
||||
return new AsyncFileLoader(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCrumbSelection(BreadCrumbLayout.Crumb crumb, int index) {
|
||||
setCrumb(crumb, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFileMenuClicked(final File file, View view) {
|
||||
PopupMenu popupMenu = new PopupMenu(getActivity(), view);
|
||||
if (file.isDirectory()) {
|
||||
popupMenu.inflate(R.menu.menu_item_directory);
|
||||
popupMenu.setOnMenuItemClickListener(item -> {
|
||||
final int itemId = item.getItemId();
|
||||
switch (itemId) {
|
||||
case R.id.action_play_next:
|
||||
case R.id.action_add_to_current_playing:
|
||||
case R.id.action_add_to_playlist:
|
||||
case R.id.action_delete_from_device:
|
||||
new ListSongsAsyncTask(getActivity(), null, (songs, extra) -> {
|
||||
if (!songs.isEmpty()) {
|
||||
SongsMenuHelper.INSTANCE.handleMenuClick(getActivity(), songs, itemId);
|
||||
}
|
||||
}).execute(new ListSongsAsyncTask.LoadingInfo(toList(file), AUDIO_FILE_FILTER,
|
||||
getFileComparator()));
|
||||
return true;
|
||||
case R.id.action_set_as_start_directory:
|
||||
PreferenceUtil.INSTANCE.setStartDirectory(file);
|
||||
Toast.makeText(getActivity(),
|
||||
String.format(getString(R.string.new_start_directory), file.getPath()),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
return true;
|
||||
case R.id.action_scan:
|
||||
new ListPathsAsyncTask(getActivity(), this::scanPaths)
|
||||
.execute(new ListPathsAsyncTask.LoadingInfo(file, AUDIO_FILE_FILTER));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
} else {
|
||||
popupMenu.inflate(R.menu.menu_item_file);
|
||||
popupMenu.setOnMenuItemClickListener(item -> {
|
||||
final int itemId = item.getItemId();
|
||||
switch (itemId) {
|
||||
case R.id.action_play_next:
|
||||
case R.id.action_add_to_current_playing:
|
||||
case R.id.action_add_to_playlist:
|
||||
case R.id.action_go_to_album:
|
||||
case R.id.action_go_to_artist:
|
||||
case R.id.action_share:
|
||||
case R.id.action_tag_editor:
|
||||
case R.id.action_details:
|
||||
case R.id.action_set_as_ringtone:
|
||||
case R.id.action_delete_from_device:
|
||||
new ListSongsAsyncTask(getActivity(), null,
|
||||
(songs, extra) -> SongMenuHelper.INSTANCE.handleMenuClick(getActivity(),
|
||||
songs.get(0), itemId))
|
||||
.execute(new ListSongsAsyncTask.LoadingInfo(toList(file), AUDIO_FILE_FILTER,
|
||||
getFileComparator()));
|
||||
return true;
|
||||
case R.id.action_scan:
|
||||
new ListPathsAsyncTask(getActivity(), this::scanPaths)
|
||||
.execute(new ListPathsAsyncTask.LoadingInfo(file, AUDIO_FILE_FILTER));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
popupMenu.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFileSelected(File file) {
|
||||
file = tryGetCanonicalFile(file); // important as we compare the path value later
|
||||
if (file.isDirectory()) {
|
||||
setCrumb(new BreadCrumbLayout.Crumb(file), true);
|
||||
} else {
|
||||
FileFilter fileFilter = pathname -> !pathname.isDirectory() && AUDIO_FILE_FILTER
|
||||
.accept(pathname);
|
||||
new ListSongsAsyncTask(getActivity(), file, (songs, extra) -> {
|
||||
File file1 = (File) extra;
|
||||
int startIndex = -1;
|
||||
for (int i = 0; i < songs.size(); i++) {
|
||||
if (file1.getPath().equals(songs.get(i).getData())) { // path is already canonical here
|
||||
startIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (startIndex > -1) {
|
||||
MusicPlayerRemote.INSTANCE.openQueue(songs, startIndex, true);
|
||||
} else {
|
||||
final File finalFile = file1;
|
||||
Snackbar.make(coordinatorLayout, Html.fromHtml(
|
||||
String.format(getString(R.string.not_listed_in_media_store), file1.getName())),
|
||||
Snackbar.LENGTH_LONG)
|
||||
.setAction(R.string.action_scan,
|
||||
v -> new ListPathsAsyncTask(requireActivity(), this::scanPaths)
|
||||
.execute(
|
||||
new ListPathsAsyncTask.LoadingInfo(finalFile, AUDIO_FILE_FILTER)))
|
||||
.setActionTextColor(ThemeStore.Companion.accentColor(requireActivity()))
|
||||
.show();
|
||||
}
|
||||
}).execute(new ListSongsAsyncTask.LoadingInfo(toList(file.getParentFile()), fileFilter,
|
||||
getFileComparator()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(@NonNull Loader<List<File>> loader, List<File> data) {
|
||||
updateAdapter(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(@NonNull Loader<List<File>> loader) {
|
||||
updateAdapter(new LinkedList<File>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMultipleItemAction(MenuItem item, ArrayList<File> files) {
|
||||
final int itemId = item.getItemId();
|
||||
new ListSongsAsyncTask(getActivity(), null,
|
||||
(songs, extra) -> SongsMenuHelper.INSTANCE.handleMenuClick(getActivity(), songs, itemId))
|
||||
.execute(new ListSongsAsyncTask.LoadingInfo(files, AUDIO_FILE_FILTER, getFileComparator()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_go_to_start_directory:
|
||||
setCrumb(new BreadCrumbLayout.Crumb(
|
||||
tryGetCanonicalFile(PreferenceUtil.INSTANCE.getStartDirectory())), true);
|
||||
return true;
|
||||
case R.id.action_scan:
|
||||
BreadCrumbLayout.Crumb crumb = getActiveCrumb();
|
||||
if (crumb != null) {
|
||||
//noinspection Convert2MethodRef
|
||||
new ListPathsAsyncTask(getActivity(), paths -> scanPaths(paths))
|
||||
.execute(new ListPathsAsyncTask.LoadingInfo(crumb.getFile(),
|
||||
AUDIO_FILE_FILTER));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQueueChanged() {
|
||||
super.onQueueChanged();
|
||||
checkForPadding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected() {
|
||||
super.onServiceConnected();
|
||||
checkForPadding();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public MaterialCab openCab(int menuRes, MaterialCab.Callback callback) {
|
||||
if (cab != null && cab.isActive()) {
|
||||
cab.finish();
|
||||
}
|
||||
cab = new MaterialCab(getMainActivity(), R.id.cab_stub)
|
||||
.setMenu(menuRes)
|
||||
.setCloseDrawableRes(R.drawable.ic_close_white_24dp)
|
||||
.setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(
|
||||
ATHUtil.INSTANCE.resolveColor(requireContext(), R.attr.colorSurface)))
|
||||
.start(callback);
|
||||
return cab;
|
||||
}
|
||||
|
||||
private void checkForPadding() {
|
||||
final int count = adapter.getItemCount();
|
||||
final MarginLayoutParams params = (MarginLayoutParams) coordinatorLayout.getLayoutParams();
|
||||
params.bottomMargin = count > 0 && !MusicPlayerRemote.getPlayingQueue().isEmpty() ? DensityUtil
|
||||
.dip2px(requireContext(), 104f) : DensityUtil.dip2px(requireContext(), 54f);
|
||||
}
|
||||
|
||||
private void checkIsEmpty() {
|
||||
emojiText.setText(getEmojiByUnicode(0x1F631));
|
||||
if (empty != null) {
|
||||
empty.setVisibility(adapter == null || adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private BreadCrumbLayout.Crumb getActiveCrumb() {
|
||||
return breadCrumbs != null && breadCrumbs.size() > 0 ? breadCrumbs
|
||||
.getCrumb(breadCrumbs.getActiveIndex()) : null;
|
||||
}
|
||||
|
||||
private String getEmojiByUnicode(int unicode) {
|
||||
return new String(Character.toChars(unicode));
|
||||
}
|
||||
|
||||
private Comparator<File> getFileComparator() {
|
||||
return fileComparator;
|
||||
}
|
||||
|
||||
private void initViews(View view) {
|
||||
coordinatorLayout = view.findViewById(R.id.coordinatorLayout);
|
||||
recyclerView = view.findViewById(R.id.recyclerView);
|
||||
breadCrumbs = view.findViewById(R.id.breadCrumbs);
|
||||
empty = view.findViewById(android.R.id.empty);
|
||||
emojiText = view.findViewById(R.id.emptyEmoji);
|
||||
}
|
||||
|
||||
private void saveScrollPosition() {
|
||||
BreadCrumbLayout.Crumb crumb = getActiveCrumb();
|
||||
if (crumb != null) {
|
||||
crumb.setScrollPosition(
|
||||
((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition());
|
||||
}
|
||||
}
|
||||
|
||||
private void scanPaths(@Nullable String[] toBeScanned) {
|
||||
if (getActivity() == null) {
|
||||
return;
|
||||
}
|
||||
if (toBeScanned == null || toBeScanned.length < 1) {
|
||||
Toast.makeText(getActivity(), R.string.nothing_to_scan, Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
MediaScannerConnection.scanFile(getActivity().getApplicationContext(), toBeScanned, null,
|
||||
new UpdateToastMediaScannerCompletionListener(getActivity(), toBeScanned));
|
||||
}
|
||||
}
|
||||
|
||||
private void setCrumb(BreadCrumbLayout.Crumb crumb, boolean addToHistory) {
|
||||
if (crumb == null) {
|
||||
return;
|
||||
}
|
||||
saveScrollPosition();
|
||||
breadCrumbs.setActiveOrAdd(crumb, false);
|
||||
if (addToHistory) {
|
||||
breadCrumbs.addHistory(crumb);
|
||||
}
|
||||
getLoaderManager().restartLoader(LOADER_ID, null, this);
|
||||
}
|
||||
|
||||
private void setUpAdapter() {
|
||||
adapter = new SongFileAdapter(getMainActivity(), new LinkedList<>(), R.layout.item_list,
|
||||
this, this);
|
||||
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
|
||||
@Override
|
||||
public void onChanged() {
|
||||
super.onChanged();
|
||||
checkIsEmpty();
|
||||
checkForPadding();
|
||||
}
|
||||
});
|
||||
recyclerView.setAdapter(adapter);
|
||||
checkIsEmpty();
|
||||
}
|
||||
|
||||
private void setUpAppbarColor() {
|
||||
breadCrumbs.setActivatedContentColor(
|
||||
ATHUtil.INSTANCE.resolveColor(requireContext(), android.R.attr.textColorPrimary));
|
||||
breadCrumbs.setDeactivatedContentColor(
|
||||
ATHUtil.INSTANCE.resolveColor(requireContext(), android.R.attr.textColorSecondary));
|
||||
}
|
||||
|
||||
private void setUpBreadCrumbs() {
|
||||
breadCrumbs.setCallback(this);
|
||||
}
|
||||
|
||||
private void setUpRecyclerView() {
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||
FastScroller fastScroller = ThemedFastScroller.INSTANCE.create(recyclerView);
|
||||
recyclerView.setOnApplyWindowInsetsListener(
|
||||
new ScrollingViewOnApplyWindowInsetsListener(recyclerView, fastScroller));
|
||||
}
|
||||
|
||||
private ArrayList<File> toList(File file) {
|
||||
ArrayList<File> files = new ArrayList<>(1);
|
||||
files.add(file);
|
||||
return files;
|
||||
}
|
||||
|
||||
private void updateAdapter(@NonNull List<File> files) {
|
||||
adapter.swapDataSet(files);
|
||||
BreadCrumbLayout.Crumb crumb = getActiveCrumb();
|
||||
if (crumb != null && recyclerView != null) {
|
||||
((LinearLayoutManager) recyclerView.getLayoutManager())
|
||||
.scrollToPositionWithOffset(crumb.getScrollPosition(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ListPathsAsyncTask extends
|
||||
ListingFilesDialogAsyncTask<ListPathsAsyncTask.LoadingInfo, String, String[]> {
|
||||
|
||||
private WeakReference<OnPathsListedCallback> onPathsListedCallbackWeakReference;
|
||||
|
||||
public ListPathsAsyncTask(Context context, OnPathsListedCallback callback) {
|
||||
super(context);
|
||||
onPathsListedCallbackWeakReference = new WeakReference<>(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] doInBackground(LoadingInfo... params) {
|
||||
try {
|
||||
if (isCancelled() || checkCallbackReference() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
LoadingInfo info = params[0];
|
||||
|
||||
final String[] paths;
|
||||
|
||||
if (info.file.isDirectory()) {
|
||||
List<File> files = FileUtil.listFilesDeep(info.file, info.fileFilter);
|
||||
|
||||
if (isCancelled() || checkCallbackReference() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
paths = new String[files.size()];
|
||||
for (int i = 0; i < files.size(); i++) {
|
||||
File f = files.get(i);
|
||||
paths[i] = FileUtil.safeGetCanonicalPath(f);
|
||||
|
||||
if (isCancelled() || checkCallbackReference() == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
paths = new String[1];
|
||||
paths[0] = info.file.getPath();
|
||||
}
|
||||
|
||||
return paths;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
cancel(false);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String[] paths) {
|
||||
super.onPostExecute(paths);
|
||||
OnPathsListedCallback callback = checkCallbackReference();
|
||||
if (callback != null && paths != null) {
|
||||
callback.onPathsListed(paths);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
super.onPreExecute();
|
||||
checkCallbackReference();
|
||||
}
|
||||
|
||||
private OnPathsListedCallback checkCallbackReference() {
|
||||
OnPathsListedCallback callback = onPathsListedCallbackWeakReference.get();
|
||||
if (callback == null) {
|
||||
cancel(false);
|
||||
}
|
||||
return callback;
|
||||
}
|
||||
|
||||
public interface OnPathsListedCallback {
|
||||
|
||||
void onPathsListed(@NonNull String[] paths);
|
||||
}
|
||||
|
||||
public static class LoadingInfo {
|
||||
|
||||
public final File file;
|
||||
|
||||
final FileFilter fileFilter;
|
||||
|
||||
public LoadingInfo(File file, FileFilter fileFilter) {
|
||||
this.file = file;
|
||||
this.fileFilter = fileFilter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class AsyncFileLoader extends WrappedAsyncTaskLoader<List<File>> {
|
||||
|
||||
private WeakReference<FoldersFragment> fragmentWeakReference;
|
||||
|
||||
AsyncFileLoader(FoldersFragment foldersFragment) {
|
||||
super(foldersFragment.requireActivity());
|
||||
fragmentWeakReference = new WeakReference<>(foldersFragment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<File> loadInBackground() {
|
||||
FoldersFragment foldersFragment = fragmentWeakReference.get();
|
||||
File directory = null;
|
||||
if (foldersFragment != null) {
|
||||
BreadCrumbLayout.Crumb crumb = foldersFragment.getActiveCrumb();
|
||||
if (crumb != null) {
|
||||
directory = crumb.getFile();
|
||||
}
|
||||
}
|
||||
if (directory != null) {
|
||||
List<File> files = FileUtil.listFiles(directory, AUDIO_FILE_FILTER);
|
||||
Collections.sort(files, foldersFragment.getFileComparator());
|
||||
return files;
|
||||
} else {
|
||||
return new LinkedList<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ListSongsAsyncTask
|
||||
extends ListingFilesDialogAsyncTask<ListSongsAsyncTask.LoadingInfo, Void, ArrayList<Song>> {
|
||||
|
||||
private final Object extra;
|
||||
private WeakReference<OnSongsListedCallback> callbackWeakReference;
|
||||
private WeakReference<Context> contextWeakReference;
|
||||
|
||||
ListSongsAsyncTask(Context context, Object extra, OnSongsListedCallback callback) {
|
||||
super(context);
|
||||
this.extra = extra;
|
||||
contextWeakReference = new WeakReference<>(context);
|
||||
callbackWeakReference = new WeakReference<>(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ArrayList<Song> doInBackground(LoadingInfo... params) {
|
||||
try {
|
||||
LoadingInfo info = params[0];
|
||||
List<File> files = FileUtil.listFilesDeep(info.files, info.fileFilter);
|
||||
|
||||
if (isCancelled() || checkContextReference() == null
|
||||
|| checkCallbackReference() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Collections.sort(files, info.fileComparator);
|
||||
|
||||
Context context = checkContextReference();
|
||||
if (isCancelled() || context == null || checkCallbackReference() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return FileUtil.matchFilesWithMediaStore(context, files);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
cancel(false);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(ArrayList<Song> songs) {
|
||||
super.onPostExecute(songs);
|
||||
OnSongsListedCallback callback = checkCallbackReference();
|
||||
if (songs != null && callback != null) {
|
||||
callback.onSongsListed(songs, extra);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
super.onPreExecute();
|
||||
checkCallbackReference();
|
||||
checkContextReference();
|
||||
}
|
||||
|
||||
private OnSongsListedCallback checkCallbackReference() {
|
||||
OnSongsListedCallback callback = callbackWeakReference.get();
|
||||
if (callback == null) {
|
||||
cancel(false);
|
||||
}
|
||||
return callback;
|
||||
}
|
||||
|
||||
private Context checkContextReference() {
|
||||
Context context = contextWeakReference.get();
|
||||
if (context == null) {
|
||||
cancel(false);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
public interface OnSongsListedCallback {
|
||||
|
||||
void onSongsListed(@NonNull ArrayList<Song> songs, Object extra);
|
||||
}
|
||||
|
||||
static class LoadingInfo {
|
||||
|
||||
final Comparator<File> fileComparator;
|
||||
|
||||
final FileFilter fileFilter;
|
||||
|
||||
final List<File> files;
|
||||
|
||||
LoadingInfo(@NonNull List<File> files, @NonNull FileFilter fileFilter,
|
||||
@NonNull Comparator<File> fileComparator) {
|
||||
this.fileComparator = fileComparator;
|
||||
this.fileFilter = fileFilter;
|
||||
this.files = files;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static abstract class ListingFilesDialogAsyncTask<Params, Progress, Result> extends
|
||||
DialogAsyncTask<Params, Progress, Result> {
|
||||
|
||||
ListingFilesDialogAsyncTask(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public ListingFilesDialogAsyncTask(Context context, int showDelay) {
|
||||
super(context, showDelay);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog createDialog(@NonNull Context context) {
|
||||
return new MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.listing_files)
|
||||
.setCancelable(false)
|
||||
.setView(R.layout.loading)
|
||||
.setOnCancelListener(dialog -> cancel(false))
|
||||
.setOnDismissListener(dialog -> cancel(false))
|
||||
.create();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
package io.github.muntashirakon.music.fragments.player
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.adapter.album.AlbumCoverPagerAdapter
|
||||
import io.github.muntashirakon.music.adapter.album.AlbumCoverPagerAdapter.AlbumCoverFragment
|
||||
import io.github.muntashirakon.music.fragments.NowPlayingScreen.*
|
||||
import io.github.muntashirakon.music.fragments.base.AbsMusicServiceFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.transform.CarousalPagerTransformer
|
||||
import io.github.muntashirakon.music.transform.ParallaxPagerTransformer
|
||||
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_player_album_cover.*
|
||||
|
||||
|
||||
class PlayerAlbumCoverFragment : AbsMusicServiceFragment(), ViewPager.OnPageChangeListener {
|
||||
private var callbacks: Callbacks? = null
|
||||
private var currentPosition: Int = 0
|
||||
private val colorReceiver = object : AlbumCoverFragment.ColorReceiver {
|
||||
override fun onColorReady(color: MediaNotificationProcessor, request: Int) {
|
||||
if (currentPosition == request) {
|
||||
notifyColorChange(color)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun removeSlideEffect() {
|
||||
val transformer = ParallaxPagerTransformer(R.id.player_image)
|
||||
transformer.setSpeed(0.3f)
|
||||
//viewPager.setPageTransformer(true, transformer)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_player_album_cover, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
viewPager.addOnPageChangeListener(this)
|
||||
val nps = PreferenceUtil.nowPlayingScreen
|
||||
|
||||
val metrics = resources.displayMetrics
|
||||
val ratio = metrics.heightPixels.toFloat() / metrics.widthPixels.toFloat()
|
||||
|
||||
if (nps == Full || nps == Classic || nps == Fit || nps == Gradient) {
|
||||
viewPager.offscreenPageLimit = 2
|
||||
} else if (PreferenceUtil.isCarouselEffect) {
|
||||
viewPager.clipToPadding = false
|
||||
val padding =
|
||||
if (ratio >= 1.777f) {
|
||||
40
|
||||
} else {
|
||||
100
|
||||
}
|
||||
viewPager.setPadding(padding, 0, padding, 0)
|
||||
viewPager.pageMargin = 0
|
||||
viewPager.setPageTransformer(false, CarousalPagerTransformer(requireContext()))
|
||||
} else {
|
||||
viewPager.offscreenPageLimit = 2
|
||||
viewPager.setPageTransformer(
|
||||
true,
|
||||
PreferenceUtil.albumCoverTransform
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
viewPager.removeOnPageChangeListener(this)
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
updatePlayingQueue()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
viewPager.currentItem = MusicPlayerRemote.position
|
||||
}
|
||||
|
||||
override fun onQueueChanged() {
|
||||
updatePlayingQueue()
|
||||
}
|
||||
|
||||
private fun updatePlayingQueue() {
|
||||
viewPager.apply {
|
||||
adapter = AlbumCoverPagerAdapter(childFragmentManager, MusicPlayerRemote.playingQueue)
|
||||
viewPager.adapter!!.notifyDataSetChanged()
|
||||
viewPager.currentItem = MusicPlayerRemote.position
|
||||
onPageSelected(MusicPlayerRemote.position)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
|
||||
}
|
||||
|
||||
override fun onPageSelected(position: Int) {
|
||||
currentPosition = position
|
||||
if (viewPager.adapter != null) {
|
||||
(viewPager.adapter as AlbumCoverPagerAdapter).receiveColor(colorReceiver, position)
|
||||
}
|
||||
if (position != MusicPlayerRemote.position) {
|
||||
MusicPlayerRemote.playSongAt(position)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPageScrollStateChanged(state: Int) {
|
||||
}
|
||||
|
||||
private fun notifyColorChange(color: MediaNotificationProcessor) {
|
||||
callbacks?.onColorChanged(color)
|
||||
}
|
||||
|
||||
fun setCallbacks(listener: Callbacks) {
|
||||
callbacks = listener
|
||||
}
|
||||
|
||||
|
||||
interface Callbacks {
|
||||
|
||||
fun onColorChanged(color: MediaNotificationProcessor)
|
||||
|
||||
fun onFavoriteToggled()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG: String = PlayerAlbumCoverFragment::class.java.simpleName
|
||||
}
|
||||
}
|
|
@ -0,0 +1,226 @@
|
|||
package io.github.muntashirakon.music.fragments.player.adaptive
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.extensions.surfaceColor
|
||||
import io.github.muntashirakon.music.extensions.textColorPrimary
|
||||
import io.github.muntashirakon.music.extensions.textColorSecondary
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerFragment
|
||||
import io.github.muntashirakon.music.fragments.player.PlayerAlbumCoverFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
import io.github.muntashirakon.music.model.lyrics.AbsSynchronizedLyrics
|
||||
import io.github.muntashirakon.music.model.lyrics.Lyrics
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_adaptive_player.*
|
||||
|
||||
class AdaptiveFragment : AbsPlayerFragment(), MusicProgressViewUpdateHelper.Callback {
|
||||
|
||||
private lateinit var lyricsLayout: FrameLayout
|
||||
private lateinit var lyricsLine1: TextView
|
||||
private lateinit var lyricsLine2: TextView
|
||||
|
||||
private var lyrics: Lyrics? = null
|
||||
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
if (!isLyricsLayoutBound()) return
|
||||
|
||||
if (!isLyricsLayoutVisible()) {
|
||||
hideLyricsLayout()
|
||||
return
|
||||
}
|
||||
|
||||
if (lyrics !is AbsSynchronizedLyrics) return
|
||||
val synchronizedLyrics = lyrics as AbsSynchronizedLyrics
|
||||
|
||||
lyricsLayout.visibility = View.VISIBLE
|
||||
lyricsLayout.alpha = 1f
|
||||
|
||||
val oldLine = lyricsLine2.text.toString()
|
||||
val line = synchronizedLyrics.getLine(progress)
|
||||
|
||||
if (oldLine != line || oldLine.isEmpty()) {
|
||||
lyricsLine1.text = oldLine
|
||||
lyricsLine2.text = line
|
||||
|
||||
lyricsLine1.visibility = View.VISIBLE
|
||||
lyricsLine2.visibility = View.VISIBLE
|
||||
|
||||
lyricsLine2.measure(
|
||||
View.MeasureSpec.makeMeasureSpec(
|
||||
lyricsLine2.measuredWidth,
|
||||
View.MeasureSpec.EXACTLY
|
||||
),
|
||||
View.MeasureSpec.UNSPECIFIED
|
||||
)
|
||||
val h: Float = lyricsLine2.measuredHeight.toFloat()
|
||||
|
||||
lyricsLine1.alpha = 1f
|
||||
lyricsLine1.translationY = 0f
|
||||
lyricsLine1.animate().alpha(0f).translationY(-h).duration = VISIBILITY_ANIM_DURATION
|
||||
|
||||
lyricsLine2.alpha = 0f
|
||||
lyricsLine2.translationY = h
|
||||
lyricsLine2.animate().alpha(1f).translationY(0f).duration = VISIBILITY_ANIM_DURATION
|
||||
}
|
||||
}
|
||||
|
||||
private fun isLyricsLayoutVisible(): Boolean {
|
||||
return lyrics != null && lyrics!!.isSynchronized && lyrics!!.isValid
|
||||
}
|
||||
|
||||
private fun isLyricsLayoutBound(): Boolean {
|
||||
return lyricsLayout != null && lyricsLine1 != null && lyricsLine2 != null
|
||||
}
|
||||
|
||||
private fun hideLyricsLayout() {
|
||||
lyricsLayout.animate().alpha(0f).setDuration(VISIBILITY_ANIM_DURATION)
|
||||
.withEndAction(Runnable {
|
||||
if (!isLyricsLayoutBound()) return@Runnable
|
||||
lyricsLayout.visibility = View.GONE
|
||||
lyricsLine1.text = null
|
||||
lyricsLine2.text = null
|
||||
})
|
||||
}
|
||||
|
||||
override fun setLyrics(l: Lyrics?) {
|
||||
lyrics = l
|
||||
|
||||
if (!isLyricsLayoutBound()) return
|
||||
|
||||
if (!isLyricsLayoutVisible()) {
|
||||
hideLyricsLayout()
|
||||
return
|
||||
}
|
||||
|
||||
lyricsLine1.text = null
|
||||
lyricsLine2.text = null
|
||||
|
||||
lyricsLayout.visibility = View.VISIBLE
|
||||
lyricsLayout.animate().alpha(1f).duration = VISIBILITY_ANIM_DURATION
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
progressViewUpdateHelper.stop()
|
||||
}
|
||||
|
||||
override fun playerToolbar(): Toolbar {
|
||||
return playerToolbar
|
||||
}
|
||||
|
||||
private var lastColor: Int = 0
|
||||
private lateinit var playbackControlsFragment: AdaptivePlaybackControlsFragment
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_adaptive_player, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
lyricsLayout = view.findViewById(R.id.player_lyrics)
|
||||
lyricsLine1 = view.findViewById(R.id.player_lyrics_line1)
|
||||
lyricsLine2 = view.findViewById(R.id.player_lyrics_line2)
|
||||
|
||||
setUpSubFragments()
|
||||
setUpPlayerToolbar()
|
||||
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this, 500, 1000)
|
||||
progressViewUpdateHelper.start()
|
||||
}
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
playbackControlsFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as AdaptivePlaybackControlsFragment
|
||||
val playerAlbumCoverFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment
|
||||
playerAlbumCoverFragment.apply {
|
||||
removeSlideEffect()
|
||||
setCallbacks(this@AdaptiveFragment)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpPlayerToolbar() {
|
||||
playerToolbar.apply {
|
||||
inflateMenu(R.menu.menu_player)
|
||||
setNavigationOnClickListener { requireActivity().onBackPressed() }
|
||||
ToolbarContentTintHelper.colorizeToolbar(this, surfaceColor(), requireActivity())
|
||||
setTitleTextColor(textColorPrimary())
|
||||
setSubtitleTextColor(textColorSecondary())
|
||||
setOnMenuItemClickListener(this@AdaptiveFragment)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
updateIsFavorite()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
updateIsFavorite()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
playerToolbar.apply {
|
||||
title = song.title
|
||||
subtitle = song.artistName
|
||||
}
|
||||
}
|
||||
|
||||
override fun toggleFavorite(song: Song) {
|
||||
super.toggleFavorite(song)
|
||||
if (song.id == MusicPlayerRemote.currentSong.id) {
|
||||
updateIsFavorite()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFavoriteToggled() {
|
||||
toggleFavorite(MusicPlayerRemote.currentSong)
|
||||
}
|
||||
|
||||
override fun onColorChanged(color: MediaNotificationProcessor) {
|
||||
playbackControlsFragment.setColor(color)
|
||||
lastColor = color.primaryTextColor
|
||||
callbacks?.onPaletteColorChanged()
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
playerToolbar,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onShow() {
|
||||
}
|
||||
|
||||
override fun onHide() {
|
||||
onBackPressed()
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun toolbarIconColor(): Int {
|
||||
return ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal)
|
||||
}
|
||||
|
||||
override val paletteColor: Int
|
||||
get() = lastColor
|
||||
}
|
|
@ -0,0 +1,259 @@
|
|||
package io.github.muntashirakon.music.fragments.player.adaptive
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.graphics.PorterDuff
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.SeekBar
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.extensions.applyColor
|
||||
import io.github.muntashirakon.music.extensions.hide
|
||||
import io.github.muntashirakon.music.extensions.ripAlpha
|
||||
import io.github.muntashirakon.music.extensions.show
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerControlsFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper
|
||||
import io.github.muntashirakon.music.helper.PlayPauseButtonOnClickHandler
|
||||
import io.github.muntashirakon.music.misc.SimpleOnSeekbarChangeListener
|
||||
import io.github.muntashirakon.music.service.MusicService
|
||||
import io.github.muntashirakon.music.util.MusicUtil
|
||||
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_adaptive_player_playback_controls.*
|
||||
|
||||
class AdaptivePlaybackControlsFragment : AbsPlayerControlsFragment() {
|
||||
|
||||
private var lastPlaybackControlsColor: Int = 0
|
||||
private var lastDisabledPlaybackControlsColor: Int = 0
|
||||
private var progressViewUpdateHelper: MusicProgressViewUpdateHelper? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(
|
||||
R.layout.fragment_adaptive_player_playback_controls,
|
||||
container,
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpMusicControllers()
|
||||
|
||||
playPauseButton.setOnClickListener {
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
MusicPlayerRemote.pauseSong()
|
||||
} else {
|
||||
MusicPlayerRemote.resumePlaying()
|
||||
}
|
||||
showBonceAnimation(playPauseButton)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
if (PreferenceUtil.isSongInfo) {
|
||||
songInfo?.text = getSongInfo(MusicPlayerRemote.currentSong)
|
||||
songInfo.show()
|
||||
} else {
|
||||
songInfo?.hide()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
progressViewUpdateHelper!!.start()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
progressViewUpdateHelper!!.stop()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
updatePlayPauseDrawableState()
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onPlayStateChanged() {
|
||||
updatePlayPauseDrawableState()
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged() {
|
||||
updateRepeatState()
|
||||
}
|
||||
|
||||
override fun onShuffleModeChanged() {
|
||||
updateShuffleState()
|
||||
}
|
||||
|
||||
override fun setColor(color: MediaNotificationProcessor) {
|
||||
if (ColorUtil.isColorLight(
|
||||
ATHUtil.resolveColor(
|
||||
requireContext(),
|
||||
android.R.attr.windowBackground
|
||||
)
|
||||
)
|
||||
) {
|
||||
lastPlaybackControlsColor = MaterialValueHelper.getSecondaryTextColor(activity, true)
|
||||
lastDisabledPlaybackControlsColor =
|
||||
MaterialValueHelper.getSecondaryDisabledTextColor(activity, true)
|
||||
} else {
|
||||
lastPlaybackControlsColor = MaterialValueHelper.getPrimaryTextColor(activity, false)
|
||||
lastDisabledPlaybackControlsColor =
|
||||
MaterialValueHelper.getPrimaryDisabledTextColor(activity, false)
|
||||
}
|
||||
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updatePrevNextColor()
|
||||
updatePlayPauseColor()
|
||||
|
||||
val colorFinal = if (PreferenceUtil.isAdaptiveColor) {
|
||||
color.primaryTextColor
|
||||
} else {
|
||||
ThemeStore.accentColor(requireContext())
|
||||
}.ripAlpha()
|
||||
|
||||
TintHelper.setTintAuto(
|
||||
playPauseButton,
|
||||
MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(colorFinal)),
|
||||
false
|
||||
)
|
||||
TintHelper.setTintAuto(playPauseButton, colorFinal, true)
|
||||
progressSlider.applyColor(colorFinal)
|
||||
volumeFragment?.setTintable(colorFinal)
|
||||
}
|
||||
|
||||
private fun updatePlayPauseColor() {
|
||||
//playPauseButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN);
|
||||
}
|
||||
|
||||
private fun setUpPlayPauseFab() {
|
||||
playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
||||
}
|
||||
|
||||
private fun updatePlayPauseDrawableState() {
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp)
|
||||
} else {
|
||||
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_32dp)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpMusicControllers() {
|
||||
setUpPlayPauseFab()
|
||||
setUpPrevNext()
|
||||
setUpRepeatButton()
|
||||
setUpShuffleButton()
|
||||
setUpProgressSlider()
|
||||
}
|
||||
|
||||
private fun setUpPrevNext() {
|
||||
updatePrevNextColor()
|
||||
nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
|
||||
previousButton.setOnClickListener { MusicPlayerRemote.back() }
|
||||
}
|
||||
|
||||
private fun updatePrevNextColor() {
|
||||
nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
|
||||
private fun setUpShuffleButton() {
|
||||
shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
|
||||
}
|
||||
|
||||
override fun show() {
|
||||
|
||||
}
|
||||
|
||||
override fun hide() {
|
||||
|
||||
}
|
||||
|
||||
override fun updateShuffleState() {
|
||||
when (MusicPlayerRemote.shuffleMode) {
|
||||
MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton.setColorFilter(
|
||||
lastPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
else -> shuffleButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpRepeatButton() {
|
||||
repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
|
||||
}
|
||||
|
||||
override fun updateRepeatState() {
|
||||
when (MusicPlayerRemote.repeatMode) {
|
||||
MusicService.REPEAT_MODE_NONE -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
MusicService.REPEAT_MODE_ALL -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
MusicService.REPEAT_MODE_THIS -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setUpProgressSlider() {
|
||||
progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
MusicPlayerRemote.seekTo(progress)
|
||||
onUpdateProgressViews(
|
||||
MusicPlayerRemote.songProgressMillis,
|
||||
MusicPlayerRemote.songDurationMillis
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
progressSlider.max = total
|
||||
|
||||
val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress)
|
||||
animator.duration = SLIDER_ANIMATION_TIME
|
||||
animator.interpolator = LinearInterpolator()
|
||||
animator.start()
|
||||
|
||||
songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
|
||||
songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,284 @@
|
|||
package io.github.muntashirakon.music.fragments.player.blur
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.graphics.Color
|
||||
import android.graphics.PorterDuff
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.AccelerateInterpolator
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.SeekBar
|
||||
import androidx.core.content.ContextCompat
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.extensions.hide
|
||||
import io.github.muntashirakon.music.extensions.show
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerControlsFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper
|
||||
import io.github.muntashirakon.music.helper.PlayPauseButtonOnClickHandler
|
||||
import io.github.muntashirakon.music.misc.SimpleOnSeekbarChangeListener
|
||||
import io.github.muntashirakon.music.service.MusicService
|
||||
import io.github.muntashirakon.music.util.MusicUtil
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_blur_player_playback_controls.*
|
||||
|
||||
class BlurPlaybackControlsFragment : AbsPlayerControlsFragment() {
|
||||
|
||||
private var lastPlaybackControlsColor: Int = 0
|
||||
private var lastDisabledPlaybackControlsColor: Int = 0
|
||||
private var progressViewUpdateHelper: MusicProgressViewUpdateHelper? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_blur_player_playback_controls, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpMusicControllers()
|
||||
|
||||
playPauseButton.setOnClickListener {
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
MusicPlayerRemote.pauseSong()
|
||||
} else {
|
||||
MusicPlayerRemote.resumePlaying()
|
||||
}
|
||||
showBonceAnimation()
|
||||
}
|
||||
title.isSelected = true
|
||||
text.isSelected = true
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
title.text = song.title
|
||||
text.text = String.format("%s • %s", song.artistName, song.albumName)
|
||||
|
||||
if (PreferenceUtil.isSongInfo) {
|
||||
songInfo.show()
|
||||
songInfo?.text = getSongInfo(song)
|
||||
} else {
|
||||
songInfo?.hide()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
progressViewUpdateHelper!!.start()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
progressViewUpdateHelper!!.stop()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
updatePlayPauseDrawableState()
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onPlayStateChanged() {
|
||||
updatePlayPauseDrawableState()
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged() {
|
||||
updateRepeatState()
|
||||
}
|
||||
|
||||
override fun onShuffleModeChanged() {
|
||||
updateShuffleState()
|
||||
}
|
||||
|
||||
override fun setColor(color: MediaNotificationProcessor) {
|
||||
lastPlaybackControlsColor = Color.WHITE
|
||||
lastDisabledPlaybackControlsColor =
|
||||
ContextCompat.getColor(requireContext(), R.color.md_grey_500)
|
||||
|
||||
title.setTextColor(lastPlaybackControlsColor)
|
||||
|
||||
songCurrentProgress.setTextColor(lastPlaybackControlsColor)
|
||||
songTotalTime.setTextColor(lastPlaybackControlsColor)
|
||||
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updatePrevNextColor()
|
||||
|
||||
text.setTextColor(lastDisabledPlaybackControlsColor)
|
||||
songInfo.setTextColor(lastDisabledPlaybackControlsColor)
|
||||
|
||||
TintHelper.setTintAuto(progressSlider, lastPlaybackControlsColor, false)
|
||||
volumeFragment?.setTintableColor(lastPlaybackControlsColor)
|
||||
setFabColor(lastPlaybackControlsColor)
|
||||
}
|
||||
|
||||
private fun setFabColor(i: Int) {
|
||||
TintHelper.setTintAuto(
|
||||
playPauseButton,
|
||||
MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(i)),
|
||||
false
|
||||
)
|
||||
TintHelper.setTintAuto(playPauseButton, i, true)
|
||||
}
|
||||
|
||||
private fun setUpPlayPauseFab() {
|
||||
playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
||||
}
|
||||
|
||||
private fun updatePlayPauseDrawableState() {
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp)
|
||||
} else {
|
||||
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_32dp)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpMusicControllers() {
|
||||
setUpPlayPauseFab()
|
||||
setUpPrevNext()
|
||||
setUpRepeatButton()
|
||||
setUpShuffleButton()
|
||||
setUpProgressSlider()
|
||||
}
|
||||
|
||||
private fun setUpPrevNext() {
|
||||
updatePrevNextColor()
|
||||
nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
|
||||
previousButton.setOnClickListener { MusicPlayerRemote.back() }
|
||||
}
|
||||
|
||||
private fun updatePrevNextColor() {
|
||||
nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
|
||||
private fun setUpShuffleButton() {
|
||||
shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
|
||||
}
|
||||
|
||||
override fun updateShuffleState() {
|
||||
when (MusicPlayerRemote.shuffleMode) {
|
||||
MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton.setColorFilter(
|
||||
lastPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
else -> shuffleButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpRepeatButton() {
|
||||
repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
|
||||
}
|
||||
|
||||
override fun updateRepeatState() {
|
||||
when (MusicPlayerRemote.repeatMode) {
|
||||
MusicService.REPEAT_MODE_NONE -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
MusicService.REPEAT_MODE_ALL -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
MusicService.REPEAT_MODE_THIS -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override fun show() {
|
||||
playPauseButton!!.animate()
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.rotation(360f)
|
||||
.setInterpolator(DecelerateInterpolator())
|
||||
.start()
|
||||
}
|
||||
|
||||
public override fun hide() {
|
||||
if (playPauseButton != null) {
|
||||
playPauseButton!!.apply {
|
||||
scaleX = 0f
|
||||
scaleY = 0f
|
||||
rotation = 0f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showBonceAnimation() {
|
||||
playPauseButton.apply {
|
||||
clearAnimation()
|
||||
scaleX = 0.9f
|
||||
scaleY = 0.9f
|
||||
visibility = View.VISIBLE
|
||||
pivotX = (width / 2).toFloat()
|
||||
pivotY = (height / 2).toFloat()
|
||||
|
||||
animate().setDuration(200)
|
||||
.setInterpolator(DecelerateInterpolator())
|
||||
.scaleX(1.1f)
|
||||
.scaleY(1.1f)
|
||||
.withEndAction {
|
||||
animate().setDuration(200)
|
||||
.setInterpolator(AccelerateInterpolator())
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.alpha(1f).start()
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
|
||||
override fun setUpProgressSlider() {
|
||||
progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
MusicPlayerRemote.seekTo(progress)
|
||||
onUpdateProgressViews(
|
||||
MusicPlayerRemote.songProgressMillis,
|
||||
MusicPlayerRemote.songDurationMillis
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
progressSlider.max = total
|
||||
|
||||
val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress)
|
||||
animator.duration = SLIDER_ANIMATION_TIME
|
||||
animator.interpolator = LinearInterpolator()
|
||||
animator.start()
|
||||
|
||||
songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
|
||||
songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
package io.github.muntashirakon.music.fragments.player.blur
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.preference.PreferenceManager
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import io.github.muntashirakon.music.NEW_BLUR_AMOUNT
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerFragment
|
||||
import io.github.muntashirakon.music.fragments.player.PlayerAlbumCoverFragment
|
||||
import io.github.muntashirakon.music.glide.BlurTransformation
|
||||
import io.github.muntashirakon.music.glide.RetroMusicColoredTarget
|
||||
import io.github.muntashirakon.music.glide.SongGlideRequest
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import com.bumptech.glide.Glide
|
||||
import kotlinx.android.synthetic.main.fragment_blur.*
|
||||
|
||||
class BlurPlayerFragment : AbsPlayerFragment(), SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
override fun playerToolbar(): Toolbar {
|
||||
return playerToolbar
|
||||
}
|
||||
|
||||
private lateinit var playbackControlsFragment: BlurPlaybackControlsFragment
|
||||
|
||||
private var lastColor: Int = 0
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_blur, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpSubFragments()
|
||||
setUpPlayerToolbar()
|
||||
}
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
playbackControlsFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as BlurPlaybackControlsFragment
|
||||
val playerAlbumCoverFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment
|
||||
playerAlbumCoverFragment.setCallbacks(this)
|
||||
}
|
||||
|
||||
private fun setUpPlayerToolbar() {
|
||||
playerToolbar.apply {
|
||||
inflateMenu(R.menu.menu_player)
|
||||
setNavigationOnClickListener { requireActivity().onBackPressed() }
|
||||
ToolbarContentTintHelper.colorizeToolbar(this, Color.WHITE, activity)
|
||||
}.setOnMenuItemClickListener(this)
|
||||
}
|
||||
|
||||
override fun onFavoriteToggled() {
|
||||
toggleFavorite(MusicPlayerRemote.currentSong)
|
||||
}
|
||||
|
||||
override fun onColorChanged(color: MediaNotificationProcessor) {
|
||||
playbackControlsFragment.setColor(color)
|
||||
lastColor = color.backgroundColor
|
||||
callbacks?.onPaletteColorChanged()
|
||||
ToolbarContentTintHelper.colorizeToolbar(playerToolbar, Color.WHITE, activity)
|
||||
}
|
||||
|
||||
override fun toggleFavorite(song: Song) {
|
||||
super.toggleFavorite(song)
|
||||
if (song.id == MusicPlayerRemote.currentSong.id) {
|
||||
updateIsFavorite()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onShow() {
|
||||
}
|
||||
|
||||
override fun onHide() {
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun toolbarIconColor(): Int {
|
||||
return Color.WHITE
|
||||
}
|
||||
|
||||
override val paletteColor: Int
|
||||
get() = lastColor
|
||||
|
||||
private fun updateBlur() {
|
||||
val blurAmount = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
.getInt(NEW_BLUR_AMOUNT, 25)
|
||||
colorBackground.clearColorFilter()
|
||||
SongGlideRequest.Builder.from(Glide.with(requireActivity()), MusicPlayerRemote.currentSong)
|
||||
.checkIgnoreMediaStore(requireContext())
|
||||
.generatePalette(requireContext()).build()
|
||||
.dontAnimate()
|
||||
.transform(
|
||||
BlurTransformation.Builder(requireContext())
|
||||
.blurRadius(blurAmount.toFloat())
|
||||
.build()
|
||||
)
|
||||
.into(object : RetroMusicColoredTarget(colorBackground) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
if (colors.backgroundColor == defaultFooterColor) {
|
||||
colorBackground.setColorFilter(colors.backgroundColor)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
updateIsFavorite()
|
||||
updateBlur()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
updateIsFavorite()
|
||||
updateBlur()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
.registerOnSharedPreferenceChangeListener(this)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
.unregisterOnSharedPreferenceChangeListener(this)
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||
if (key == NEW_BLUR_AMOUNT) {
|
||||
updateBlur()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
package io.github.muntashirakon.music.fragments.player.card
|
||||
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerFragment
|
||||
import io.github.muntashirakon.music.fragments.player.PlayerAlbumCoverFragment
|
||||
import io.github.muntashirakon.music.fragments.player.normal.PlayerFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_card_player.*
|
||||
|
||||
class CardFragment : AbsPlayerFragment() {
|
||||
override fun playerToolbar(): Toolbar {
|
||||
return playerToolbar
|
||||
}
|
||||
|
||||
private var lastColor: Int = 0
|
||||
override val paletteColor: Int
|
||||
get() = lastColor
|
||||
|
||||
private lateinit var playbackControlsFragment: CardPlaybackControlsFragment
|
||||
|
||||
override fun onShow() {
|
||||
playbackControlsFragment.show()
|
||||
}
|
||||
|
||||
override fun onHide() {
|
||||
playbackControlsFragment.hide()
|
||||
onBackPressed()
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun toolbarIconColor(): Int {
|
||||
return Color.WHITE
|
||||
}
|
||||
|
||||
override fun onColorChanged(color: MediaNotificationProcessor) {
|
||||
playbackControlsFragment.setColor(color)
|
||||
lastColor = color.primaryTextColor
|
||||
callbacks?.onPaletteColorChanged()
|
||||
ToolbarContentTintHelper.colorizeToolbar(playerToolbar, Color.WHITE, activity)
|
||||
}
|
||||
|
||||
override fun toggleFavorite(song: Song) {
|
||||
super.toggleFavorite(song)
|
||||
if (song.id == MusicPlayerRemote.currentSong.id) {
|
||||
updateIsFavorite()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFavoriteToggled() {
|
||||
toggleFavorite(MusicPlayerRemote.currentSong)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
|
||||
return inflater.inflate(R.layout.fragment_card_player, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpSubFragments()
|
||||
setUpPlayerToolbar()
|
||||
}
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
playbackControlsFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as CardPlaybackControlsFragment
|
||||
val playerAlbumCoverFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment
|
||||
playerAlbumCoverFragment.setCallbacks(this)
|
||||
playerAlbumCoverFragment.removeSlideEffect()
|
||||
}
|
||||
|
||||
private fun setUpPlayerToolbar() {
|
||||
playerToolbar.inflateMenu(R.menu.menu_player)
|
||||
playerToolbar.setNavigationOnClickListener { requireActivity().onBackPressed() }
|
||||
playerToolbar.setOnMenuItemClickListener(this)
|
||||
|
||||
ToolbarContentTintHelper.colorizeToolbar(playerToolbar, Color.WHITE, activity)
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
updateIsFavorite()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
updateIsFavorite()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun newInstance(): PlayerFragment {
|
||||
val args = Bundle()
|
||||
val fragment = PlayerFragment()
|
||||
fragment.arguments = args
|
||||
return fragment
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,263 @@
|
|||
package io.github.muntashirakon.music.fragments.player.card
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.graphics.PorterDuff
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.SeekBar
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.extensions.hide
|
||||
import io.github.muntashirakon.music.extensions.ripAlpha
|
||||
import io.github.muntashirakon.music.extensions.show
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerControlsFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper
|
||||
import io.github.muntashirakon.music.helper.PlayPauseButtonOnClickHandler
|
||||
import io.github.muntashirakon.music.misc.SimpleOnSeekbarChangeListener
|
||||
import io.github.muntashirakon.music.service.MusicService
|
||||
import io.github.muntashirakon.music.util.MusicUtil
|
||||
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_card_player_playback_controls.*
|
||||
import kotlinx.android.synthetic.main.media_button.*
|
||||
|
||||
class CardPlaybackControlsFragment : AbsPlayerControlsFragment() {
|
||||
|
||||
private var lastPlaybackControlsColor: Int = 0
|
||||
private var lastDisabledPlaybackControlsColor: Int = 0
|
||||
private var progressViewUpdateHelper: MusicProgressViewUpdateHelper? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_card_player_playback_controls, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpMusicControllers()
|
||||
|
||||
playPauseButton.setOnClickListener {
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
MusicPlayerRemote.pauseSong()
|
||||
} else {
|
||||
MusicPlayerRemote.resumePlaying()
|
||||
}
|
||||
showBonceAnimation(playPauseButton)
|
||||
}
|
||||
title.isSelected = true
|
||||
text.isSelected = true
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
title.text = song.title
|
||||
text.text = song.artistName
|
||||
|
||||
if (PreferenceUtil.isSongInfo) {
|
||||
songInfo.text = getSongInfo(MusicPlayerRemote.currentSong)
|
||||
songInfo.show()
|
||||
} else {
|
||||
songInfo.hide()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
progressViewUpdateHelper!!.start()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
progressViewUpdateHelper!!.stop()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
updatePlayPauseDrawableState()
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onPlayStateChanged() {
|
||||
updatePlayPauseDrawableState()
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged() {
|
||||
updateRepeatState()
|
||||
}
|
||||
|
||||
override fun onShuffleModeChanged() {
|
||||
updateShuffleState()
|
||||
}
|
||||
|
||||
override fun setColor(color: MediaNotificationProcessor) {
|
||||
if (!ATHUtil.isWindowBackgroundDark(requireContext())
|
||||
) {
|
||||
lastPlaybackControlsColor = MaterialValueHelper.getSecondaryTextColor(activity, true)
|
||||
lastDisabledPlaybackControlsColor =
|
||||
MaterialValueHelper.getSecondaryDisabledTextColor(activity, true)
|
||||
} else {
|
||||
lastPlaybackControlsColor = MaterialValueHelper.getPrimaryTextColor(activity, false)
|
||||
lastDisabledPlaybackControlsColor =
|
||||
MaterialValueHelper.getPrimaryDisabledTextColor(activity, false)
|
||||
}
|
||||
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updatePrevNextColor()
|
||||
updatePlayPauseColor()
|
||||
updateProgressTextColor()
|
||||
|
||||
val colorFinal = if (PreferenceUtil.isAdaptiveColor) {
|
||||
color.primaryTextColor
|
||||
} else {
|
||||
ThemeStore.accentColor(requireContext()).ripAlpha()
|
||||
}
|
||||
image.setColorFilter(colorFinal, PorterDuff.Mode.SRC_IN)
|
||||
TintHelper.setTintAuto(
|
||||
playPauseButton,
|
||||
MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(colorFinal)),
|
||||
false
|
||||
)
|
||||
TintHelper.setTintAuto(playPauseButton, colorFinal, true)
|
||||
|
||||
volumeFragment?.setTintable(colorFinal)
|
||||
}
|
||||
|
||||
private fun updatePlayPauseColor() {
|
||||
//playPauseButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN);
|
||||
}
|
||||
|
||||
private fun setUpPlayPauseFab() {
|
||||
playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
||||
}
|
||||
|
||||
private fun updatePlayPauseDrawableState() {
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp)
|
||||
} else {
|
||||
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_32dp)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpMusicControllers() {
|
||||
setUpPlayPauseFab()
|
||||
setUpPrevNext()
|
||||
setUpRepeatButton()
|
||||
setUpShuffleButton()
|
||||
setUpProgressSlider()
|
||||
}
|
||||
|
||||
private fun setUpPrevNext() {
|
||||
updatePrevNextColor()
|
||||
nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
|
||||
previousButton.setOnClickListener { MusicPlayerRemote.back() }
|
||||
}
|
||||
|
||||
private fun updatePrevNextColor() {
|
||||
nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
|
||||
private fun setUpShuffleButton() {
|
||||
shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
|
||||
}
|
||||
|
||||
override fun updateShuffleState() {
|
||||
when (MusicPlayerRemote.shuffleMode) {
|
||||
MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton.setColorFilter(
|
||||
lastPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
else -> shuffleButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpRepeatButton() {
|
||||
repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
|
||||
}
|
||||
|
||||
override fun updateRepeatState() {
|
||||
when (MusicPlayerRemote.repeatMode) {
|
||||
MusicService.REPEAT_MODE_NONE -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
MusicService.REPEAT_MODE_ALL -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
MusicService.REPEAT_MODE_THIS -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
progressSlider.max = total
|
||||
|
||||
val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress)
|
||||
animator.duration = SLIDER_ANIMATION_TIME
|
||||
animator.interpolator = LinearInterpolator()
|
||||
animator.start()
|
||||
|
||||
songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
|
||||
songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
|
||||
}
|
||||
|
||||
private fun updateProgressTextColor() {
|
||||
val color = MaterialValueHelper.getPrimaryTextColor(context, false)
|
||||
songTotalTime!!.setTextColor(color)
|
||||
songCurrentProgress!!.setTextColor(color)
|
||||
}
|
||||
|
||||
public override fun show() {
|
||||
//Ignore
|
||||
}
|
||||
|
||||
public override fun hide() {
|
||||
//Ignore
|
||||
}
|
||||
|
||||
override fun setUpProgressSlider() {
|
||||
progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
MusicPlayerRemote.seekTo(progress)
|
||||
onUpdateProgressViews(
|
||||
MusicPlayerRemote.songProgressMillis,
|
||||
MusicPlayerRemote.songDurationMillis
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
package io.github.muntashirakon.music.fragments.player.cardblur
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.preference.PreferenceManager
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import io.github.muntashirakon.music.NEW_BLUR_AMOUNT
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerFragment
|
||||
import io.github.muntashirakon.music.fragments.player.PlayerAlbumCoverFragment
|
||||
import io.github.muntashirakon.music.fragments.player.normal.PlayerFragment
|
||||
import io.github.muntashirakon.music.glide.BlurTransformation
|
||||
import io.github.muntashirakon.music.glide.RetroMusicColoredTarget
|
||||
import io.github.muntashirakon.music.glide.SongGlideRequest
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import com.bumptech.glide.Glide
|
||||
import kotlinx.android.synthetic.main.fragment_card_blur_player.*
|
||||
|
||||
class CardBlurFragment : AbsPlayerFragment(), SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
override fun playerToolbar(): Toolbar {
|
||||
return playerToolbar
|
||||
}
|
||||
|
||||
private var lastColor: Int = 0
|
||||
override val paletteColor: Int
|
||||
get() = lastColor
|
||||
private lateinit var playbackControlsFragment: CardBlurPlaybackControlsFragment
|
||||
|
||||
override fun onShow() {
|
||||
playbackControlsFragment.show()
|
||||
}
|
||||
|
||||
override fun onHide() {
|
||||
playbackControlsFragment.hide()
|
||||
onBackPressed()
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun toolbarIconColor(): Int {
|
||||
return Color.WHITE
|
||||
}
|
||||
|
||||
override fun onColorChanged(color: MediaNotificationProcessor) {
|
||||
playbackControlsFragment.setColor(color)
|
||||
lastColor = color.backgroundColor
|
||||
callbacks!!.onPaletteColorChanged()
|
||||
ToolbarContentTintHelper.colorizeToolbar(playerToolbar, Color.WHITE, activity)
|
||||
|
||||
playerToolbar.setTitleTextColor(Color.WHITE)
|
||||
playerToolbar.setSubtitleTextColor(Color.WHITE)
|
||||
}
|
||||
|
||||
override fun toggleFavorite(song: Song) {
|
||||
super.toggleFavorite(song)
|
||||
if (song.id == MusicPlayerRemote.currentSong.id) {
|
||||
updateIsFavorite()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFavoriteToggled() {
|
||||
toggleFavorite(MusicPlayerRemote.currentSong)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
|
||||
return inflater.inflate(R.layout.fragment_card_blur_player, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpSubFragments()
|
||||
setUpPlayerToolbar()
|
||||
}
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
playbackControlsFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as CardBlurPlaybackControlsFragment
|
||||
(childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment?)?.setCallbacks(
|
||||
this
|
||||
)
|
||||
}
|
||||
|
||||
private fun setUpPlayerToolbar() {
|
||||
playerToolbar.apply {
|
||||
inflateMenu(R.menu.menu_player)
|
||||
setNavigationOnClickListener { requireActivity().onBackPressed() }
|
||||
setTitleTextColor(Color.WHITE)
|
||||
setSubtitleTextColor(Color.WHITE)
|
||||
ToolbarContentTintHelper.colorizeToolbar(playerToolbar, Color.WHITE, activity)
|
||||
}.setOnMenuItemClickListener(this)
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
updateIsFavorite()
|
||||
updateBlur()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
updateIsFavorite()
|
||||
updateBlur()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
playerToolbar.apply {
|
||||
title = song.title
|
||||
subtitle = song.artistName
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateBlur() {
|
||||
val blurAmount = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
.getInt(NEW_BLUR_AMOUNT, 25)
|
||||
colorBackground!!.clearColorFilter()
|
||||
SongGlideRequest.Builder.from(Glide.with(requireActivity()), MusicPlayerRemote.currentSong)
|
||||
.checkIgnoreMediaStore(requireContext())
|
||||
.generatePalette(requireContext()).build()
|
||||
.dontAnimate()
|
||||
.transform(
|
||||
BlurTransformation.Builder(requireContext()).blurRadius(blurAmount.toFloat())
|
||||
.build()
|
||||
)
|
||||
.into(object : RetroMusicColoredTarget(colorBackground) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
if (colors.backgroundColor == defaultFooterColor) {
|
||||
colorBackground.setColorFilter(colors.backgroundColor)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
.registerOnSharedPreferenceChangeListener(this)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
.unregisterOnSharedPreferenceChangeListener(this)
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||
if (key == NEW_BLUR_AMOUNT) {
|
||||
updateBlur()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun newInstance(): PlayerFragment {
|
||||
return PlayerFragment()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,242 @@
|
|||
package io.github.muntashirakon.music.fragments.player.cardblur
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.graphics.Color
|
||||
import android.graphics.PorterDuff
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.SeekBar
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.extensions.applyColor
|
||||
import io.github.muntashirakon.music.extensions.hide
|
||||
import io.github.muntashirakon.music.extensions.show
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerControlsFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper
|
||||
import io.github.muntashirakon.music.helper.PlayPauseButtonOnClickHandler
|
||||
import io.github.muntashirakon.music.misc.SimpleOnSeekbarChangeListener
|
||||
import io.github.muntashirakon.music.service.MusicService
|
||||
import io.github.muntashirakon.music.util.MusicUtil
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_card_blur_player_playback_controls.*
|
||||
import kotlinx.android.synthetic.main.media_button.*
|
||||
|
||||
class CardBlurPlaybackControlsFragment : AbsPlayerControlsFragment() {
|
||||
|
||||
private var lastPlaybackControlsColor: Int = 0
|
||||
private var lastDisabledPlaybackControlsColor: Int = 0
|
||||
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
|
||||
return inflater.inflate(
|
||||
R.layout.fragment_card_blur_player_playback_controls,
|
||||
container,
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpMusicControllers()
|
||||
}
|
||||
|
||||
override fun setColor(color: MediaNotificationProcessor) {
|
||||
lastPlaybackControlsColor = Color.WHITE
|
||||
lastDisabledPlaybackControlsColor = ColorUtil.withAlpha(Color.WHITE, 0.3f)
|
||||
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updatePrevNextColor()
|
||||
updateProgressTextColor()
|
||||
|
||||
volumeFragment?.tintWhiteColor()
|
||||
}
|
||||
|
||||
private fun setUpPlayPauseFab() {
|
||||
playPauseButton.apply {
|
||||
TintHelper.setTintAuto(this, Color.WHITE, true)
|
||||
TintHelper.setTintAuto(this, Color.BLACK, false)
|
||||
setOnClickListener(PlayPauseButtonOnClickHandler())
|
||||
}
|
||||
}
|
||||
|
||||
private fun updatePlayPauseDrawableState() {
|
||||
when {
|
||||
MusicPlayerRemote.isPlaying -> playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp)
|
||||
else -> playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_32dp)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateProgressTextColor() {
|
||||
val color = Color.WHITE
|
||||
songTotalTime.setTextColor(color)
|
||||
songCurrentProgress.setTextColor(color)
|
||||
songInfo.setTextColor(color)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
progressViewUpdateHelper.start()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
progressViewUpdateHelper.stop()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
updatePlayPauseDrawableState()
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
if (PreferenceUtil.isSongInfo) {
|
||||
songInfo.text = getSongInfo(MusicPlayerRemote.currentSong)
|
||||
songInfo.show()
|
||||
} else {
|
||||
songInfo.hide()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPlayStateChanged() {
|
||||
updatePlayPauseDrawableState()
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged() {
|
||||
updateRepeatState()
|
||||
}
|
||||
|
||||
override fun onShuffleModeChanged() {
|
||||
updateShuffleState()
|
||||
}
|
||||
|
||||
private fun setUpMusicControllers() {
|
||||
setUpPlayPauseFab()
|
||||
setUpPrevNext()
|
||||
setUpRepeatButton()
|
||||
setUpShuffleButton()
|
||||
setUpProgressSlider()
|
||||
}
|
||||
|
||||
private fun setUpPrevNext() {
|
||||
updatePrevNextColor()
|
||||
nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
|
||||
previousButton.setOnClickListener { MusicPlayerRemote.back() }
|
||||
}
|
||||
|
||||
private fun updatePrevNextColor() {
|
||||
nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
|
||||
private fun setUpShuffleButton() {
|
||||
shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
|
||||
}
|
||||
|
||||
override fun updateShuffleState() {
|
||||
when (MusicPlayerRemote.shuffleMode) {
|
||||
MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton.setColorFilter(
|
||||
lastPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
else -> shuffleButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpRepeatButton() {
|
||||
repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
|
||||
}
|
||||
|
||||
override fun updateRepeatState() {
|
||||
when (MusicPlayerRemote.repeatMode) {
|
||||
MusicService.REPEAT_MODE_NONE -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
MusicService.REPEAT_MODE_ALL -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
MusicService.REPEAT_MODE_THIS -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override fun show() {
|
||||
playPauseButton!!.animate()
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.rotation(360f)
|
||||
.setInterpolator(DecelerateInterpolator())
|
||||
.start()
|
||||
}
|
||||
|
||||
public override fun hide() {
|
||||
if (playPauseButton != null) {
|
||||
playPauseButton!!.apply {
|
||||
scaleX = 0f
|
||||
scaleY = 0f
|
||||
rotation = 0f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setUpProgressSlider() {
|
||||
progressSlider.applyColor(Color.WHITE)
|
||||
progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
MusicPlayerRemote.seekTo(progress)
|
||||
onUpdateProgressViews(
|
||||
MusicPlayerRemote.songProgressMillis,
|
||||
MusicPlayerRemote.songDurationMillis
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
progressSlider.max = total
|
||||
|
||||
val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress)
|
||||
animator.duration = SLIDER_ANIMATION_TIME
|
||||
animator.interpolator = LinearInterpolator()
|
||||
animator.start()
|
||||
|
||||
songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
|
||||
songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,266 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package io.github.muntashirakon.music.fragments.player.circle
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.PorterDuff
|
||||
import android.media.AudioManager
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.SeekBar
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.extensions.accentColor
|
||||
import io.github.muntashirakon.music.extensions.applyColor
|
||||
import io.github.muntashirakon.music.extensions.hide
|
||||
import io.github.muntashirakon.music.extensions.show
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerControlsFragment
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper
|
||||
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper.Callback
|
||||
import io.github.muntashirakon.music.helper.PlayPauseButtonOnClickHandler
|
||||
import io.github.muntashirakon.music.misc.SimpleOnSeekbarChangeListener
|
||||
import io.github.muntashirakon.music.util.MusicUtil
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
|
||||
import io.github.muntashirakon.music.util.ViewUtil
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import io.github.muntashirakon.music.views.SeekArc
|
||||
import io.github.muntashirakon.music.views.SeekArc.OnSeekArcChangeListener
|
||||
import io.github.muntashirakon.music.volume.AudioVolumeObserver
|
||||
import io.github.muntashirakon.music.volume.OnAudioVolumeChangedListener
|
||||
import kotlinx.android.synthetic.main.fragment_circle_player.*
|
||||
|
||||
/**
|
||||
* Created by hemanths on 2020-01-06.
|
||||
*/
|
||||
|
||||
class CirclePlayerFragment : AbsPlayerFragment(), Callback, OnAudioVolumeChangedListener,
|
||||
OnSeekArcChangeListener {
|
||||
|
||||
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
||||
private var audioVolumeObserver: AudioVolumeObserver? = null
|
||||
|
||||
private val audioManager: AudioManager?
|
||||
get() = requireContext().getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_circle_player, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setupViews()
|
||||
title.isSelected = true
|
||||
}
|
||||
|
||||
private fun setUpPlayerToolbar() {
|
||||
playerToolbar.apply {
|
||||
inflateMenu(R.menu.menu_player)
|
||||
setNavigationOnClickListener { requireActivity().onBackPressed() }
|
||||
setOnMenuItemClickListener(this@CirclePlayerFragment)
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
this,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupViews() {
|
||||
setUpProgressSlider()
|
||||
ViewUtil.setProgressDrawable(
|
||||
progressSlider,
|
||||
ThemeStore.accentColor(requireContext()),
|
||||
false
|
||||
)
|
||||
volumeSeekBar.progressColor = accentColor()
|
||||
volumeSeekBar.arcColor = ColorUtil.withAlpha(accentColor(), 0.25f)
|
||||
setUpPlayPauseFab()
|
||||
setUpPrevNext()
|
||||
setUpPlayerToolbar()
|
||||
}
|
||||
|
||||
private fun setUpPrevNext() {
|
||||
updatePrevNextColor()
|
||||
nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
|
||||
previousButton.setOnClickListener { MusicPlayerRemote.back() }
|
||||
}
|
||||
|
||||
private fun updatePrevNextColor() {
|
||||
val accentColor = ThemeStore.accentColor(requireContext())
|
||||
nextButton.setColorFilter(accentColor, PorterDuff.Mode.SRC_IN)
|
||||
previousButton.setColorFilter(accentColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
|
||||
private fun setUpPlayPauseFab() {
|
||||
TintHelper.setTintAuto(playPauseButton, ThemeStore.accentColor(requireContext()), false)
|
||||
playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
progressViewUpdateHelper.start()
|
||||
if (audioVolumeObserver == null) {
|
||||
audioVolumeObserver = AudioVolumeObserver(requireActivity())
|
||||
}
|
||||
audioVolumeObserver?.register(AudioManager.STREAM_MUSIC, this)
|
||||
|
||||
val audioManager = audioManager
|
||||
if (audioManager != null) {
|
||||
volumeSeekBar.max = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
|
||||
volumeSeekBar.progress = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
|
||||
}
|
||||
volumeSeekBar.setOnSeekArcChangeListener(this)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
progressViewUpdateHelper.stop()
|
||||
}
|
||||
|
||||
override fun playerToolbar(): Toolbar? {
|
||||
return playerToolbar
|
||||
}
|
||||
|
||||
override fun onShow() {
|
||||
}
|
||||
|
||||
override fun onHide() {
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean = false
|
||||
|
||||
override fun toolbarIconColor(): Int =
|
||||
ATHUtil.resolveColor(requireContext(), android.R.attr.colorControlNormal)
|
||||
|
||||
override val paletteColor: Int
|
||||
get() = Color.BLACK
|
||||
|
||||
override fun onColorChanged(color: MediaNotificationProcessor) {
|
||||
|
||||
}
|
||||
|
||||
override fun onFavoriteToggled() {
|
||||
}
|
||||
|
||||
override fun onPlayStateChanged() {
|
||||
updatePlayPauseDrawableState()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
updateSong()
|
||||
updatePlayPauseDrawableState()
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
title.text = song.title
|
||||
text.text = song.artistName
|
||||
|
||||
if (PreferenceUtil.isSongInfo) {
|
||||
songInfo.text = getSongInfo(song)
|
||||
songInfo.show()
|
||||
} else {
|
||||
songInfo.hide()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updatePlayPauseDrawableState() {
|
||||
when {
|
||||
MusicPlayerRemote.isPlaying -> playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp)
|
||||
else -> playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_24dp)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAudioVolumeChanged(currentVolume: Int, maxVolume: Int) {
|
||||
if (volumeSeekBar == null) {
|
||||
return
|
||||
}
|
||||
volumeSeekBar.max = maxVolume
|
||||
volumeSeekBar.progress = currentVolume
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
if (audioVolumeObserver != null) {
|
||||
audioVolumeObserver!!.unregister()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onProgressChanged(seekArc: SeekArc?, progress: Int, fromUser: Boolean) {
|
||||
val audioManager = audioManager
|
||||
audioManager?.setStreamVolume(AudioManager.STREAM_MUSIC, progress, 0)
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekArc: SeekArc?) {
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(seekArc: SeekArc?) {
|
||||
}
|
||||
|
||||
fun setUpProgressSlider() {
|
||||
progressSlider.applyColor(accentColor())
|
||||
progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
MusicPlayerRemote.seekTo(progress)
|
||||
onUpdateProgressViews(
|
||||
MusicPlayerRemote.songProgressMillis,
|
||||
MusicPlayerRemote.songDurationMillis
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
progressSlider.max = total
|
||||
|
||||
val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress)
|
||||
animator.duration = AbsPlayerControlsFragment.SLIDER_ANIMATION_TIME
|
||||
animator.interpolator = LinearInterpolator()
|
||||
animator.start()
|
||||
|
||||
songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
|
||||
songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,504 @@
|
|||
package io.github.muntashirakon.music.fragments.player.classic
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.graphics.PorterDuff
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.SeekBar
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.RetroBottomSheetBehavior
|
||||
import io.github.muntashirakon.music.activities.base.AbsSlidingMusicPanelActivity
|
||||
import io.github.muntashirakon.music.adapter.song.PlayingQueueAdapter
|
||||
import io.github.muntashirakon.music.extensions.hide
|
||||
import io.github.muntashirakon.music.extensions.show
|
||||
import io.github.muntashirakon.music.fragments.VolumeFragment
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerControlsFragment
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerFragment
|
||||
import io.github.muntashirakon.music.fragments.player.PlayerAlbumCoverFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper
|
||||
import io.github.muntashirakon.music.helper.PlayPauseButtonOnClickHandler
|
||||
import io.github.muntashirakon.music.misc.SimpleOnSeekbarChangeListener
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
import io.github.muntashirakon.music.service.MusicService
|
||||
import io.github.muntashirakon.music.util.MusicUtil
|
||||
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
import io.github.muntashirakon.music.util.ViewUtil
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import com.google.android.material.shape.ShapeAppearanceModel
|
||||
import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator
|
||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager
|
||||
import com.h6ah4i.android.widget.advrecyclerview.swipeable.RecyclerViewSwipeManager
|
||||
import com.h6ah4i.android.widget.advrecyclerview.touchguard.RecyclerViewTouchActionGuardManager
|
||||
import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
|
||||
import kotlinx.android.synthetic.main.fragment_classic_controls.*
|
||||
import kotlinx.android.synthetic.main.fragment_classic_player.*
|
||||
|
||||
class ClassicPlayerFragment : AbsPlayerFragment(), View.OnLayoutChangeListener,
|
||||
MusicProgressViewUpdateHelper.Callback {
|
||||
|
||||
private var lastColor: Int = 0
|
||||
private var lastPlaybackControlsColor: Int = 0
|
||||
private var lastDisabledPlaybackControlsColor: Int = 0
|
||||
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
||||
private var volumeFragment: VolumeFragment? = null
|
||||
private lateinit var shapeDrawable: MaterialShapeDrawable
|
||||
private lateinit var wrappedAdapter: RecyclerView.Adapter<*>
|
||||
private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null
|
||||
private var recyclerViewSwipeManager: RecyclerViewSwipeManager? = null
|
||||
private var recyclerViewTouchActionGuardManager: RecyclerViewTouchActionGuardManager? = null
|
||||
private var playingQueueAdapter: PlayingQueueAdapter? = null
|
||||
private lateinit var linearLayoutManager: LinearLayoutManager
|
||||
|
||||
private val bottomSheetCallbackList = object : BottomSheetBehavior.BottomSheetCallback() {
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {
|
||||
(requireActivity() as AbsSlidingMusicPanelActivity).getBottomSheetBehavior()
|
||||
.setAllowDragging(false)
|
||||
|
||||
playerQueueSheet.setContentPadding(
|
||||
playerQueueSheet.contentPaddingLeft,
|
||||
(slideOffset * status_bar.height).toInt(),
|
||||
playerQueueSheet.contentPaddingRight,
|
||||
playerQueueSheet.contentPaddingBottom
|
||||
)
|
||||
|
||||
shapeDrawable.interpolation = 1 - slideOffset
|
||||
}
|
||||
|
||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||
val activity = requireActivity() as AbsSlidingMusicPanelActivity
|
||||
when (newState) {
|
||||
BottomSheetBehavior.STATE_EXPANDED,
|
||||
BottomSheetBehavior.STATE_DRAGGING -> {
|
||||
activity.getBottomSheetBehavior().setAllowDragging(false)
|
||||
}
|
||||
BottomSheetBehavior.STATE_COLLAPSED -> {
|
||||
resetToCurrentPosition()
|
||||
activity.getBottomSheetBehavior().setAllowDragging(true)
|
||||
}
|
||||
else -> {
|
||||
activity.getBottomSheetBehavior().setAllowDragging(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_classic_player, container, false)
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setupPanel()
|
||||
setUpMusicControllers()
|
||||
setUpPlayerToolbar()
|
||||
hideVolumeIfAvailable()
|
||||
setupRecyclerView()
|
||||
|
||||
val coverFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment
|
||||
coverFragment.setCallbacks(this)
|
||||
|
||||
getQueuePanel().addBottomSheetCallback(bottomSheetCallbackList)
|
||||
|
||||
shapeDrawable = MaterialShapeDrawable(
|
||||
ShapeAppearanceModel.builder(
|
||||
requireContext(),
|
||||
R.style.ClassicThemeOverLay,
|
||||
0
|
||||
).build()
|
||||
)
|
||||
shapeDrawable.fillColor =
|
||||
ColorStateList.valueOf(ATHUtil.resolveColor(requireContext(), R.attr.colorSurface))
|
||||
playerQueueSheet.background = shapeDrawable
|
||||
|
||||
playerQueueSheet.setOnTouchListener { _, _ ->
|
||||
(requireActivity() as AbsSlidingMusicPanelActivity).getBottomSheetBehavior()
|
||||
.setAllowDragging(false)
|
||||
getQueuePanel().setAllowDragging(true)
|
||||
return@setOnTouchListener false
|
||||
}
|
||||
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
playerToolbar,
|
||||
Color.WHITE,
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
||||
private fun hideVolumeIfAvailable() {
|
||||
if (PreferenceUtil.isVolumeVisibilityMode) {
|
||||
childFragmentManager.beginTransaction()
|
||||
.replace(R.id.volumeFragmentContainer, VolumeFragment.newInstance())
|
||||
.commit()
|
||||
childFragmentManager.executePendingTransactions()
|
||||
volumeFragment =
|
||||
childFragmentManager.findFragmentById(R.id.volumeFragmentContainer) as VolumeFragment?
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
getQueuePanel().removeBottomSheetCallback(bottomSheetCallbackList)
|
||||
if (recyclerViewDragDropManager != null) {
|
||||
recyclerViewDragDropManager?.release()
|
||||
recyclerViewDragDropManager = null
|
||||
}
|
||||
|
||||
if (recyclerViewSwipeManager != null) {
|
||||
recyclerViewSwipeManager?.release()
|
||||
recyclerViewSwipeManager = null
|
||||
}
|
||||
|
||||
WrapperAdapterUtils.releaseAll(wrappedAdapter)
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
title.text = song.title
|
||||
text.text = song.artistName
|
||||
|
||||
if (PreferenceUtil.isSongInfo) {
|
||||
songInfo.text = getSongInfo(song)
|
||||
songInfo.show()
|
||||
} else {
|
||||
songInfo.hide()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
progressViewUpdateHelper.start()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
recyclerViewDragDropManager?.cancelDrag()
|
||||
super.onPause()
|
||||
progressViewUpdateHelper.stop()
|
||||
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
updateSong()
|
||||
updatePlayPauseDrawableState()
|
||||
updateQueue()
|
||||
}
|
||||
|
||||
override fun onPlayStateChanged() {
|
||||
updatePlayPauseDrawableState()
|
||||
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged() {
|
||||
updateRepeatState()
|
||||
}
|
||||
|
||||
override fun onShuffleModeChanged() {
|
||||
updateShuffleState()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateSong()
|
||||
updateQueuePosition()
|
||||
}
|
||||
|
||||
|
||||
override fun onQueueChanged() {
|
||||
super.onQueueChanged()
|
||||
updateQueue()
|
||||
}
|
||||
|
||||
|
||||
override fun playerToolbar(): Toolbar? {
|
||||
return playerToolbar
|
||||
}
|
||||
|
||||
override fun onShow() {
|
||||
|
||||
}
|
||||
|
||||
override fun onHide() {
|
||||
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
var wasExpanded = false
|
||||
if (getQueuePanel().state == BottomSheetBehavior.STATE_EXPANDED) {
|
||||
wasExpanded = getQueuePanel().state == BottomSheetBehavior.STATE_EXPANDED
|
||||
getQueuePanel().state = BottomSheetBehavior.STATE_COLLAPSED
|
||||
return wasExpanded
|
||||
}
|
||||
return wasExpanded
|
||||
}
|
||||
|
||||
override fun toolbarIconColor(): Int {
|
||||
return Color.WHITE
|
||||
}
|
||||
|
||||
override val paletteColor: Int
|
||||
get() = lastColor
|
||||
|
||||
override fun onColorChanged(color: MediaNotificationProcessor) {
|
||||
lastColor = color.backgroundColor
|
||||
callbacks?.onPaletteColorChanged()
|
||||
|
||||
lastPlaybackControlsColor = color.primaryTextColor
|
||||
lastDisabledPlaybackControlsColor = ColorUtil.withAlpha(color.primaryTextColor, 0.3f)
|
||||
|
||||
playerContainer.setBackgroundColor(color.backgroundColor)
|
||||
songInfo.setTextColor(color.primaryTextColor)
|
||||
player_queue_sub_header.setTextColor(color.primaryTextColor)
|
||||
|
||||
songCurrentProgress.setTextColor(lastPlaybackControlsColor)
|
||||
songTotalTime.setTextColor(lastPlaybackControlsColor)
|
||||
|
||||
ViewUtil.setProgressDrawable(progressSlider, color.primaryTextColor, true)
|
||||
volumeFragment?.setTintableColor(color.primaryTextColor)
|
||||
|
||||
TintHelper.setTintAuto(playPauseButton, color.primaryTextColor, true)
|
||||
TintHelper.setTintAuto(playPauseButton, color.backgroundColor, false)
|
||||
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updatePrevNextColor()
|
||||
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
playerToolbar,
|
||||
Color.WHITE,
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
||||
override fun toggleFavorite(song: Song) {
|
||||
super.toggleFavorite(song)
|
||||
if (song.id == MusicPlayerRemote.currentSong.id) {
|
||||
updateIsFavorite()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFavoriteToggled() {
|
||||
toggleFavorite(MusicPlayerRemote.currentSong)
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
progressSlider.max = total
|
||||
|
||||
val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress)
|
||||
animator.duration = AbsPlayerControlsFragment.SLIDER_ANIMATION_TIME
|
||||
animator.interpolator = LinearInterpolator()
|
||||
animator.start()
|
||||
|
||||
songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
|
||||
songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
|
||||
}
|
||||
|
||||
private fun updateQueuePosition() {
|
||||
playingQueueAdapter?.setCurrent(MusicPlayerRemote.position)
|
||||
resetToCurrentPosition()
|
||||
}
|
||||
|
||||
private fun updateQueue() {
|
||||
playingQueueAdapter?.swapDataSet(MusicPlayerRemote.playingQueue, MusicPlayerRemote.position)
|
||||
resetToCurrentPosition()
|
||||
}
|
||||
|
||||
private fun resetToCurrentPosition() {
|
||||
recyclerView.stopScroll()
|
||||
linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
|
||||
}
|
||||
|
||||
private fun getQueuePanel(): RetroBottomSheetBehavior<MaterialCardView> {
|
||||
return RetroBottomSheetBehavior.from(playerQueueSheet) as RetroBottomSheetBehavior<MaterialCardView>
|
||||
}
|
||||
|
||||
private fun setupPanel() {
|
||||
if (!ViewCompat.isLaidOut(playerContainer) || playerContainer.isLayoutRequested) {
|
||||
playerContainer.addOnLayoutChangeListener(this)
|
||||
return
|
||||
}
|
||||
val height = playerContainer.height
|
||||
val width = playerContainer.width
|
||||
val finalHeight = height - width
|
||||
val panel = getQueuePanel()
|
||||
panel.peekHeight = finalHeight
|
||||
}
|
||||
|
||||
private fun setUpPlayerToolbar() {
|
||||
playerToolbar.inflateMenu(R.menu.menu_player)
|
||||
playerToolbar.setNavigationOnClickListener { requireActivity().onBackPressed() }
|
||||
playerToolbar.setOnMenuItemClickListener(this)
|
||||
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
playerToolbar,
|
||||
Color.WHITE,
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
playingQueueAdapter = PlayingQueueAdapter(
|
||||
requireActivity() as AppCompatActivity,
|
||||
MusicPlayerRemote.playingQueue.toMutableList(),
|
||||
MusicPlayerRemote.position,
|
||||
R.layout.item_queue
|
||||
)
|
||||
linearLayoutManager = LinearLayoutManager(requireContext())
|
||||
recyclerViewTouchActionGuardManager = RecyclerViewTouchActionGuardManager()
|
||||
recyclerViewDragDropManager = RecyclerViewDragDropManager()
|
||||
recyclerViewSwipeManager = RecyclerViewSwipeManager()
|
||||
|
||||
val animator = DraggableItemAnimator()
|
||||
animator.supportsChangeAnimations = false
|
||||
wrappedAdapter =
|
||||
recyclerViewDragDropManager?.createWrappedAdapter(playingQueueAdapter!!) as RecyclerView.Adapter<*>
|
||||
wrappedAdapter =
|
||||
recyclerViewSwipeManager?.createWrappedAdapter(wrappedAdapter) as RecyclerView.Adapter<*>
|
||||
recyclerView.layoutManager = linearLayoutManager
|
||||
recyclerView.adapter = wrappedAdapter
|
||||
recyclerView.itemAnimator = animator
|
||||
recyclerViewTouchActionGuardManager?.attachRecyclerView(recyclerView)
|
||||
recyclerViewDragDropManager?.attachRecyclerView(recyclerView)
|
||||
recyclerViewSwipeManager?.attachRecyclerView(recyclerView)
|
||||
|
||||
linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
|
||||
}
|
||||
|
||||
fun setUpProgressSlider() {
|
||||
progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
MusicPlayerRemote.seekTo(progress)
|
||||
onUpdateProgressViews(
|
||||
MusicPlayerRemote.songProgressMillis,
|
||||
MusicPlayerRemote.songDurationMillis
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun setUpPlayPauseFab() {
|
||||
playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
||||
}
|
||||
|
||||
private fun updatePlayPauseDrawableState() {
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp)
|
||||
} else {
|
||||
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_24dp)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpMusicControllers() {
|
||||
setUpPlayPauseFab()
|
||||
setUpPrevNext()
|
||||
setUpRepeatButton()
|
||||
setUpShuffleButton()
|
||||
setUpProgressSlider()
|
||||
}
|
||||
|
||||
|
||||
private fun setUpPrevNext() {
|
||||
updatePrevNextColor()
|
||||
nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
|
||||
previousButton.setOnClickListener { MusicPlayerRemote.back() }
|
||||
}
|
||||
|
||||
private fun updatePrevNextColor() {
|
||||
nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
|
||||
private fun setUpShuffleButton() {
|
||||
shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
|
||||
}
|
||||
|
||||
fun updateShuffleState() {
|
||||
when (MusicPlayerRemote.shuffleMode) {
|
||||
MusicService.SHUFFLE_MODE_SHUFFLE ->
|
||||
shuffleButton.setColorFilter(
|
||||
lastPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
else -> shuffleButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpRepeatButton() {
|
||||
repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
|
||||
}
|
||||
|
||||
fun updateRepeatState() {
|
||||
when (MusicPlayerRemote.repeatMode) {
|
||||
MusicService.REPEAT_MODE_NONE -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
MusicService.REPEAT_MODE_ALL -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
MusicService.REPEAT_MODE_THIS -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onLayoutChange(
|
||||
v: View?,
|
||||
left: Int,
|
||||
top: Int,
|
||||
right: Int,
|
||||
bottom: Int,
|
||||
oldLeft: Int,
|
||||
oldTop: Int,
|
||||
oldRight: Int,
|
||||
oldBottom: Int
|
||||
) {
|
||||
val height = playerContainer.height
|
||||
val width = playerContainer.width
|
||||
val finalHeight = height - (playerControlsContainer.height + width)
|
||||
val panel = getQueuePanel()
|
||||
panel.peekHeight = finalHeight
|
||||
}
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
package io.github.muntashirakon.music.fragments.player.color
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerFragment
|
||||
import io.github.muntashirakon.music.fragments.player.PlayerAlbumCoverFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_color_player.*
|
||||
|
||||
class ColorFragment : AbsPlayerFragment() {
|
||||
|
||||
private var lastColor: Int = 0
|
||||
private var navigationColor: Int = 0
|
||||
private lateinit var playbackControlsFragment: ColorPlaybackControlsFragment
|
||||
private var valueAnimator: ValueAnimator? = null
|
||||
|
||||
override fun playerToolbar(): Toolbar {
|
||||
return playerToolbar
|
||||
}
|
||||
|
||||
override val paletteColor: Int
|
||||
get() = navigationColor
|
||||
|
||||
override fun onColorChanged(color: MediaNotificationProcessor) {
|
||||
lastColor = color.secondaryTextColor
|
||||
playbackControlsFragment.setColor(color)
|
||||
navigationColor = color.backgroundColor
|
||||
callbacks?.onPaletteColorChanged()
|
||||
colorGradientBackground?.setBackgroundColor(color.backgroundColor)
|
||||
playerActivity?.setLightNavigationBar(ColorUtil.isColorLight(color.backgroundColor))
|
||||
Handler().post {
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
playerToolbar,
|
||||
color.secondaryTextColor,
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFavoriteToggled() {
|
||||
toggleFavorite(MusicPlayerRemote.currentSong)
|
||||
}
|
||||
|
||||
override fun onShow() {
|
||||
playbackControlsFragment.show()
|
||||
}
|
||||
|
||||
override fun onHide() {
|
||||
playbackControlsFragment.hide()
|
||||
onBackPressed()
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun toolbarIconColor(): Int {
|
||||
return lastColor
|
||||
}
|
||||
|
||||
override fun toggleFavorite(song: Song) {
|
||||
super.toggleFavorite(song)
|
||||
if (song.id == MusicPlayerRemote.currentSong.id) {
|
||||
updateIsFavorite()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
if (valueAnimator != null) {
|
||||
valueAnimator!!.cancel()
|
||||
valueAnimator = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_color_player, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpSubFragments()
|
||||
setUpPlayerToolbar()
|
||||
val playerAlbumCoverFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment
|
||||
playerAlbumCoverFragment.setCallbacks(this)
|
||||
}
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
playbackControlsFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as ColorPlaybackControlsFragment
|
||||
}
|
||||
|
||||
private fun setUpPlayerToolbar() {
|
||||
playerToolbar.apply {
|
||||
inflateMenu(R.menu.menu_player)
|
||||
setNavigationOnClickListener { requireActivity().onBackPressed() }
|
||||
setOnMenuItemClickListener(this@ColorFragment)
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
this,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun newInstance(): ColorFragment {
|
||||
return ColorFragment()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,239 @@
|
|||
package io.github.muntashirakon.music.fragments.player.color
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.graphics.Color
|
||||
import android.graphics.PorterDuff
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.SeekBar
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.extensions.applyColor
|
||||
import io.github.muntashirakon.music.extensions.hide
|
||||
import io.github.muntashirakon.music.extensions.show
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerControlsFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper
|
||||
import io.github.muntashirakon.music.helper.PlayPauseButtonOnClickHandler
|
||||
import io.github.muntashirakon.music.misc.SimpleOnSeekbarChangeListener
|
||||
import io.github.muntashirakon.music.service.MusicService
|
||||
import io.github.muntashirakon.music.util.MusicUtil
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_color_player_playback_controls.*
|
||||
|
||||
class ColorPlaybackControlsFragment : AbsPlayerControlsFragment() {
|
||||
|
||||
private var lastPlaybackControlsColor: Int = 0
|
||||
private var lastDisabledPlaybackControlsColor: Int = 0
|
||||
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_color_player_playback_controls, container, false)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
progressViewUpdateHelper.start()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
progressViewUpdateHelper.stop()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpMusicControllers()
|
||||
title.isSelected = true
|
||||
text.isSelected = true
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
title.text = song.title
|
||||
text.text = song.artistName
|
||||
|
||||
if (PreferenceUtil.isSongInfo) {
|
||||
songInfo.text = getSongInfo(song)
|
||||
songInfo.show()
|
||||
} else {
|
||||
songInfo.hide()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
updatePlayPauseDrawableState()
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onPlayStateChanged() {
|
||||
updatePlayPauseDrawableState()
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged() {
|
||||
updateRepeatState()
|
||||
}
|
||||
|
||||
override fun onShuffleModeChanged() {
|
||||
updateShuffleState()
|
||||
}
|
||||
|
||||
override fun setColor(color: MediaNotificationProcessor) {
|
||||
TintHelper.setTintAuto(playPauseButton, color.primaryTextColor, true)
|
||||
TintHelper.setTintAuto(playPauseButton, color.backgroundColor, false)
|
||||
progressSlider.applyColor(color.primaryTextColor)
|
||||
|
||||
title.setTextColor(color.primaryTextColor)
|
||||
text.setTextColor(color.secondaryTextColor)
|
||||
songInfo.setTextColor(color.secondaryTextColor)
|
||||
songCurrentProgress.setTextColor(color.secondaryTextColor)
|
||||
songTotalTime.setTextColor(color.secondaryTextColor)
|
||||
volumeFragment?.setTintableColor(color.primaryTextColor)
|
||||
|
||||
lastPlaybackControlsColor = color.secondaryTextColor
|
||||
lastDisabledPlaybackControlsColor = ColorUtil.withAlpha(color.secondaryTextColor, 0.25f)
|
||||
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updatePrevNextColor()
|
||||
}
|
||||
|
||||
private fun setUpPlayPauseFab() {
|
||||
TintHelper.setTintAuto(playPauseButton, Color.WHITE, true)
|
||||
TintHelper.setTintAuto(playPauseButton, Color.BLACK, false)
|
||||
playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
||||
}
|
||||
|
||||
private fun updatePlayPauseDrawableState() {
|
||||
when {
|
||||
MusicPlayerRemote.isPlaying -> playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp)
|
||||
else -> playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_24dp)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpMusicControllers() {
|
||||
setUpPlayPauseFab()
|
||||
setUpPrevNext()
|
||||
setUpRepeatButton()
|
||||
setUpShuffleButton()
|
||||
setUpProgressSlider()
|
||||
}
|
||||
|
||||
private fun setUpPrevNext() {
|
||||
updatePrevNextColor()
|
||||
nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
|
||||
previousButton.setOnClickListener { MusicPlayerRemote.back() }
|
||||
}
|
||||
|
||||
private fun updatePrevNextColor() {
|
||||
nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
|
||||
private fun setUpShuffleButton() {
|
||||
shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
|
||||
}
|
||||
|
||||
override fun updateShuffleState() {
|
||||
when (MusicPlayerRemote.shuffleMode) {
|
||||
MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton.setColorFilter(
|
||||
lastPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
else -> shuffleButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpRepeatButton() {
|
||||
repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
|
||||
}
|
||||
|
||||
override fun updateRepeatState() {
|
||||
when (MusicPlayerRemote.repeatMode) {
|
||||
MusicService.REPEAT_MODE_NONE -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
MusicService.REPEAT_MODE_ALL -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
MusicService.REPEAT_MODE_THIS -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override fun show() {
|
||||
playPauseButton!!.animate()
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.rotation(360f)
|
||||
.setInterpolator(DecelerateInterpolator())
|
||||
.start()
|
||||
}
|
||||
|
||||
public override fun hide() {
|
||||
playPauseButton.apply {
|
||||
scaleX = 0f
|
||||
scaleY = 0f
|
||||
rotation = 0f
|
||||
}
|
||||
}
|
||||
|
||||
override fun setUpProgressSlider() {
|
||||
progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
MusicPlayerRemote.seekTo(progress)
|
||||
onUpdateProgressViews(
|
||||
MusicPlayerRemote.songProgressMillis,
|
||||
MusicPlayerRemote.songDurationMillis
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
progressSlider.max = total
|
||||
|
||||
val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress)
|
||||
animator.duration = SLIDER_ANIMATION_TIME
|
||||
animator.interpolator = LinearInterpolator()
|
||||
animator.start()
|
||||
|
||||
songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
|
||||
songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
package io.github.muntashirakon.music.fragments.player.fit
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerFragment
|
||||
import io.github.muntashirakon.music.fragments.player.PlayerAlbumCoverFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_fit.*
|
||||
|
||||
class FitFragment : AbsPlayerFragment() {
|
||||
override fun playerToolbar(): Toolbar {
|
||||
return playerToolbar
|
||||
}
|
||||
|
||||
private var lastColor: Int = 0
|
||||
override val paletteColor: Int
|
||||
get() = lastColor
|
||||
|
||||
private lateinit var playbackControlsFragment: FitPlaybackControlsFragment
|
||||
|
||||
override fun onShow() {
|
||||
playbackControlsFragment.show()
|
||||
}
|
||||
|
||||
override fun onHide() {
|
||||
playbackControlsFragment.hide()
|
||||
onBackPressed()
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun toolbarIconColor(): Int {
|
||||
return ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal)
|
||||
}
|
||||
|
||||
override fun onColorChanged(color: MediaNotificationProcessor) {
|
||||
playbackControlsFragment.setColor(color)
|
||||
lastColor = color.primaryTextColor
|
||||
callbacks?.onPaletteColorChanged()
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
playerToolbar,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
||||
override fun toggleFavorite(song: Song) {
|
||||
super.toggleFavorite(song)
|
||||
if (song.id == MusicPlayerRemote.currentSong.id) {
|
||||
updateIsFavorite()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFavoriteToggled() {
|
||||
toggleFavorite(MusicPlayerRemote.currentSong)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
|
||||
return inflater.inflate(R.layout.fragment_fit, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpSubFragments()
|
||||
setUpPlayerToolbar()
|
||||
}
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
playbackControlsFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as FitPlaybackControlsFragment
|
||||
val playerAlbumCoverFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment
|
||||
playerAlbumCoverFragment.setCallbacks(this)
|
||||
}
|
||||
|
||||
private fun setUpPlayerToolbar() {
|
||||
playerToolbar.apply {
|
||||
inflateMenu(R.menu.menu_player)
|
||||
setNavigationOnClickListener { requireActivity().onBackPressed() }
|
||||
setOnMenuItemClickListener(this@FitFragment)
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
this,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
updateIsFavorite()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
updateIsFavorite()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun newInstance(): FitFragment {
|
||||
return FitFragment()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,293 @@
|
|||
package io.github.muntashirakon.music.fragments.player.fit
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.graphics.PorterDuff
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.AccelerateInterpolator
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.SeekBar
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.extensions.hide
|
||||
import io.github.muntashirakon.music.extensions.ripAlpha
|
||||
import io.github.muntashirakon.music.extensions.show
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerControlsFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper
|
||||
import io.github.muntashirakon.music.helper.PlayPauseButtonOnClickHandler
|
||||
import io.github.muntashirakon.music.misc.SimpleOnSeekbarChangeListener
|
||||
import io.github.muntashirakon.music.service.MusicService
|
||||
import io.github.muntashirakon.music.util.MusicUtil
|
||||
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_fit_playback_controls.*
|
||||
|
||||
class FitPlaybackControlsFragment : AbsPlayerControlsFragment() {
|
||||
|
||||
|
||||
private var lastPlaybackControlsColor: Int = 0
|
||||
private var lastDisabledPlaybackControlsColor: Int = 0
|
||||
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
|
||||
return inflater.inflate(R.layout.fragment_fit_playback_controls, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpMusicControllers()
|
||||
|
||||
title.isSelected = true
|
||||
text.isSelected = true
|
||||
|
||||
playPauseButton.setOnClickListener {
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
MusicPlayerRemote.pauseSong()
|
||||
} else {
|
||||
MusicPlayerRemote.resumePlaying()
|
||||
}
|
||||
showBonceAnimation()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
title.text = song.title
|
||||
text.text = song.artistName
|
||||
if (PreferenceUtil.isSongInfo) {
|
||||
songInfo.text = getSongInfo(song)
|
||||
songInfo.show()
|
||||
} else {
|
||||
songInfo.hide()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
progressViewUpdateHelper.start()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
progressViewUpdateHelper.stop()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
updatePlayPauseDrawableState()
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onPlayStateChanged() {
|
||||
updatePlayPauseDrawableState()
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged() {
|
||||
updateRepeatState()
|
||||
}
|
||||
|
||||
override fun onShuffleModeChanged() {
|
||||
updateShuffleState()
|
||||
}
|
||||
|
||||
override fun setColor(color: MediaNotificationProcessor) {
|
||||
val colorBg = ATHUtil.resolveColor(requireContext(), android.R.attr.colorBackground)
|
||||
if (ColorUtil.isColorLight(colorBg)) {
|
||||
lastPlaybackControlsColor =
|
||||
MaterialValueHelper.getSecondaryTextColor(requireContext(), true)
|
||||
lastDisabledPlaybackControlsColor =
|
||||
MaterialValueHelper.getSecondaryDisabledTextColor(requireContext(), true)
|
||||
} else {
|
||||
lastPlaybackControlsColor = MaterialValueHelper.getPrimaryTextColor(activity, false)
|
||||
lastDisabledPlaybackControlsColor =
|
||||
MaterialValueHelper.getPrimaryDisabledTextColor(activity, false)
|
||||
}
|
||||
|
||||
val colorFinal = if (PreferenceUtil.isAdaptiveColor) {
|
||||
color.primaryTextColor
|
||||
} else {
|
||||
ThemeStore.accentColor(requireContext()).ripAlpha()
|
||||
}
|
||||
|
||||
volumeFragment?.setTintable(colorFinal)
|
||||
|
||||
setFabColor(colorFinal)
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updatePrevNextColor()
|
||||
}
|
||||
|
||||
private fun setFabColor(i: Int) {
|
||||
TintHelper.setTintAuto(
|
||||
playPauseButton,
|
||||
MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(i)),
|
||||
false
|
||||
)
|
||||
TintHelper.setTintAuto(playPauseButton, i, true)
|
||||
}
|
||||
|
||||
private fun setUpPlayPauseFab() {
|
||||
playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
||||
}
|
||||
|
||||
private fun updatePlayPauseDrawableState() {
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp)
|
||||
} else {
|
||||
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_32dp)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpMusicControllers() {
|
||||
setUpPlayPauseFab()
|
||||
setUpPrevNext()
|
||||
setUpRepeatButton()
|
||||
setUpShuffleButton()
|
||||
setUpProgressSlider()
|
||||
}
|
||||
|
||||
private fun setUpPrevNext() {
|
||||
updatePrevNextColor()
|
||||
nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
|
||||
previousButton.setOnClickListener { MusicPlayerRemote.back() }
|
||||
}
|
||||
|
||||
private fun updatePrevNextColor() {
|
||||
nextButton!!.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
previousButton!!.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
|
||||
private fun setUpShuffleButton() {
|
||||
shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
|
||||
}
|
||||
|
||||
override fun updateShuffleState() {
|
||||
when (MusicPlayerRemote.shuffleMode) {
|
||||
MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton.setColorFilter(
|
||||
lastPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
else -> shuffleButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpRepeatButton() {
|
||||
repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
|
||||
}
|
||||
|
||||
override fun updateRepeatState() {
|
||||
when (MusicPlayerRemote.repeatMode) {
|
||||
MusicService.REPEAT_MODE_NONE -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
MusicService.REPEAT_MODE_ALL -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
MusicService.REPEAT_MODE_THIS -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override fun show() {
|
||||
playPauseButton!!.animate()
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.rotation(360f)
|
||||
.setInterpolator(DecelerateInterpolator())
|
||||
.start()
|
||||
}
|
||||
|
||||
public override fun hide() {
|
||||
if (playPauseButton != null) {
|
||||
playPauseButton!!.apply {
|
||||
scaleX = 0f
|
||||
scaleY = 0f
|
||||
rotation = 0f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun showBonceAnimation() {
|
||||
playPauseButton.apply {
|
||||
clearAnimation()
|
||||
scaleX = 0.9f
|
||||
scaleY = 0.9f
|
||||
visibility = View.VISIBLE
|
||||
pivotX = (width / 2).toFloat()
|
||||
pivotY = (height / 2).toFloat()
|
||||
|
||||
animate().setDuration(200)
|
||||
.setInterpolator(DecelerateInterpolator())
|
||||
.scaleX(1.1f)
|
||||
.scaleY(1.1f)
|
||||
.withEndAction {
|
||||
animate().setDuration(200)
|
||||
.setInterpolator(AccelerateInterpolator())
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.alpha(1f).start()
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
|
||||
override fun setUpProgressSlider() {
|
||||
progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
MusicPlayerRemote.seekTo(progress)
|
||||
onUpdateProgressViews(
|
||||
MusicPlayerRemote.songProgressMillis,
|
||||
MusicPlayerRemote.songDurationMillis
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
progressSlider.max = total
|
||||
|
||||
val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress)
|
||||
animator.duration = SLIDER_ANIMATION_TIME
|
||||
animator.interpolator = LinearInterpolator()
|
||||
animator.start()
|
||||
|
||||
songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
|
||||
songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
package io.github.muntashirakon.music.fragments.player.flat
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.graphics.PorterDuff
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.SeekBar
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.extensions.applyColor
|
||||
import io.github.muntashirakon.music.extensions.hide
|
||||
import io.github.muntashirakon.music.extensions.ripAlpha
|
||||
import io.github.muntashirakon.music.extensions.show
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerControlsFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper
|
||||
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper.Callback
|
||||
import io.github.muntashirakon.music.helper.PlayPauseButtonOnClickHandler
|
||||
import io.github.muntashirakon.music.misc.SimpleOnSeekbarChangeListener
|
||||
import io.github.muntashirakon.music.service.MusicService
|
||||
import io.github.muntashirakon.music.util.MusicUtil
|
||||
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_flat_player_playback_controls.*
|
||||
|
||||
class FlatPlaybackControlsFragment : AbsPlayerControlsFragment(), Callback {
|
||||
|
||||
private var lastPlaybackControlsColor: Int = 0
|
||||
private var lastDisabledPlaybackControlsColor: Int = 0
|
||||
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_flat_player_playback_controls, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpMusicControllers()
|
||||
title.isSelected = true
|
||||
text.isSelected = true
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
progressViewUpdateHelper.start()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
progressViewUpdateHelper.stop()
|
||||
}
|
||||
|
||||
public override fun show() {
|
||||
playPauseButton!!.animate()
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.setInterpolator(DecelerateInterpolator())
|
||||
.start()
|
||||
}
|
||||
|
||||
public override fun hide() {
|
||||
playPauseButton!!.apply {
|
||||
scaleX = 0f
|
||||
scaleY = 0f
|
||||
rotation = 0f
|
||||
}
|
||||
}
|
||||
|
||||
override fun setColor(color: MediaNotificationProcessor) {
|
||||
if (ATHUtil.isWindowBackgroundDark(requireContext())) {
|
||||
lastPlaybackControlsColor =
|
||||
MaterialValueHelper.getSecondaryTextColor(requireContext(), true)
|
||||
lastDisabledPlaybackControlsColor =
|
||||
MaterialValueHelper.getSecondaryDisabledTextColor(requireContext(), true)
|
||||
} else {
|
||||
lastPlaybackControlsColor =
|
||||
MaterialValueHelper.getPrimaryTextColor(requireContext(), false)
|
||||
lastDisabledPlaybackControlsColor =
|
||||
MaterialValueHelper.getPrimaryDisabledTextColor(requireContext(), false)
|
||||
}
|
||||
|
||||
val colorFinal = if (PreferenceUtil.isAdaptiveColor) {
|
||||
color.primaryTextColor
|
||||
} else {
|
||||
ThemeStore.accentColor(requireContext()).ripAlpha()
|
||||
}
|
||||
|
||||
updateTextColors(colorFinal)
|
||||
volumeFragment?.setTintable(colorFinal)
|
||||
progressSlider.applyColor(colorFinal)
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
}
|
||||
|
||||
private fun updateTextColors(color: Int) {
|
||||
val isDark = ColorUtil.isColorLight(color)
|
||||
val darkColor = ColorUtil.darkenColor(color)
|
||||
val colorPrimary = MaterialValueHelper.getPrimaryTextColor(context, isDark)
|
||||
val colorSecondary =
|
||||
MaterialValueHelper.getSecondaryTextColor(context, ColorUtil.isColorLight(darkColor))
|
||||
|
||||
TintHelper.setTintAuto(playPauseButton!!, colorPrimary, false)
|
||||
TintHelper.setTintAuto(playPauseButton!!, color, true)
|
||||
|
||||
title.setBackgroundColor(color)
|
||||
title.setTextColor(colorPrimary)
|
||||
text.setBackgroundColor(darkColor)
|
||||
text.setTextColor(colorSecondary)
|
||||
songInfo.setBackgroundColor(darkColor)
|
||||
songInfo.setTextColor(colorSecondary)
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
updatePlayPauseDrawableState()
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onPlayStateChanged() {
|
||||
updatePlayPauseDrawableState()
|
||||
}
|
||||
|
||||
private fun setUpPlayPauseFab() {
|
||||
playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
||||
}
|
||||
|
||||
private fun updatePlayPauseDrawableState() {
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp)
|
||||
} else {
|
||||
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_32dp)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpMusicControllers() {
|
||||
setUpPlayPauseFab()
|
||||
setUpRepeatButton()
|
||||
setUpShuffleButton()
|
||||
setUpProgressSlider()
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
title.text = song.title
|
||||
text.text = song.artistName
|
||||
if (PreferenceUtil.isSongInfo) {
|
||||
songInfo.text = getSongInfo(song)
|
||||
songInfo.show()
|
||||
} else {
|
||||
songInfo.hide()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged() {
|
||||
updateRepeatState()
|
||||
}
|
||||
|
||||
override fun onShuffleModeChanged() {
|
||||
updateShuffleState()
|
||||
}
|
||||
|
||||
private fun setUpRepeatButton() {
|
||||
repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
|
||||
}
|
||||
|
||||
override fun updateRepeatState() {
|
||||
when (MusicPlayerRemote.repeatMode) {
|
||||
MusicService.REPEAT_MODE_NONE -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
MusicService.REPEAT_MODE_ALL -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
MusicService.REPEAT_MODE_THIS -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpShuffleButton() {
|
||||
shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
|
||||
}
|
||||
|
||||
override fun updateShuffleState() {
|
||||
when (MusicPlayerRemote.shuffleMode) {
|
||||
MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton.setColorFilter(
|
||||
lastPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
else -> shuffleButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setUpProgressSlider() {
|
||||
progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
MusicPlayerRemote.seekTo(progress)
|
||||
onUpdateProgressViews(
|
||||
MusicPlayerRemote.songProgressMillis,
|
||||
MusicPlayerRemote.songDurationMillis
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
progressSlider.max = total
|
||||
|
||||
val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress)
|
||||
animator.duration = SLIDER_ANIMATION_TIME
|
||||
animator.interpolator = LinearInterpolator()
|
||||
animator.start()
|
||||
|
||||
songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
|
||||
songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
package io.github.muntashirakon.music.fragments.player.flat
|
||||
|
||||
import android.animation.ArgbEvaluator
|
||||
import android.animation.ValueAnimator
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerFragment
|
||||
import io.github.muntashirakon.music.fragments.player.PlayerAlbumCoverFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
import io.github.muntashirakon.music.util.ViewUtil
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import io.github.muntashirakon.music.views.DrawableGradient
|
||||
import kotlinx.android.synthetic.main.fragment_flat_player.*
|
||||
|
||||
class FlatPlayerFragment : AbsPlayerFragment() {
|
||||
override fun playerToolbar(): Toolbar {
|
||||
return playerToolbar
|
||||
}
|
||||
|
||||
private var valueAnimator: ValueAnimator? = null
|
||||
private lateinit var controlsFragment: FlatPlaybackControlsFragment
|
||||
private var lastColor: Int = 0
|
||||
override val paletteColor: Int
|
||||
get() = lastColor
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
controlsFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as FlatPlaybackControlsFragment
|
||||
val playerAlbumCoverFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment
|
||||
playerAlbumCoverFragment.setCallbacks(this)
|
||||
}
|
||||
|
||||
private fun setUpPlayerToolbar() {
|
||||
playerToolbar.inflateMenu(R.menu.menu_player)
|
||||
playerToolbar.setNavigationOnClickListener { _ -> requireActivity().onBackPressed() }
|
||||
playerToolbar.setOnMenuItemClickListener(this)
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
playerToolbar,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
||||
private fun colorize(i: Int) {
|
||||
if (valueAnimator != null) {
|
||||
valueAnimator?.cancel()
|
||||
}
|
||||
|
||||
valueAnimator = ValueAnimator.ofObject(ArgbEvaluator(), android.R.color.transparent, i)
|
||||
valueAnimator?.addUpdateListener { animation ->
|
||||
val drawable = DrawableGradient(
|
||||
GradientDrawable.Orientation.TOP_BOTTOM,
|
||||
intArrayOf(animation.animatedValue as Int, android.R.color.transparent), 0
|
||||
)
|
||||
colorGradientBackground?.background = drawable
|
||||
|
||||
}
|
||||
valueAnimator?.setDuration(ViewUtil.RETRO_MUSIC_ANIM_TIME.toLong())?.start()
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_flat_player, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpPlayerToolbar()
|
||||
setUpSubFragments()
|
||||
}
|
||||
|
||||
override fun onShow() {
|
||||
controlsFragment.show()
|
||||
}
|
||||
|
||||
override fun onHide() {
|
||||
controlsFragment.hide()
|
||||
onBackPressed()
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun toolbarIconColor(): Int {
|
||||
val isLight = ColorUtil.isColorLight(paletteColor)
|
||||
return if (PreferenceUtil.isAdaptiveColor)
|
||||
MaterialValueHelper.getPrimaryTextColor(requireContext(), isLight)
|
||||
else
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal)
|
||||
}
|
||||
|
||||
override fun onColorChanged(color: MediaNotificationProcessor) {
|
||||
lastColor = color.backgroundColor
|
||||
controlsFragment.setColor(color)
|
||||
callbacks?.onPaletteColorChanged()
|
||||
val isLight = ColorUtil.isColorLight(color.backgroundColor)
|
||||
val iconColor = if (PreferenceUtil.isAdaptiveColor)
|
||||
MaterialValueHelper.getPrimaryTextColor(requireContext(), isLight)
|
||||
else
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal)
|
||||
ToolbarContentTintHelper.colorizeToolbar(playerToolbar, iconColor, requireActivity())
|
||||
if (PreferenceUtil.isAdaptiveColor) {
|
||||
colorize(color.backgroundColor)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFavoriteToggled() {
|
||||
toggleFavorite(MusicPlayerRemote.currentSong)
|
||||
}
|
||||
|
||||
override fun toggleFavorite(song: Song) {
|
||||
super.toggleFavorite(song)
|
||||
if (song.id == MusicPlayerRemote.currentSong.id) {
|
||||
updateIsFavorite()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,328 @@
|
|||
package io.github.muntashirakon.music.fragments.player.full
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.graphics.PorterDuff
|
||||
import android.os.AsyncTask
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.PopupMenu
|
||||
import android.widget.SeekBar
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.extensions.applyColor
|
||||
import io.github.muntashirakon.music.extensions.hide
|
||||
import io.github.muntashirakon.music.extensions.show
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerControlsFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper
|
||||
import io.github.muntashirakon.music.helper.PlayPauseButtonOnClickHandler
|
||||
import io.github.muntashirakon.music.misc.SimpleOnSeekbarChangeListener
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
import io.github.muntashirakon.music.service.MusicService
|
||||
import io.github.muntashirakon.music.util.MusicUtil
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_full_player_controls.*
|
||||
|
||||
/**
|
||||
* Created by hemanths on 20/09/17.
|
||||
*/
|
||||
|
||||
class FullPlaybackControlsFragment : AbsPlayerControlsFragment(),
|
||||
PopupMenu.OnMenuItemClickListener {
|
||||
|
||||
private var lastPlaybackControlsColor: Int = 0
|
||||
private var lastDisabledPlaybackControlsColor: Int = 0
|
||||
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_full_player_controls, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpMusicControllers()
|
||||
|
||||
songTotalTime.setTextColor(Color.WHITE)
|
||||
songCurrentProgress.setTextColor(Color.WHITE)
|
||||
title.isSelected = true
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
progressViewUpdateHelper.start()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
progressViewUpdateHelper.stop()
|
||||
}
|
||||
|
||||
|
||||
public override fun show() {
|
||||
playPauseButton!!.animate()
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.setInterpolator(DecelerateInterpolator())
|
||||
.start()
|
||||
}
|
||||
|
||||
public override fun hide() {
|
||||
playPauseButton.apply {
|
||||
scaleX = 0f
|
||||
scaleY = 0f
|
||||
rotation = 0f
|
||||
}
|
||||
}
|
||||
|
||||
override fun setColor(color: MediaNotificationProcessor) {
|
||||
lastPlaybackControlsColor = color.primaryTextColor
|
||||
lastDisabledPlaybackControlsColor = ColorUtil.withAlpha(color.primaryTextColor, 0.3f)
|
||||
|
||||
val tintList = ColorStateList.valueOf(color.primaryTextColor)
|
||||
playerMenu.imageTintList = tintList
|
||||
songFavourite.imageTintList = tintList
|
||||
volumeFragment?.setTintableColor(color.primaryTextColor)
|
||||
progressSlider.applyColor(color.primaryTextColor)
|
||||
title.setTextColor(color.primaryTextColor)
|
||||
text.setTextColor(color.secondaryTextColor)
|
||||
songInfo.setTextColor(color.secondaryTextColor)
|
||||
songCurrentProgress.setTextColor(color.secondaryTextColor)
|
||||
songTotalTime.setTextColor(color.secondaryTextColor)
|
||||
|
||||
playPauseButton.backgroundTintList = tintList
|
||||
playPauseButton.imageTintList = ColorStateList.valueOf(color.backgroundColor)
|
||||
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updatePrevNextColor()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
updatePlayPauseDrawableState()
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
title.text = song.title
|
||||
text.text = song.artistName
|
||||
updateIsFavorite()
|
||||
if (PreferenceUtil.isSongInfo) {
|
||||
songInfo.text = getSongInfo(song)
|
||||
songInfo.show()
|
||||
} else {
|
||||
songInfo.hide()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onPlayStateChanged() {
|
||||
updatePlayPauseDrawableState()
|
||||
}
|
||||
|
||||
private fun updatePlayPauseDrawableState() {
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp)
|
||||
} else {
|
||||
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_32dp)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpPlayPauseFab() {
|
||||
|
||||
playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
||||
playPauseButton.post {
|
||||
if (playPauseButton != null) {
|
||||
playPauseButton.pivotX = (playPauseButton.width / 2).toFloat()
|
||||
playPauseButton.pivotY = (playPauseButton.height / 2).toFloat()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpMusicControllers() {
|
||||
setUpPlayPauseFab()
|
||||
setUpPrevNext()
|
||||
setUpRepeatButton()
|
||||
setUpShuffleButton()
|
||||
setUpProgressSlider()
|
||||
setupFavourite()
|
||||
setupMenu()
|
||||
}
|
||||
|
||||
private fun setupMenu() {
|
||||
playerMenu.setOnClickListener {
|
||||
val popupMenu = PopupMenu(requireContext(), it)
|
||||
popupMenu.setOnMenuItemClickListener(this)
|
||||
popupMenu.inflate(R.menu.menu_player)
|
||||
popupMenu.show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMenuItemClick(item: MenuItem?): Boolean {
|
||||
return (parentFragment as FullPlayerFragment).onMenuItemClick(item!!)
|
||||
}
|
||||
|
||||
private fun setUpPrevNext() {
|
||||
updatePrevNextColor()
|
||||
nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
|
||||
previousButton.setOnClickListener { MusicPlayerRemote.back() }
|
||||
}
|
||||
|
||||
private fun updatePrevNextColor() {
|
||||
nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
|
||||
override fun setUpProgressSlider() {
|
||||
progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
MusicPlayerRemote.seekTo(progress)
|
||||
onUpdateProgressViews(
|
||||
MusicPlayerRemote.songProgressMillis,
|
||||
MusicPlayerRemote.songDurationMillis
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
progressSlider.max = total
|
||||
|
||||
val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress)
|
||||
animator.duration = SLIDER_ANIMATION_TIME
|
||||
animator.interpolator = LinearInterpolator()
|
||||
animator.start()
|
||||
|
||||
songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
|
||||
songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged() {
|
||||
updateRepeatState()
|
||||
}
|
||||
|
||||
override fun onShuffleModeChanged() {
|
||||
updateShuffleState()
|
||||
}
|
||||
|
||||
private fun setUpShuffleButton() {
|
||||
shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
|
||||
}
|
||||
|
||||
override fun updateShuffleState() {
|
||||
when (MusicPlayerRemote.shuffleMode) {
|
||||
MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton.setColorFilter(
|
||||
lastPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
else -> shuffleButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpRepeatButton() {
|
||||
repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
|
||||
}
|
||||
|
||||
override fun updateRepeatState() {
|
||||
when (MusicPlayerRemote.repeatMode) {
|
||||
MusicService.REPEAT_MODE_NONE -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
MusicService.REPEAT_MODE_ALL -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
MusicService.REPEAT_MODE_THIS -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupFavourite() {
|
||||
songFavourite?.setOnClickListener {
|
||||
toggleFavorite(MusicPlayerRemote.currentSong)
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleFavorite(song: Song) {
|
||||
MusicUtil.toggleFavorite(requireContext(), song)
|
||||
if (song.id == MusicPlayerRemote.currentSong.id) {
|
||||
updateIsFavorite()
|
||||
}
|
||||
}
|
||||
|
||||
private var updateIsFavoriteTask: AsyncTask<*, *, *>? = null
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
fun updateIsFavorite() {
|
||||
if (updateIsFavoriteTask != null) {
|
||||
updateIsFavoriteTask?.cancel(false)
|
||||
}
|
||||
updateIsFavoriteTask = object : AsyncTask<Song, Void, Boolean>() {
|
||||
override fun doInBackground(vararg params: Song): Boolean? {
|
||||
val activity = activity
|
||||
return if (activity != null) {
|
||||
MusicUtil.isFavorite(requireActivity(), params[0])
|
||||
} else {
|
||||
cancel(false)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPostExecute(isFavorite: Boolean?) {
|
||||
val activity = activity
|
||||
if (activity != null) {
|
||||
val res = if (isFavorite!!)
|
||||
R.drawable.ic_favorite_white_24dp
|
||||
else
|
||||
R.drawable.ic_favorite_border_white_24dp
|
||||
|
||||
val drawable = TintHelper.createTintedDrawable(activity, res, Color.WHITE)
|
||||
songFavourite?.setImageDrawable(drawable)
|
||||
}
|
||||
}
|
||||
}.execute(MusicPlayerRemote.currentSong)
|
||||
}
|
||||
|
||||
fun onFavoriteToggled() {
|
||||
toggleFavorite(MusicPlayerRemote.currentSong)
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,275 @@
|
|||
package io.github.muntashirakon.music.fragments.player.full
|
||||
|
||||
import android.app.ActivityOptions
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.extensions.hide
|
||||
import io.github.muntashirakon.music.extensions.show
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerFragment
|
||||
import io.github.muntashirakon.music.fragments.player.PlayerAlbumCoverFragment
|
||||
import io.github.muntashirakon.music.glide.ArtistGlideRequest
|
||||
import io.github.muntashirakon.music.glide.RetroMusicColoredTarget
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper
|
||||
import io.github.muntashirakon.music.loaders.ArtistLoader
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
import io.github.muntashirakon.music.model.lyrics.AbsSynchronizedLyrics
|
||||
import io.github.muntashirakon.music.model.lyrics.Lyrics
|
||||
import io.github.muntashirakon.music.util.NavigationUtil
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import com.bumptech.glide.Glide
|
||||
import kotlinx.android.synthetic.main.fragment_full.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class FullPlayerFragment : AbsPlayerFragment(), MusicProgressViewUpdateHelper.Callback {
|
||||
private lateinit var lyricsLayout: FrameLayout
|
||||
private lateinit var lyricsLine1: TextView
|
||||
private lateinit var lyricsLine2: TextView
|
||||
|
||||
private var lyrics: Lyrics? = null
|
||||
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
if (!isLyricsLayoutBound()) return
|
||||
|
||||
if (!isLyricsLayoutVisible()) {
|
||||
hideLyricsLayout()
|
||||
return
|
||||
}
|
||||
|
||||
if (lyrics !is AbsSynchronizedLyrics) return
|
||||
val synchronizedLyrics = lyrics as AbsSynchronizedLyrics
|
||||
|
||||
lyricsLayout.visibility = View.VISIBLE
|
||||
lyricsLayout.alpha = 1f
|
||||
|
||||
val oldLine = lyricsLine2.text.toString()
|
||||
val line = synchronizedLyrics.getLine(progress)
|
||||
|
||||
if (oldLine != line || oldLine.isEmpty()) {
|
||||
lyricsLine1.text = oldLine
|
||||
lyricsLine2.text = line
|
||||
|
||||
lyricsLine1.visibility = View.VISIBLE
|
||||
lyricsLine2.visibility = View.VISIBLE
|
||||
|
||||
lyricsLine2.measure(
|
||||
View.MeasureSpec.makeMeasureSpec(
|
||||
lyricsLine2.measuredWidth,
|
||||
View.MeasureSpec.EXACTLY
|
||||
),
|
||||
View.MeasureSpec.UNSPECIFIED
|
||||
)
|
||||
val h: Float = lyricsLine2.measuredHeight.toFloat()
|
||||
|
||||
lyricsLine1.alpha = 1f
|
||||
lyricsLine1.translationY = 0f
|
||||
lyricsLine1.animate().alpha(0f).translationY(-h).duration = VISIBILITY_ANIM_DURATION
|
||||
|
||||
lyricsLine2.alpha = 0f
|
||||
lyricsLine2.translationY = h
|
||||
lyricsLine2.animate().alpha(1f).translationY(0f).duration = VISIBILITY_ANIM_DURATION
|
||||
}
|
||||
}
|
||||
|
||||
private fun isLyricsLayoutVisible(): Boolean {
|
||||
return lyrics != null && lyrics!!.isSynchronized && lyrics!!.isValid
|
||||
}
|
||||
|
||||
private fun isLyricsLayoutBound(): Boolean {
|
||||
return lyricsLayout != null && lyricsLine1 != null && lyricsLine2 != null
|
||||
}
|
||||
|
||||
private fun hideLyricsLayout() {
|
||||
lyricsLayout.animate().alpha(0f).setDuration(VISIBILITY_ANIM_DURATION)
|
||||
.withEndAction(Runnable {
|
||||
if (!isLyricsLayoutBound()) return@Runnable
|
||||
lyricsLayout.visibility = View.GONE
|
||||
lyricsLine1.text = null
|
||||
lyricsLine2.text = null
|
||||
})
|
||||
}
|
||||
|
||||
override fun setLyrics(l: Lyrics?) {
|
||||
lyrics = l
|
||||
|
||||
if (!isLyricsLayoutBound()) return
|
||||
|
||||
if (!isLyricsLayoutVisible()) {
|
||||
hideLyricsLayout()
|
||||
return
|
||||
}
|
||||
|
||||
lyricsLine1.text = null
|
||||
lyricsLine2.text = null
|
||||
|
||||
lyricsLayout.visibility = View.VISIBLE
|
||||
lyricsLayout.animate().alpha(1f).duration = VISIBILITY_ANIM_DURATION
|
||||
}
|
||||
|
||||
override fun playerToolbar(): Toolbar {
|
||||
return playerToolbar
|
||||
}
|
||||
|
||||
private var lastColor: Int = 0
|
||||
override val paletteColor: Int
|
||||
get() = lastColor
|
||||
private lateinit var controlsFragment: FullPlaybackControlsFragment
|
||||
|
||||
private fun setUpPlayerToolbar() {
|
||||
playerToolbar.apply {
|
||||
setNavigationOnClickListener { requireActivity().onBackPressed() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_full, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
lyricsLayout = view.findViewById(R.id.playerLyrics)
|
||||
lyricsLine1 = view.findViewById(R.id.player_lyrics_line1)
|
||||
lyricsLine2 = view.findViewById(R.id.player_lyrics_line2)
|
||||
|
||||
setUpSubFragments()
|
||||
setUpPlayerToolbar()
|
||||
setupArtist()
|
||||
nextSong.isSelected = true
|
||||
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this, 500, 1000)
|
||||
progressViewUpdateHelper.start()
|
||||
}
|
||||
|
||||
private fun setupArtist() {
|
||||
artistImage.setOnClickListener {
|
||||
val transitionName =
|
||||
"${getString(R.string.transition_artist_image)}_${MusicPlayerRemote.currentSong.artistId}"
|
||||
val activityOptions =
|
||||
ActivityOptions.makeSceneTransitionAnimation(
|
||||
requireActivity(),
|
||||
artistImage,
|
||||
transitionName
|
||||
)
|
||||
NavigationUtil.goToArtistOptions(
|
||||
requireActivity(),
|
||||
MusicPlayerRemote.currentSong.artistId,
|
||||
activityOptions
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
controlsFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as FullPlaybackControlsFragment
|
||||
|
||||
val playerAlbumCoverFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment
|
||||
playerAlbumCoverFragment.setCallbacks(this)
|
||||
playerAlbumCoverFragment.removeSlideEffect()
|
||||
}
|
||||
|
||||
override fun onShow() {
|
||||
}
|
||||
|
||||
override fun onHide() {
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun toolbarIconColor(): Int {
|
||||
return Color.WHITE
|
||||
}
|
||||
|
||||
override fun onColorChanged(color: MediaNotificationProcessor) {
|
||||
lastColor = color.backgroundColor
|
||||
mask.backgroundTintList = ColorStateList.valueOf(color.backgroundColor)
|
||||
controlsFragment.setColor(color)
|
||||
callbacks?.onPaletteColorChanged()
|
||||
ToolbarContentTintHelper.colorizeToolbar(playerToolbar, Color.WHITE, activity)
|
||||
}
|
||||
|
||||
override fun onFavoriteToggled() {
|
||||
toggleFavorite(MusicPlayerRemote.currentSong)
|
||||
controlsFragment.onFavoriteToggled()
|
||||
}
|
||||
|
||||
override fun toggleFavorite(song: Song) {
|
||||
super.toggleFavorite(song)
|
||||
if (song.id == MusicPlayerRemote.currentSong.id) {
|
||||
updateIsFavorite()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
updateArtistImage()
|
||||
updateLabel()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateArtistImage()
|
||||
updateLabel()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
progressViewUpdateHelper.stop()
|
||||
}
|
||||
|
||||
private fun updateArtistImage() {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val artist =
|
||||
ArtistLoader.getArtist(requireContext(), MusicPlayerRemote.currentSong.artistId)
|
||||
withContext(Dispatchers.Main) {
|
||||
ArtistGlideRequest.Builder.from(Glide.with(requireContext()), artist)
|
||||
.generatePalette(requireContext())
|
||||
.build()
|
||||
.into(object : RetroMusicColoredTarget(artistImage) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onQueueChanged() {
|
||||
super.onQueueChanged()
|
||||
if (MusicPlayerRemote.playingQueue.isNotEmpty()) updateLabel()
|
||||
}
|
||||
|
||||
private fun updateLabel() {
|
||||
(MusicPlayerRemote.playingQueue.size - 1).apply {
|
||||
if (this == (MusicPlayerRemote.position)) {
|
||||
nextSongLabel.setText(R.string.last_song)
|
||||
nextSong.hide()
|
||||
} else {
|
||||
val title = MusicPlayerRemote.playingQueue[MusicPlayerRemote.position + 1].title
|
||||
nextSongLabel.setText(R.string.next_song)
|
||||
nextSong.apply {
|
||||
text = title
|
||||
show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,515 @@
|
|||
package io.github.muntashirakon.music.fragments.player.gradient
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.graphics.PorterDuff
|
||||
import android.os.AsyncTask
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.PopupMenu
|
||||
import android.widget.SeekBar
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.RetroBottomSheetBehavior
|
||||
import io.github.muntashirakon.music.activities.base.AbsSlidingMusicPanelActivity
|
||||
import io.github.muntashirakon.music.adapter.song.PlayingQueueAdapter
|
||||
import io.github.muntashirakon.music.extensions.hide
|
||||
import io.github.muntashirakon.music.extensions.ripAlpha
|
||||
import io.github.muntashirakon.music.extensions.show
|
||||
import io.github.muntashirakon.music.fragments.VolumeFragment
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerControlsFragment
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper
|
||||
import io.github.muntashirakon.music.helper.PlayPauseButtonOnClickHandler
|
||||
import io.github.muntashirakon.music.misc.SimpleOnSeekbarChangeListener
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
import io.github.muntashirakon.music.service.MusicService
|
||||
import io.github.muntashirakon.music.util.MusicUtil
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
import io.github.muntashirakon.music.util.ViewUtil
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator
|
||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager
|
||||
import com.h6ah4i.android.widget.advrecyclerview.swipeable.RecyclerViewSwipeManager
|
||||
import com.h6ah4i.android.widget.advrecyclerview.touchguard.RecyclerViewTouchActionGuardManager
|
||||
import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
|
||||
import kotlinx.android.synthetic.main.fragment_gradient_controls.*
|
||||
import kotlinx.android.synthetic.main.fragment_gradient_player.*
|
||||
import kotlinx.android.synthetic.main.status_bar.*
|
||||
|
||||
class GradientPlayerFragment : AbsPlayerFragment(), MusicProgressViewUpdateHelper.Callback,
|
||||
View.OnLayoutChangeListener, PopupMenu.OnMenuItemClickListener {
|
||||
private var lastColor: Int = 0
|
||||
private var lastPlaybackControlsColor: Int = 0
|
||||
private var lastDisabledPlaybackControlsColor: Int = 0
|
||||
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
||||
private var volumeFragment: VolumeFragment? = null
|
||||
private lateinit var wrappedAdapter: RecyclerView.Adapter<*>
|
||||
private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null
|
||||
private var recyclerViewSwipeManager: RecyclerViewSwipeManager? = null
|
||||
private var recyclerViewTouchActionGuardManager: RecyclerViewTouchActionGuardManager? = null
|
||||
private var playingQueueAdapter: PlayingQueueAdapter? = null
|
||||
private var updateIsFavoriteTask: AsyncTask<*, *, *>? = null
|
||||
private lateinit var linearLayoutManager: LinearLayoutManager
|
||||
|
||||
private val bottomSheetCallbackList = object : BottomSheetBehavior.BottomSheetCallback() {
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {
|
||||
(requireActivity() as AbsSlidingMusicPanelActivity).getBottomSheetBehavior()
|
||||
.setAllowDragging(false)
|
||||
|
||||
playerQueueSheet.setPadding(
|
||||
playerQueueSheet.paddingLeft,
|
||||
(slideOffset * status_bar.height).toInt(),
|
||||
playerQueueSheet.paddingRight,
|
||||
playerQueueSheet.paddingBottom
|
||||
)
|
||||
}
|
||||
|
||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||
val activity = requireActivity() as AbsSlidingMusicPanelActivity
|
||||
when (newState) {
|
||||
BottomSheetBehavior.STATE_EXPANDED,
|
||||
BottomSheetBehavior.STATE_DRAGGING -> {
|
||||
activity.getBottomSheetBehavior().setAllowDragging(false)
|
||||
}
|
||||
BottomSheetBehavior.STATE_COLLAPSED -> {
|
||||
resetToCurrentPosition()
|
||||
activity.getBottomSheetBehavior().setAllowDragging(true)
|
||||
}
|
||||
else -> {
|
||||
activity.getBottomSheetBehavior().setAllowDragging(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupFavourite() {
|
||||
songFavourite.setOnClickListener {
|
||||
toggleFavorite(MusicPlayerRemote.currentSong)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupMenu() {
|
||||
playerMenu.setOnClickListener {
|
||||
val popupMenu = PopupMenu(requireContext(), it)
|
||||
popupMenu.setOnMenuItemClickListener(this)
|
||||
popupMenu.inflate(R.menu.menu_player)
|
||||
popupMenu.show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupPanel() {
|
||||
if (!ViewCompat.isLaidOut(colorBackground) || colorBackground.isLayoutRequested) {
|
||||
colorBackground.addOnLayoutChangeListener(this)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_gradient_player, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
hideVolumeIfAvailable()
|
||||
setUpMusicControllers()
|
||||
setupPanel()
|
||||
setupRecyclerView()
|
||||
setupSheet()
|
||||
setupMenu()
|
||||
setupFavourite()
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private fun setupSheet() {
|
||||
getQueuePanel().addBottomSheetCallback(bottomSheetCallbackList)
|
||||
playerQueueSheet.setOnTouchListener { _, _ ->
|
||||
(requireActivity() as AbsSlidingMusicPanelActivity).getBottomSheetBehavior()
|
||||
.setAllowDragging(false)
|
||||
getQueuePanel().setAllowDragging(true)
|
||||
return@setOnTouchListener false
|
||||
}
|
||||
}
|
||||
|
||||
private fun getQueuePanel(): RetroBottomSheetBehavior<ConstraintLayout> {
|
||||
return RetroBottomSheetBehavior.from(playerQueueSheet) as RetroBottomSheetBehavior<ConstraintLayout>
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
progressViewUpdateHelper.start()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
recyclerViewDragDropManager?.cancelDrag()
|
||||
super.onPause()
|
||||
progressViewUpdateHelper.stop()
|
||||
|
||||
}
|
||||
|
||||
override fun playerToolbar(): Toolbar? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onShow() {
|
||||
|
||||
}
|
||||
|
||||
override fun onHide() {
|
||||
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
var wasExpanded = false
|
||||
if (getQueuePanel().state == BottomSheetBehavior.STATE_EXPANDED) {
|
||||
wasExpanded = getQueuePanel().state == BottomSheetBehavior.STATE_EXPANDED
|
||||
getQueuePanel().state = BottomSheetBehavior.STATE_COLLAPSED
|
||||
return wasExpanded
|
||||
}
|
||||
return wasExpanded
|
||||
}
|
||||
|
||||
override fun toolbarIconColor(): Int {
|
||||
return Color.WHITE
|
||||
}
|
||||
|
||||
override val paletteColor: Int
|
||||
get() = lastColor
|
||||
|
||||
override fun onColorChanged(color: MediaNotificationProcessor) {
|
||||
lastColor = color.backgroundColor
|
||||
callbacks?.onPaletteColorChanged()
|
||||
mask.backgroundTintList = ColorStateList.valueOf(color.backgroundColor)
|
||||
colorBackground.setBackgroundColor(color.backgroundColor)
|
||||
playerQueueSheet.setBackgroundColor(ColorUtil.darkenColor(color.backgroundColor))
|
||||
|
||||
lastPlaybackControlsColor = color.primaryTextColor
|
||||
lastDisabledPlaybackControlsColor = ColorUtil.withAlpha(color.primaryTextColor, 0.3f)
|
||||
|
||||
title.setTextColor(lastPlaybackControlsColor)
|
||||
text.setTextColor(lastDisabledPlaybackControlsColor)
|
||||
playPauseButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
songFavourite.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
queueIcon.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
playerMenu.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
songCurrentProgress.setTextColor(lastDisabledPlaybackControlsColor)
|
||||
songTotalTime.setTextColor(lastDisabledPlaybackControlsColor)
|
||||
nextSong.setTextColor(lastPlaybackControlsColor)
|
||||
songInfo.setTextColor(lastDisabledPlaybackControlsColor)
|
||||
|
||||
volumeFragment?.setTintableColor(lastPlaybackControlsColor.ripAlpha())
|
||||
ViewUtil.setProgressDrawable(progressSlider, color.primaryTextColor.ripAlpha(), true)
|
||||
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updatePrevNextColor()
|
||||
}
|
||||
|
||||
override fun toggleFavorite(song: Song) {
|
||||
super.toggleFavorite(song)
|
||||
MusicUtil.toggleFavorite(requireContext(), song)
|
||||
if (song.id == MusicPlayerRemote.currentSong.id) {
|
||||
updateFavorite()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFavoriteToggled() {
|
||||
toggleFavorite(MusicPlayerRemote.currentSong)
|
||||
}
|
||||
|
||||
private fun hideVolumeIfAvailable() {
|
||||
if (PreferenceUtil.isVolumeVisibilityMode) {
|
||||
childFragmentManager.beginTransaction()
|
||||
.replace(R.id.volumeFragmentContainer, VolumeFragment.newInstance())
|
||||
.commit()
|
||||
childFragmentManager.executePendingTransactions()
|
||||
volumeFragment =
|
||||
childFragmentManager.findFragmentById(R.id.volumeFragmentContainer) as VolumeFragment?
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
updateSong()
|
||||
updatePlayPauseDrawableState()
|
||||
updatePlayPauseDrawableState()
|
||||
updateQueue()
|
||||
}
|
||||
|
||||
override fun onPlayStateChanged() {
|
||||
updatePlayPauseDrawableState()
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged() {
|
||||
updateRepeatState()
|
||||
}
|
||||
|
||||
override fun onShuffleModeChanged() {
|
||||
updateShuffleState()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateSong()
|
||||
updateQueuePosition()
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
title.text = song.title
|
||||
text.text = song.artistName
|
||||
updateLabel()
|
||||
if (PreferenceUtil.isSongInfo) {
|
||||
songInfo.text = getSongInfo(song)
|
||||
songInfo.show()
|
||||
} else {
|
||||
songInfo.hide()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpMusicControllers() {
|
||||
setUpPlayPauseFab()
|
||||
setUpPrevNext()
|
||||
setUpRepeatButton()
|
||||
setUpShuffleButton()
|
||||
setUpProgressSlider()
|
||||
title.isSelected = true
|
||||
text.isSelected = true
|
||||
}
|
||||
|
||||
private fun updatePlayPauseDrawableState() {
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
playPauseButton.setImageResource(R.drawable.ic_pause_white_64dp)
|
||||
} else {
|
||||
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_64dp)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun setUpPlayPauseFab() {
|
||||
playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
||||
}
|
||||
|
||||
private fun setUpPrevNext() {
|
||||
updatePrevNextColor()
|
||||
nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
|
||||
previousButton.setOnClickListener { MusicPlayerRemote.back() }
|
||||
}
|
||||
|
||||
private fun updatePrevNextColor() {
|
||||
nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
|
||||
private fun setUpShuffleButton() {
|
||||
shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
|
||||
}
|
||||
|
||||
fun updateShuffleState() {
|
||||
when (MusicPlayerRemote.shuffleMode) {
|
||||
MusicService.SHUFFLE_MODE_SHUFFLE ->
|
||||
shuffleButton.setColorFilter(
|
||||
lastPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
else -> shuffleButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpRepeatButton() {
|
||||
repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
|
||||
}
|
||||
|
||||
fun updateRepeatState() {
|
||||
when (MusicPlayerRemote.repeatMode) {
|
||||
MusicService.REPEAT_MODE_NONE -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
MusicService.REPEAT_MODE_ALL -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
MusicService.REPEAT_MODE_THIS -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateLabel() {
|
||||
(MusicPlayerRemote.playingQueue.size - 1).apply {
|
||||
if (this == (MusicPlayerRemote.position)) {
|
||||
nextSong.hide()
|
||||
} else {
|
||||
val title = MusicPlayerRemote.playingQueue[MusicPlayerRemote.position + 1].title
|
||||
nextSong.apply {
|
||||
text = "Next: $title"
|
||||
show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLayoutChange(
|
||||
v: View?,
|
||||
left: Int,
|
||||
top: Int,
|
||||
right: Int,
|
||||
bottom: Int,
|
||||
oldLeft: Int,
|
||||
oldTop: Int,
|
||||
oldRight: Int,
|
||||
oldBottom: Int
|
||||
) {
|
||||
val panel = getQueuePanel()
|
||||
panel.peekHeight = container.height
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
playingQueueAdapter = PlayingQueueAdapter(
|
||||
requireActivity() as AppCompatActivity,
|
||||
MusicPlayerRemote.playingQueue.toMutableList(),
|
||||
MusicPlayerRemote.position,
|
||||
R.layout.item_queue
|
||||
)
|
||||
linearLayoutManager = LinearLayoutManager(requireContext())
|
||||
recyclerViewTouchActionGuardManager = RecyclerViewTouchActionGuardManager()
|
||||
recyclerViewDragDropManager = RecyclerViewDragDropManager()
|
||||
recyclerViewSwipeManager = RecyclerViewSwipeManager()
|
||||
|
||||
val animator = DraggableItemAnimator()
|
||||
animator.supportsChangeAnimations = false
|
||||
wrappedAdapter =
|
||||
recyclerViewDragDropManager?.createWrappedAdapter(playingQueueAdapter!!) as RecyclerView.Adapter<*>
|
||||
wrappedAdapter =
|
||||
recyclerViewSwipeManager?.createWrappedAdapter(wrappedAdapter) as RecyclerView.Adapter<*>
|
||||
recyclerView.layoutManager = linearLayoutManager
|
||||
recyclerView.adapter = wrappedAdapter
|
||||
recyclerView.itemAnimator = animator
|
||||
recyclerViewTouchActionGuardManager?.attachRecyclerView(recyclerView)
|
||||
recyclerViewDragDropManager?.attachRecyclerView(recyclerView)
|
||||
recyclerViewSwipeManager?.attachRecyclerView(recyclerView)
|
||||
|
||||
linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
getQueuePanel().removeBottomSheetCallback(bottomSheetCallbackList)
|
||||
if (recyclerViewDragDropManager != null) {
|
||||
recyclerViewDragDropManager?.release()
|
||||
recyclerViewDragDropManager = null
|
||||
}
|
||||
|
||||
if (recyclerViewSwipeManager != null) {
|
||||
recyclerViewSwipeManager?.release()
|
||||
recyclerViewSwipeManager = null
|
||||
}
|
||||
|
||||
WrapperAdapterUtils.releaseAll(wrappedAdapter)
|
||||
}
|
||||
|
||||
private fun updateQueuePosition() {
|
||||
playingQueueAdapter?.setCurrent(MusicPlayerRemote.position)
|
||||
resetToCurrentPosition()
|
||||
}
|
||||
|
||||
private fun updateQueue() {
|
||||
playingQueueAdapter?.swapDataSet(MusicPlayerRemote.playingQueue, MusicPlayerRemote.position)
|
||||
resetToCurrentPosition()
|
||||
}
|
||||
|
||||
private fun resetToCurrentPosition() {
|
||||
recyclerView.stopScroll()
|
||||
linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
|
||||
}
|
||||
|
||||
fun setUpProgressSlider() {
|
||||
progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
MusicPlayerRemote.seekTo(progress)
|
||||
onUpdateProgressViews(
|
||||
MusicPlayerRemote.songProgressMillis,
|
||||
MusicPlayerRemote.songDurationMillis
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
progressSlider.max = total
|
||||
|
||||
val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress)
|
||||
animator.duration = AbsPlayerControlsFragment.SLIDER_ANIMATION_TIME
|
||||
animator.interpolator = LinearInterpolator()
|
||||
animator.start()
|
||||
|
||||
songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
|
||||
songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
|
||||
}
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private fun updateFavorite() {
|
||||
if (updateIsFavoriteTask != null) {
|
||||
updateIsFavoriteTask?.cancel(false)
|
||||
}
|
||||
updateIsFavoriteTask =
|
||||
object : AsyncTask<Song, Void, Boolean>() {
|
||||
override fun doInBackground(vararg params: Song): Boolean? {
|
||||
val activity = activity
|
||||
return if (activity != null) {
|
||||
MusicUtil.isFavorite(requireActivity(), params[0])
|
||||
} else {
|
||||
cancel(false)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPostExecute(isFavorite: Boolean?) {
|
||||
val activity = activity
|
||||
if (activity != null) {
|
||||
val res = if (isFavorite!!)
|
||||
R.drawable.ic_favorite_white_24dp
|
||||
else
|
||||
R.drawable.ic_favorite_border_white_24dp
|
||||
|
||||
val drawable = TintHelper.createTintedDrawable(activity, res, Color.WHITE)
|
||||
songFavourite?.setImageDrawable(drawable)
|
||||
}
|
||||
}
|
||||
}.execute(MusicPlayerRemote.currentSong)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
package io.github.muntashirakon.music.fragments.player.home
|
||||
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
import io.github.muntashirakon.music.util.MusicUtil
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_home_player.*
|
||||
|
||||
|
||||
class HomePlayerFragment : AbsPlayerFragment(), MusicProgressViewUpdateHelper.Callback {
|
||||
private var lastColor: Int = 0
|
||||
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_home_player, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpPlayerToolbar()
|
||||
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
progressViewUpdateHelper.start()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
progressViewUpdateHelper.stop()
|
||||
|
||||
}
|
||||
|
||||
override fun playerToolbar(): Toolbar? {
|
||||
return playerToolbar
|
||||
}
|
||||
|
||||
override fun onShow() {
|
||||
|
||||
}
|
||||
|
||||
override fun onHide() {
|
||||
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
title.text = song.title
|
||||
text.text = song.artistName
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun toolbarIconColor(): Int {
|
||||
return Color.WHITE
|
||||
}
|
||||
|
||||
override val paletteColor: Int
|
||||
get() = lastColor
|
||||
|
||||
override fun onColorChanged(color: MediaNotificationProcessor) {
|
||||
lastColor = color.backgroundColor
|
||||
callbacks?.onPaletteColorChanged()
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
playerToolbar,
|
||||
Color.WHITE,
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
||||
override fun toggleFavorite(song: Song) {
|
||||
super.toggleFavorite(song)
|
||||
if (song.id == MusicPlayerRemote.currentSong.id) {
|
||||
updateIsFavorite()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFavoriteToggled() {
|
||||
toggleFavorite(MusicPlayerRemote.currentSong)
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
songTotalTime.text = MusicUtil.getReadableDurationString(progress.toLong())
|
||||
}
|
||||
|
||||
private fun setUpPlayerToolbar() {
|
||||
playerToolbar.inflateMenu(R.menu.menu_player)
|
||||
playerToolbar.setNavigationOnClickListener { requireActivity().onBackPressed() }
|
||||
playerToolbar.setOnMenuItemClickListener(this)
|
||||
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
playerToolbar,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package io.github.muntashirakon.music.fragments.player.lockscreen
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.graphics.PorterDuff
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.SeekBar
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.extensions.applyColor
|
||||
import io.github.muntashirakon.music.extensions.ripAlpha
|
||||
import io.github.muntashirakon.music.extensions.textColorSecondary
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerControlsFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper
|
||||
import io.github.muntashirakon.music.helper.PlayPauseButtonOnClickHandler
|
||||
import io.github.muntashirakon.music.misc.SimpleOnSeekbarChangeListener
|
||||
import io.github.muntashirakon.music.service.MusicService
|
||||
import io.github.muntashirakon.music.util.MusicUtil
|
||||
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_lock_screen_playback_controls.*
|
||||
|
||||
/**
|
||||
* @author Hemanth S (h4h13).
|
||||
*/
|
||||
class LockScreenPlayerControlsFragment : AbsPlayerControlsFragment() {
|
||||
|
||||
private var progressViewUpdateHelper: MusicProgressViewUpdateHelper? = null
|
||||
private var lastPlaybackControlsColor: Int = 0
|
||||
private var lastDisabledPlaybackControlsColor: Int = 0
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
|
||||
return inflater.inflate(R.layout.fragment_lock_screen_playback_controls, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpMusicControllers()
|
||||
title.isSelected = true
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
title.text = song.title
|
||||
text.text = String.format("%s - %s", song.artistName, song.albumName)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
progressViewUpdateHelper?.start()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
progressViewUpdateHelper?.stop()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
updatePlayPauseDrawableState()
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onPlayStateChanged() {
|
||||
updatePlayPauseDrawableState()
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged() {
|
||||
updateRepeatState()
|
||||
}
|
||||
|
||||
override fun onShuffleModeChanged() {
|
||||
updateShuffleState()
|
||||
}
|
||||
|
||||
override fun setColor(color: MediaNotificationProcessor) {
|
||||
|
||||
val colorBg = ATHUtil.resolveColor(requireContext(), android.R.attr.colorBackground)
|
||||
if (ColorUtil.isColorLight(colorBg)) {
|
||||
lastPlaybackControlsColor =
|
||||
MaterialValueHelper.getSecondaryTextColor(requireContext(), true)
|
||||
lastDisabledPlaybackControlsColor =
|
||||
MaterialValueHelper.getSecondaryDisabledTextColor(requireContext(), true)
|
||||
} else {
|
||||
lastPlaybackControlsColor =
|
||||
MaterialValueHelper.getPrimaryTextColor(requireContext(), false)
|
||||
lastDisabledPlaybackControlsColor =
|
||||
MaterialValueHelper.getPrimaryDisabledTextColor(requireContext(), false)
|
||||
}
|
||||
|
||||
val colorFinal = if (PreferenceUtil.isAdaptiveColor) {
|
||||
color.primaryTextColor
|
||||
} else {
|
||||
textColorSecondary()
|
||||
}.ripAlpha()
|
||||
|
||||
volumeFragment?.setTintable(colorFinal)
|
||||
progressSlider.applyColor(colorFinal)
|
||||
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updatePrevNextColor()
|
||||
|
||||
val isDark = ColorUtil.isColorLight(colorFinal)
|
||||
text.setTextColor(colorFinal)
|
||||
|
||||
TintHelper.setTintAuto(
|
||||
playPauseButton,
|
||||
MaterialValueHelper.getPrimaryTextColor(requireContext(), isDark),
|
||||
false
|
||||
)
|
||||
TintHelper.setTintAuto(playPauseButton, colorFinal, true)
|
||||
}
|
||||
|
||||
private fun setUpPlayPauseFab() {
|
||||
playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
||||
}
|
||||
|
||||
private fun updatePlayPauseDrawableState() {
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp)
|
||||
} else {
|
||||
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_32dp)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpMusicControllers() {
|
||||
setUpPlayPauseFab()
|
||||
setUpPrevNext()
|
||||
setUpProgressSlider()
|
||||
setUpShuffleButton()
|
||||
setUpRepeatButton()
|
||||
}
|
||||
|
||||
private fun setUpPrevNext() {
|
||||
updatePrevNextColor()
|
||||
nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
|
||||
previousButton.setOnClickListener { MusicPlayerRemote.back() }
|
||||
}
|
||||
|
||||
private fun updatePrevNextColor() {
|
||||
nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
|
||||
private fun setUpShuffleButton() {
|
||||
shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
|
||||
}
|
||||
|
||||
override fun updateShuffleState() {
|
||||
when (MusicPlayerRemote.shuffleMode) {
|
||||
MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton.setColorFilter(
|
||||
lastPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
else -> shuffleButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpRepeatButton() {
|
||||
repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
|
||||
}
|
||||
|
||||
override fun updateRepeatState() {
|
||||
when (MusicPlayerRemote.repeatMode) {
|
||||
MusicService.REPEAT_MODE_NONE -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
MusicService.REPEAT_MODE_ALL -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
MusicService.REPEAT_MODE_THIS -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override fun show() {
|
||||
playPauseButton!!.animate()
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.rotation(360f)
|
||||
.setInterpolator(DecelerateInterpolator())
|
||||
.start()
|
||||
}
|
||||
|
||||
public override fun hide() {
|
||||
if (playPauseButton != null) {
|
||||
playPauseButton!!.apply {
|
||||
scaleX = 0f
|
||||
scaleY = 0f
|
||||
rotation = 0f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setUpProgressSlider() {
|
||||
progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
MusicPlayerRemote.seekTo(progress)
|
||||
onUpdateProgressViews(
|
||||
MusicPlayerRemote.songProgressMillis,
|
||||
MusicPlayerRemote.songDurationMillis
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
progressSlider.max = total
|
||||
|
||||
val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress)
|
||||
animator.duration = SLIDER_ANIMATION_TIME
|
||||
animator.interpolator = LinearInterpolator()
|
||||
animator.start()
|
||||
|
||||
songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
|
||||
songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,241 @@
|
|||
package io.github.muntashirakon.music.fragments.player.material
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.graphics.PorterDuff
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.SeekBar
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.extensions.*
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerControlsFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper
|
||||
import io.github.muntashirakon.music.helper.PlayPauseButtonOnClickHandler
|
||||
import io.github.muntashirakon.music.misc.SimpleOnSeekbarChangeListener
|
||||
import io.github.muntashirakon.music.service.MusicService
|
||||
import io.github.muntashirakon.music.util.MusicUtil
|
||||
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_material_playback_controls.*
|
||||
|
||||
/**
|
||||
* @author Hemanth S (h4h13).
|
||||
*/
|
||||
class MaterialControlsFragment : AbsPlayerControlsFragment() {
|
||||
|
||||
private var lastPlaybackControlsColor: Int = 0
|
||||
private var lastDisabledPlaybackControlsColor: Int = 0
|
||||
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_material_playback_controls, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpMusicControllers()
|
||||
title.isSelected = true
|
||||
text.isSelected = true
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
title.text = song.title
|
||||
text.text = song.artistName
|
||||
|
||||
if (PreferenceUtil.isSongInfo) {
|
||||
songInfo.text = getSongInfo(song)
|
||||
songInfo.show()
|
||||
} else {
|
||||
songInfo.hide()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
progressViewUpdateHelper.start()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
progressViewUpdateHelper.stop()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
updatePlayPauseDrawableState()
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onPlayStateChanged() {
|
||||
updatePlayPauseDrawableState()
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged() {
|
||||
updateRepeatState()
|
||||
}
|
||||
|
||||
override fun onShuffleModeChanged() {
|
||||
updateShuffleState()
|
||||
}
|
||||
|
||||
override fun setColor(color: MediaNotificationProcessor) {
|
||||
if (ATHUtil.isWindowBackgroundDark(requireContext())) {
|
||||
lastPlaybackControlsColor =
|
||||
MaterialValueHelper.getPrimaryTextColor(requireContext(), false)
|
||||
lastDisabledPlaybackControlsColor =
|
||||
MaterialValueHelper.getPrimaryDisabledTextColor(requireContext(), false)
|
||||
} else {
|
||||
lastPlaybackControlsColor =
|
||||
MaterialValueHelper.getSecondaryTextColor(requireContext(), true)
|
||||
lastDisabledPlaybackControlsColor =
|
||||
MaterialValueHelper.getSecondaryDisabledTextColor(requireContext(), true)
|
||||
}
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
|
||||
val colorFinal = if (PreferenceUtil.isAdaptiveColor) {
|
||||
lastPlaybackControlsColor
|
||||
} else {
|
||||
textColorSecondary()
|
||||
}.ripAlpha()
|
||||
|
||||
text.setTextColor(colorFinal)
|
||||
progressSlider.applyColor(colorFinal)
|
||||
|
||||
volumeFragment?.setTintable(colorFinal)
|
||||
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updatePlayPauseColor()
|
||||
updatePrevNextColor()
|
||||
}
|
||||
|
||||
private fun updatePlayPauseColor() {
|
||||
playPauseButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
|
||||
private fun setUpPlayPauseFab() {
|
||||
playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
||||
}
|
||||
|
||||
private fun updatePlayPauseDrawableState() {
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
playPauseButton.setImageResource(R.drawable.ic_pause_sharp_white_64dp)
|
||||
} else {
|
||||
playPauseButton.setImageResource(R.drawable.ic_play_arrow_sharp_white_64dp)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpMusicControllers() {
|
||||
setUpPlayPauseFab()
|
||||
setUpPrevNext()
|
||||
setUpRepeatButton()
|
||||
setUpShuffleButton()
|
||||
setUpProgressSlider()
|
||||
}
|
||||
|
||||
private fun setUpPrevNext() {
|
||||
updatePrevNextColor()
|
||||
nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
|
||||
previousButton.setOnClickListener { MusicPlayerRemote.back() }
|
||||
}
|
||||
|
||||
private fun updatePrevNextColor() {
|
||||
nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
|
||||
private fun setUpShuffleButton() {
|
||||
shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
|
||||
}
|
||||
|
||||
override fun updateShuffleState() {
|
||||
when (MusicPlayerRemote.shuffleMode) {
|
||||
MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton.setColorFilter(
|
||||
lastPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
else -> shuffleButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpRepeatButton() {
|
||||
repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
|
||||
}
|
||||
|
||||
override fun updateRepeatState() {
|
||||
when (MusicPlayerRemote.repeatMode) {
|
||||
MusicService.REPEAT_MODE_NONE -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_sharp_white_24dp)
|
||||
repeatButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
MusicService.REPEAT_MODE_ALL -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_sharp_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
MusicService.REPEAT_MODE_THIS -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_one_sharp_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override fun show() {
|
||||
}
|
||||
|
||||
public override fun hide() {
|
||||
}
|
||||
|
||||
override fun setUpProgressSlider() {
|
||||
progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
MusicPlayerRemote.seekTo(progress)
|
||||
onUpdateProgressViews(
|
||||
MusicPlayerRemote.songProgressMillis,
|
||||
MusicPlayerRemote.songDurationMillis
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
progressSlider.max = total
|
||||
|
||||
val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress)
|
||||
animator.duration = SLIDER_ANIMATION_TIME
|
||||
animator.interpolator = LinearInterpolator()
|
||||
animator.start()
|
||||
|
||||
songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
|
||||
songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
package io.github.muntashirakon.music.fragments.player.material
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerFragment
|
||||
import io.github.muntashirakon.music.fragments.player.PlayerAlbumCoverFragment
|
||||
import io.github.muntashirakon.music.fragments.player.normal.PlayerFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_material.*
|
||||
|
||||
/**
|
||||
* @author Hemanth S (h4h13).
|
||||
*/
|
||||
class MaterialFragment : AbsPlayerFragment() {
|
||||
|
||||
override fun playerToolbar(): Toolbar {
|
||||
return playerToolbar
|
||||
}
|
||||
|
||||
private var lastColor: Int = 0
|
||||
|
||||
override val paletteColor: Int
|
||||
get() = lastColor
|
||||
|
||||
private lateinit var playbackControlsFragment: MaterialControlsFragment
|
||||
|
||||
override fun onShow() {
|
||||
playbackControlsFragment.show()
|
||||
}
|
||||
|
||||
override fun onHide() {
|
||||
playbackControlsFragment.hide()
|
||||
onBackPressed()
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun toolbarIconColor(): Int {
|
||||
return ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal)
|
||||
}
|
||||
|
||||
override fun onColorChanged(color: MediaNotificationProcessor) {
|
||||
playbackControlsFragment.setColor(color)
|
||||
lastColor = color.backgroundColor
|
||||
callbacks?.onPaletteColorChanged()
|
||||
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
playerToolbar,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
||||
override fun toggleFavorite(song: Song) {
|
||||
super.toggleFavorite(song)
|
||||
if (song.id == MusicPlayerRemote.currentSong.id) {
|
||||
updateIsFavorite()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFavoriteToggled() {
|
||||
toggleFavorite(MusicPlayerRemote.currentSong)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_material, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpSubFragments()
|
||||
setUpPlayerToolbar()
|
||||
}
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
playbackControlsFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as MaterialControlsFragment
|
||||
val playerAlbumCoverFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment
|
||||
playerAlbumCoverFragment.setCallbacks(this)
|
||||
}
|
||||
|
||||
private fun setUpPlayerToolbar() {
|
||||
playerToolbar.apply {
|
||||
inflateMenu(R.menu.menu_player)
|
||||
setNavigationOnClickListener { requireActivity().onBackPressed() }
|
||||
setOnMenuItemClickListener(this@MaterialFragment)
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
this,
|
||||
ATHUtil.resolveColor(context, R.attr.colorControlNormal),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
updateIsFavorite()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
updateIsFavorite()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun newInstance(): PlayerFragment {
|
||||
return PlayerFragment()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
package io.github.muntashirakon.music.fragments.player.normal
|
||||
|
||||
import android.animation.ArgbEvaluator
|
||||
import android.animation.ValueAnimator
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerFragment
|
||||
import io.github.muntashirakon.music.fragments.player.PlayerAlbumCoverFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
import io.github.muntashirakon.music.util.ViewUtil
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import io.github.muntashirakon.music.views.DrawableGradient
|
||||
import kotlinx.android.synthetic.main.fragment_player.*
|
||||
|
||||
|
||||
class PlayerFragment : AbsPlayerFragment() {
|
||||
|
||||
private var lastColor: Int = 0
|
||||
override val paletteColor: Int
|
||||
get() = lastColor
|
||||
|
||||
private lateinit var controlsFragment: PlayerPlaybackControlsFragment
|
||||
private var valueAnimator: ValueAnimator? = null
|
||||
|
||||
|
||||
private fun colorize(i: Int) {
|
||||
if (valueAnimator != null) {
|
||||
valueAnimator?.cancel()
|
||||
}
|
||||
|
||||
valueAnimator = ValueAnimator.ofObject(
|
||||
ArgbEvaluator(),
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorSurface),
|
||||
i
|
||||
)
|
||||
valueAnimator?.addUpdateListener { animation ->
|
||||
if (isAdded) {
|
||||
val drawable = DrawableGradient(
|
||||
GradientDrawable.Orientation.TOP_BOTTOM,
|
||||
intArrayOf(
|
||||
animation.animatedValue as Int,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorSurface)
|
||||
), 0
|
||||
)
|
||||
colorGradientBackground?.background = drawable
|
||||
}
|
||||
}
|
||||
valueAnimator?.setDuration(ViewUtil.RETRO_MUSIC_ANIM_TIME.toLong())?.start()
|
||||
}
|
||||
|
||||
override fun onShow() {
|
||||
controlsFragment.show()
|
||||
}
|
||||
|
||||
override fun onHide() {
|
||||
controlsFragment.hide()
|
||||
onBackPressed()
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun toolbarIconColor(): Int {
|
||||
return ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal)
|
||||
}
|
||||
|
||||
override fun onColorChanged(color: MediaNotificationProcessor) {
|
||||
controlsFragment.setColor(color)
|
||||
lastColor = color.backgroundColor
|
||||
callbacks?.onPaletteColorChanged()
|
||||
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
playerToolbar,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
requireActivity()
|
||||
)
|
||||
|
||||
if (PreferenceUtil.isAdaptiveColor) {
|
||||
colorize(color.backgroundColor)
|
||||
}
|
||||
}
|
||||
|
||||
override fun toggleFavorite(song: Song) {
|
||||
super.toggleFavorite(song)
|
||||
if (song.id == MusicPlayerRemote.currentSong.id) {
|
||||
updateIsFavorite()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFavoriteToggled() {
|
||||
toggleFavorite(MusicPlayerRemote.currentSong)
|
||||
}
|
||||
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
|
||||
return inflater.inflate(R.layout.fragment_player, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpSubFragments()
|
||||
setUpPlayerToolbar()
|
||||
}
|
||||
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
controlsFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as PlayerPlaybackControlsFragment
|
||||
val playerAlbumCoverFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment
|
||||
playerAlbumCoverFragment.setCallbacks(this)
|
||||
}
|
||||
|
||||
private fun setUpPlayerToolbar() {
|
||||
playerToolbar.inflateMenu(R.menu.menu_player)
|
||||
playerToolbar.setNavigationOnClickListener { requireActivity().onBackPressed() }
|
||||
playerToolbar.setOnMenuItemClickListener(this)
|
||||
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
playerToolbar,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
updateIsFavorite()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
updateIsFavorite()
|
||||
}
|
||||
|
||||
override fun playerToolbar(): Toolbar {
|
||||
return playerToolbar
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun newInstance(): PlayerFragment {
|
||||
return PlayerFragment()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,264 @@
|
|||
package io.github.muntashirakon.music.fragments.player.normal
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.graphics.PorterDuff
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.SeekBar
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.extensions.applyColor
|
||||
import io.github.muntashirakon.music.extensions.hide
|
||||
import io.github.muntashirakon.music.extensions.ripAlpha
|
||||
import io.github.muntashirakon.music.extensions.show
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerControlsFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper
|
||||
import io.github.muntashirakon.music.helper.PlayPauseButtonOnClickHandler
|
||||
import io.github.muntashirakon.music.misc.SimpleOnSeekbarChangeListener
|
||||
import io.github.muntashirakon.music.service.MusicService
|
||||
import io.github.muntashirakon.music.util.MusicUtil
|
||||
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_player_playback_controls.*
|
||||
|
||||
class PlayerPlaybackControlsFragment : AbsPlayerControlsFragment() {
|
||||
|
||||
private var lastPlaybackControlsColor: Int = 0
|
||||
private var lastDisabledPlaybackControlsColor: Int = 0
|
||||
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_player_playback_controls, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpMusicControllers()
|
||||
playPauseButton.setOnClickListener {
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
MusicPlayerRemote.pauseSong()
|
||||
} else {
|
||||
MusicPlayerRemote.resumePlaying()
|
||||
}
|
||||
showBonceAnimation(playPauseButton)
|
||||
}
|
||||
title.isSelected = true
|
||||
|
||||
}
|
||||
|
||||
override fun setColor(color: MediaNotificationProcessor) {
|
||||
val colorBg = ATHUtil.resolveColor(requireContext(), android.R.attr.colorBackground)
|
||||
if (ColorUtil.isColorLight(colorBg)) {
|
||||
lastPlaybackControlsColor =
|
||||
MaterialValueHelper.getSecondaryTextColor(requireContext(), true)
|
||||
lastDisabledPlaybackControlsColor =
|
||||
MaterialValueHelper.getSecondaryDisabledTextColor(requireContext(), true)
|
||||
} else {
|
||||
lastPlaybackControlsColor =
|
||||
MaterialValueHelper.getPrimaryTextColor(requireContext(), false)
|
||||
lastDisabledPlaybackControlsColor =
|
||||
MaterialValueHelper.getPrimaryDisabledTextColor(requireContext(), false)
|
||||
}
|
||||
|
||||
val colorFinal = if (PreferenceUtil.isAdaptiveColor) {
|
||||
color.primaryTextColor
|
||||
} else {
|
||||
ThemeStore.accentColor(requireContext())
|
||||
}.ripAlpha()
|
||||
|
||||
TintHelper.setTintAuto(
|
||||
playPauseButton,
|
||||
MaterialValueHelper.getPrimaryTextColor(
|
||||
requireContext(),
|
||||
ColorUtil.isColorLight(colorFinal)
|
||||
),
|
||||
false
|
||||
)
|
||||
TintHelper.setTintAuto(playPauseButton, colorFinal, true)
|
||||
progressSlider.applyColor(colorFinal)
|
||||
volumeFragment?.setTintable(colorFinal)
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updatePrevNextColor()
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
title.text = song.title
|
||||
text.text = song.artistName
|
||||
|
||||
if (PreferenceUtil.isSongInfo) {
|
||||
songInfo.text = getSongInfo(song)
|
||||
songInfo.show()
|
||||
} else {
|
||||
songInfo.hide()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
progressViewUpdateHelper.start()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
progressViewUpdateHelper.stop()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
updatePlayPauseDrawableState()
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onPlayStateChanged() {
|
||||
updatePlayPauseDrawableState()
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged() {
|
||||
updateRepeatState()
|
||||
}
|
||||
|
||||
override fun onShuffleModeChanged() {
|
||||
updateShuffleState()
|
||||
}
|
||||
|
||||
private fun setUpPlayPauseFab() {
|
||||
playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
||||
}
|
||||
|
||||
private fun updatePlayPauseDrawableState() {
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp)
|
||||
} else {
|
||||
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_24dp)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpMusicControllers() {
|
||||
setUpPlayPauseFab()
|
||||
setUpPrevNext()
|
||||
setUpRepeatButton()
|
||||
setUpShuffleButton()
|
||||
setUpProgressSlider()
|
||||
}
|
||||
|
||||
private fun setUpPrevNext() {
|
||||
updatePrevNextColor()
|
||||
nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
|
||||
previousButton.setOnClickListener { MusicPlayerRemote.back() }
|
||||
}
|
||||
|
||||
private fun updatePrevNextColor() {
|
||||
nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
|
||||
private fun setUpShuffleButton() {
|
||||
shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
|
||||
}
|
||||
|
||||
override fun updateShuffleState() {
|
||||
when (MusicPlayerRemote.shuffleMode) {
|
||||
MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton.setColorFilter(
|
||||
lastPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
else -> shuffleButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpRepeatButton() {
|
||||
repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
|
||||
}
|
||||
|
||||
override fun updateRepeatState() {
|
||||
when (MusicPlayerRemote.repeatMode) {
|
||||
MusicService.REPEAT_MODE_NONE -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
MusicService.REPEAT_MODE_ALL -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
MusicService.REPEAT_MODE_THIS -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override fun show() {
|
||||
playPauseButton.animate()
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.rotation(360f)
|
||||
.setInterpolator(DecelerateInterpolator())
|
||||
.start()
|
||||
}
|
||||
|
||||
public override fun hide() {
|
||||
playPauseButton.apply {
|
||||
scaleX = 0f
|
||||
scaleY = 0f
|
||||
rotation = 0f
|
||||
}
|
||||
}
|
||||
|
||||
override fun setUpProgressSlider() {
|
||||
progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
MusicPlayerRemote.seekTo(progress)
|
||||
onUpdateProgressViews(
|
||||
MusicPlayerRemote.songProgressMillis,
|
||||
MusicPlayerRemote.songDurationMillis
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
progressSlider.max = total
|
||||
|
||||
val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress)
|
||||
animator.duration = SLIDER_ANIMATION_TIME
|
||||
animator.interpolator = LinearInterpolator()
|
||||
animator.start()
|
||||
|
||||
songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
|
||||
songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package io.github.muntashirakon.music.fragments.player.peak
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.graphics.Color
|
||||
import android.graphics.PorterDuff
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.SeekBar
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.extensions.applyColor
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerControlsFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper
|
||||
import io.github.muntashirakon.music.helper.PlayPauseButtonOnClickHandler
|
||||
import io.github.muntashirakon.music.misc.SimpleOnSeekbarChangeListener
|
||||
import io.github.muntashirakon.music.service.MusicService
|
||||
import io.github.muntashirakon.music.util.MusicUtil
|
||||
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_peak_control_player.*
|
||||
|
||||
/**
|
||||
* Created by hemanths on 2019-10-04.
|
||||
*/
|
||||
|
||||
class PeakPlayerControlFragment : AbsPlayerControlsFragment() {
|
||||
|
||||
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
||||
private var lastPlaybackControlsColor: Int = 0
|
||||
private var lastDisabledPlaybackControlsColor: Int = 0
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
progressViewUpdateHelper.start()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
progressViewUpdateHelper.stop()
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_peak_control_player, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(
|
||||
view: View,
|
||||
savedInstanceState: Bundle?
|
||||
) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpMusicControllers()
|
||||
}
|
||||
|
||||
override fun show() {
|
||||
}
|
||||
|
||||
override fun hide() {
|
||||
}
|
||||
|
||||
override fun setColor(color: MediaNotificationProcessor) {
|
||||
val controlsColor =
|
||||
if (PreferenceUtil.isAdaptiveColor) {
|
||||
color.primaryTextColor
|
||||
} else {
|
||||
ThemeStore.accentColor(requireContext())
|
||||
}
|
||||
progressSlider.applyColor(controlsColor)
|
||||
volumeFragment?.setTintableColor(controlsColor)
|
||||
playPauseButton.setColorFilter(controlsColor, PorterDuff.Mode.SRC_IN)
|
||||
nextButton.setColorFilter(controlsColor, PorterDuff.Mode.SRC_IN)
|
||||
previousButton.setColorFilter(controlsColor, PorterDuff.Mode.SRC_IN)
|
||||
|
||||
if (!ATHUtil.isWindowBackgroundDark(requireContext())) {
|
||||
lastPlaybackControlsColor =
|
||||
MaterialValueHelper.getSecondaryTextColor(requireContext(), true)
|
||||
lastDisabledPlaybackControlsColor =
|
||||
MaterialValueHelper.getSecondaryDisabledTextColor(requireContext(), true)
|
||||
} else {
|
||||
lastPlaybackControlsColor =
|
||||
MaterialValueHelper.getPrimaryTextColor(requireContext(), false)
|
||||
lastDisabledPlaybackControlsColor =
|
||||
MaterialValueHelper.getPrimaryDisabledTextColor(requireContext(), false)
|
||||
}
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
}
|
||||
|
||||
private fun updatePlayPauseDrawableState() {
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp)
|
||||
} else {
|
||||
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_32dp)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpMusicControllers() {
|
||||
setUpPlayPauseFab()
|
||||
setUpPrevNext()
|
||||
setUpRepeatButton()
|
||||
setUpShuffleButton()
|
||||
setUpProgressSlider()
|
||||
}
|
||||
|
||||
private fun setUpShuffleButton() {
|
||||
shuffleButton.setOnClickListener {
|
||||
MusicPlayerRemote.toggleShuffleMode()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpRepeatButton() {
|
||||
repeatButton.setOnClickListener {
|
||||
MusicPlayerRemote.cycleRepeatMode()
|
||||
}
|
||||
}
|
||||
|
||||
override fun setUpProgressSlider() {
|
||||
progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
MusicPlayerRemote.seekTo(progress)
|
||||
onUpdateProgressViews(
|
||||
MusicPlayerRemote.songProgressMillis,
|
||||
MusicPlayerRemote.songDurationMillis
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
progressSlider.max = total
|
||||
|
||||
val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress)
|
||||
animator.duration = SLIDER_ANIMATION_TIME
|
||||
animator.interpolator = LinearInterpolator()
|
||||
animator.start()
|
||||
|
||||
songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
|
||||
songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
|
||||
}
|
||||
|
||||
private fun setUpPlayPauseFab() {
|
||||
TintHelper.setTintAuto(playPauseButton, Color.WHITE, true)
|
||||
TintHelper.setTintAuto(playPauseButton, Color.BLACK, false)
|
||||
playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
||||
}
|
||||
|
||||
private fun setUpPrevNext() {
|
||||
updatePrevNextColor()
|
||||
nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
|
||||
previousButton.setOnClickListener { MusicPlayerRemote.back() }
|
||||
}
|
||||
|
||||
private fun updatePrevNextColor() {
|
||||
nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
|
||||
override fun updateShuffleState() {
|
||||
when (MusicPlayerRemote.shuffleMode) {
|
||||
MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton.setColorFilter(
|
||||
lastPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
else -> shuffleButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateRepeatState() {
|
||||
when (MusicPlayerRemote.repeatMode) {
|
||||
MusicService.REPEAT_MODE_NONE -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
MusicService.REPEAT_MODE_ALL -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
MusicService.REPEAT_MODE_THIS -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPlayStateChanged() {
|
||||
super.onPlayStateChanged()
|
||||
updatePlayPauseDrawableState()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
updatePlayPauseDrawableState()
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged() {
|
||||
updateRepeatState()
|
||||
}
|
||||
|
||||
override fun onShuffleModeChanged() {
|
||||
updateShuffleState()
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package io.github.muntashirakon.music.fragments.player.peak
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.extensions.hide
|
||||
import io.github.muntashirakon.music.extensions.show
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerFragment
|
||||
import io.github.muntashirakon.music.fragments.player.PlayerAlbumCoverFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_peak_player.*
|
||||
|
||||
/**
|
||||
* Created by hemanths on 2019-10-03.
|
||||
*/
|
||||
|
||||
class PeakPlayerFragment : AbsPlayerFragment() {
|
||||
|
||||
private lateinit var controlsFragment: PeakPlayerControlFragment
|
||||
private var lastColor: Int = 0
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_peak_player, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpPlayerToolbar()
|
||||
setUpSubFragments()
|
||||
title.isSelected = true
|
||||
|
||||
}
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
controlsFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as PeakPlayerControlFragment
|
||||
|
||||
val coverFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment
|
||||
coverFragment.setCallbacks(this)
|
||||
}
|
||||
|
||||
private fun setUpPlayerToolbar() {
|
||||
playerToolbar.apply {
|
||||
inflateMenu(R.menu.menu_player)
|
||||
setNavigationOnClickListener { requireActivity().onBackPressed() }
|
||||
setOnMenuItemClickListener(this@PeakPlayerFragment)
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
this,
|
||||
ATHUtil.resolveColor(context, R.attr.colorControlNormal),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun playerToolbar(): Toolbar {
|
||||
return playerToolbar
|
||||
}
|
||||
|
||||
override fun onShow() {
|
||||
}
|
||||
|
||||
override fun onHide() {
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun toolbarIconColor(): Int {
|
||||
return ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal)
|
||||
}
|
||||
|
||||
override val paletteColor: Int
|
||||
get() = lastColor
|
||||
|
||||
override fun onColorChanged(color: MediaNotificationProcessor) {
|
||||
lastColor = color.primaryTextColor
|
||||
callbacks?.onPaletteColorChanged()
|
||||
controlsFragment.setColor(color)
|
||||
}
|
||||
|
||||
override fun onFavoriteToggled() {
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
title.text = song.title
|
||||
text.text = song.artistName
|
||||
|
||||
if (PreferenceUtil.isSongInfo) {
|
||||
songInfo.text = getSongInfo(song)
|
||||
songInfo.show()
|
||||
} else {
|
||||
songInfo.hide()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateSong()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,299 @@
|
|||
package io.github.muntashirakon.music.fragments.player.plain
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.graphics.PorterDuff
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.AccelerateInterpolator
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.SeekBar
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.extensions.applyColor
|
||||
import io.github.muntashirakon.music.extensions.hide
|
||||
import io.github.muntashirakon.music.extensions.show
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerControlsFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper
|
||||
import io.github.muntashirakon.music.helper.PlayPauseButtonOnClickHandler
|
||||
import io.github.muntashirakon.music.misc.SimpleOnSeekbarChangeListener
|
||||
import io.github.muntashirakon.music.service.MusicService
|
||||
import io.github.muntashirakon.music.util.MusicUtil
|
||||
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_adaptive_player_playback_controls.*
|
||||
import kotlinx.android.synthetic.main.fragment_plain_controls_fragment.nextButton
|
||||
import kotlinx.android.synthetic.main.fragment_plain_controls_fragment.playPauseButton
|
||||
import kotlinx.android.synthetic.main.fragment_plain_controls_fragment.previousButton
|
||||
import kotlinx.android.synthetic.main.fragment_plain_controls_fragment.progressSlider
|
||||
import kotlinx.android.synthetic.main.fragment_plain_controls_fragment.repeatButton
|
||||
import kotlinx.android.synthetic.main.fragment_plain_controls_fragment.shuffleButton
|
||||
import kotlinx.android.synthetic.main.fragment_plain_controls_fragment.songCurrentProgress
|
||||
import kotlinx.android.synthetic.main.fragment_plain_controls_fragment.songTotalTime
|
||||
|
||||
/**
|
||||
* @author Hemanth S (h4h13).
|
||||
*/
|
||||
|
||||
class PlainPlaybackControlsFragment : AbsPlayerControlsFragment() {
|
||||
|
||||
private var lastPlaybackControlsColor: Int = 0
|
||||
private var lastDisabledPlaybackControlsColor: Int = 0
|
||||
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
||||
|
||||
override fun onPlayStateChanged() {
|
||||
updatePlayPauseDrawableState()
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged() {
|
||||
updateRepeatState()
|
||||
}
|
||||
|
||||
override fun onShuffleModeChanged() {
|
||||
updateShuffleState()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
updatePlayPauseDrawableState()
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
if (PreferenceUtil.isSongInfo) {
|
||||
songInfo.text = getSongInfo(MusicPlayerRemote.currentSong)
|
||||
songInfo.show()
|
||||
} else {
|
||||
songInfo.hide()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
|
||||
return inflater.inflate(R.layout.fragment_plain_controls_fragment, container, false)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
progressViewUpdateHelper.start()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
progressViewUpdateHelper.stop()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpMusicControllers()
|
||||
|
||||
playPauseButton.setOnClickListener {
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
MusicPlayerRemote.pauseSong()
|
||||
} else {
|
||||
MusicPlayerRemote.resumePlaying()
|
||||
}
|
||||
showBonceAnimation()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpPlayPauseFab() {
|
||||
playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
||||
}
|
||||
|
||||
private fun setUpMusicControllers() {
|
||||
setUpPlayPauseFab()
|
||||
setUpPrevNext()
|
||||
setUpRepeatButton()
|
||||
setUpShuffleButton()
|
||||
setUpProgressSlider()
|
||||
}
|
||||
|
||||
private fun setUpPrevNext() {
|
||||
updatePrevNextColor()
|
||||
nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
|
||||
previousButton.setOnClickListener { MusicPlayerRemote.back() }
|
||||
}
|
||||
|
||||
private fun updatePrevNextColor() {
|
||||
nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
|
||||
override fun setColor(color: MediaNotificationProcessor) {
|
||||
val colorBg = ATHUtil.resolveColor(requireContext(), android.R.attr.colorBackground)
|
||||
if (ColorUtil.isColorLight(colorBg)) {
|
||||
lastPlaybackControlsColor =
|
||||
MaterialValueHelper.getSecondaryTextColor(requireContext(), true)
|
||||
lastDisabledPlaybackControlsColor =
|
||||
MaterialValueHelper.getSecondaryDisabledTextColor(requireContext(), true)
|
||||
} else {
|
||||
lastPlaybackControlsColor =
|
||||
MaterialValueHelper.getPrimaryTextColor(requireContext(), false)
|
||||
lastDisabledPlaybackControlsColor =
|
||||
MaterialValueHelper.getPrimaryDisabledTextColor(requireContext(), false)
|
||||
}
|
||||
|
||||
val colorFinal = if (PreferenceUtil.isAdaptiveColor) {
|
||||
color.primaryTextColor
|
||||
} else {
|
||||
ThemeStore.accentColor(requireContext())
|
||||
}
|
||||
volumeFragment?.setTintable(colorFinal)
|
||||
progressSlider.applyColor(colorFinal)
|
||||
|
||||
TintHelper.setTintAuto(
|
||||
playPauseButton,
|
||||
MaterialValueHelper.getPrimaryTextColor(
|
||||
requireContext(),
|
||||
ColorUtil.isColorLight(colorFinal)
|
||||
),
|
||||
false
|
||||
)
|
||||
TintHelper.setTintAuto(playPauseButton, colorFinal, true)
|
||||
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updatePrevNextColor()
|
||||
}
|
||||
|
||||
private fun setUpShuffleButton() {
|
||||
shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
|
||||
}
|
||||
|
||||
override fun updateShuffleState() {
|
||||
when (MusicPlayerRemote.shuffleMode) {
|
||||
MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton.setColorFilter(
|
||||
lastPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
else -> shuffleButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpRepeatButton() {
|
||||
repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
|
||||
}
|
||||
|
||||
override fun updateRepeatState() {
|
||||
when (MusicPlayerRemote.repeatMode) {
|
||||
MusicService.REPEAT_MODE_NONE -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
MusicService.REPEAT_MODE_ALL -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
MusicService.REPEAT_MODE_THIS -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override fun show() {
|
||||
playPauseButton!!.animate()
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.rotation(360f)
|
||||
.setInterpolator(DecelerateInterpolator())
|
||||
.start()
|
||||
}
|
||||
|
||||
public override fun hide() {
|
||||
if (playPauseButton != null) {
|
||||
playPauseButton!!.apply {
|
||||
scaleX = 0f
|
||||
scaleY = 0f
|
||||
rotation = 0f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showBonceAnimation() {
|
||||
playPauseButton.apply {
|
||||
clearAnimation()
|
||||
scaleX = 0.9f
|
||||
scaleY = 0.9f
|
||||
visibility = View.VISIBLE
|
||||
pivotX = (width / 2).toFloat()
|
||||
pivotY = (height / 2).toFloat()
|
||||
|
||||
animate().setDuration(200)
|
||||
.setInterpolator(DecelerateInterpolator())
|
||||
.scaleX(1.1f)
|
||||
.scaleY(1.1f)
|
||||
.withEndAction {
|
||||
animate().setDuration(200)
|
||||
.setInterpolator(AccelerateInterpolator())
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.alpha(1f).start()
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updatePlayPauseDrawableState() {
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp)
|
||||
} else {
|
||||
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_32dp)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setUpProgressSlider() {
|
||||
progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
MusicPlayerRemote.seekTo(progress)
|
||||
onUpdateProgressViews(
|
||||
MusicPlayerRemote.songProgressMillis,
|
||||
MusicPlayerRemote.songDurationMillis
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
progressSlider.max = total
|
||||
|
||||
val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress)
|
||||
animator.duration = SLIDER_ANIMATION_TIME
|
||||
animator.interpolator = LinearInterpolator()
|
||||
animator.start()
|
||||
|
||||
songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
|
||||
songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
package io.github.muntashirakon.music.fragments.player.plain
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerFragment
|
||||
import io.github.muntashirakon.music.fragments.player.PlayerAlbumCoverFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_plain_player.*
|
||||
|
||||
class PlainPlayerFragment : AbsPlayerFragment() {
|
||||
override fun playerToolbar(): Toolbar {
|
||||
return playerToolbar
|
||||
}
|
||||
|
||||
private lateinit var plainPlaybackControlsFragment: PlainPlaybackControlsFragment
|
||||
private var lastColor: Int = 0
|
||||
override val paletteColor: Int
|
||||
get() = lastColor
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
title.text = song.title
|
||||
text.text = song.artistName
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_plain_player, container, false)
|
||||
}
|
||||
|
||||
private fun setUpPlayerToolbar() {
|
||||
playerToolbar.apply {
|
||||
inflateMenu(R.menu.menu_player)
|
||||
setNavigationOnClickListener { requireActivity().onBackPressed() }
|
||||
setOnMenuItemClickListener(this@PlainPlayerFragment)
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
this,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpSubFragments()
|
||||
setUpPlayerToolbar()
|
||||
title.isSelected = true
|
||||
text.isSelected = true
|
||||
}
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
plainPlaybackControlsFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as PlainPlaybackControlsFragment
|
||||
val playerAlbumCoverFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment
|
||||
playerAlbumCoverFragment.setCallbacks(this)
|
||||
}
|
||||
|
||||
override fun onShow() {
|
||||
plainPlaybackControlsFragment.show()
|
||||
}
|
||||
|
||||
override fun onHide() {
|
||||
plainPlaybackControlsFragment.hide()
|
||||
onBackPressed()
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun toolbarIconColor(): Int {
|
||||
return ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal)
|
||||
}
|
||||
|
||||
override fun onColorChanged(color: MediaNotificationProcessor) {
|
||||
plainPlaybackControlsFragment.setColor(color)
|
||||
lastColor = color.primaryTextColor
|
||||
callbacks!!.onPaletteColorChanged()
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
playerToolbar,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onFavoriteToggled() {
|
||||
toggleFavorite(MusicPlayerRemote.currentSong)
|
||||
}
|
||||
|
||||
override fun toggleFavorite(song: Song) {
|
||||
super.toggleFavorite(song)
|
||||
if (song.id == MusicPlayerRemote.currentSong.id) {
|
||||
updateIsFavorite()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
package io.github.muntashirakon.music.fragments.player.simple
|
||||
|
||||
import android.graphics.PorterDuff
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.extensions.hide
|
||||
import io.github.muntashirakon.music.extensions.show
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerControlsFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper
|
||||
import io.github.muntashirakon.music.helper.PlayPauseButtonOnClickHandler
|
||||
import io.github.muntashirakon.music.service.MusicService
|
||||
import io.github.muntashirakon.music.util.MusicUtil
|
||||
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_simple_controls_fragment.*
|
||||
|
||||
/**
|
||||
* @author Hemanth S (h4h13).
|
||||
*/
|
||||
|
||||
class SimplePlaybackControlsFragment : AbsPlayerControlsFragment() {
|
||||
|
||||
private var lastPlaybackControlsColor: Int = 0
|
||||
private var lastDisabledPlaybackControlsColor: Int = 0
|
||||
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
||||
|
||||
override fun onPlayStateChanged() {
|
||||
updatePlayPauseDrawableState()
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged() {
|
||||
updateRepeatState()
|
||||
}
|
||||
|
||||
override fun onShuffleModeChanged() {
|
||||
updateShuffleState()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
updatePlayPauseDrawableState()
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_simple_controls_fragment, container, false)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
progressViewUpdateHelper.start()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
progressViewUpdateHelper.stop()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpMusicControllers()
|
||||
title.isSelected = true
|
||||
playPauseButton.setOnClickListener {
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
MusicPlayerRemote.pauseSong()
|
||||
} else {
|
||||
MusicPlayerRemote.resumePlaying()
|
||||
}
|
||||
showBonceAnimation(playPauseButton)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpMusicControllers() {
|
||||
setUpPlayPauseFab()
|
||||
setUpPrevNext()
|
||||
setUpRepeatButton()
|
||||
setUpShuffleButton()
|
||||
setUpProgressSlider()
|
||||
}
|
||||
|
||||
private fun setUpPrevNext() {
|
||||
updatePrevNextColor()
|
||||
nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
|
||||
previousButton.setOnClickListener { MusicPlayerRemote.back() }
|
||||
}
|
||||
|
||||
private fun updatePrevNextColor() {
|
||||
nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
|
||||
private fun setUpShuffleButton() {
|
||||
shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
|
||||
}
|
||||
|
||||
override fun updateShuffleState() {
|
||||
when (MusicPlayerRemote.shuffleMode) {
|
||||
MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton.setColorFilter(
|
||||
lastPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
else -> shuffleButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpRepeatButton() {
|
||||
repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
|
||||
}
|
||||
|
||||
override fun updateRepeatState() {
|
||||
when (MusicPlayerRemote.repeatMode) {
|
||||
MusicService.REPEAT_MODE_NONE -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
MusicService.REPEAT_MODE_ALL -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
MusicService.REPEAT_MODE_THIS -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
title.text = song.title
|
||||
text.text = song.artistName
|
||||
|
||||
if (PreferenceUtil.isSongInfo) {
|
||||
songInfo.text = getSongInfo(song)
|
||||
songInfo.show()
|
||||
} else {
|
||||
songInfo.hide()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
public override fun show() {
|
||||
playPauseButton!!.animate()
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.rotation(360f)
|
||||
.setInterpolator(DecelerateInterpolator())
|
||||
.start()
|
||||
}
|
||||
|
||||
public override fun hide() {
|
||||
if (playPauseButton != null) {
|
||||
playPauseButton!!.apply {
|
||||
scaleX = 0f
|
||||
scaleY = 0f
|
||||
rotation = 0f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setUpProgressSlider() {
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
songCurrentProgress!!.text = String.format(
|
||||
"%s / %s",
|
||||
MusicUtil.getReadableDurationString(progress.toLong()),
|
||||
MusicUtil.getReadableDurationString(total.toLong())
|
||||
)
|
||||
}
|
||||
|
||||
override fun setColor(color: MediaNotificationProcessor) {
|
||||
val colorBg = ATHUtil.resolveColor(requireContext(), android.R.attr.colorBackground)
|
||||
if (ColorUtil.isColorLight(colorBg)) {
|
||||
lastPlaybackControlsColor =
|
||||
MaterialValueHelper.getSecondaryTextColor(requireContext(), true)
|
||||
lastDisabledPlaybackControlsColor =
|
||||
MaterialValueHelper.getSecondaryDisabledTextColor(requireContext(), true)
|
||||
} else {
|
||||
lastPlaybackControlsColor =
|
||||
MaterialValueHelper.getPrimaryTextColor(requireContext(), false)
|
||||
lastDisabledPlaybackControlsColor =
|
||||
MaterialValueHelper.getPrimaryDisabledTextColor(requireContext(), false)
|
||||
}
|
||||
|
||||
val colorFinal = if (PreferenceUtil.isAdaptiveColor) {
|
||||
color.primaryTextColor
|
||||
} else {
|
||||
ThemeStore.accentColor(requireContext())
|
||||
}
|
||||
|
||||
volumeFragment?.setTintable(colorFinal)
|
||||
|
||||
TintHelper.setTintAuto(
|
||||
playPauseButton,
|
||||
MaterialValueHelper.getPrimaryTextColor(
|
||||
requireContext(),
|
||||
ColorUtil.isColorLight(colorFinal)
|
||||
),
|
||||
false
|
||||
)
|
||||
TintHelper.setTintAuto(playPauseButton, colorFinal, true)
|
||||
text.setTextColor(colorFinal)
|
||||
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
updatePrevNextColor()
|
||||
}
|
||||
|
||||
private fun setUpPlayPauseFab() {
|
||||
playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
||||
}
|
||||
|
||||
private fun updatePlayPauseDrawableState() {
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp)
|
||||
} else {
|
||||
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_32dp)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package io.github.muntashirakon.music.fragments.player.simple
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerFragment
|
||||
import io.github.muntashirakon.music.fragments.player.PlayerAlbumCoverFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_simple_player.*
|
||||
|
||||
/**
|
||||
* @author Hemanth S (h4h13).
|
||||
*/
|
||||
|
||||
class SimplePlayerFragment : AbsPlayerFragment() {
|
||||
|
||||
override fun playerToolbar(): Toolbar {
|
||||
return playerToolbar
|
||||
}
|
||||
|
||||
private var lastColor: Int = 0
|
||||
override val paletteColor: Int
|
||||
get() = lastColor
|
||||
|
||||
private lateinit var controlsFragment: SimplePlaybackControlsFragment
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
|
||||
return inflater.inflate(R.layout.fragment_simple_player, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpSubFragments()
|
||||
setUpPlayerToolbar()
|
||||
}
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
val playerAlbumCoverFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment
|
||||
playerAlbumCoverFragment.setCallbacks(this)
|
||||
controlsFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as SimplePlaybackControlsFragment
|
||||
}
|
||||
|
||||
override fun onShow() {
|
||||
controlsFragment.show()
|
||||
}
|
||||
|
||||
override fun onHide() {
|
||||
controlsFragment.hide()
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun toolbarIconColor(): Int {
|
||||
return ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal)
|
||||
}
|
||||
|
||||
override fun onColorChanged(color: MediaNotificationProcessor) {
|
||||
lastColor = color.backgroundColor
|
||||
callbacks?.onPaletteColorChanged()
|
||||
controlsFragment.setColor(color)
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
playerToolbar,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onFavoriteToggled() {
|
||||
toggleFavorite(MusicPlayerRemote.currentSong)
|
||||
}
|
||||
|
||||
override fun toggleFavorite(song: Song) {
|
||||
super.toggleFavorite(song)
|
||||
if (song.id == MusicPlayerRemote.currentSong.id) {
|
||||
updateIsFavorite()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpPlayerToolbar() {
|
||||
playerToolbar.inflateMenu(R.menu.menu_player)
|
||||
playerToolbar.setNavigationOnClickListener { requireActivity().onBackPressed() }
|
||||
playerToolbar.setOnMenuItemClickListener(this)
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
playerToolbar,
|
||||
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
package io.github.muntashirakon.music.fragments.player.tiny
|
||||
|
||||
import android.graphics.PorterDuff
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerControlsFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.service.MusicService
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_tiny_controls_fragment.*
|
||||
|
||||
class TinyPlaybackControlsFragment : AbsPlayerControlsFragment() {
|
||||
|
||||
override fun show() {
|
||||
}
|
||||
|
||||
override fun hide() {
|
||||
}
|
||||
|
||||
override fun setUpProgressSlider() {
|
||||
}
|
||||
|
||||
override fun setColor(color: MediaNotificationProcessor) {
|
||||
lastPlaybackControlsColor = color.secondaryTextColor
|
||||
lastDisabledPlaybackControlsColor = ColorUtil.withAlpha(color.secondaryTextColor, 0.25f)
|
||||
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
}
|
||||
|
||||
private var lastPlaybackControlsColor: Int = 0
|
||||
private var lastDisabledPlaybackControlsColor: Int = 0
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_tiny_controls_fragment, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpMusicControllers()
|
||||
}
|
||||
|
||||
private fun setUpMusicControllers() {
|
||||
setUpRepeatButton()
|
||||
setUpShuffleButton()
|
||||
setUpProgressSlider()
|
||||
}
|
||||
|
||||
private fun setUpShuffleButton() {
|
||||
playerShuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
|
||||
}
|
||||
|
||||
private fun setUpRepeatButton() {
|
||||
playerRepeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
|
||||
}
|
||||
|
||||
override fun updateShuffleState() {
|
||||
when (MusicPlayerRemote.shuffleMode) {
|
||||
MusicService.SHUFFLE_MODE_SHUFFLE -> playerShuffleButton.setColorFilter(
|
||||
lastPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
else -> playerShuffleButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateRepeatState() {
|
||||
when (MusicPlayerRemote.repeatMode) {
|
||||
MusicService.REPEAT_MODE_NONE -> {
|
||||
playerRepeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
playerRepeatButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
MusicService.REPEAT_MODE_ALL -> {
|
||||
playerRepeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
playerRepeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
MusicService.REPEAT_MODE_THIS -> {
|
||||
playerRepeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp)
|
||||
playerRepeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged() {
|
||||
updateRepeatState()
|
||||
}
|
||||
|
||||
override fun onShuffleModeChanged() {
|
||||
updateShuffleState()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
package io.github.muntashirakon.music.fragments.player.tiny
|
||||
|
||||
import android.animation.AnimatorSet
|
||||
import android.animation.ObjectAnimator
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.LinearInterpolator
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.extensions.hide
|
||||
import io.github.muntashirakon.music.extensions.show
|
||||
import io.github.muntashirakon.music.fragments.MiniPlayerFragment
|
||||
import io.github.muntashirakon.music.fragments.base.AbsPlayerFragment
|
||||
import io.github.muntashirakon.music.fragments.player.PlayerAlbumCoverFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper
|
||||
import io.github.muntashirakon.music.helper.PlayPauseButtonOnClickHandler
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
import io.github.muntashirakon.music.util.MusicUtil
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
|
||||
import io.github.muntashirakon.music.util.ViewUtil
|
||||
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
|
||||
import kotlinx.android.synthetic.main.fragment_tiny_player.*
|
||||
|
||||
class TinyPlayerFragment : AbsPlayerFragment(), MusicProgressViewUpdateHelper.Callback {
|
||||
private var lastColor: Int = 0
|
||||
private var toolbarColor: Int = 0
|
||||
|
||||
|
||||
override fun playerToolbar(): Toolbar {
|
||||
return playerToolbar
|
||||
}
|
||||
|
||||
override fun onShow() {
|
||||
}
|
||||
|
||||
override fun onHide() {
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun toolbarIconColor(): Int {
|
||||
return toolbarColor
|
||||
}
|
||||
|
||||
|
||||
override val paletteColor: Int
|
||||
get() = lastColor
|
||||
|
||||
|
||||
override fun onColorChanged(color: MediaNotificationProcessor) {
|
||||
lastColor = color.backgroundColor
|
||||
toolbarColor = color.secondaryTextColor
|
||||
controlsFragment.setColor(color)
|
||||
callbacks?.onPaletteColorChanged()
|
||||
|
||||
title.setTextColor(color.primaryTextColor)
|
||||
playerSongTotalTime.setTextColor(color.primaryTextColor)
|
||||
text.setTextColor(color.secondaryTextColor)
|
||||
songInfo.setTextColor(color.secondaryTextColor)
|
||||
ViewUtil.setProgressDrawable(progressBar, color.backgroundColor)
|
||||
|
||||
Handler().post {
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
playerToolbar,
|
||||
color.secondaryTextColor,
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFavoriteToggled() {
|
||||
toggleFavorite(MusicPlayerRemote.currentSong)
|
||||
}
|
||||
|
||||
private lateinit var controlsFragment: TinyPlaybackControlsFragment
|
||||
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
progressViewUpdateHelper.start()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
progressViewUpdateHelper.stop()
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
title.text = song.title
|
||||
text.text = String.format("%s \nby - %s", song.albumName, song.artistName)
|
||||
|
||||
if (PreferenceUtil.isSongInfo) {
|
||||
songInfo.text = getSongInfo(song)
|
||||
songInfo.show()
|
||||
} else {
|
||||
songInfo.hide()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_tiny_player, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
title.isSelected = true
|
||||
progressBar.setOnClickListener(PlayPauseButtonOnClickHandler())
|
||||
progressBar.setOnTouchListener(MiniPlayerFragment.FlingPlayBackController(requireContext()))
|
||||
|
||||
setUpPlayerToolbar()
|
||||
setUpSubFragments()
|
||||
}
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
controlsFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as TinyPlaybackControlsFragment
|
||||
val playerAlbumCoverFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment
|
||||
playerAlbumCoverFragment.setCallbacks(this)
|
||||
}
|
||||
|
||||
private fun setUpPlayerToolbar() {
|
||||
playerToolbar.apply {
|
||||
inflateMenu(R.menu.menu_player)
|
||||
setNavigationOnClickListener { requireActivity().onBackPressed() }
|
||||
setOnMenuItemClickListener(this@TinyPlayerFragment)
|
||||
}
|
||||
}
|
||||
|
||||
override fun toggleFavorite(song: Song) {
|
||||
super.toggleFavorite(song)
|
||||
if (song.id == MusicPlayerRemote.currentSong.id) {
|
||||
updateIsFavorite()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateSong()
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
progressBar.max = total
|
||||
|
||||
val animator = ObjectAnimator.ofInt(progressBar, "progress", progress)
|
||||
|
||||
val animatorSet = AnimatorSet()
|
||||
animatorSet.playSequentially(animator)
|
||||
|
||||
animatorSet.duration = 1500
|
||||
animatorSet.interpolator = LinearInterpolator()
|
||||
animatorSet.start()
|
||||
|
||||
playerSongTotalTime.text = String.format(
|
||||
"%s/%s", MusicUtil.getReadableDurationString(total.toLong()),
|
||||
MusicUtil.getReadableDurationString(progress.toLong())
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package io.github.muntashirakon.music.fragments.playlists
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.View
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.adapter.playlist.PlaylistAdapter
|
||||
import io.github.muntashirakon.music.fragments.base.AbsLibraryPagerRecyclerViewFragment
|
||||
import io.github.muntashirakon.music.interfaces.MainActivityFragmentCallbacks
|
||||
|
||||
class PlaylistsFragment :
|
||||
AbsLibraryPagerRecyclerViewFragment<PlaylistAdapter, GridLayoutManager>(),
|
||||
MainActivityFragmentCallbacks {
|
||||
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
mainActivity.libraryViewModel.allPlaylisits()
|
||||
.observe(viewLifecycleOwner, Observer { playlists ->
|
||||
if (playlists.isNotEmpty()) {
|
||||
adapter?.swapDataSet(playlists)
|
||||
} else {
|
||||
adapter?.swapDataSet(listOf())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun handleBackPress(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override val emptyMessage: Int
|
||||
get() = R.string.no_playlists
|
||||
|
||||
override fun createLayoutManager(): GridLayoutManager {
|
||||
return GridLayoutManager(requireContext(), 1)
|
||||
}
|
||||
|
||||
override fun createAdapter(): PlaylistAdapter {
|
||||
return PlaylistAdapter(
|
||||
mainActivity,
|
||||
ArrayList(),
|
||||
R.layout.item_list,
|
||||
mainActivity
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
menu.apply {
|
||||
removeItem(R.id.action_sort_order)
|
||||
removeItem(R.id.action_grid_size)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val TAG: String = PlaylistsFragment::class.java.simpleName
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance(): PlaylistsFragment {
|
||||
return PlaylistsFragment()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
package io.github.muntashirakon.music.fragments.queue
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.adapter.song.PlayingQueueAdapter
|
||||
import io.github.muntashirakon.music.fragments.base.AbsLibraryPagerRecyclerViewFragment
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.interfaces.MainActivityFragmentCallbacks
|
||||
import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator
|
||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager
|
||||
import com.h6ah4i.android.widget.advrecyclerview.swipeable.RecyclerViewSwipeManager
|
||||
import com.h6ah4i.android.widget.advrecyclerview.touchguard.RecyclerViewTouchActionGuardManager
|
||||
import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
|
||||
import kotlinx.android.synthetic.main.activity_playing_queue.*
|
||||
|
||||
/**
|
||||
* Created by hemanths on 2019-12-08.
|
||||
*/
|
||||
class PlayingQueueFragment :
|
||||
AbsLibraryPagerRecyclerViewFragment<PlayingQueueAdapter, LinearLayoutManager>(),
|
||||
MainActivityFragmentCallbacks {
|
||||
|
||||
override fun handleBackPress(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
private lateinit var wrappedAdapter: RecyclerView.Adapter<*>
|
||||
private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null
|
||||
private var recyclerViewSwipeManager: RecyclerViewSwipeManager? = null
|
||||
private var recyclerViewTouchActionGuardManager: RecyclerViewTouchActionGuardManager? = null
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setupRecyclerView()
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
recyclerViewTouchActionGuardManager = RecyclerViewTouchActionGuardManager()
|
||||
recyclerViewDragDropManager = RecyclerViewDragDropManager()
|
||||
recyclerViewSwipeManager = RecyclerViewSwipeManager()
|
||||
|
||||
val animator = DraggableItemAnimator()
|
||||
animator.supportsChangeAnimations = false
|
||||
wrappedAdapter =
|
||||
recyclerViewDragDropManager?.createWrappedAdapter(adapter!!) as RecyclerView.Adapter<*>
|
||||
wrappedAdapter =
|
||||
recyclerViewSwipeManager?.createWrappedAdapter(wrappedAdapter) as RecyclerView.Adapter<*>
|
||||
recyclerView().layoutManager = layoutManager
|
||||
recyclerView().adapter = wrappedAdapter
|
||||
recyclerView().itemAnimator = animator
|
||||
recyclerViewTouchActionGuardManager?.attachRecyclerView(recyclerView)
|
||||
recyclerViewDragDropManager?.attachRecyclerView(recyclerView)
|
||||
recyclerViewSwipeManager?.attachRecyclerView(recyclerView)
|
||||
|
||||
layoutManager?.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
|
||||
}
|
||||
|
||||
override fun createLayoutManager(): LinearLayoutManager {
|
||||
return LinearLayoutManager(requireContext())
|
||||
}
|
||||
|
||||
override fun createAdapter(): PlayingQueueAdapter {
|
||||
return PlayingQueueAdapter(
|
||||
requireActivity() as AppCompatActivity,
|
||||
MusicPlayerRemote.playingQueue.toMutableList(),
|
||||
MusicPlayerRemote.position,
|
||||
R.layout.item_queue
|
||||
)
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
updateQueue()
|
||||
}
|
||||
|
||||
override fun onQueueChanged() {
|
||||
super.onQueueChanged()
|
||||
updateQueue()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
updateQueuePosition()
|
||||
}
|
||||
|
||||
private fun updateQueuePosition() {
|
||||
adapter?.setCurrent(MusicPlayerRemote.position)
|
||||
resetToCurrentPosition()
|
||||
}
|
||||
|
||||
private fun updateQueue() {
|
||||
adapter?.swapDataSet(MusicPlayerRemote.playingQueue, MusicPlayerRemote.position)
|
||||
resetToCurrentPosition()
|
||||
}
|
||||
|
||||
private fun resetToCurrentPosition() {
|
||||
recyclerView.stopScroll()
|
||||
layoutManager?.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
recyclerViewDragDropManager?.cancelDrag()
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override val emptyMessage: Int
|
||||
get() = R.string.no_playing_queue
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
if (recyclerViewDragDropManager != null) {
|
||||
recyclerViewDragDropManager?.release()
|
||||
recyclerViewDragDropManager = null
|
||||
}
|
||||
|
||||
if (recyclerViewSwipeManager != null) {
|
||||
recyclerViewSwipeManager?.release()
|
||||
recyclerViewSwipeManager = null
|
||||
}
|
||||
|
||||
WrapperAdapterUtils.releaseAll(wrappedAdapter)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val TAG: String = PlayingQueueFragment::class.java.simpleName
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance(): PlayingQueueFragment {
|
||||
return PlayingQueueFragment()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package io.github.muntashirakon.music.fragments.settings
|
||||
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceManager
|
||||
import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEPreferenceFragmentCompat
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.preferences.*
|
||||
|
||||
/**
|
||||
* @author Hemanth S (h4h13).
|
||||
*/
|
||||
|
||||
abstract class AbsSettingsFragment : ATEPreferenceFragmentCompat() {
|
||||
|
||||
internal fun setSummary(preference: Preference, value: Any?) {
|
||||
val stringValue = value.toString()
|
||||
if (preference is ListPreference) {
|
||||
val index = preference.findIndexOfValue(stringValue)
|
||||
preference.setSummary(if (index >= 0) preference.entries[index] else null)
|
||||
} else {
|
||||
preference.summary = stringValue
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun invalidateSettings()
|
||||
|
||||
protected fun setSummary(preference: Preference?) {
|
||||
preference?.let {
|
||||
setSummary(
|
||||
it, PreferenceManager
|
||||
.getDefaultSharedPreferences(it.context)
|
||||
.getString(it.key, "")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setDivider(ColorDrawable(Color.TRANSPARENT))
|
||||
listView.setBackgroundColor(ATHUtil.resolveColor(requireContext(), R.attr.colorSurface))
|
||||
listView.overScrollMode = View.OVER_SCROLL_NEVER
|
||||
listView.setPadding(0, 0, 0, 0)
|
||||
listView.setPaddingRelative(0, 0, 0, 0)
|
||||
invalidateSettings()
|
||||
}
|
||||
|
||||
override fun onDisplayPreferenceDialog(preference: Preference?) {
|
||||
when (preference) {
|
||||
is LibraryPreference -> {
|
||||
val fragment = LibraryPreferenceDialog.newInstance()
|
||||
fragment.show(childFragmentManager, preference.key)
|
||||
}
|
||||
is NowPlayingScreenPreference -> {
|
||||
val fragment = NowPlayingScreenPreferenceDialog.newInstance()
|
||||
fragment.show(childFragmentManager, preference.key)
|
||||
}
|
||||
is AlbumCoverStylePreference -> {
|
||||
val fragment = AlbumCoverStylePreferenceDialog.newInstance()
|
||||
fragment.show(childFragmentManager, preference.key)
|
||||
}
|
||||
is BlacklistPreference -> {
|
||||
val fragment = BlacklistPreferenceDialog.newInstance()
|
||||
fragment.show(childFragmentManager, preference.key)
|
||||
}
|
||||
else -> super.onDisplayPreferenceDialog(preference)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package io.github.muntashirakon.music.fragments.settings
|
||||
|
||||
import android.content.Intent
|
||||
import android.media.audiofx.AudioEffect
|
||||
import android.os.Bundle
|
||||
import androidx.preference.Preference
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.util.NavigationUtil
|
||||
|
||||
|
||||
/**
|
||||
* @author Hemanth S (h4h13).
|
||||
*/
|
||||
|
||||
class AudioSettings : AbsSettingsFragment() {
|
||||
override fun invalidateSettings() {
|
||||
val findPreference: Preference = findPreference("equalizer")!!
|
||||
if (!hasEqualizer()) {
|
||||
findPreference.isEnabled = false
|
||||
findPreference.summary = resources.getString(R.string.no_equalizer)
|
||||
} else {
|
||||
findPreference.isEnabled = true
|
||||
}
|
||||
findPreference.setOnPreferenceClickListener {
|
||||
NavigationUtil.openEqualizer(requireActivity())
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasEqualizer(): Boolean {
|
||||
val effects = Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL)
|
||||
|
||||
val pm = requireActivity().packageManager
|
||||
val ri = pm.resolveActivity(effects, 0)
|
||||
return ri != null
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.pref_audio)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package io.github.muntashirakon.music.fragments.settings
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.preference.Preference
|
||||
import io.github.muntashirakon.music.R
|
||||
|
||||
/**
|
||||
* @author Hemanth S (h4h13).
|
||||
*/
|
||||
|
||||
class ImageSettingFragment : AbsSettingsFragment() {
|
||||
override fun invalidateSettings() {
|
||||
val autoDownloadImagesPolicy: Preference = findPreference("auto_download_images_policy")!!
|
||||
setSummary(autoDownloadImagesPolicy)
|
||||
autoDownloadImagesPolicy.setOnPreferenceChangeListener { _, o ->
|
||||
setSummary(autoDownloadImagesPolicy, o)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.pref_images)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val preference: Preference? = findPreference("auto_download_images_policy")
|
||||
preference?.let { setSummary(it) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package io.github.muntashirakon.music.fragments.settings
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.fragment.app.Fragment
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.activities.SettingsActivity
|
||||
import io.github.muntashirakon.music.util.NavigationUtil
|
||||
import kotlinx.android.synthetic.main.fragment_main_settings.*
|
||||
|
||||
class MainSettingsFragment : Fragment(), View.OnClickListener {
|
||||
override fun onClick(view: View) {
|
||||
when (view.id) {
|
||||
R.id.generalSettings -> inflateFragment(
|
||||
ThemeSettingsFragment(),
|
||||
R.string.general_settings_title
|
||||
)
|
||||
R.id.audioSettings -> inflateFragment(AudioSettings(), R.string.pref_header_audio)
|
||||
R.id.nowPlayingSettings -> inflateFragment(
|
||||
NowPlayingSettingsFragment(),
|
||||
R.string.now_playing
|
||||
)
|
||||
R.id.personalizeSettings -> inflateFragment(
|
||||
PersonalizeSettingsFragment(),
|
||||
R.string.personalize
|
||||
)
|
||||
R.id.imageSettings -> inflateFragment(
|
||||
ImageSettingFragment(),
|
||||
R.string.pref_header_images
|
||||
)
|
||||
R.id.notificationSettings -> inflateFragment(
|
||||
NotificationSettingsFragment(),
|
||||
R.string.notification
|
||||
)
|
||||
R.id.otherSettings -> inflateFragment(OtherSettingsFragment(), R.string.others)
|
||||
R.id.aboutSettings -> NavigationUtil.goToAbout(requireActivity())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_main_settings, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
generalSettings.setOnClickListener(this)
|
||||
audioSettings.setOnClickListener(this)
|
||||
nowPlayingSettings.setOnClickListener(this)
|
||||
personalizeSettings.setOnClickListener(this)
|
||||
imageSettings.setOnClickListener(this)
|
||||
notificationSettings.setOnClickListener(this)
|
||||
otherSettings.setOnClickListener(this)
|
||||
aboutSettings.setOnClickListener(this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
}
|
||||
|
||||
private fun inflateFragment(fragment: Fragment, @StringRes title: Int) {
|
||||
(requireActivity() as SettingsActivity).setupFragment(fragment, title)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package io.github.muntashirakon.music.fragments.settings
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Build.VERSION
|
||||
import android.os.Build.VERSION_CODES
|
||||
import android.os.Bundle
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.TwoStatePreference
|
||||
import io.github.muntashirakon.music.CLASSIC_NOTIFICATION
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
|
||||
|
||||
/**
|
||||
* @author Hemanth S (h4h13).
|
||||
*/
|
||||
|
||||
class NotificationSettingsFragment : AbsSettingsFragment(),
|
||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||
if (key == CLASSIC_NOTIFICATION) {
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.O) {
|
||||
findPreference<Preference>("colored_notification")?.isEnabled =
|
||||
sharedPreferences?.getBoolean(key, false)!!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun invalidateSettings() {
|
||||
|
||||
val classicNotification: TwoStatePreference? = findPreference("classic_notification")
|
||||
if (VERSION.SDK_INT < VERSION_CODES.N) {
|
||||
classicNotification?.isVisible = false
|
||||
} else {
|
||||
classicNotification?.apply {
|
||||
isChecked = PreferenceUtil.isClassicNotification
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
// Save preference
|
||||
PreferenceUtil.isClassicNotification = newValue as Boolean
|
||||
invalidateSettings()
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val coloredNotification: TwoStatePreference? = findPreference("colored_notification")
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.O) {
|
||||
coloredNotification?.isEnabled = PreferenceUtil.isClassicNotification
|
||||
} else {
|
||||
coloredNotification?.apply {
|
||||
isChecked = PreferenceUtil.isColoredNotification
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
PreferenceUtil.isColoredNotification = newValue as Boolean
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
PreferenceUtil.registerOnSharedPreferenceChangedListener(this)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
PreferenceUtil.unregisterOnSharedPreferenceChangedListener(this)
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.pref_notification)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package io.github.muntashirakon.music.fragments.settings
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.TwoStatePreference
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.ALBUM_COVER_STYLE
|
||||
import io.github.muntashirakon.music.CAROUSEL_EFFECT
|
||||
import io.github.muntashirakon.music.CIRCULAR_ALBUM_ART
|
||||
import io.github.muntashirakon.music.NOW_PLAYING_SCREEN_ID
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
|
||||
/**
|
||||
* @author Hemanth S (h4h13).
|
||||
*/
|
||||
|
||||
class NowPlayingSettingsFragment : AbsSettingsFragment(),
|
||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
override fun invalidateSettings() {
|
||||
updateNowPlayingScreenSummary()
|
||||
updateAlbumCoverStyleSummary()
|
||||
|
||||
val carouselEffect: TwoStatePreference = findPreference("carousel_effect")!!
|
||||
carouselEffect.setOnPreferenceChangeListener { _, _ ->
|
||||
return@setOnPreferenceChangeListener true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.pref_now_playing_screen)
|
||||
}
|
||||
|
||||
private fun updateAlbumCoverStyleSummary() {
|
||||
val preference: Preference? = findPreference(ALBUM_COVER_STYLE)
|
||||
preference?.setSummary(PreferenceUtil.albumCoverStyle.titleRes)
|
||||
}
|
||||
|
||||
private fun updateNowPlayingScreenSummary() {
|
||||
val preference: Preference? = findPreference(NOW_PLAYING_SCREEN_ID)
|
||||
preference?.setSummary(PreferenceUtil.nowPlayingScreen.titleRes)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
PreferenceUtil.registerOnSharedPreferenceChangedListener(this)
|
||||
val preference: Preference? = findPreference("album_cover_transform")
|
||||
preference?.setOnPreferenceChangeListener { albumPrefs, newValue ->
|
||||
setSummary(albumPrefs, newValue)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
PreferenceUtil.unregisterOnSharedPreferenceChangedListener(this)
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
|
||||
when (key) {
|
||||
NOW_PLAYING_SCREEN_ID -> updateNowPlayingScreenSummary()
|
||||
ALBUM_COVER_STYLE -> updateAlbumCoverStyleSummary()
|
||||
CIRCULAR_ALBUM_ART, CAROUSEL_EFFECT -> invalidateSettings()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package io.github.muntashirakon.music.fragments.settings
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.preference.Preference
|
||||
import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEListPreference
|
||||
import io.github.muntashirakon.music.R
|
||||
|
||||
/**
|
||||
* @author Hemanth S (h4h13).
|
||||
*/
|
||||
|
||||
class OtherSettingsFragment : AbsSettingsFragment() {
|
||||
override fun invalidateSettings() {
|
||||
val languagePreference: ATEListPreference? = findPreference("language_name")
|
||||
languagePreference?.setOnPreferenceChangeListener { _, _ ->
|
||||
requireActivity().recreate()
|
||||
return@setOnPreferenceChangeListener true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.pref_advanced)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val preference: Preference? = findPreference("last_added_interval")
|
||||
preference?.setOnPreferenceChangeListener { lastAdded, newValue ->
|
||||
setSummary(lastAdded, newValue)
|
||||
true
|
||||
}
|
||||
val languagePreference: Preference? = findPreference("language_name")
|
||||
languagePreference?.setOnPreferenceChangeListener { prefs, newValue ->
|
||||
setSummary(prefs, newValue)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package io.github.muntashirakon.music.fragments.settings
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.preference.TwoStatePreference
|
||||
import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEListPreference
|
||||
import io.github.muntashirakon.music.R
|
||||
|
||||
class PersonalizeSettingsFragment : AbsSettingsFragment() {
|
||||
|
||||
override fun invalidateSettings() {
|
||||
val toggleFullScreen: TwoStatePreference = findPreference("toggle_full_screen")!!
|
||||
toggleFullScreen.setOnPreferenceChangeListener { _, _ ->
|
||||
requireActivity().recreate()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.pref_ui)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val homeArtistStyle: ATEListPreference? = findPreference("home_artist_grid_style")
|
||||
homeArtistStyle?.setOnPreferenceChangeListener { preference, newValue ->
|
||||
setSummary(preference, newValue)
|
||||
true
|
||||
}
|
||||
val tabTextMode: ATEListPreference? = findPreference("tab_text_mode")
|
||||
tabTextMode?.setOnPreferenceChangeListener { prefs, newValue ->
|
||||
setSummary(prefs, newValue)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package io.github.muntashirakon.music.fragments.settings
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.TwoStatePreference
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEColorPreference
|
||||
import code.name.monkey.appthemehelper.common.prefs.supportv7.ATESwitchPreference
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import io.github.muntashirakon.music.DESATURATED_COLOR
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.appshortcuts.DynamicShortcutManager
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
import com.afollestad.materialdialogs.color.ColorChooserDialog
|
||||
|
||||
/**
|
||||
* @author Hemanth S (h4h13).
|
||||
*/
|
||||
|
||||
class ThemeSettingsFragment : AbsSettingsFragment() {
|
||||
override fun invalidateSettings() {
|
||||
val generalTheme: Preference? = findPreference("general_theme")
|
||||
generalTheme?.let {
|
||||
setSummary(it)
|
||||
it.setOnPreferenceChangeListener { _, newValue ->
|
||||
val theme = newValue as String
|
||||
setSummary(it, newValue)
|
||||
ThemeStore.markChanged(requireContext())
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
||||
requireActivity().setTheme(PreferenceUtil.themeResFromPrefValue(theme))
|
||||
DynamicShortcutManager(requireContext()).updateDynamicShortcuts()
|
||||
}
|
||||
requireActivity().recreate()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
val accentColorPref: ATEColorPreference = findPreference("accent_color")!!
|
||||
val accentColor = ThemeStore.accentColor(requireContext())
|
||||
accentColorPref.setColor(accentColor, ColorUtil.darkenColor(accentColor))
|
||||
|
||||
accentColorPref.setOnPreferenceClickListener {
|
||||
ColorChooserDialog.Builder(requireContext(), R.string.accent_color)
|
||||
.accentMode(true)
|
||||
.allowUserColorInput(true)
|
||||
.allowUserColorInputAlpha(false)
|
||||
.preselect(accentColor)
|
||||
.show(requireActivity())
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
val blackTheme: ATESwitchPreference? = findPreference("black_theme")
|
||||
blackTheme?.setOnPreferenceChangeListener { _, _ ->
|
||||
ThemeStore.markChanged(requireContext())
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
||||
requireActivity().setTheme(PreferenceUtil.themeResFromPrefValue("black"))
|
||||
DynamicShortcutManager(requireContext()).updateDynamicShortcuts()
|
||||
}
|
||||
requireActivity().recreate()
|
||||
true
|
||||
}
|
||||
|
||||
val desaturatedColor: ATESwitchPreference? = findPreference(DESATURATED_COLOR)
|
||||
desaturatedColor?.setOnPreferenceChangeListener { _, value ->
|
||||
val desaturated = value as Boolean
|
||||
ThemeStore.prefs(requireContext())
|
||||
.edit()
|
||||
.putBoolean("desaturated_color", desaturated)
|
||||
.apply()
|
||||
PreferenceUtil.isDesaturatedColor = desaturated
|
||||
requireActivity().recreate()
|
||||
true
|
||||
}
|
||||
|
||||
|
||||
val colorAppShortcuts: TwoStatePreference = findPreference("should_color_app_shortcuts")!!
|
||||
if (!VersionUtils.hasNougatMR()) {
|
||||
colorAppShortcuts.isVisible = false
|
||||
} else {
|
||||
colorAppShortcuts.isChecked = PreferenceUtil.isColoredAppShortcuts
|
||||
colorAppShortcuts.setOnPreferenceChangeListener { _, newValue ->
|
||||
PreferenceUtil.isColoredAppShortcuts = newValue as Boolean
|
||||
DynamicShortcutManager(requireContext()).updateDynamicShortcuts()
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.pref_general)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
package io.github.muntashirakon.music.fragments.songs
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.adapter.song.ShuffleButtonSongAdapter
|
||||
import io.github.muntashirakon.music.adapter.song.SongAdapter
|
||||
import io.github.muntashirakon.music.fragments.ReloadType
|
||||
import io.github.muntashirakon.music.fragments.base.AbsLibraryPagerRecyclerViewCustomGridSizeFragment
|
||||
import io.github.muntashirakon.music.interfaces.MainActivityFragmentCallbacks
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
|
||||
class SongsFragment :
|
||||
AbsLibraryPagerRecyclerViewCustomGridSizeFragment<SongAdapter, GridLayoutManager>(),
|
||||
MainActivityFragmentCallbacks {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
mainActivity.libraryViewModel.allSongs()
|
||||
.observe(viewLifecycleOwner, androidx.lifecycle.Observer {
|
||||
if (it.isNotEmpty()) {
|
||||
adapter?.swapDataSet(it)
|
||||
} else {
|
||||
adapter?.swapDataSet(listOf())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override val emptyMessage: Int
|
||||
get() = R.string.no_songs
|
||||
|
||||
override fun createLayoutManager(): GridLayoutManager {
|
||||
return GridLayoutManager(requireActivity(), getGridSize()).apply {
|
||||
spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
||||
override fun getSpanSize(position: Int): Int {
|
||||
return if (position == 0) {
|
||||
getGridSize()
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun createAdapter(): SongAdapter {
|
||||
val dataSet = if (adapter == null) mutableListOf() else adapter!!.dataSet
|
||||
return ShuffleButtonSongAdapter(
|
||||
mainActivity,
|
||||
dataSet,
|
||||
itemLayoutRes(),
|
||||
mainActivity
|
||||
)
|
||||
}
|
||||
|
||||
override fun loadGridSize(): Int {
|
||||
return PreferenceUtil.songGridSize
|
||||
}
|
||||
|
||||
override fun saveGridSize(gridColumns: Int) {
|
||||
PreferenceUtil.songGridSize = gridColumns
|
||||
}
|
||||
|
||||
override fun loadGridSizeLand(): Int {
|
||||
return PreferenceUtil.songGridSizeLand
|
||||
}
|
||||
|
||||
override fun saveGridSizeLand(gridColumns: Int) {
|
||||
PreferenceUtil.songGridSizeLand = gridColumns
|
||||
}
|
||||
|
||||
override fun setGridSize(gridSize: Int) {
|
||||
adapter?.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun loadSortOrder(): String {
|
||||
return PreferenceUtil.songSortOrder
|
||||
}
|
||||
|
||||
override fun saveSortOrder(sortOrder: String) {
|
||||
PreferenceUtil.songSortOrder = sortOrder
|
||||
}
|
||||
|
||||
@LayoutRes
|
||||
override fun loadLayoutRes(): Int {
|
||||
return PreferenceUtil.songGridStyle
|
||||
}
|
||||
|
||||
override fun saveLayoutRes(@LayoutRes layoutRes: Int) {
|
||||
PreferenceUtil.songGridStyle = layoutRes
|
||||
}
|
||||
|
||||
override fun setSortOrder(sortOrder: String) {
|
||||
mainActivity.libraryViewModel.forceReload(ReloadType.Songs)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
var TAG: String = SongsFragment::class.java.simpleName
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance(): SongsFragment {
|
||||
return SongsFragment()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun handleBackPress(): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue