home: merge all library views into home
Merge LibraryFragment, SongsFragment, and others into a new fragment called HomeFragment. This is the beginning of the Auxio UI overhaul. This has caused some regressions here and there, but these will be rectified over time.
This commit is contained in:
parent
e83867270b
commit
a3e7cb93aa
33 changed files with 528 additions and 1097 deletions
|
@ -18,32 +18,21 @@
|
|||
|
||||
package org.oxycblt.auxio
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
import org.oxycblt.auxio.accent.Accent
|
||||
import org.oxycblt.auxio.databinding.FragmentMainBinding
|
||||
import org.oxycblt.auxio.detail.DetailViewModel
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
|
||||
/**
|
||||
* The primary "Home" [Fragment] for Auxio.
|
||||
* A wrapper around the home
|
||||
*/
|
||||
class MainFragment : Fragment() {
|
||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
|
@ -52,51 +41,10 @@ class MainFragment : Fragment() {
|
|||
): View {
|
||||
val binding = FragmentMainBinding.inflate(inflater)
|
||||
|
||||
val colorActive = Accent.get().color.resolveColor(requireContext())
|
||||
val colorInactive = ColorUtils.setAlphaComponent(colorActive, 150)
|
||||
|
||||
// Set up the tints for the navigation icons + text
|
||||
val navTints = ColorStateList(
|
||||
arrayOf(
|
||||
intArrayOf(-android.R.attr.state_checked),
|
||||
intArrayOf(android.R.attr.state_checked)
|
||||
),
|
||||
intArrayOf(colorInactive, colorActive)
|
||||
)
|
||||
|
||||
val navController = (
|
||||
childFragmentManager.findFragmentById(R.id.explore_nav_host)
|
||||
as NavHostFragment?
|
||||
)?.findNavController()
|
||||
|
||||
// --- UI SETUP ---
|
||||
|
||||
binding.lifecycleOwner = viewLifecycleOwner
|
||||
|
||||
// Speed up the slide-in effect on the controls view, solely to improve the UX
|
||||
// and maybe hide the problem where the main view will snap-shrink before the compact
|
||||
// view can slide.
|
||||
(binding.controlsContainer as ViewGroup).layoutTransition.setDuration(150)
|
||||
|
||||
binding.navBar.apply {
|
||||
itemIconTintList = navTints
|
||||
itemTextColor = navTints
|
||||
|
||||
if (requireContext().isTablet() && !requireContext().isLandscape()) {
|
||||
labelVisibilityMode = BottomNavigationView.LABEL_VISIBILITY_LABELED
|
||||
}
|
||||
|
||||
navController?.let { controller ->
|
||||
binding.navBar.setOnItemSelectedListener { item ->
|
||||
navigateWithItem(controller, item)
|
||||
}
|
||||
}
|
||||
|
||||
// BottomNavigationView is a special little snowflake and doesn't like it when
|
||||
// we set the background in XML
|
||||
setBackgroundColor(R.attr.colorSurface.resolveAttr(requireContext()))
|
||||
}
|
||||
|
||||
// --- VIEWMODEL SETUP ---
|
||||
|
||||
// Change CompactPlaybackFragment's visibility here so that an animation occurs.
|
||||
|
@ -106,49 +54,11 @@ class MainFragment : Fragment() {
|
|||
handleCompactPlaybackVisibility(binding, song)
|
||||
}
|
||||
|
||||
detailModel.navToItem.observe(viewLifecycleOwner) { item ->
|
||||
if (item != null && navController != null) {
|
||||
val curDest = navController.currentDestination?.id
|
||||
|
||||
// SongsFragment and SettingsFragment have no navigation pathways, so correct
|
||||
// them to the library tab instead.
|
||||
if (curDest == R.id.songs_fragment || curDest == R.id.settings_fragment) {
|
||||
binding.navBar.selectedItemId = R.id.library_fragment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
playbackModel.setupPlayback(requireContext())
|
||||
|
||||
logD("Fragment Created.")
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom navigator code that has proper animations, unlike BottomNavigationView.setupWithNavController().
|
||||
*/
|
||||
private fun navigateWithItem(navController: NavController, item: MenuItem): Boolean {
|
||||
if (navController.currentDestination!!.id != item.itemId) {
|
||||
val options = NavOptions.Builder()
|
||||
.setLaunchSingleTop(true)
|
||||
.setEnterAnim(R.animator.nav_default_enter_anim)
|
||||
.setExitAnim(R.animator.nav_default_exit_anim)
|
||||
.setPopEnterAnim(R.animator.nav_default_pop_enter_anim)
|
||||
.setPopExitAnim(R.animator.nav_default_pop_exit_anim)
|
||||
.build()
|
||||
|
||||
return try {
|
||||
navController.navigate(item.itemId, null, options)
|
||||
true
|
||||
} catch (e: IllegalArgumentException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the visibility of CompactPlaybackFragment. Done here so that there's a nice animation.
|
||||
*/
|
||||
|
@ -156,13 +66,13 @@ class MainFragment : Fragment() {
|
|||
if (song == null) {
|
||||
logD("Hiding CompactPlaybackFragment since no song is being played.")
|
||||
|
||||
binding.compactPlayback.visibility = if (requireContext().isLandscape()) {
|
||||
binding.mainPlayback.visibility = if (requireContext().isLandscape()) {
|
||||
View.INVISIBLE
|
||||
} else {
|
||||
View.GONE
|
||||
}
|
||||
} else {
|
||||
binding.compactPlayback.visibility = View.VISIBLE
|
||||
binding.mainPlayback.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Auxio Project
|
||||
* LibraryAdapter.kt is part of Auxio.
|
||||
* HomeAdapter.kt is part of Auxio.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.library
|
||||
package org.oxycblt.auxio.home
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.View
|
||||
|
@ -24,22 +24,25 @@ import android.view.ViewGroup
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Parent
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.recycler.viewholders.AlbumViewHolder
|
||||
import org.oxycblt.auxio.recycler.viewholders.ArtistViewHolder
|
||||
import org.oxycblt.auxio.recycler.viewholders.GenreViewHolder
|
||||
import org.oxycblt.auxio.recycler.viewholders.SongViewHolder
|
||||
|
||||
/**
|
||||
* An adapter for displaying library items. Supports [Parent]s only.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class LibraryAdapter(
|
||||
private val doOnClick: (data: Parent) -> Unit,
|
||||
private val doOnLongClick: (view: View, data: Parent) -> Unit
|
||||
class HomeAdapter(
|
||||
private val doOnClick: (data: BaseModel) -> Unit,
|
||||
private val doOnLongClick: (view: View, data: BaseModel) -> Unit
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
private var data = listOf<Parent>()
|
||||
private var data = listOf<BaseModel>()
|
||||
|
||||
override fun getItemCount(): Int = data.size
|
||||
|
||||
|
@ -48,6 +51,8 @@ class LibraryAdapter(
|
|||
is Genre -> GenreViewHolder.ITEM_TYPE
|
||||
is Artist -> ArtistViewHolder.ITEM_TYPE
|
||||
is Album -> AlbumViewHolder.ITEM_TYPE
|
||||
is Song -> SongViewHolder.ITEM_TYPE
|
||||
else -> error("Unsupported item ${data[position]::class.simpleName}")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,6 +70,10 @@ class LibraryAdapter(
|
|||
parent.context, doOnClick, doOnLongClick
|
||||
)
|
||||
|
||||
SongViewHolder.ITEM_TYPE -> SongViewHolder.from(
|
||||
parent.context, doOnClick, doOnLongClick
|
||||
)
|
||||
|
||||
else -> error("Invalid viewholder item type.")
|
||||
}
|
||||
}
|
||||
|
@ -74,6 +83,7 @@ class LibraryAdapter(
|
|||
is Genre -> (holder as GenreViewHolder).bind(item)
|
||||
is Artist -> (holder as ArtistViewHolder).bind(item)
|
||||
is Album -> (holder as AlbumViewHolder).bind(item)
|
||||
is Song -> (holder as SongViewHolder).bind(item)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,7 +91,7 @@ class LibraryAdapter(
|
|||
* Update the data with [newData]. [notifyDataSetChanged] will be called.
|
||||
*/
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun updateData(newData: List<Parent>) {
|
||||
fun updateData(newData: List<BaseModel>) {
|
||||
data = newData
|
||||
|
||||
notifyDataSetChanged()
|
113
app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt
Normal file
113
app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt
Normal file
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Auxio Project
|
||||
* MainFragment.kt is part of Auxio.
|
||||
*
|
||||
* This program 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 program 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.home
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import org.oxycblt.auxio.MainFragmentDirections
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentHomeBinding
|
||||
import org.oxycblt.auxio.home.pager.AlbumListFragment
|
||||
import org.oxycblt.auxio.home.pager.ArtistListFragment
|
||||
import org.oxycblt.auxio.home.pager.GenreListFragment
|
||||
import org.oxycblt.auxio.home.pager.SongListFragment
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
|
||||
/**
|
||||
* The main "Launching Point" fragment of Auxio, allowing navigation to the detail
|
||||
* views for each respective fragment.
|
||||
* TODO: Re-add sorting (but new and improved)
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class HomeFragment : Fragment() {
|
||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
val binding = FragmentHomeBinding.inflate(inflater)
|
||||
|
||||
// --- UI SETUP ---
|
||||
|
||||
binding.lifecycleOwner = viewLifecycleOwner
|
||||
|
||||
binding.homeToolbar.setOnMenuItemClickListener { item ->
|
||||
when (item.itemId) {
|
||||
R.id.action_settings -> {
|
||||
parentFragment?.parentFragment?.findNavController()?.navigate(
|
||||
MainFragmentDirections.actionShowSettings()
|
||||
)
|
||||
}
|
||||
|
||||
R.id.action_search -> {
|
||||
findNavController().navigate(HomeFragmentDirections.actionShowSearch())
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
binding.homePager.adapter = HomePagerAdapter()
|
||||
|
||||
TabLayoutMediator(binding.homeTabs, binding.homePager) { tab, pos ->
|
||||
val labelRes = when (pos) {
|
||||
0 -> R.string.lbl_songs
|
||||
1 -> R.string.lbl_albums
|
||||
2 -> R.string.lbl_artists
|
||||
3 -> R.string.lbl_genres
|
||||
else -> error("Unreachable")
|
||||
}
|
||||
|
||||
tab.setText(labelRes)
|
||||
}.attach()
|
||||
|
||||
// --- VIEWMODEL SETUP ---
|
||||
|
||||
playbackModel.setupPlayback(requireContext())
|
||||
|
||||
logD("Fragment Created.")
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
private inner class HomePagerAdapter :
|
||||
FragmentStateAdapter(childFragmentManager, viewLifecycleOwner.lifecycle) {
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
return when (position) {
|
||||
0 -> SongListFragment()
|
||||
1 -> AlbumListFragment()
|
||||
2 -> ArtistListFragment()
|
||||
3 -> GenreListFragment()
|
||||
else -> error("Unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = 4
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Auxio Project
|
||||
* GenreListFragment.kt is part of Auxio.
|
||||
*
|
||||
* This program 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 program 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.home.pager
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
||||
import org.oxycblt.auxio.home.HomeAdapter
|
||||
import org.oxycblt.auxio.home.HomeFragmentDirections
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.ui.newMenu
|
||||
|
||||
class AlbumListFragment : Fragment() {
|
||||
private val playbackModel: PlaybackViewModel by viewModels()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
val binding = FragmentHomeListBinding.inflate(inflater)
|
||||
|
||||
val adapter = HomeAdapter(
|
||||
doOnClick = { item ->
|
||||
HomeFragmentDirections.actionShowAlbum(item.id)
|
||||
},
|
||||
::newMenu
|
||||
)
|
||||
|
||||
adapter.updateData(MusicStore.getInstance().albums)
|
||||
|
||||
// --- UI SETUP ---
|
||||
|
||||
binding.homeRecycler.adapter = adapter
|
||||
|
||||
return binding.root
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Auxio Project
|
||||
* GenreListFragment.kt is part of Auxio.
|
||||
*
|
||||
* This program 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 program 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.home.pager
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
||||
import org.oxycblt.auxio.home.HomeAdapter
|
||||
import org.oxycblt.auxio.home.HomeFragmentDirections
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.ui.newMenu
|
||||
|
||||
class ArtistListFragment : Fragment() {
|
||||
private val playbackModel: PlaybackViewModel by viewModels()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
val binding = FragmentHomeListBinding.inflate(inflater)
|
||||
|
||||
val adapter = HomeAdapter(
|
||||
doOnClick = { item ->
|
||||
HomeFragmentDirections.actionShowArtist(item.id)
|
||||
},
|
||||
::newMenu
|
||||
)
|
||||
|
||||
adapter.updateData(MusicStore.getInstance().artists)
|
||||
|
||||
// --- UI SETUP ---
|
||||
|
||||
binding.homeRecycler.adapter = adapter
|
||||
|
||||
return binding.root
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Auxio Project
|
||||
* GenreListFragment.kt is part of Auxio.
|
||||
*
|
||||
* This program 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 program 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.home.pager
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
||||
import org.oxycblt.auxio.home.HomeAdapter
|
||||
import org.oxycblt.auxio.home.HomeFragmentDirections
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.ui.newMenu
|
||||
|
||||
class GenreListFragment : Fragment() {
|
||||
private val playbackModel: PlaybackViewModel by viewModels()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
val binding = FragmentHomeListBinding.inflate(inflater)
|
||||
|
||||
val adapter = HomeAdapter(
|
||||
doOnClick = { item ->
|
||||
HomeFragmentDirections.actionShowGenre(item.id)
|
||||
},
|
||||
::newMenu
|
||||
)
|
||||
|
||||
adapter.updateData(MusicStore.getInstance().genres)
|
||||
|
||||
// --- UI SETUP ---
|
||||
|
||||
binding.homeRecycler.adapter = adapter
|
||||
|
||||
return binding.root
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Auxio Project
|
||||
* GenreListFragment.kt is part of Auxio.
|
||||
*
|
||||
* This program 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 program 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.home.pager
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
||||
import org.oxycblt.auxio.home.HomeAdapter
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.ui.newMenu
|
||||
|
||||
class SongListFragment : Fragment() {
|
||||
private val playbackModel: PlaybackViewModel by viewModels()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
val binding = FragmentHomeListBinding.inflate(inflater)
|
||||
|
||||
val adapter = HomeAdapter(
|
||||
doOnClick = { item ->
|
||||
playbackModel.playSong(item as Song)
|
||||
},
|
||||
::newMenu
|
||||
)
|
||||
|
||||
adapter.updateData(MusicStore.getInstance().songs)
|
||||
|
||||
// --- UI SETUP ---
|
||||
|
||||
binding.homeRecycler.adapter = adapter
|
||||
|
||||
return binding.root
|
||||
}
|
||||
}
|
|
@ -1,142 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Auxio Project
|
||||
* LibraryFragment.kt is part of Auxio.
|
||||
*
|
||||
* This program 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 program 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.library
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentLibraryBinding
|
||||
import org.oxycblt.auxio.detail.DetailViewModel
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Parent
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.recycler.sliceArticle
|
||||
import org.oxycblt.auxio.spans
|
||||
import org.oxycblt.auxio.ui.newMenu
|
||||
|
||||
/**
|
||||
* A [Fragment] that shows a custom list of [Genre], [Artist], or [Album] data. Also allows for
|
||||
* search functionality.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class LibraryFragment : Fragment() {
|
||||
private val libraryModel: LibraryViewModel by activityViewModels()
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
val binding = FragmentLibraryBinding.inflate(inflater)
|
||||
val libraryAdapter = LibraryAdapter(::navToDetail, ::newMenu)
|
||||
|
||||
// --- UI SETUP ---
|
||||
|
||||
binding.lifecycleOwner = viewLifecycleOwner
|
||||
|
||||
binding.libraryToolbar.apply {
|
||||
menu.findItem(libraryModel.sortMode.toMenuId()).isChecked = true
|
||||
|
||||
setOnMenuItemClickListener { item ->
|
||||
if (item.itemId != R.id.submenu_sorting) {
|
||||
libraryModel.updateSortMode(item.itemId)
|
||||
item.isChecked = true
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding.libraryRecycler.apply {
|
||||
adapter = libraryAdapter
|
||||
setHasFixedSize(true)
|
||||
|
||||
if (spans != 1) {
|
||||
layoutManager = GridLayoutManager(requireContext(), spans)
|
||||
}
|
||||
}
|
||||
|
||||
binding.libraryFastScroll.setup(binding.libraryRecycler) { pos ->
|
||||
val item = libraryModel.libraryData.value!![pos]
|
||||
val char = item.displayName.sliceArticle().first().uppercaseChar()
|
||||
|
||||
if (char.isDigit()) '#' else char
|
||||
}
|
||||
|
||||
// --- VIEWMODEL SETUP ---
|
||||
|
||||
libraryModel.libraryData.observe(viewLifecycleOwner) { data ->
|
||||
libraryAdapter.updateData(data)
|
||||
}
|
||||
|
||||
detailModel.navToItem.observe(viewLifecycleOwner) { item ->
|
||||
if (item != null) {
|
||||
libraryModel.setNavigating(false)
|
||||
|
||||
if (item is Parent) {
|
||||
navToDetail(item)
|
||||
} else if (item is Song) {
|
||||
navToDetail(item.album)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logD("Fragment created.")
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
libraryModel.setNavigating(false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the detail UI for a [parent].
|
||||
*/
|
||||
private fun navToDetail(parent: Parent) {
|
||||
requireView().rootView.clearFocus()
|
||||
|
||||
if (!libraryModel.isNavigating) {
|
||||
libraryModel.setNavigating(true)
|
||||
|
||||
logD("Navigating to the detail fragment for ${parent.name}")
|
||||
|
||||
findNavController().navigate(
|
||||
when (parent) {
|
||||
is Genre -> LibraryFragmentDirections.actionShowGenre(parent.id)
|
||||
is Artist -> LibraryFragmentDirections.actionShowArtist(parent.id)
|
||||
is Album -> LibraryFragmentDirections.actionShowAlbum(parent.id)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Auxio Project
|
||||
* LibraryViewModel.kt is part of Auxio.
|
||||
*
|
||||
* This program 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 program 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.library
|
||||
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.music.Parent
|
||||
import org.oxycblt.auxio.recycler.DisplayMode
|
||||
import org.oxycblt.auxio.recycler.SortMode
|
||||
import org.oxycblt.auxio.settings.SettingsManager
|
||||
|
||||
/**
|
||||
* A [ViewModel] that manages what [LibraryFragment] is currently showing, and also the search
|
||||
* functionality.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class LibraryViewModel : ViewModel(), SettingsManager.Callback {
|
||||
private val mLibraryData = MutableLiveData(listOf<Parent>())
|
||||
val libraryData: LiveData<List<Parent>> get() = mLibraryData
|
||||
|
||||
private var mSortMode = SortMode.ALPHA_DOWN
|
||||
val sortMode: SortMode get() = mSortMode
|
||||
|
||||
private var mDisplayMode = DisplayMode.SHOW_ARTISTS
|
||||
|
||||
private var mIsNavigating = false
|
||||
val isNavigating: Boolean get() = mIsNavigating
|
||||
|
||||
private val settingsManager = SettingsManager.getInstance()
|
||||
private val musicStore = MusicStore.getInstance()
|
||||
|
||||
init {
|
||||
settingsManager.addCallback(this)
|
||||
|
||||
// Set up the display/sort modes
|
||||
mDisplayMode = settingsManager.libraryDisplayMode
|
||||
mSortMode = settingsManager.librarySortMode
|
||||
|
||||
// Handle "NONE" SortMode that was removed in 1.4.1
|
||||
if (mSortMode == SortMode.NONE) {
|
||||
mSortMode = SortMode.ALPHA_DOWN
|
||||
}
|
||||
|
||||
updateLibraryData()
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the current [SortMode] using an menu [itemId].
|
||||
*/
|
||||
fun updateSortMode(@IdRes itemId: Int) {
|
||||
val mode = when (itemId) {
|
||||
R.id.option_sort_alpha_down -> SortMode.ALPHA_DOWN
|
||||
R.id.option_sort_alpha_up -> SortMode.ALPHA_UP
|
||||
|
||||
else -> SortMode.NONE
|
||||
}
|
||||
|
||||
if (mode != mSortMode) {
|
||||
mSortMode = mode
|
||||
settingsManager.librarySortMode = mode
|
||||
|
||||
updateLibraryData()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the current navigation status
|
||||
*/
|
||||
fun setNavigating(isNavigating: Boolean) {
|
||||
mIsNavigating = isNavigating
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut function for updating the library data with the current [SortMode]/[DisplayMode]
|
||||
*/
|
||||
private fun updateLibraryData() {
|
||||
mLibraryData.value = when (mDisplayMode) {
|
||||
DisplayMode.SHOW_GENRES -> mSortMode.getSortedGenreList(musicStore.genres)
|
||||
|
||||
DisplayMode.SHOW_ARTISTS -> mSortMode.getSortedArtistList(musicStore.artists)
|
||||
|
||||
DisplayMode.SHOW_ALBUMS -> mSortMode.getSortedAlbumList(musicStore.albums)
|
||||
|
||||
else -> error("DisplayMode $mDisplayMode is unsupported.")
|
||||
}
|
||||
}
|
||||
|
||||
// --- OVERRIDES ---
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
|
||||
settingsManager.removeCallback(this)
|
||||
}
|
||||
|
||||
override fun onLibDisplayModeUpdate(displayMode: DisplayMode) {
|
||||
mDisplayMode = displayMode
|
||||
|
||||
updateLibraryData()
|
||||
}
|
||||
}
|
|
@ -1,293 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Auxio Project
|
||||
* FastScrollView.kt is part of Auxio.
|
||||
*
|
||||
* This program 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 program 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.recycler
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.AttributeSet
|
||||
import android.util.TypedValue
|
||||
import android.view.HapticFeedbackConstants
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.postDelayed
|
||||
import androidx.dynamicanimation.animation.DynamicAnimation
|
||||
import androidx.dynamicanimation.animation.SpringAnimation
|
||||
import androidx.dynamicanimation.animation.SpringForce
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.accent.Accent
|
||||
import org.oxycblt.auxio.canScroll
|
||||
import org.oxycblt.auxio.databinding.ViewFastScrollBinding
|
||||
import org.oxycblt.auxio.inflater
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.resolveAttr
|
||||
import org.oxycblt.auxio.resolveColor
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* A view that allows for quick scrolling through a [RecyclerView] with many items. Unlike other
|
||||
* fast-scrollers, this one displays indicators and a thumb instead of simply a scroll bar.
|
||||
* This code is fundamentally an adaptation of Reddit's IndicatorFastScroll, albeit specialized
|
||||
* towards Auxio. The original library is here: https://github.com/reddit/IndicatorFastScroll/
|
||||
* TODO: Replace this with something similar to AndroidFastScroll [but optimized for Auxio],
|
||||
* since this thumb view is a blocker to a better sort system.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class FastScrollView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = -1
|
||||
) : ConstraintLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
// --- UI ---
|
||||
|
||||
private val binding = ViewFastScrollBinding.inflate(context.inflater, this, true)
|
||||
private val thumbAnim: SpringAnimation
|
||||
|
||||
// --- RECYCLER ---
|
||||
|
||||
private var mRecycler: RecyclerView? = null
|
||||
private var mGetItem: ((Int) -> Char)? = null
|
||||
private val mObserver = object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onChanged() = postIndicatorUpdate()
|
||||
|
||||
override fun onItemRangeChanged(
|
||||
positionStart: Int,
|
||||
itemCount: Int,
|
||||
payload: Any?
|
||||
) = postIndicatorUpdate()
|
||||
|
||||
override fun onItemRangeInserted(
|
||||
positionStart: Int,
|
||||
itemCount: Int
|
||||
) = onChanged()
|
||||
|
||||
override fun onItemRangeMoved(
|
||||
fromPosition: Int,
|
||||
toPosition: Int,
|
||||
itemCount: Int
|
||||
) = onChanged()
|
||||
|
||||
override fun onItemRangeRemoved(
|
||||
positionStart: Int,
|
||||
itemCount: Int
|
||||
) = onChanged()
|
||||
}
|
||||
|
||||
// --- INDICATORS ---
|
||||
|
||||
private data class Indicator(val char: Char, val pos: Int)
|
||||
|
||||
private var indicators = listOf<Indicator>()
|
||||
private val activeColor = Accent.get().color.resolveColor(context)
|
||||
private val inactiveColor = android.R.attr.textColorSecondary.resolveAttr(context)
|
||||
|
||||
// --- STATE ---
|
||||
|
||||
private var hasPostedItemUpdate = false
|
||||
private var wasValidTouch = false
|
||||
private var lastPos = -1
|
||||
|
||||
init {
|
||||
isFocusableInTouchMode = true
|
||||
isClickable = true
|
||||
|
||||
thumbAnim = SpringAnimation(binding.scrollThumb, DynamicAnimation.TRANSLATION_Y).apply {
|
||||
spring = SpringForce().also {
|
||||
it.dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent the disappear animation from being displayed on startup by making the thumb
|
||||
// invisible, it will be made visible once the animation ends
|
||||
binding.scrollThumb.visibility = View.INVISIBLE
|
||||
|
||||
postDelayed(200) {
|
||||
binding.scrollThumb.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up this view with a [RecyclerView]. [getItem] is called when the first character
|
||||
* of a piece of data is needed.
|
||||
*/
|
||||
fun setup(recycler: RecyclerView, getItem: (Int) -> Char) {
|
||||
check(mRecycler == null) { "Only set up this view once." }
|
||||
|
||||
mRecycler = recycler
|
||||
mGetItem = getItem
|
||||
|
||||
recycler.adapter?.registerAdapterDataObserver(mObserver)
|
||||
|
||||
postIndicatorUpdate()
|
||||
}
|
||||
|
||||
// --- INDICATOR UPDATES ---
|
||||
|
||||
private fun postIndicatorUpdate() {
|
||||
if (!hasPostedItemUpdate) {
|
||||
hasPostedItemUpdate = true
|
||||
|
||||
post {
|
||||
val recycler = requireNotNull(mRecycler)
|
||||
|
||||
if (recycler.isAttachedToWindow && recycler.adapter != null) {
|
||||
updateIndicators()
|
||||
binding.scrollIndicatorText.requestLayout()
|
||||
}
|
||||
|
||||
// Hide this view if there is nothing to scroll
|
||||
isVisible = recycler.canScroll()
|
||||
|
||||
hasPostedItemUpdate = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateIndicators() {
|
||||
val recycler = requireNotNull(mRecycler)
|
||||
val getItem = requireNotNull(mGetItem)
|
||||
|
||||
indicators = 0.until(recycler.adapter!!.itemCount).mapNotNull { pos ->
|
||||
Indicator(getItem(pos), pos)
|
||||
}.distinctBy { it.char }
|
||||
|
||||
val textHeight = TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_SP, 14F, resources.displayMetrics
|
||||
)
|
||||
|
||||
// If the scroller size is too small to contain all the entries, truncate entries
|
||||
// so that the fast scroller entries fit. Include the thumb in here so it isn't cut
|
||||
// off.
|
||||
val maxEntries = (height - (binding.scrollThumb.height * 2)) / textHeight
|
||||
|
||||
if (indicators.size > maxEntries) {
|
||||
val truncateInterval = ceil(indicators.size / maxEntries).toInt()
|
||||
|
||||
check(truncateInterval > 1) {
|
||||
"Needed to truncate, but truncateInterval was 1 or lower anyway"
|
||||
}
|
||||
|
||||
logD("More entries than screen space, truncating by $truncateInterval.")
|
||||
|
||||
indicators = indicators.filterIndexed { index, _ ->
|
||||
index % truncateInterval == 0
|
||||
}
|
||||
}
|
||||
|
||||
// Then set it as the unified TextView text, for efficiency purposes.
|
||||
binding.scrollIndicatorText.text = indicators.joinToString("\n") { indicator ->
|
||||
indicator.char.toString()
|
||||
}
|
||||
}
|
||||
|
||||
// --- TOUCH ---
|
||||
|
||||
@Suppress("ClickableViewAccessibility")
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||
super.onTouchEvent(event)
|
||||
performClick()
|
||||
|
||||
val success = handleTouch(event.action, event.x.roundToInt(), event.y.roundToInt())
|
||||
|
||||
// Depending on the results, update the visibility of the thumb and the pressed state of
|
||||
// this view.
|
||||
binding.scrollThumb.isActivated = success
|
||||
binding.scrollIndicatorText.isPressed = success
|
||||
|
||||
return success
|
||||
}
|
||||
|
||||
private fun handleTouch(action: Int, touchX: Int, touchY: Int): Boolean {
|
||||
when (action) {
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
||||
binding.scrollIndicatorText.setTextColor(inactiveColor)
|
||||
wasValidTouch = false
|
||||
lastPos = -1
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Since this view is unified between the thumb and the indicators, we have
|
||||
// to check if the initial pointer position was in the indicators to prevent the
|
||||
// scroll from being triggered outside its bounds.
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
wasValidTouch = binding.scrollIndicatorText.contains(touchX, touchY)
|
||||
}
|
||||
}
|
||||
|
||||
// Try to figure out which indicator the pointer has landed on
|
||||
if (binding.scrollIndicatorText.containsY(touchY) && wasValidTouch) {
|
||||
// Get the touch position in regards to the TextView and the rough text height
|
||||
val indicatorTouchY = touchY - binding.scrollIndicatorText.top
|
||||
val textHeight = binding.scrollIndicatorText.height / indicators.size
|
||||
|
||||
// Use that to calculate the indicator index, if the calculation is
|
||||
// invalid just ignore it.
|
||||
val index = min(indicatorTouchY / textHeight, indicators.lastIndex)
|
||||
|
||||
// Also calculate the rough center position of the indicator for the scroll thumb
|
||||
val centerY = binding.scrollIndicatorText.y + (textHeight / 2) + (index * textHeight)
|
||||
|
||||
selectIndicator(indicators[index], centerY)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun selectIndicator(indicator: Indicator, centerY: Float) {
|
||||
if (indicator.pos != lastPos) {
|
||||
lastPos = indicator.pos
|
||||
binding.scrollIndicatorText.setTextColor(activeColor)
|
||||
|
||||
// Stop any scroll momentum and snap-scroll to the position
|
||||
mRecycler?.apply {
|
||||
stopScroll()
|
||||
(layoutManager as LinearLayoutManager).scrollToPositionWithOffset(indicator.pos, 0)
|
||||
}
|
||||
|
||||
// Update the thumb position/text
|
||||
binding.scrollThumbText.text = indicator.char.toString()
|
||||
thumbAnim.animateToFinalPosition(centerY - (binding.scrollThumb.measuredHeight / 2))
|
||||
|
||||
performHapticFeedback(
|
||||
if (Build.VERSION.SDK_INT >= 27) {
|
||||
// Dragging across a scroller is closer to moving a text handle
|
||||
HapticFeedbackConstants.TEXT_HANDLE_MOVE
|
||||
} else {
|
||||
HapticFeedbackConstants.KEYBOARD_TAP
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun View.contains(x: Int, y: Int): Boolean {
|
||||
return x in (left until right) && containsY(y)
|
||||
}
|
||||
|
||||
private fun View.containsY(y: Int): Boolean {
|
||||
return y in (top until bottom)
|
||||
}
|
||||
}
|
|
@ -72,6 +72,10 @@ class SearchFragment : Fragment() {
|
|||
binding.searchToolbar.apply {
|
||||
menu.findItem(searchModel.filterMode.toId()).isChecked = true
|
||||
|
||||
setNavigationOnClickListener {
|
||||
findNavController().navigateUp()
|
||||
}
|
||||
|
||||
setOnMenuItemClickListener { item ->
|
||||
if (item.itemId != R.id.submenu_filtering) {
|
||||
searchModel.updateFilterModeWithId(item.itemId, requireContext())
|
||||
|
|
|
@ -24,7 +24,6 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import org.oxycblt.auxio.MainFragmentDirections
|
||||
import org.oxycblt.auxio.databinding.FragmentSettingsBinding
|
||||
|
||||
/**
|
||||
|
@ -39,12 +38,18 @@ class SettingsFragment : Fragment() {
|
|||
): View {
|
||||
val binding = FragmentSettingsBinding.inflate(inflater)
|
||||
|
||||
binding.settingsToolbar.setOnMenuItemClickListener {
|
||||
parentFragment?.parentFragment?.findNavController()?.navigate(
|
||||
MainFragmentDirections.actionShowAbout()
|
||||
)
|
||||
binding.settingsToolbar.apply {
|
||||
setOnMenuItemClickListener {
|
||||
findNavController().navigate(
|
||||
SettingsFragmentDirections.actionShowAbout()
|
||||
)
|
||||
|
||||
true
|
||||
true
|
||||
}
|
||||
|
||||
setNavigationOnClickListener {
|
||||
findNavController().navigateUp()
|
||||
}
|
||||
}
|
||||
|
||||
return binding.root
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Auxio Project
|
||||
* SongsAdapter.kt is part of Auxio.
|
||||
*
|
||||
* This program 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 program 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.songs
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.recycler.viewholders.SongViewHolder
|
||||
|
||||
/**
|
||||
* The adapter for [SongsFragment], shows basic songs without durations.
|
||||
* @param data List of [Song]s to be shown
|
||||
* @param doOnClick What to do on a click action
|
||||
* @param doOnLongClick What to do on a long click action
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class SongsAdapter(
|
||||
private val data: List<Song>,
|
||||
private val doOnClick: (data: Song) -> Unit,
|
||||
private val doOnLongClick: (view: View, data: Song) -> Unit
|
||||
) : RecyclerView.Adapter<SongViewHolder>() {
|
||||
|
||||
override fun getItemCount(): Int = data.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SongViewHolder {
|
||||
return SongViewHolder.from(parent.context, doOnClick, doOnLongClick)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: SongViewHolder, position: Int) {
|
||||
holder.bind(data[position])
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Auxio Project
|
||||
* SongsFragment.kt is part of Auxio.
|
||||
*
|
||||
* This program 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 program 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.songs
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentSongsBinding
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.recycler.sliceArticle
|
||||
import org.oxycblt.auxio.spans
|
||||
import org.oxycblt.auxio.ui.newMenu
|
||||
|
||||
/**
|
||||
* A [Fragment] that shows a list of all songs on the device.
|
||||
* Contains options to search/shuffle them.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class SongsFragment : Fragment() {
|
||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
private val musicStore = MusicStore.getInstance()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
val binding = FragmentSongsBinding.inflate(inflater)
|
||||
val songAdapter = SongsAdapter(musicStore.songs, playbackModel::playSong, ::newMenu)
|
||||
|
||||
// --- UI SETUP ---
|
||||
|
||||
binding.songToolbar.apply {
|
||||
setOnMenuItemClickListener {
|
||||
if (it.itemId == R.id.action_shuffle) {
|
||||
playbackModel.shuffleAll()
|
||||
true
|
||||
} else false
|
||||
}
|
||||
}
|
||||
|
||||
binding.songRecycler.apply {
|
||||
adapter = songAdapter
|
||||
setHasFixedSize(true)
|
||||
|
||||
if (spans != 1) {
|
||||
layoutManager = GridLayoutManager(requireContext(), spans)
|
||||
}
|
||||
}
|
||||
|
||||
binding.songFastScroll.setup(binding.songRecycler) { pos ->
|
||||
// Get the first character [respecting articles]
|
||||
val char = musicStore.songs[pos].name.sliceArticle().first().uppercaseChar()
|
||||
|
||||
if (char.isDigit()) '#' else char
|
||||
}
|
||||
|
||||
logD("Fragment created.")
|
||||
|
||||
return binding.root
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="@android:color/white" />
|
||||
</shape>
|
|
@ -1,58 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout 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"
|
||||
tools:context=".MainFragment">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/main_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:animateLayoutChanges="true"
|
||||
android:fitsSystemWindows="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/explore_nav_host"
|
||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
app:layout_constraintBottom_toTopOf="@+id/controls_container"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:navGraph="@navigation/nav_explore"
|
||||
tools:layout="@layout/fragment_library" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/controls_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:animateLayoutChanges="true"
|
||||
android:background="?android:attr/colorBackground"
|
||||
android:baselineAligned="false"
|
||||
android:elevation="8dp"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
android:id="@+id/nav_bar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1.5"
|
||||
android:background="?android:attr/colorBackground"
|
||||
android:elevation="0dp"
|
||||
app:elevation="0dp"
|
||||
app:menu="@menu/menu_nav" />
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/compact_playback"
|
||||
android:name="org.oxycblt.auxio.playback.CompactPlaybackFragment"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="2"
|
||||
tools:layout="@layout/fragment_compact_playback" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</layout>
|
|
@ -36,8 +36,7 @@
|
|||
|
||||
<ImageView
|
||||
android:id="@+id/about_auxio_icon"
|
||||
android:layout_width="@dimen/size_app_icon"
|
||||
android:layout_height="@dimen/size_app_icon"
|
||||
style="@style/Widget.ImageView.Compact"
|
||||
android:layout_marginStart="@dimen/spacing_medium"
|
||||
android:layout_marginTop="@dimen/spacing_medium"
|
||||
android:contentDescription="@string/desc_auxio_icon"
|
||||
|
|
50
app/src/main/res/layout/fragment_home.xml
Normal file
50
app/src/main/res/layout/fragment_home.xml
Normal file
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout 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"
|
||||
tools:context=".home.HomeFragment">
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/main_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:animateLayoutChanges="true"
|
||||
android:fitsSystemWindows="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/home_appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/colorSurface"
|
||||
app:liftOnScroll="true">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/home_toolbar"
|
||||
style="@style/Widget.Toolbar"
|
||||
app:menu="@menu/menu_home"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
app:title="@string/info_app_name" />
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/home_tabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:tabTextAppearance="@style/TextAppearance.TabLayout.Label"
|
||||
app:tabMode="scrollable"
|
||||
app:tabContentStart="@dimen/spacing_medium"
|
||||
app:tabTextColor="?android:attr/textColorPrimary"
|
||||
app:tabIndicatorColor="?attr/colorAccent" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/home_pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||
tools:listitem="@layout/item_artist" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
</layout>
|
13
app/src/main/res/layout/fragment_home_list.xml
Normal file
13
app/src/main/res/layout/fragment_home_list.xml
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout 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">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/home_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||
tools:listitem="@layout/item_artist" />
|
||||
</layout>
|
|
@ -1,41 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout 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"
|
||||
tools:context=".library.LibraryFragment">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:animateLayoutChanges="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/library_toolbar"
|
||||
style="@style/Widget.Toolbar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:menu="@menu/menu_library"
|
||||
app:title="@string/lbl_library" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/library_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/library_toolbar"
|
||||
tools:listitem="@layout/item_artist" />
|
||||
|
||||
<org.oxycblt.auxio.recycler.FastScrollView
|
||||
android:id="@+id/library_fast_scroll"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/library_toolbar" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
|
@ -19,35 +19,15 @@
|
|||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
app:navGraph="@navigation/nav_explore"
|
||||
tools:layout="@layout/fragment_library" />
|
||||
tools:layout="@layout/fragment_home" />
|
||||
|
||||
<org.oxycblt.auxio.ui.SlideLinearLayout
|
||||
android:id="@+id/controls_container"
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/main_playback"
|
||||
android:name="org.oxycblt.auxio.playback.CompactPlaybackFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:animateLayoutChanges="true"
|
||||
android:background="?android:attr/colorBackground"
|
||||
android:baselineAligned="false"
|
||||
android:elevation="8dp"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/compact_playback"
|
||||
android:name="org.oxycblt.auxio.playback.CompactPlaybackFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:layout="@layout/fragment_compact_playback" />
|
||||
|
||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
android:id="@+id/nav_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:background="?attr/colorSurface"
|
||||
app:elevation="0dp"
|
||||
app:menu="@menu/menu_nav" />
|
||||
|
||||
</org.oxycblt.auxio.ui.SlideLinearLayout>
|
||||
android:background="?attr/colorSurface"
|
||||
tools:layout="@layout/fragment_compact_playback" />
|
||||
</LinearLayout>
|
||||
</layout>
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/search_toolbar"
|
||||
style="@style/Widget.Toolbar"
|
||||
style="@style/Widget.Toolbar.Icon"
|
||||
android:elevation="0dp"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
app:menu="@menu/menu_search"
|
||||
|
|
|
@ -7,11 +7,13 @@
|
|||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
android:background="?attr/colorSurface"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/settings_toolbar"
|
||||
style="@style/Widget.Toolbar"
|
||||
style="@style/Widget.Toolbar.Icon.Down"
|
||||
app:menu="@menu/menu_settings"
|
||||
app:title="@string/set_title" />
|
||||
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout 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"
|
||||
tools:context=".songs.SongsFragment">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:animateLayoutChanges="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/song_toolbar"
|
||||
style="@style/Widget.Toolbar"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:menu="@menu/menu_songs"
|
||||
app:title="@string/lbl_all_songs" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/song_recycler"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/song_toolbar"
|
||||
tools:listitem="@layout/item_song" />
|
||||
|
||||
<org.oxycblt.auxio.recycler.FastScrollView
|
||||
android:id="@+id/song_fast_scroll"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/song_toolbar" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
|
@ -1,50 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout 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">
|
||||
|
||||
<merge
|
||||
tools:layout_height="match_parent"
|
||||
tools:layout_width="match_parent"
|
||||
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/scroll_thumb"
|
||||
android:layout_width="@dimen/size_scroll_thumb"
|
||||
android:layout_height="@dimen/size_scroll_thumb"
|
||||
android:background="@drawable/ui_circle"
|
||||
android:backgroundTint="?attr/colorAccent"
|
||||
android:elevation="@dimen/elevation_small"
|
||||
android:stateListAnimator="@animator/animator_thumb"
|
||||
app:layout_constraintEnd_toStartOf="@+id/scroll_indicator_text">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/scroll_thumb_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fontFamily="@font/inter_semibold"
|
||||
android:gravity="center"
|
||||
android:textColor="?android:attr/windowBackground"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
tools:text="A" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/scroll_indicator_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/inter_semibold"
|
||||
android:gravity="center"
|
||||
android:includeFontPadding="false"
|
||||
android:lineSpacingExtra="@dimen/spacing_tiny"
|
||||
android:minWidth="@dimen/width_fast_scroll"
|
||||
android:paddingTop="@dimen/spacing_tiny"
|
||||
android:paddingBottom="@dimen/spacing_tiny"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="A\nB\n\C\nD\nE" />
|
||||
|
||||
</merge>
|
||||
</layout>
|
|
@ -2,11 +2,17 @@
|
|||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_search"
|
||||
android:icon="@drawable/ic_search"
|
||||
android:title="@string/lbl_search"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/submenu_sorting"
|
||||
android:icon="@drawable/ic_sort_none"
|
||||
android:title="@string/lbl_sort"
|
||||
app:showAsAction="always">
|
||||
app:showAsAction="ifRoom">
|
||||
<menu>
|
||||
<group android:checkableBehavior="single">
|
||||
<item
|
||||
|
@ -18,4 +24,10 @@
|
|||
</group>
|
||||
</menu>
|
||||
</item>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_settings"
|
||||
android:icon="@drawable/ic_settings"
|
||||
android:title="@string/set_title"
|
||||
app:showAsAction="ifRoom"/>
|
||||
</menu>
|
|
@ -1,19 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/library_fragment"
|
||||
android:icon="@drawable/ic_library"
|
||||
android:title="@string/lbl_library" />
|
||||
<item
|
||||
android:id="@+id/songs_fragment"
|
||||
android:icon="@drawable/ic_song"
|
||||
android:title="@string/lbl_songs" />
|
||||
<item
|
||||
android:id="@+id/search_fragment"
|
||||
android:icon="@drawable/ic_search"
|
||||
android:title="@string/lbl_search" />
|
||||
<item
|
||||
android:id="@+id/settings_fragment"
|
||||
android:icon="@drawable/ic_settings"
|
||||
android:title="@string/set_title" />
|
||||
</menu>
|
|
@ -1,9 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/action_shuffle"
|
||||
android:icon="@drawable/ic_shuffle"
|
||||
android:title="@string/lbl_shuffle"
|
||||
app:showAsAction="always" />
|
||||
</menu>
|
|
@ -2,35 +2,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"
|
||||
app:startDestination="@id/library_fragment">
|
||||
app:startDestination="@id/home_fragment">
|
||||
|
||||
<fragment
|
||||
android:id="@+id/library_fragment"
|
||||
android:name="org.oxycblt.auxio.library.LibraryFragment"
|
||||
android:label="fragment_library"
|
||||
tools:layout="@layout/fragment_library">
|
||||
<action
|
||||
android:id="@+id/action_show_genre"
|
||||
app:destination="@id/genre_detail_fragment"
|
||||
app:enterAnim="@anim/nav_default_enter_anim"
|
||||
app:exitAnim="@anim/nav_default_exit_anim"
|
||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
||||
<action
|
||||
android:id="@+id/action_show_artist"
|
||||
app:destination="@id/artist_detail_fragment"
|
||||
app:enterAnim="@anim/nav_default_enter_anim"
|
||||
app:exitAnim="@anim/nav_default_exit_anim"
|
||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
||||
<action
|
||||
android:id="@+id/action_show_album"
|
||||
app:destination="@id/album_detail_fragment"
|
||||
app:enterAnim="@anim/nav_default_enter_anim"
|
||||
app:exitAnim="@anim/nav_default_exit_anim"
|
||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/artist_detail_fragment"
|
||||
android:name="org.oxycblt.auxio.detail.ArtistDetailFragment"
|
||||
|
@ -100,11 +73,6 @@
|
|||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/songs_fragment"
|
||||
android:name="org.oxycblt.auxio.songs.SongsFragment"
|
||||
android:label="fragment_songs"
|
||||
tools:layout="@layout/fragment_songs" />
|
||||
<fragment
|
||||
android:id="@+id/search_fragment"
|
||||
android:name="org.oxycblt.auxio.search.SearchFragment"
|
||||
|
@ -133,8 +101,37 @@
|
|||
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/settings_fragment"
|
||||
android:name="org.oxycblt.auxio.settings.SettingsFragment"
|
||||
android:label="SettingsFragment"
|
||||
tools:layout="@layout/fragment_settings" />
|
||||
android:id="@+id/home_fragment"
|
||||
android:name="org.oxycblt.auxio.home.HomeFragment"
|
||||
android:label="fragment_home"
|
||||
tools:layout="@layout/fragment_home">
|
||||
<action
|
||||
android:id="@+id/action_show_search"
|
||||
app:destination="@id/search_fragment"
|
||||
app:enterAnim="@anim/nav_default_enter_anim"
|
||||
app:exitAnim="@anim/nav_default_exit_anim"
|
||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
||||
<action
|
||||
android:id="@+id/action_show_genre"
|
||||
app:destination="@id/genre_detail_fragment"
|
||||
app:enterAnim="@anim/nav_default_enter_anim"
|
||||
app:exitAnim="@anim/nav_default_exit_anim"
|
||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
||||
<action
|
||||
android:id="@+id/action_show_artist"
|
||||
app:destination="@id/artist_detail_fragment"
|
||||
app:enterAnim="@anim/nav_default_enter_anim"
|
||||
app:exitAnim="@anim/nav_default_exit_anim"
|
||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
||||
<action
|
||||
android:id="@+id/action_show_album"
|
||||
app:destination="@id/album_detail_fragment"
|
||||
app:enterAnim="@anim/nav_default_enter_anim"
|
||||
app:exitAnim="@anim/nav_default_exit_anim"
|
||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
||||
</fragment>
|
||||
</navigation>
|
|
@ -32,8 +32,8 @@
|
|||
app:popEnterAnim="@anim/anim_stationary"
|
||||
app:popExitAnim="@anim/anim_nav_slide_down" />
|
||||
<action
|
||||
android:id="@+id/action_show_about"
|
||||
app:destination="@id/about_fragment"
|
||||
android:id="@+id/action_show_settings"
|
||||
app:destination="@id/settings_fragment"
|
||||
app:enterAnim="@anim/anim_nav_slide_up"
|
||||
app:exitAnim="@anim/anim_stationary"
|
||||
app:popEnterAnim="@anim/anim_stationary"
|
||||
|
@ -62,4 +62,17 @@
|
|||
android:name="org.oxycblt.auxio.settings.AboutFragment"
|
||||
android:label="dialog_about"
|
||||
tools:layout="@layout/fragment_about" />
|
||||
<fragment
|
||||
android:id="@+id/settings_fragment"
|
||||
android:name="org.oxycblt.auxio.settings.SettingsFragment"
|
||||
android:label="fragment_settings"
|
||||
tools:layout="@layout/fragment_settings">
|
||||
<action
|
||||
android:id="@+id/action_show_about"
|
||||
app:destination="@id/about_fragment"
|
||||
app:enterAnim="@anim/anim_nav_slide_up"
|
||||
app:exitAnim="@anim/anim_stationary"
|
||||
app:popEnterAnim="@anim/anim_stationary"
|
||||
app:popExitAnim="@anim/anim_nav_slide_down" />
|
||||
</fragment>
|
||||
</navigation>
|
|
@ -6,7 +6,6 @@
|
|||
-->
|
||||
|
||||
<!-- Spacing Namespace | Dimens for padding/margin attributes -->
|
||||
<dimen name="spacing_tiny">4dp</dimen>
|
||||
<dimen name="spacing_small">8dp</dimen>
|
||||
<dimen name="spacing_medium">16dp</dimen>
|
||||
<dimen name="spacing_mid_large">24dp</dimen>
|
||||
|
@ -22,9 +21,6 @@
|
|||
<dimen name="size_btn_small">48dp</dimen>
|
||||
<dimen name="size_btn_large">64dp</dimen>
|
||||
|
||||
<dimen name="size_small_unb_ripple">20dp</dimen>
|
||||
<dimen name="size_unb_ripple">24dp</dimen>
|
||||
|
||||
<dimen name="size_cover_compact">48dp</dimen>
|
||||
<dimen name="size_cover_normal">56dp</dimen>
|
||||
<dimen name="size_cover_huge_land">136dp</dimen>
|
||||
|
@ -33,8 +29,8 @@
|
|||
<dimen name="size_stroke_small">1dp</dimen>
|
||||
<dimen name="size_stroke_large">2dp</dimen>
|
||||
|
||||
<dimen name="size_app_icon">48dp</dimen>
|
||||
<dimen name="size_scroll_thumb">48dp</dimen>
|
||||
<dimen name="size_small_unb_ripple">20dp</dimen>
|
||||
<dimen name="size_unb_ripple">24dp</dimen>
|
||||
|
||||
<!-- Text Size Namespace | Text Sizes -->
|
||||
<dimen name="text_size_small">16sp</dimen>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<!-- Colors -->
|
||||
<item name="colorSurface">@color/surface</item>
|
||||
<item name="colorAccent">@color/design_default_color_primary</item>
|
||||
<item name="colorOnSurface">#FFFFFF</item>
|
||||
<item name="colorOnSurface">#00000000</item>
|
||||
|
||||
<item name="colorPrimary">?attr/colorAccent</item>
|
||||
<item name="colorSecondary">?attr/colorAccent</item>
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">?android:attr/actionBarSize</item>
|
||||
<item name="android:background">?attr/colorSurface</item>
|
||||
<item name="android:elevation">@dimen/elevation_normal</item>
|
||||
<item name="popupTheme">@style/ThemeOverlay.ToolbarPopup</item>
|
||||
|
||||
<item name="titleTextAppearance">@style/TextAppearance.Toolbar.Header</item>
|
||||
|
@ -61,6 +60,10 @@
|
|||
<item name="android:outlineProvider">bounds</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.TabLayout.Label" parent="@style/TextAppearance.Design.Tab">
|
||||
<item name="android:fontFamily">@font/inter_semibold</item>
|
||||
</style>
|
||||
|
||||
<!-- VIEWGROUP STYLES -->
|
||||
|
||||
<style name="TextAppearance.Toolbar.Header" parent="TextAppearance.Widget.AppCompat.Toolbar.Title">
|
||||
|
@ -78,6 +81,7 @@
|
|||
</style>
|
||||
|
||||
<!-- TEXTVIEW STYLES -->
|
||||
|
||||
<style name="Widget.TextView.Item.Base" parent="Widget.AppCompat.TextView">
|
||||
<item name="android:layout_width">0dp</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
|
|
Loading…
Reference in a new issue