Updated code

Added Fragment options
Renamed Interfaces
Rolled back to previous implementaion for Now playing to handle backpress
main
Hemanth S 2020-09-24 02:25:12 +05:30
parent 7c0b3ee82c
commit 3f368e186b
45 changed files with 274 additions and 457 deletions

View File

@ -12,7 +12,6 @@ import code.name.monkey.retromusic.fragments.albums.AlbumDetailsViewModel
import code.name.monkey.retromusic.fragments.artists.ArtistDetailsViewModel
import code.name.monkey.retromusic.fragments.genres.GenreDetailsViewModel
import code.name.monkey.retromusic.fragments.playlists.PlaylistDetailsViewModel
import code.name.monkey.retromusic.fragments.search.SearchViewModel
import code.name.monkey.retromusic.model.Genre
import code.name.monkey.retromusic.network.*
import code.name.monkey.retromusic.repository.*
@ -189,10 +188,6 @@ private val viewModules = module {
genre
)
}
viewModel {
SearchViewModel(get())
}
}
val appModules = listOf(mainModule, dataModule, viewModules, networkModule, roomModule)

View File

@ -8,7 +8,7 @@ import androidx.lifecycle.lifecycleScope
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.db.toPlayCount
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener
import code.name.monkey.retromusic.repository.RealRepository
import code.name.monkey.retromusic.service.MusicService.*
import kotlinx.coroutines.Dispatchers
@ -17,9 +17,9 @@ import org.koin.android.ext.android.inject
import java.lang.ref.WeakReference
import java.util.*
abstract class AbsMusicServiceActivity : AbsBaseActivity(), MusicServiceEventListener {
abstract class AbsMusicServiceActivity : AbsBaseActivity(), IMusicServiceEventListener {
private val mMusicServiceEventListeners = ArrayList<MusicServiceEventListener>()
private val mMusicServiceEventListeners = ArrayList<IMusicServiceEventListener>()
private val repository: RealRepository by inject()
private var serviceToken: MusicPlayerRemote.ServiceToken? = null
private var musicStateReceiver: MusicStateReceiver? = null
@ -49,15 +49,15 @@ abstract class AbsMusicServiceActivity : AbsBaseActivity(), MusicServiceEventLis
}
}
fun addMusicServiceEventListener(listener: MusicServiceEventListener?) {
if (listener != null) {
mMusicServiceEventListeners.add(listener)
fun addMusicServiceEventListener(listenerI: IMusicServiceEventListener?) {
if (listenerI != null) {
mMusicServiceEventListeners.add(listenerI)
}
}
fun removeMusicServiceEventListener(listener: MusicServiceEventListener?) {
if (listener != null) {
mMusicServiceEventListeners.remove(listener)
fun removeMusicServiceEventListener(listenerI: IMusicServiceEventListener?) {
if (listenerI != null) {
mMusicServiceEventListeners.remove(listenerI)
}
}

View File

@ -8,25 +8,41 @@ import android.view.ViewTreeObserver
import android.widget.FrameLayout
import androidx.annotation.LayoutRes
import androidx.core.view.ViewCompat
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.RetroBottomSheetBehavior
import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.peekHeightAnimate
import code.name.monkey.retromusic.extensions.translateXAnimate
import code.name.monkey.retromusic.extensions.whichFragment
import code.name.monkey.retromusic.fragments.LibraryViewModel
import code.name.monkey.retromusic.fragments.MiniPlayerFragment
import code.name.monkey.retromusic.fragments.NowPlayingScreen
import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.player.adaptive.AdaptiveFragment
import code.name.monkey.retromusic.fragments.player.blur.BlurPlayerFragment
import code.name.monkey.retromusic.fragments.player.card.CardFragment
import code.name.monkey.retromusic.fragments.player.cardblur.CardBlurFragment
import code.name.monkey.retromusic.fragments.player.circle.CirclePlayerFragment
import code.name.monkey.retromusic.fragments.player.color.ColorFragment
import code.name.monkey.retromusic.fragments.player.fit.FitFragment
import code.name.monkey.retromusic.fragments.player.flat.FlatPlayerFragment
import code.name.monkey.retromusic.fragments.player.full.FullPlayerFragment
import code.name.monkey.retromusic.fragments.player.gradient.GradientPlayerFragment
import code.name.monkey.retromusic.fragments.player.material.MaterialFragment
import code.name.monkey.retromusic.fragments.player.normal.PlayerFragment
import code.name.monkey.retromusic.fragments.player.plain.PlainPlayerFragment
import code.name.monkey.retromusic.fragments.player.simple.SimplePlayerFragment
import code.name.monkey.retromusic.fragments.player.tiny.TinyPlayerFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.model.CategoryInfo
import code.name.monkey.retromusic.state.NowPlayingPanelState.*
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.views.BottomNavigationBarTinted
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetBehavior.*
import kotlinx.android.synthetic.main.sliding_music_panel_layout.*
import org.koin.androidx.viewmodel.ext.android.viewModel
@ -36,9 +52,9 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
}
protected val libraryViewModel by viewModel<LibraryViewModel>()
private lateinit var behavior: RetroBottomSheetBehavior<FrameLayout>
private lateinit var bottomSheetBehavior: RetroBottomSheetBehavior<FrameLayout>
private var miniPlayerFragment: MiniPlayerFragment? = null
private var cps: NowPlayingScreen? = null
private var nowPlayingScreen: NowPlayingScreen? = null
private var navigationBarColor: Int = 0
private var taskColor: Int = 0
private var lightStatusBar: Boolean = false
@ -46,9 +62,9 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
private var paletteColor: Int = Color.WHITE
protected abstract fun createContentView(): View
private val panelState: Int
get() = behavior.state
get() = bottomSheetBehavior.state
private val bottomSheetCallbackList = object : BottomSheetBehavior.BottomSheetCallback() {
private val bottomSheetCallbackList = object : BottomSheetCallback() {
override fun onSlide(bottomSheet: View, slideOffset: Float) {
setMiniPlayerAlphaProgress(slideOffset)
@ -56,14 +72,14 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
override fun onStateChanged(bottomSheet: View, newState: Int) {
when (newState) {
BottomSheetBehavior.STATE_EXPANDED -> {
STATE_EXPANDED -> {
onPanelExpanded()
}
BottomSheetBehavior.STATE_COLLAPSED -> {
STATE_COLLAPSED -> {
onPanelCollapsed()
}
else -> {
println("Do something")
}
}
}
@ -75,34 +91,29 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
setContentView(createContentView())
chooseFragmentForTheme()
setupSlidingUpPanel()
setupBottomSheet()
updatePanelState()
updateColor()
}
fun getBottomSheetBehavior() = behavior
fun getBottomSheetBehavior() = bottomSheetBehavior
private fun setupBottomSheet() {
behavior = BottomSheetBehavior.from(slidingPanel) as RetroBottomSheetBehavior
behavior.addBottomSheetCallback(bottomSheetCallbackList)
if (behavior.state == BottomSheetBehavior.STATE_EXPANDED) {
setMiniPlayerAlphaProgress(1f)
}
bottomSheetBehavior = from(slidingPanel) as RetroBottomSheetBehavior
bottomSheetBehavior.addBottomSheetCallback(bottomSheetCallbackList)
}
override fun onResume() {
super.onResume()
if (cps != PreferenceUtil.nowPlayingScreen) {
if (nowPlayingScreen != PreferenceUtil.nowPlayingScreen) {
postRecreate()
}
}
override fun onDestroy() {
super.onDestroy()
behavior.removeBottomSheetCallback(bottomSheetCallbackList)
bottomSheetBehavior.removeBottomSheetCallback(bottomSheetCallbackList)
}
protected fun wrapSlidingMusicPanel(@LayoutRes resId: Int): View {
@ -115,17 +126,14 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
}
fun collapsePanel() {
behavior.state = BottomSheetBehavior.STATE_COLLAPSED
setMiniPlayerAlphaProgress(0f)
bottomSheetBehavior.state = STATE_COLLAPSED
}
fun expandPanel() {
behavior.state = BottomSheetBehavior.STATE_EXPANDED
setMiniPlayerAlphaProgress(1f)
bottomSheetBehavior.state = STATE_EXPANDED
}
private fun setMiniPlayerAlphaProgress(progress: Float) {
if (miniPlayerFragment?.view == null) return
val alpha = 1 - progress
miniPlayerFragment?.view?.alpha = alpha
miniPlayerFragment?.view?.visibility = if (alpha == 0f) View.GONE else View.VISIBLE
@ -151,8 +159,8 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
override fun onGlobalLayout() {
slidingPanel.viewTreeObserver.removeOnGlobalLayoutListener(this)
when (panelState) {
BottomSheetBehavior.STATE_EXPANDED -> onPanelExpanded()
BottomSheetBehavior.STATE_COLLAPSED -> onPanelCollapsed()
STATE_EXPANDED -> onPanelExpanded()
STATE_COLLAPSED -> onPanelCollapsed()
else -> {
//playerFragment!!.onHide()
}
@ -175,24 +183,20 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
val isBottomBarVisible = bottomNavigationView.isVisible
if (hide) {
behavior.isHideable = true
behavior.peekHeight = 0
bottomSheetBehavior.isHideable = true
bottomSheetBehavior.peekHeight = 0
collapsePanel()
ViewCompat.setElevation(slidingPanel, 0f)
ViewCompat.setElevation(bottomNavigationView, 10f)
} else {
ViewCompat.setElevation(bottomNavigationView, 10f)
ViewCompat.setElevation(slidingPanel, 10f)
behavior.isHideable = false
behavior.peekHeight = (if (isBottomBarVisible) heightOfBar * 2 else heightOfBar) - 24
bottomSheetBehavior.isHideable = false
bottomSheetBehavior.peekHeight =
(if (isBottomBarVisible) heightOfBar * 2 else heightOfBar) - 24
}
}
private fun chooseFragmentForTheme() {
cps = PreferenceUtil.nowPlayingScreen
miniPlayerFragment = whichFragment<MiniPlayerFragment>(R.id.miniPlayerFragment)
miniPlayerFragment?.view?.setOnClickListener { expandPanel() }
}
override fun onServiceConnected() {
super.onServiceConnected()
@ -229,8 +233,9 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
if (!handleBackPress()) super.onBackPressed()
}
open fun handleBackPress(): Boolean {
if (panelState == BottomSheetBehavior.STATE_EXPANDED) {
private fun handleBackPress(): Boolean {
if (bottomSheetBehavior.peekHeight != 0 && playerFragment!!.onBackPressed()) return true
if (panelState == STATE_EXPANDED) {
collapsePanel()
return true
}
@ -238,27 +243,27 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
}
private fun onPaletteColorChanged() {
if (panelState == BottomSheetBehavior.STATE_EXPANDED) {
if (panelState == STATE_EXPANDED) {
super.setTaskDescriptionColor(paletteColor)
val isColorLight = ColorUtil.isColorLight(paletteColor)
if (PreferenceUtil.isAdaptiveColor && (cps == Normal || cps == Flat)) {
if (PreferenceUtil.isAdaptiveColor && (nowPlayingScreen == Normal || nowPlayingScreen == Flat)) {
super.setLightNavigationBar(true)
super.setLightStatusbar(isColorLight)
} else if (cps == Card || cps == Blur || cps == BlurCard) {
} else if (nowPlayingScreen == Card || nowPlayingScreen == Blur || nowPlayingScreen == BlurCard) {
super.setLightStatusbar(false)
super.setLightNavigationBar(true)
super.setNavigationbarColor(Color.BLACK)
} else if (cps == Color || cps == Tiny || cps == Gradient) {
} else if (nowPlayingScreen == Color || nowPlayingScreen == Tiny || nowPlayingScreen == Gradient) {
super.setNavigationbarColor(paletteColor)
super.setLightNavigationBar(isColorLight)
super.setLightStatusbar(isColorLight)
} else if (cps == Full) {
} else if (nowPlayingScreen == Full) {
super.setNavigationbarColor(paletteColor)
super.setLightNavigationBar(isColorLight)
super.setLightStatusbar(false)
} else if (cps == Classic) {
} else if (nowPlayingScreen == Classic) {
super.setLightStatusbar(false)
} else if (cps == Fit) {
} else if (nowPlayingScreen == Fit) {
super.setLightStatusbar(false)
} else {
super.setLightStatusbar(
@ -276,28 +281,28 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
override fun setLightStatusbar(enabled: Boolean) {
lightStatusBar = enabled
if (panelState == BottomSheetBehavior.STATE_COLLAPSED) {
if (panelState == STATE_COLLAPSED) {
super.setLightStatusbar(enabled)
}
}
override fun setLightNavigationBar(enabled: Boolean) {
lightNavigationBar = enabled
if (panelState == BottomSheetBehavior.STATE_COLLAPSED) {
if (panelState == STATE_COLLAPSED) {
super.setLightNavigationBar(enabled)
}
}
override fun setNavigationbarColor(color: Int) {
navigationBarColor = color
if (panelState == BottomSheetBehavior.STATE_COLLAPSED) {
if (panelState == STATE_COLLAPSED) {
super.setNavigationbarColor(color)
}
}
override fun setTaskDescriptionColor(color: Int) {
taskColor = color
if (panelState == BottomSheetBehavior.STATE_COLLAPSED) {
if (panelState == STATE_COLLAPSED) {
super.setTaskDescriptionColor(color)
}
}
@ -335,32 +340,38 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
println("HIDE")
ViewCompat.setElevation(slidingPanel, 0f)
ViewCompat.setElevation(bottomNavigationView, 10f)
behavior.isHideable = true
behavior.peekHeightAnimate(0)
collapsePanel()
bottomSheetBehavior.isHideable = true
bottomSheetBehavior.setPeekHeight(0, true)
bottomSheetBehavior.state = STATE_COLLAPSED
}
COLLAPSED_WITH -> {
println("COLLAPSED_WITH")
val heightOfBar = bottomNavigationView.height
ViewCompat.setElevation(bottomNavigationView, 10f)
ViewCompat.setElevation(slidingPanel, 10f)
behavior.isHideable = false
behavior.peekHeightAnimate(if(isQueueEmpty) 0 else (heightOfBar * 2) - 24)
bottomNavigationView.translateXAnimate(0f)
bottomSheetBehavior.isHideable = false
bottomSheetBehavior.setPeekHeight(
if (isQueueEmpty) 0 else (heightOfBar * 2) - 24,
true
)
bottomNavigationView.isVisible = true
}
COLLAPSED_WITHOUT -> {
println("COLLAPSED_WITHOUT")
val heightOfBar = bottomNavigationView.height
ViewCompat.setElevation(bottomNavigationView, 10f)
ViewCompat.setElevation(slidingPanel, 10f)
behavior.isHideable = false
behavior.peekHeightAnimate(if(isQueueEmpty) 0 else heightOfBar - 24)
bottomNavigationView.translateXAnimate(heightOfBar.toFloat())
bottomSheetBehavior.isHideable = false
bottomSheetBehavior.setPeekHeight(
if (isQueueEmpty) 0 else heightOfBar - 24,
true
)
bottomNavigationView.isGone = true
}
else -> {
println("ELSE")
behavior.isHideable = true
behavior.peekHeight = 0
bottomSheetBehavior.isHideable = true
bottomSheetBehavior.peekHeight = 0
collapsePanel()
ViewCompat.setElevation(slidingPanel, 0f)
ViewCompat.setElevation(bottomNavigationView, 10f)
@ -368,4 +379,35 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
}
})
}
private var playerFragment: AbsPlayerFragment? = null
private fun chooseFragmentForTheme() {
nowPlayingScreen = PreferenceUtil.nowPlayingScreen
val fragment: Fragment = when (nowPlayingScreen) {
Blur -> BlurPlayerFragment()
Adaptive -> AdaptiveFragment()
Normal -> PlayerFragment()
Card -> CardFragment()
BlurCard -> CardBlurFragment()
Fit -> FitFragment()
Flat -> FlatPlayerFragment()
Full -> FullPlayerFragment()
Plain -> PlainPlayerFragment()
Simple -> SimplePlayerFragment()
Material -> MaterialFragment()
Color -> ColorFragment()
Gradient -> GradientPlayerFragment()
Tiny -> TinyPlayerFragment()
//PEAK -> PeakPlayerFragment()
Circle -> CirclePlayerFragment()
else -> PlayerFragment()
} // must implement AbsPlayerFragment
supportFragmentManager.beginTransaction().replace(R.id.playerFragmentContainer, fragment)
.commit()
supportFragmentManager.executePendingTransactions()
playerFragment = supportFragmentManager.findFragmentById(R.id.playerFragmentContainer) as AbsPlayerFragment
miniPlayerFragment = whichFragment<MiniPlayerFragment>(R.id.miniPlayerFragment)
miniPlayerFragment?.view?.setOnClickListener { expandPanel() }
}
}

View File

@ -25,7 +25,7 @@ class SearchAdapter(
private var dataSet: List<Any>
) : RecyclerView.Adapter<SearchAdapter.ViewHolder>() {
fun swapDataSet(dataSet: MutableList<Any>) {
fun swapDataSet(dataSet: List<Any>) {
this.dataSet = dataSet
notifyDataSetChanged()
}

View File

@ -26,8 +26,8 @@ import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
import code.name.monkey.retromusic.glide.audiocover.AudioFileCover
import code.name.monkey.retromusic.interfaces.CabHolder
import code.name.monkey.retromusic.interfaces.Callbacks
import code.name.monkey.retromusic.interfaces.ICabHolder
import code.name.monkey.retromusic.interfaces.ICallbacks
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.RetroUtil
import com.bumptech.glide.Glide
@ -43,10 +43,10 @@ class SongFileAdapter(
private val activity: AppCompatActivity,
private var dataSet: List<File>,
private val itemLayoutRes: Int,
private val callbacks: Callbacks?,
cabHolder: CabHolder?
private val ICallbacks: ICallbacks?,
ICabHolder: ICabHolder?
) : AbsMultiSelectAdapter<SongFileAdapter.ViewHolder, File>(
activity, cabHolder, R.menu.menu_media_selection
activity, ICabHolder, R.menu.menu_media_selection
), PopupTextProvider {
init {
@ -136,8 +136,8 @@ class SongFileAdapter(
}
override fun onMultipleItemAction(menuItem: MenuItem, selection: List<File>) {
if (callbacks == null) return
callbacks.onMultipleItemAction(menuItem, selection as ArrayList<File>)
if (ICallbacks == null) return
ICallbacks.onMultipleItemAction(menuItem, selection as ArrayList<File>)
}
override fun getPopupText(position: Int): String {
@ -152,11 +152,11 @@ class SongFileAdapter(
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
init {
if (menu != null && callbacks != null) {
if (menu != null && ICallbacks != null) {
menu?.setOnClickListener { v ->
val position = layoutPosition
if (isPositionInRange(position)) {
callbacks.onFileMenuClicked(dataSet[position], v)
ICallbacks.onFileMenuClicked(dataSet[position], v)
}
}
}
@ -171,7 +171,7 @@ class SongFileAdapter(
if (isInQuickSelectMode) {
toggleChecked(position)
} else {
callbacks?.onFileSelected(dataSet[position])
ICallbacks?.onFileSelected(dataSet[position])
}
}
}

View File

@ -16,7 +16,7 @@ import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.SortOrder
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
import code.name.monkey.retromusic.interfaces.CabHolder
import code.name.monkey.retromusic.interfaces.ICabHolder
import code.name.monkey.retromusic.model.Album
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.MusicUtil
@ -29,11 +29,11 @@ open class AlbumAdapter(
protected val activity: FragmentActivity,
var dataSet: List<Album>,
protected var itemLayoutRes: Int,
cabHolder: CabHolder?,
ICabHolder: ICabHolder?,
private val albumClickListener: AlbumClickListener?
) : AbsMultiSelectAdapter<AlbumAdapter.ViewHolder, Album>(
activity,
cabHolder,
ICabHolder,
R.menu.menu_media_selection
), PopupTextProvider {

View File

@ -7,7 +7,7 @@ import code.name.monkey.retromusic.fragments.albums.AlbumClickListener
import code.name.monkey.retromusic.glide.AlbumGlideRequest
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
import code.name.monkey.retromusic.helper.HorizontalAdapterHelper
import code.name.monkey.retromusic.interfaces.CabHolder
import code.name.monkey.retromusic.interfaces.ICabHolder
import code.name.monkey.retromusic.model.Album
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
@ -16,10 +16,10 @@ import com.bumptech.glide.Glide
class HorizontalAlbumAdapter(
activity: FragmentActivity,
dataSet: List<Album>,
cabHolder: CabHolder?,
ICabHolder: ICabHolder?,
albumClickListener: AlbumClickListener
) : AlbumAdapter(
activity, dataSet, HorizontalAdapterHelper.LAYOUT_RES, cabHolder, albumClickListener
activity, dataSet, HorizontalAdapterHelper.LAYOUT_RES, ICabHolder, albumClickListener
) {
override fun createViewHolder(view: View, viewType: Int): ViewHolder {

View File

@ -16,7 +16,7 @@ import code.name.monkey.retromusic.fragments.artists.ArtistClickListener
import code.name.monkey.retromusic.glide.ArtistGlideRequest
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
import code.name.monkey.retromusic.interfaces.CabHolder
import code.name.monkey.retromusic.interfaces.ICabHolder
import code.name.monkey.retromusic.model.Artist
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.MusicUtil
@ -29,10 +29,10 @@ class ArtistAdapter(
val activity: FragmentActivity,
var dataSet: List<Artist>,
var itemLayoutRes: Int,
cabHolder: CabHolder?,
ICabHolder: ICabHolder?,
private val artistClickListener: ArtistClickListener
) : AbsMultiSelectAdapter<ArtistAdapter.ViewHolder, Artist>(
activity, cabHolder, R.menu.menu_media_selection
activity, ICabHolder, R.menu.menu_media_selection
), PopupTextProvider {
init {

View File

@ -15,21 +15,21 @@ import java.util.ArrayList;
import java.util.List;
import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.interfaces.CabHolder;
import code.name.monkey.retromusic.interfaces.ICabHolder;
public abstract class AbsMultiSelectAdapter<V extends RecyclerView.ViewHolder, I> extends RecyclerView.Adapter<V>
implements MaterialCab.Callback {
@Nullable
private final CabHolder cabHolder;
private final ICabHolder ICabHolder;
private final Context context;
private MaterialCab cab;
private List<I> checked;
private int menuRes;
public AbsMultiSelectAdapter(@NonNull Context context, @Nullable CabHolder cabHolder, @MenuRes int menuRes) {
this.cabHolder = cabHolder;
public AbsMultiSelectAdapter(@NonNull Context context, @Nullable ICabHolder ICabHolder, @MenuRes int menuRes) {
this.ICabHolder = ICabHolder;
checked = new ArrayList<>();
this.menuRes = menuRes;
this.context = context;
@ -59,7 +59,7 @@ public abstract class AbsMultiSelectAdapter<V extends RecyclerView.ViewHolder, I
}
protected void checkAll() {
if (cabHolder != null) {
if (ICabHolder != null) {
checked.clear();
for (int i = 0; i < getItemCount(); i++) {
I identifier = getIdentifier(i);
@ -94,7 +94,7 @@ public abstract class AbsMultiSelectAdapter<V extends RecyclerView.ViewHolder, I
}
protected boolean toggleChecked(final int position) {
if (cabHolder != null) {
if (ICabHolder != null) {
I identifier = getIdentifier(position);
if (identifier == null) {
return false;
@ -117,9 +117,9 @@ public abstract class AbsMultiSelectAdapter<V extends RecyclerView.ViewHolder, I
}
private void updateCab() {
if (cabHolder != null) {
if (ICabHolder != null) {
if (cab == null || !cab.isActive()) {
cab = cabHolder.openCab(menuRes, this);
cab = ICabHolder.openCab(menuRes, this);
}
final int size = checked.size();
if (size <= 0) {

View File

@ -27,7 +27,7 @@ import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
import code.name.monkey.retromusic.interfaces.CabHolder
import code.name.monkey.retromusic.interfaces.ICabHolder
import code.name.monkey.retromusic.model.Playlist
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.repository.PlaylistSongsLoader
@ -39,10 +39,10 @@ class PlaylistAdapter(
private val activity: FragmentActivity,
var dataSet: List<PlaylistWithSongs>,
private var itemLayoutRes: Int,
cabHolder: CabHolder?
ICabHolder: ICabHolder?
) : AbsMultiSelectAdapter<PlaylistAdapter.ViewHolder, PlaylistWithSongs>(
activity,
cabHolder,
ICabHolder,
R.menu.menu_playlists_selection
) {

View File

@ -7,15 +7,15 @@ import androidx.annotation.LayoutRes
import androidx.appcompat.app.AppCompatActivity
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.interfaces.CabHolder
import code.name.monkey.retromusic.interfaces.ICabHolder
import code.name.monkey.retromusic.model.Song
abstract class AbsOffsetSongAdapter(
activity: AppCompatActivity,
dataSet: MutableList<Song>,
@LayoutRes itemLayoutRes: Int,
cabHolder: CabHolder?
) : SongAdapter(activity, dataSet, itemLayoutRes, cabHolder) {
ICabHolder: ICabHolder?
) : SongAdapter(activity, dataSet, itemLayoutRes, ICabHolder) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SongAdapter.ViewHolder {
if (viewType == OFFSET_ITEM) {

View File

@ -9,7 +9,7 @@ import code.name.monkey.retromusic.db.PlaylistEntity
import code.name.monkey.retromusic.db.toSongEntity
import code.name.monkey.retromusic.db.toSongs
import code.name.monkey.retromusic.dialogs.RemoveSongFromPlaylistDialog
import code.name.monkey.retromusic.interfaces.CabHolder
import code.name.monkey.retromusic.interfaces.ICabHolder
import code.name.monkey.retromusic.model.PlaylistSong
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.ViewUtil
@ -23,13 +23,13 @@ class OrderablePlaylistSongAdapter(
activity: FragmentActivity,
dataSet: ArrayList<Song>,
itemLayoutRes: Int,
cabHolder: CabHolder?,
ICabHolder: ICabHolder?,
private val onMoveItemListener: OnMoveItemListener?
) : SongAdapter(
activity,
dataSet,
itemLayoutRes,
cabHolder
ICabHolder
), DraggableItemAdapter<OrderablePlaylistSongAdapter.ViewHolder> {
init {

View File

@ -8,7 +8,7 @@ import androidx.navigation.findNavController
import code.name.monkey.retromusic.EXTRA_ALBUM_ID
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.interfaces.CabHolder
import code.name.monkey.retromusic.interfaces.ICabHolder
import code.name.monkey.retromusic.model.Song
import com.google.android.material.button.MaterialButton
@ -16,8 +16,8 @@ open class PlaylistSongAdapter(
activity: AppCompatActivity,
dataSet: MutableList<Song>,
itemLayoutRes: Int,
cabHolder: CabHolder?
) : AbsOffsetSongAdapter(activity, dataSet, itemLayoutRes, cabHolder) {
ICabHolder: ICabHolder?
) : AbsOffsetSongAdapter(activity, dataSet, itemLayoutRes, ICabHolder) {
init {
this.setMultiSelectMenuRes(R.menu.menu_cannot_delete_single_songs_playlist_songs_selection)

View File

@ -4,7 +4,7 @@ import android.view.View
import androidx.appcompat.app.AppCompatActivity
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.interfaces.CabHolder
import code.name.monkey.retromusic.interfaces.ICabHolder
import code.name.monkey.retromusic.model.Song
import com.google.android.material.button.MaterialButton
@ -12,8 +12,8 @@ class ShuffleButtonSongAdapter(
activity: AppCompatActivity,
dataSet: MutableList<Song>,
itemLayoutRes: Int,
cabHolder: CabHolder?
) : AbsOffsetSongAdapter(activity, dataSet, itemLayoutRes, cabHolder) {
ICabHolder: ICabHolder?
) : AbsOffsetSongAdapter(activity, dataSet, itemLayoutRes, ICabHolder) {
override fun createViewHolder(view: View): SongAdapter.ViewHolder {
return ViewHolder(view)

View File

@ -3,7 +3,7 @@ package code.name.monkey.retromusic.adapter.song
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.fragment.app.FragmentActivity
import code.name.monkey.retromusic.interfaces.CabHolder
import code.name.monkey.retromusic.interfaces.ICabHolder
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.MusicUtil
import java.util.*
@ -12,8 +12,8 @@ class SimpleSongAdapter(
context: FragmentActivity,
songs: ArrayList<Song>,
layoutRes: Int,
cabHolder: CabHolder?
) : SongAdapter(context, songs, layoutRes, cabHolder) {
ICabHolder: ICabHolder?
) : SongAdapter(context, songs, layoutRes, ICabHolder) {
override fun swapDataSet(dataSet: List<Song>) {
this.dataSet = dataSet.toMutableList()

View File

@ -21,7 +21,7 @@ import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.SortOrder
import code.name.monkey.retromusic.helper.menu.SongMenuHelper
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
import code.name.monkey.retromusic.interfaces.CabHolder
import code.name.monkey.retromusic.interfaces.ICabHolder
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
@ -38,11 +38,11 @@ open class SongAdapter(
protected val activity: FragmentActivity,
var dataSet: MutableList<Song>,
protected var itemLayoutRes: Int,
cabHolder: CabHolder?,
ICabHolder: ICabHolder?,
showSectionName: Boolean = true
) : AbsMultiSelectAdapter<SongAdapter.ViewHolder, Song>(
activity,
cabHolder,
ICabHolder,
R.menu.menu_media_selection
), MaterialCab.Callback, PopupTextProvider {

View File

@ -1,6 +1,7 @@
package code.name.monkey.retromusic.dialogs
import android.app.Dialog
import android.media.MediaScannerConnection
import android.os.Bundle
import android.widget.Toast
import androidx.core.os.bundleOf
@ -33,9 +34,15 @@ class SavePlaylistDialog : DialogFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch(Dispatchers.IO) {
val playlistWithSongs: PlaylistWithSongs =
extraNotNull<PlaylistWithSongs>(EXTRA_PLAYLIST).value
val file = PlaylistsUtil.savePlaylistWithSongs(requireContext(), playlistWithSongs)
val playlistWithSongs = extraNotNull<PlaylistWithSongs>(EXTRA_PLAYLIST).value
val file = PlaylistsUtil.savePlaylistWithSongs(playlistWithSongs)
MediaScannerConnection.scanFile(
requireActivity(),
arrayOf<String>(file.path),
null
) { _, _ ->
}
withContext(Dispatchers.Main) {
Toast.makeText(
requireContext(),

View File

@ -20,7 +20,6 @@ import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import androidx.annotation.LayoutRes
import androidx.core.animation.doOnEnd
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.TintHelper
import com.google.android.material.bottomsheet.BottomSheetBehavior
@ -55,12 +54,6 @@ fun View.translateXAnimate(value: Float) {
.apply {
duration = 300
start()
doOnEnd {
if (value != 0f) {
this@translateXAnimate.hide()
}
}
}
}

View File

@ -8,7 +8,7 @@ import code.name.monkey.retromusic.TOP_ARTISTS
import code.name.monkey.retromusic.db.*
import code.name.monkey.retromusic.fragments.ReloadType.*
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener
import code.name.monkey.retromusic.model.*
import code.name.monkey.retromusic.repository.RealRepository
@ -21,24 +21,21 @@ import kotlinx.coroutines.launch
class LibraryViewModel(
private val repository: RealRepository
) : ViewModel(), MusicServiceEventListener {
) : ViewModel(), IMusicServiceEventListener {
private val _paletteColor = MutableLiveData<Int>()
private val home = MutableLiveData<List<Home>>()
private val albums = MutableLiveData<List<Album>>()
private val songs = MutableLiveData<List<Song>>()
private val artists = MutableLiveData<List<Artist>>()
private val playlists = MutableLiveData<List<PlaylistWithSongs>>()
private val legacyPlaylists = MutableLiveData<List<Playlist>>()
private val genres = MutableLiveData<List<Genre>>()
private val home = MutableLiveData<List<Home>>()
private val searchResults = MutableLiveData<List<Any>>()
val paletteColor: LiveData<Int> = _paletteColor
val panelState: MutableLiveData<NowPlayingPanelState> = MutableLiveData<NowPlayingPanelState>()
init {
fetchHomeSections()
}
fun setPanelState(state: NowPlayingPanelState) {
panelState.postValue(state)
}
@ -52,6 +49,8 @@ class LibraryViewModel(
fetchPlaylists()
}
fun getSearchResult(): LiveData<List<Any>> = searchResults
fun getSongs(): LiveData<List<Song>> {
fetchSongs()
return songs
@ -83,6 +82,7 @@ class LibraryViewModel(
}
fun getHome(): LiveData<List<Home>> {
fetchHomeSections()
return home
}
@ -134,6 +134,11 @@ class LibraryViewModel(
}
}
fun search(query: String?) = viewModelScope.launch(IO) {
val result = repository.search(query)
searchResults.postValue(result)
}
fun forceReload(reloadType: ReloadType) = viewModelScope.launch {
when (reloadType) {
Songs -> fetchSongs()
@ -278,6 +283,16 @@ class LibraryViewModel(
}
}
}
fun clearSearchResult() {
viewModelScope.launch {
searchResults.postValue(emptyList())
}
}
fun artist(artistId: Long): LiveData<Artist> = liveData {
emit(repository.artistById(artistId))
}
}
enum class ReloadType {

View File

@ -133,7 +133,7 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
override fun onDestroy() {
super.onDestroy()
playerActivity?.removeMusicServiceEventListener(detailsViewModel)
serviceActivity?.removeMusicServiceEventListener(detailsViewModel)
}

View File

@ -3,7 +3,7 @@ package code.name.monkey.retromusic.fragments.albums
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.liveData
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener
import code.name.monkey.retromusic.model.Album
import code.name.monkey.retromusic.model.Artist
import code.name.monkey.retromusic.network.Result
@ -14,7 +14,7 @@ import kotlinx.coroutines.Dispatchers.IO
class AlbumDetailsViewModel(
private val repository: RealRepository,
private val albumId: Long
) : ViewModel(), MusicServiceEventListener {
) : ViewModel(), IMusicServiceEventListener {
fun getAlbum(): LiveData<Album> = liveData(IO) {
emit(repository.albumByIdAsync(albumId))

View File

@ -3,7 +3,7 @@ package code.name.monkey.retromusic.fragments.artists
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.liveData
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener
import code.name.monkey.retromusic.model.Artist
import code.name.monkey.retromusic.network.Result
import code.name.monkey.retromusic.network.model.LastFmArtist
@ -13,7 +13,7 @@ import kotlinx.coroutines.Dispatchers.IO
class ArtistDetailsViewModel(
private val realRepository: RealRepository,
private val artistId: Long
) : ViewModel(), MusicServiceEventListener {
) : ViewModel(), IMusicServiceEventListener {
fun getArtist(): LiveData<Artist> = liveData(IO) {
val artist = realRepository.artistById(artistId)

View File

@ -10,7 +10,7 @@ import androidx.fragment.app.Fragment
import androidx.navigation.navOptions
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.RetroUtil
import org.jaudiotagger.audio.AudioFileIO
@ -23,7 +23,7 @@ import java.util.*
*/
open class AbsMusicServiceFragment(@LayoutRes layout: Int) : Fragment(layout),
MusicServiceEventListener {
IMusicServiceEventListener {
val navOptions by lazy {
navOptions {
@ -40,13 +40,13 @@ open class AbsMusicServiceFragment(@LayoutRes layout: Int) : Fragment(layout),
}
}
var playerActivity: AbsMusicServiceActivity? = null
var serviceActivity: AbsMusicServiceActivity? = null
private set
override fun onAttach(context: Context) {
super.onAttach(context)
try {
playerActivity = context as AbsMusicServiceActivity?
serviceActivity = context as AbsMusicServiceActivity?
} catch (e: ClassCastException) {
throw RuntimeException(context.javaClass.simpleName + " must be an instance of " + AbsMusicServiceActivity::class.java.simpleName)
}
@ -54,17 +54,17 @@ open class AbsMusicServiceFragment(@LayoutRes layout: Int) : Fragment(layout),
override fun onDetach() {
super.onDetach()
playerActivity = null
serviceActivity = null
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
playerActivity?.addMusicServiceEventListener(this)
serviceActivity?.addMusicServiceEventListener(this)
}
override fun onDestroyView() {
super.onDestroyView()
playerActivity?.removeMusicServiceEventListener(this)
serviceActivity?.removeMusicServiceEventListener(this)
}
override fun onPlayingMetaChanged() {

View File

@ -31,7 +31,7 @@ import code.name.monkey.retromusic.fragments.LibraryViewModel
import code.name.monkey.retromusic.fragments.ReloadType
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.interfaces.PaletteColorHolder
import code.name.monkey.retromusic.interfaces.IPaletteColorHolder
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.model.lyrics.Lyrics
import code.name.monkey.retromusic.repository.RealRepository
@ -47,7 +47,7 @@ import org.koin.androidx.viewmodel.ext.android.sharedViewModel
import java.io.FileNotFoundException
abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragment(layout),
Toolbar.OnMenuItemClickListener, PaletteColorHolder, PlayerAlbumCoverFragment.Callbacks {
Toolbar.OnMenuItemClickListener, IPaletteColorHolder, PlayerAlbumCoverFragment.Callbacks {
private var playerAlbumCoverFragment: PlayerAlbumCoverFragment? = null
protected val libraryViewModel by sharedViewModel<LibraryViewModel>()

View File

@ -21,6 +21,8 @@ import android.os.Bundle;
import android.os.Environment;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
@ -41,6 +43,8 @@ import com.afollestad.materialcab.MaterialCab;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.snackbar.Snackbar;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
@ -59,9 +63,9 @@ import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment;
import code.name.monkey.retromusic.helper.MusicPlayerRemote;
import code.name.monkey.retromusic.helper.menu.SongMenuHelper;
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper;
import code.name.monkey.retromusic.interfaces.CabHolder;
import code.name.monkey.retromusic.interfaces.Callbacks;
import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks;
import code.name.monkey.retromusic.interfaces.ICabHolder;
import code.name.monkey.retromusic.interfaces.ICallbacks;
import code.name.monkey.retromusic.interfaces.IMainActivityFragmentCallbacks;
import code.name.monkey.retromusic.misc.DialogAsyncTask;
import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener;
import code.name.monkey.retromusic.misc.WrappedAsyncTaskLoader;
@ -76,10 +80,10 @@ import code.name.monkey.retromusic.views.ScrollingViewOnApplyWindowInsetsListene
import me.zhanghai.android.fastscroll.FastScroller;
public class FoldersFragment extends AbsMainActivityFragment implements
MainActivityFragmentCallbacks,
CabHolder,
IMainActivityFragmentCallbacks,
ICabHolder,
BreadCrumbLayout.SelectionCallback,
Callbacks,
ICallbacks,
LoaderManager.LoaderCallbacks<List<File>> {
public static final String TAG = FoldersFragment.class.getSimpleName();
@ -137,6 +141,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements
}
}
@NonNull
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@ -159,13 +164,12 @@ public class FoldersFragment extends AbsMainActivityFragment implements
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(true);
if (savedInstanceState == null) {
//noinspection ConstantConditions
setCrumb(new BreadCrumbLayout.Crumb(FileUtil.safeGetCanonicalFile(PreferenceUtil.INSTANCE.getStartDirectory())), true);
} else {
breadCrumbs.restoreFromStateWrapper(savedInstanceState.getParcelable(CRUMBS));
getLoaderManager().initLoader(LOADER_ID, null, this);
LoaderManager.getInstance(this).initLoader(LOADER_ID, null, this);
}
}
@ -181,7 +185,6 @@ public class FoldersFragment extends AbsMainActivityFragment implements
if (breadCrumbs != null) {
outState.putParcelable(CRUMBS, breadCrumbs.getStateWrapper());
}
}
@Override
@ -209,7 +212,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements
}
@Override
public void onFileMenuClicked(final File file, View view) {
public void onFileMenuClicked(final File file, @NotNull View view) {
PopupMenu popupMenu = new PopupMenu(getActivity(), view);
if (file.isDirectory()) {
popupMenu.inflate(R.menu.menu_item_directory);
@ -222,7 +225,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements
case R.id.action_delete_from_device:
new ListSongsAsyncTask(getActivity(), null, (songs, extra) -> {
if (!songs.isEmpty()) {
SongsMenuHelper.INSTANCE.handleMenuClick(getActivity(), songs, itemId);
SongsMenuHelper.INSTANCE.handleMenuClick(requireActivity(), songs, itemId);
}
}).execute(new ListSongsAsyncTask.LoadingInfo(toList(file), AUDIO_FILE_FILTER,
getFileComparator()));
@ -256,7 +259,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements
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, extra) -> SongMenuHelper.INSTANCE.handleMenuClick(requireActivity(),
songs.get(0), itemId))
.execute(new ListSongsAsyncTask.LoadingInfo(toList(file), AUDIO_FILE_FILTER,
getFileComparator()));
@ -273,7 +276,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements
}
@Override
public void onFileSelected(File file) {
public void onFileSelected(@NotNull File file) {
file = tryGetCanonicalFile(file); // important as we compare the path value later
if (file.isDirectory()) {
setCrumb(new BreadCrumbLayout.Crumb(file), true);
@ -319,13 +322,23 @@ public class FoldersFragment extends AbsMainActivityFragment implements
}
@Override
public void onMultipleItemAction(MenuItem item, ArrayList<File> files) {
public void onMultipleItemAction(MenuItem item, @NotNull ArrayList<File> files) {
final int itemId = item.getItemId();
new ListSongsAsyncTask(getActivity(), null,
(songs, extra) -> SongsMenuHelper.INSTANCE.handleMenuClick(getActivity(), songs, itemId))
(songs, extra) -> SongsMenuHelper.INSTANCE.handleMenuClick(requireActivity(), songs, itemId))
.execute(new ListSongsAsyncTask.LoadingInfo(files, AUDIO_FILE_FILTER, getFileComparator()));
}
@Override
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
menu.add(0, R.id.action_scan, 0, R.string.scan_media).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
menu.add(0, R.id.action_go_to_start_directory, 1, R.string.action_go_to_start_directory).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
menu.removeItem(R.id.action_grid_size);
menu.removeItem(R.id.action_layout_type);
menu.removeItem(R.id.action_sort_order);
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
@ -360,7 +373,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements
@NonNull
@Override
public MaterialCab openCab(int menuRes, MaterialCab.Callback callback) {
public MaterialCab openCab(int menuRes, @NotNull MaterialCab.Callback callback) {
if (cab != null && cab.isActive()) {
cab.finish();
}
@ -438,7 +451,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements
if (addToHistory) {
breadCrumbs.addHistory(crumb);
}
getLoaderManager().restartLoader(LOADER_ID, null, this);
LoaderManager.getInstance(this).restartLoader(LOADER_ID, null, this);
}
private void setUpAdapter() {

View File

@ -4,7 +4,7 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener
import code.name.monkey.retromusic.model.Genre
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.repository.RealRepository
@ -15,7 +15,7 @@ import kotlinx.coroutines.withContext
class GenreDetailsViewModel(
private val realRepository: RealRepository,
private val genre: Genre
) : ViewModel(), MusicServiceEventListener {
) : ViewModel(), IMusicServiceEventListener {
private val _playListSongs = MutableLiveData<List<Song>>()
private val _genre = MutableLiveData<Genre>().apply {

View File

@ -19,7 +19,6 @@ import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
import code.name.monkey.retromusic.state.NowPlayingPanelState
import kotlinx.android.synthetic.main.fragment_library.*
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
import java.lang.String
class LibraryFragment : AbsMainActivityFragment(R.layout.fragment_library) {

View File

@ -1,42 +0,0 @@
package code.name.monkey.retromusic.fragments.player
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.navigation.NavController
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.extensions.findNavController
import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
import code.name.monkey.retromusic.util.PreferenceUtil
class NowPlayingPlayerFragment : Fragment(R.layout.fragment_now_playing_player) {
companion object {
const val TAG = "NowPlaying"
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
val navController = findNavController(R.id.playerFragmentContainer)
updateNowPlaying(navController)
}
private fun updateNowPlaying(navController: NavController) {
when (PreferenceUtil.nowPlayingScreen) {
Adaptive -> navController.navigate(R.id.adaptiveFragment)
Blur -> navController.navigate(R.id.blurPlayerFragment)
BlurCard -> navController.navigate(R.id.cardBlurFragment)
Card -> navController.navigate(R.id.cardFragment)
Circle -> navController.navigate(R.id.circlePlayerFragment)
Classic -> navController.navigate(R.id.classicPlayerFragment)
Color -> navController.navigate(R.id.colorFragment)
Fit -> navController.navigate(R.id.fitFragment)
Flat -> navController.navigate(R.id.flatPlayerFragment)
Full -> navController.navigate(R.id.fullPlayerFragment)
Gradient -> navController.navigate(R.id.gradientPlayerFragment)
Material -> navController.navigate(R.id.materialFragment)
Plain -> navController.navigate(R.id.plainPlayerFragment)
Simple -> navController.navigate(R.id.simplePlayerFragment)
Tiny -> navController.navigate(R.id.tinyPlayerFragment)
else -> navController.navigate(R.id.playerFragment)
}
}
}

View File

@ -37,7 +37,7 @@ class ColorFragment : AbsPlayerFragment(R.layout.fragment_color_player) {
navigationColor = color.backgroundColor
colorGradientBackground?.setBackgroundColor(color.backgroundColor)
playerActivity?.setLightNavigationBar(ColorUtil.isColorLight(color.backgroundColor))
serviceActivity?.setLightNavigationBar(ColorUtil.isColorLight(color.backgroundColor))
Handler().post {
ToolbarContentTintHelper.colorizeToolbar(
playerToolbar,

View File

@ -8,7 +8,6 @@ import android.widget.FrameLayout
import android.widget.TextView
import androidx.appcompat.widget.Toolbar
import androidx.core.os.bundleOf
import androidx.lifecycle.lifecycleScope
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.EXTRA_ARTIST_ID
import code.name.monkey.retromusic.R
@ -25,18 +24,12 @@ import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.model.lyrics.AbsSynchronizedLyrics
import code.name.monkey.retromusic.model.lyrics.Lyrics
import code.name.monkey.retromusic.repository.ArtistRepository
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
import com.bumptech.glide.Glide
import kotlinx.android.synthetic.main.fragment_full.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.koin.android.ext.android.inject
class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full),
MusicProgressViewUpdateHelper.Callback {
private val artistRepository by inject<ArtistRepository>()
private lateinit var lyricsLayout: FrameLayout
private lateinit var lyricsLine1: TextView
private lateinit var lyricsLine2: TextView
@ -222,9 +215,8 @@ class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full),
}
private fun updateArtistImage() {
lifecycleScope.launch {
val artist = artistRepository.artist(MusicPlayerRemote.currentSong.artistId)
withContext(Dispatchers.Main) {
libraryViewModel.artist(MusicPlayerRemote.currentSong.artistId)
.observe(viewLifecycleOwner, { artist ->
ArtistGlideRequest.Builder.from(Glide.with(requireContext()), artist)
.generatePalette(requireContext())
.build()
@ -233,8 +225,8 @@ class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full),
}
})
}
}
})
}
override fun onQueueChanged() {

View File

@ -5,14 +5,14 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import code.name.monkey.retromusic.db.PlaylistWithSongs
import code.name.monkey.retromusic.db.SongEntity
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.repository.RealRepository
class PlaylistDetailsViewModel(
private val realRepository: RealRepository,
private var playlist: PlaylistWithSongs
) : ViewModel(), MusicServiceEventListener {
) : ViewModel(), IMusicServiceEventListener {
private val _playListSongs = MutableLiveData<List<Song>>()
private val _playlist = MutableLiveData<PlaylistWithSongs>().apply {

View File

@ -12,20 +12,19 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat.getSystemService
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.transition.TransitionManager
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.SearchAdapter
import code.name.monkey.retromusic.extensions.accentColor
import code.name.monkey.retromusic.extensions.dipToPix
import code.name.monkey.retromusic.extensions.showToast
import code.name.monkey.retromusic.fragments.LibraryViewModel
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
import code.name.monkey.retromusic.state.NowPlayingPanelState
import com.google.android.material.textfield.TextInputEditText
import kotlinx.android.synthetic.main.fragment_search.*
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
import java.util.*
import kotlin.collections.ArrayList
@ -37,38 +36,37 @@ class SearchFragment : AbsMainActivityFragment(R.layout.fragment_search), TextWa
}
private val libraryViewModel by sharedViewModel<LibraryViewModel>()
private val viewModel: SearchViewModel by inject()
private lateinit var searchAdapter: SearchAdapter
private var query: String? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mainActivity.setSupportActionBar(toolbar)
libraryViewModel.setPanelState(NowPlayingPanelState.COLLAPSED_WITHOUT)
mainActivity.setSupportActionBar(toolbar)
libraryViewModel.clearSearchResult();
setupRecyclerView()
keyboardPopup.accentColor()
searchView.addTextChangedListener(this)
voiceSearch.setOnClickListener { startMicSearch() }
clearText.setOnClickListener { searchView.clearText() }
keyboardPopup.setOnClickListener {
val inputManager =
getSystemService<InputMethodManager>(
keyboardPopup.apply {
accentColor()
setOnClickListener {
val inputManager = getSystemService(
requireContext(),
InputMethodManager::class.java
)
inputManager?.showSoftInput(searchView, InputMethodManager.SHOW_IMPLICIT)
inputManager?.showSoftInput(searchView, InputMethodManager.SHOW_IMPLICIT)
}
}
if (savedInstanceState != null) {
query = savedInstanceState.getString(QUERY)
}
viewModel.getSearchResult().observe(viewLifecycleOwner, Observer {
libraryViewModel.getSearchResult().observe(viewLifecycleOwner, {
showData(it)
})
}
private fun showData(data: MutableList<Any>) {
private fun showData(data: List<Any>) {
if (data.isNotEmpty()) {
searchAdapter.swapDataSet(data)
} else {
@ -83,6 +81,8 @@ class SearchFragment : AbsMainActivityFragment(R.layout.fragment_search), TextWa
override fun onChanged() {
super.onChanged()
empty.isVisible = searchAdapter.itemCount < 1
val height = dipToPix(52f)
recyclerView.setPadding(0, 0, 0, height.toInt())
}
})
recyclerView.apply {
@ -118,7 +118,7 @@ class SearchFragment : AbsMainActivityFragment(R.layout.fragment_search), TextWa
TransitionManager.beginDelayedTransition(appBarLayout)
voiceSearch.isGone = query.isNotEmpty()
clearText.isVisible = query.isNotEmpty()
viewModel.search(query)
libraryViewModel.search(query)
}
private fun startMicSearch() {

View File

@ -1,20 +0,0 @@
package code.name.monkey.retromusic.fragments.search
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import code.name.monkey.retromusic.repository.RealRepository
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.launch
class SearchViewModel(private val realRepository: RealRepository) : ViewModel() {
private val results = MutableLiveData<MutableList<Any>>()
fun getSearchResult(): LiveData<MutableList<Any>> = results
fun search(query: String?) = viewModelScope.launch(IO) {
val result = realRepository.search(query)
results.postValue(result)
}
}

View File

@ -30,7 +30,7 @@ import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog
import code.name.monkey.retromusic.dialogs.DeleteSongsDialog
import code.name.monkey.retromusic.dialogs.SongDetailDialog
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.interfaces.PaletteColorHolder
import code.name.monkey.retromusic.interfaces.IPaletteColorHolder
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.repository.RealRepository
import code.name.monkey.retromusic.util.MusicUtil
@ -90,10 +90,10 @@ object SongMenuHelper : KoinComponent {
R.id.action_tag_editor -> {
val tagEditorIntent = Intent(activity, SongTagEditorActivity::class.java)
tagEditorIntent.putExtra(AbsTagEditorActivity.EXTRA_ID, song.id)
if (activity is PaletteColorHolder)
if (activity is IPaletteColorHolder)
tagEditorIntent.putExtra(
AbsTagEditorActivity.EXTRA_PALETTE,
(activity as PaletteColorHolder).paletteColor
(activity as IPaletteColorHolder).paletteColor
)
activity.startActivity(tagEditorIntent)
return true

View File

@ -17,7 +17,7 @@ package code.name.monkey.retromusic.interfaces
import com.afollestad.materialcab.MaterialCab
interface CabHolder {
interface ICabHolder {
fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab
}

View File

@ -4,7 +4,7 @@ import android.view.MenuItem
import android.view.View
import java.io.File
interface Callbacks {
interface ICallbacks {
fun onFileSelected(file: File)
fun onFileMenuClicked(file: File, view: View)

View File

@ -16,6 +16,6 @@ package code.name.monkey.retromusic.interfaces
/**
* Created by hemanths on 14/08/17.
*/
internal interface MainActivityFragmentCallbacks {
interface IMainActivityFragmentCallbacks {
fun handleBackPress(): Boolean
}

View File

@ -15,7 +15,7 @@
package code.name.monkey.retromusic.interfaces
interface MusicServiceEventListener {
interface IMusicServiceEventListener {
fun onServiceConnected()
fun onServiceDisconnected()

View File

@ -17,6 +17,6 @@ package code.name.monkey.retromusic.interfaces
/**
* @author Aidan Follestad (afollestad)
*/
interface PaletteColorHolder {
interface IPaletteColorHolder {
val paletteColor: Int
}

View File

@ -24,6 +24,7 @@ import code.name.monkey.retromusic.Constants.IS_MUSIC
import code.name.monkey.retromusic.Constants.baseProjection
import code.name.monkey.retromusic.extensions.getLong
import code.name.monkey.retromusic.extensions.getString
import code.name.monkey.retromusic.extensions.getStringOrNull
import code.name.monkey.retromusic.model.Genre
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.PreferenceUtil
@ -53,9 +54,9 @@ class RealGenreRepository(
private fun getGenreFromCursor(cursor: Cursor): Genre {
val id = cursor.getLong(Genres._ID)
val name = cursor.getString(Genres.NAME)
val name = cursor.getStringOrNull(Genres.NAME)
val songCount = songs(id).size
return Genre(id, name, songCount)
return Genre(id, name ?: "", songCount)
}

View File

@ -251,7 +251,7 @@ public class PlaylistsUtil {
return M3UWriter.write(new File(Environment.getExternalStorageDirectory(), "Playlists"), playlist);
}
public static File savePlaylistWithSongs(Context context, PlaylistWithSongs playlist) throws IOException {
public static File savePlaylistWithSongs(PlaylistWithSongs playlist) throws IOException {
return M3UWriter.writeIO(new File(Environment.getExternalStorageDirectory(), "Playlists"), playlist);
}

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/playerFragmentContainer"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/now_playing" />

View File

@ -21,9 +21,8 @@
app:behavior_peekHeight="0dp"
app:layout_behavior="code.name.monkey.retromusic.RetroBottomSheetBehavior">
<androidx.fragment.app.FragmentContainerView
<FrameLayout
android:id="@+id/playerFragmentContainer"
android:name="code.name.monkey.retromusic.fragments.player.NowPlayingPlayerFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View File

@ -90,6 +90,7 @@
<menu></menu>
</item>
<item
android:orderInCategory="10"
android:id="@+id/action_settings"
android:icon="@drawable/ic_settings"
android:title="@string/action_settings"

View File

@ -1,165 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/now_playing"
app:startDestination="@id/mainPlayerFragment">
<fragment
tools:layout="@layout/fragment_player"
android:id="@+id/playerFragment"
android:name="code.name.monkey.retromusic.fragments.player.normal.PlayerFragment"
android:label="PlayerFragment" />
<fragment
tools:layout="@layout/fragment_adaptive_player"
android:id="@+id/adaptiveFragment"
android:name="code.name.monkey.retromusic.fragments.player.adaptive.AdaptiveFragment"
android:label="AdaptiveFragment" />
<fragment
tools:layout="@layout/fragment_blur"
android:id="@+id/blurPlayerFragment"
android:name="code.name.monkey.retromusic.fragments.player.blur.BlurPlayerFragment"
android:label="BlurPlayerFragment" />
<fragment
tools:layout="@layout/fragment_card_blur_player"
android:id="@+id/cardBlurFragment"
android:name="code.name.monkey.retromusic.fragments.player.cardblur.CardBlurFragment"
android:label="CardBlurFragment" />
<fragment
tools:layout="@layout/fragment_card_player"
android:id="@+id/cardFragment"
android:name="code.name.monkey.retromusic.fragments.player.card.CardFragment"
android:label="CardFragment" />
<fragment
tools:layout="@layout/fragment_circle_player"
android:id="@+id/circlePlayerFragment"
android:name="code.name.monkey.retromusic.fragments.player.circle.CirclePlayerFragment"
android:label="CirclePlayerFragment" />
<fragment
tools:layout="@layout/fragment_classic_player"
android:id="@+id/classicPlayerFragment"
android:name="code.name.monkey.retromusic.fragments.player.classic.ClassicPlayerFragment"
android:label="ClassicPlayerFragment" />
<fragment
tools:layout="@layout/fragment_color_player"
android:id="@+id/colorFragment"
android:name="code.name.monkey.retromusic.fragments.player.color.ColorFragment"
android:label="ColorFragment" />
<fragment
tools:layout="@layout/fragment_fit"
android:id="@+id/fitFragment"
android:name="code.name.monkey.retromusic.fragments.player.fit.FitFragment"
android:label="FitFragment" />
<fragment
tools:layout="@layout/fragment_flat_player"
android:id="@+id/flatPlayerFragment"
android:name="code.name.monkey.retromusic.fragments.player.flat.FlatPlayerFragment"
android:label="FlatPlayerFragment" />
<fragment
tools:layout="@layout/fragment_full"
android:id="@+id/fullPlayerFragment"
android:name="code.name.monkey.retromusic.fragments.player.full.FullPlayerFragment"
android:label="FullPlayerFragment" />
<fragment
tools:layout="@layout/fragment_gradient_player"
android:id="@+id/gradientPlayerFragment"
android:name="code.name.monkey.retromusic.fragments.player.gradient.GradientPlayerFragment"
android:label="GradientPlayerFragment" />
<fragment
tools:layout="@layout/fragment_material"
android:id="@+id/materialFragment"
android:name="code.name.monkey.retromusic.fragments.player.material.MaterialFragment"
android:label="MaterialFragment" />
<fragment
android:id="@+id/peakPlayerFragment"
tools:layout="@layout/fragment_peak_player"
android:name="code.name.monkey.retromusic.fragments.player.peak.PeakPlayerFragment"
android:label="PeakPlayerFragment" />
<fragment
android:id="@+id/plainPlayerFragment"
tools:layout="@layout/fragment_plain_player"
android:name="code.name.monkey.retromusic.fragments.player.plain.PlainPlayerFragment"
android:label="PlainPlayerFragment" />
<fragment
tools:layout="@layout/fragment_simple_player"
android:id="@+id/simplePlayerFragment"
android:name="code.name.monkey.retromusic.fragments.player.simple.SimplePlayerFragment"
android:label="SimplePlayerFragment" />
<fragment
tools:layout="@layout/fragment_tiny_player"
android:id="@+id/tinyPlayerFragment"
android:name="code.name.monkey.retromusic.fragments.player.tiny.TinyPlayerFragment"
android:label="TinyPlayerFragment" />
<fragment
android:id="@+id/mainPlayerFragment"
android:name="code.name.monkey.retromusic.fragments.player.NowPlayingPlayerFragment"
android:label="MainPlayerFragment">
<action
android:id="@+id/action_mainPlayerFragment_to_adaptiveFragment"
app:destination="@id/adaptiveFragment" />
<action
android:id="@+id/action_mainPlayerFragment_to_cardBlurFragment"
app:destination="@id/cardBlurFragment" />
<action
android:id="@+id/action_mainPlayerFragment_to_circlePlayerFragment"
app:destination="@id/circlePlayerFragment" />
<action
android:id="@+id/action_mainPlayerFragment_to_blurPlayerFragment"
app:destination="@id/blurPlayerFragment" />
<action
android:id="@+id/action_mainPlayerFragment_to_classicPlayerFragment"
app:destination="@id/classicPlayerFragment" />
<action
android:id="@+id/action_mainPlayerFragment_to_plainPlayerFragment"
app:destination="@id/plainPlayerFragment" />
<action
android:id="@+id/action_mainPlayerFragment_to_flatPlayerFragment"
app:destination="@id/flatPlayerFragment" />
<action
android:id="@+id/action_mainPlayerFragment_to_gradientPlayerFragment"
app:destination="@id/gradientPlayerFragment" />
<action
android:id="@+id/action_mainPlayerFragment_to_cardFragment"
app:destination="@id/cardFragment" />
<action
android:id="@+id/action_mainPlayerFragment_to_fitFragment"
app:destination="@id/fitFragment" />
<action
android:id="@+id/action_mainPlayerFragment_to_simplePlayerFragment"
app:destination="@id/simplePlayerFragment" />
<action
android:id="@+id/action_mainPlayerFragment_to_fullPlayerFragment"
app:destination="@id/fullPlayerFragment" />
<action
android:id="@+id/action_mainPlayerFragment_to_colorFragment"
app:destination="@id/colorFragment" />
<action
android:id="@+id/action_mainPlayerFragment_to_peakPlayerFragment"
app:destination="@id/peakPlayerFragment" />
<action
android:id="@+id/action_mainPlayerFragment_to_tinyPlayerFragment"
app:destination="@id/tinyPlayerFragment" />
<action
android:id="@+id/action_mainPlayerFragment_to_materialFragment"
app:destination="@id/materialFragment" />
<action
android:id="@+id/action_mainPlayer_to_playerFragment"
app:destination="@id/playerFragment" />
</fragment>
</navigation>