Update semantics slightly

Change some naming/structure somewhat, and move all TODOs from AuxioTODO to the project itself.
This commit is contained in:
OxygenCobalt 2020-10-05 19:32:42 -06:00
parent a72cb48c71
commit 59c087d653
32 changed files with 126 additions and 126 deletions

View file

@ -1,41 +0,0 @@
TODO:
TODOs surrounded with ?s are things I want to do, but need to do more research on and may be a nightmare to implement.
TODOs surrounded with !s are things I tried to do, but failed for reasons included in the TODO. I still want to add these, but theres no way for me to do them at the moment.
/detail/
- ? Implement Toolbar update functionality ?
- ! Implement shared element transitions [Return transitions are broken] !
/music/
- Add option to show all genres
- ! Move genres to songs [Wont find songs w/o genres]!
- ! Remove lists from music models [Current genre loading system makes it impossible] !
- ! Dont determine track/album/artist counts on the fly [Audio columns for those values are broken] !
/songs/
- Search when LibraryFragment isnt enabled
- ? Sorting ?
- ? Fast Scrolling ?
/library/
- Exit functionality
- ? Add icons to overflow menu items ?
- ? Add Nested Nav to Library ViewPager fragment [Hold fire on this until everything else is added, there could be sneaky bugs later on if you add it now] ?
- ! Move Adapter functionality to ListAdapter [RecyclerView scrolls to middle/bottom when data is re-sorted] !
/playback/
-
/other/
- Highlight recycler items when they are being played
/bugs/
- Fix issue where fast navigations will cause the app to not display anything
To be added:
/prefs/

View file

@ -7,6 +7,9 @@ import android.view.View
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import org.oxycblt.auxio.theme.accent import org.oxycblt.auxio.theme.accent
// FIXME: Fix bug where fast navigation will break the fade animation and
// lead to nothing being displayed [Possibly Un-fixable]
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? { override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {

View file

@ -57,6 +57,8 @@ class MainFragment : Fragment() {
// --- UI SETUP --- // --- UI SETUP ---
// TODO: Add nested viewpager navigation [If practical]
binding.lifecycleOwner = this binding.lifecycleOwner = this
binding.mainViewPager.adapter = PagerAdapter() binding.mainViewPager.adapter = PagerAdapter()
@ -70,6 +72,8 @@ class MainFragment : Fragment() {
} }
}.attach() }.attach()
binding.compactPlayback.visibility = View.GONE
// Set up the selected/deselected colors // Set up the selected/deselected colors
binding.mainTabs.addOnTabSelectedListener( binding.mainTabs.addOnTabSelectedListener(
object : TabLayout.OnTabSelectedListener { object : TabLayout.OnTabSelectedListener {

View file

@ -16,6 +16,9 @@ import org.oxycblt.auxio.recycler.ClickListener
import org.oxycblt.auxio.theme.applyDivider import org.oxycblt.auxio.theme.applyDivider
import org.oxycblt.auxio.theme.disable import org.oxycblt.auxio.theme.disable
// TODO: Add media controls to DetailFragments [Play, Shuffle]
// TODO: Implement a system where if the main info [Name, Parent, Counts] scrolls off of
// the screen, then update the toolbar to show that info w/the media controls
class ArtistDetailFragment : Fragment() { class ArtistDetailFragment : Fragment() {
private val args: ArtistDetailFragmentArgs by navArgs() private val args: ArtistDetailFragmentArgs by navArgs()

View file

@ -8,6 +8,11 @@ import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.recycler.SortMode import org.oxycblt.auxio.recycler.SortMode
// ViewModel for the Detail Fragments.
// TODO:
// - Implement a system where the Toolbar will update with the info [And Media Controls] when
// the main info of the detail fragment is removed.
// - Implement shared element transitions [If that is even possible]
class DetailViewModel : ViewModel() { class DetailViewModel : ViewModel() {
private var mIsNavigating = false private var mIsNavigating = false
val isNavigating: Boolean get() = mIsNavigating val isNavigating: Boolean get() = mIsNavigating

View file

@ -31,6 +31,7 @@ import org.oxycblt.auxio.theme.applyColor
import org.oxycblt.auxio.theme.applyDivider import org.oxycblt.auxio.theme.applyDivider
import org.oxycblt.auxio.theme.resolveAttr import org.oxycblt.auxio.theme.resolveAttr
// A Fragment to show all the music in the Library.
class LibraryFragment : Fragment(), SearchView.OnQueryTextListener { class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
private val musicModel: MusicViewModel by activityViewModels { private val musicModel: MusicViewModel by activityViewModels {
@ -57,6 +58,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
// --- UI SETUP --- // --- UI SETUP ---
// TODO: Add exit functionality
binding.libraryToolbar.apply { binding.libraryToolbar.apply {
overflowIcon = ContextCompat.getDrawable( overflowIcon = ContextCompat.getDrawable(
requireContext(), R.drawable.ic_sort_none requireContext(), R.drawable.ic_sort_none
@ -78,6 +80,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
true true
} }
// TODO: Add icons to overflow menu items
menu.apply { menu.apply {
val item = findItem(R.id.action_search) val item = findItem(R.id.action_search)
val searchView = item.actionView as SearchView val searchView = item.actionView as SearchView
@ -115,6 +118,8 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
} }
} }
// TODO: Change LibraryAdapter to a ListAdapter
// [If there's a way to preserve scroll position properly]
binding.libraryRecycler.apply { binding.libraryRecycler.apply {
adapter = libraryAdapter adapter = libraryAdapter
@ -176,8 +181,10 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
} }
private fun navToItem(baseModel: BaseModel) { private fun navToItem(baseModel: BaseModel) {
// TODO: Implement shared element transitions to the DetailFragments [If possible]
// If the item is a song [That was selected through search], then update the playback // If the item is a song [That was selected through search], then update the playback
// to that song instead of doing any naviagation // to that song instead of doing any navigation
if (baseModel is Song) { if (baseModel is Song) {
playbackModel.updateSong(baseModel) playbackModel.updateSong(baseModel)
return return

View file

@ -7,7 +7,6 @@ import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
@ -38,7 +37,7 @@ class LoadingFragment : Fragment(R.layout.fragment_loading) {
) { granted: Boolean -> ) { granted: Boolean ->
// If its actually granted, restart the loading process again. // If its actually granted, restart the loading process again.
if (granted) { if (granted) {
wipeViews(binding) returnToLoading(binding)
musicModel.reload() musicModel.reload()
} }
@ -51,13 +50,45 @@ class LoadingFragment : Fragment(R.layout.fragment_loading) {
// --- VIEWMODEL SETUP --- // --- VIEWMODEL SETUP ---
musicModel.response.observe(viewLifecycleOwner) { onMusicLoadResponse(it, binding) } musicModel.response.observe(viewLifecycleOwner) {
musicModel.doReload.observe(viewLifecycleOwner) { onRetry(it, binding) } if (it == MusicLoaderResponse.DONE) {
musicModel.doGrant.observe(viewLifecycleOwner) { onGrant(it, permLauncher) } findNavController().navigate(
LoadingFragmentDirections.actionToMain()
)
} else {
binding.loadingErrorText.text =
if (it == MusicLoaderResponse.NO_MUSIC)
getString(R.string.error_no_music)
else
getString(R.string.error_music_load_failed)
// If the response wasn't a success, then show the specific error message
// depending on which error response was given, along with a retry button
showError(binding)
binding.loadingRetryButton.visibility = View.VISIBLE
}
}
musicModel.doReload.observe(viewLifecycleOwner) {
if (it) {
returnToLoading(binding)
musicModel.doneWithReload()
}
}
musicModel.doGrant.observe(viewLifecycleOwner) {
if (it) {
permLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
musicModel.doneWithGrant()
}
}
// Force an error screen if the permissions are denied or the prompt needs to be shown. // Force an error screen if the permissions are denied or the prompt needs to be shown.
if (checkPerms()) { if (checkPerms()) {
onNoPerms(binding) showError(binding)
binding.loadingGrantButton.visibility = View.VISIBLE
binding.loadingErrorText.text = getString(R.string.error_no_perms)
} else { } else {
musicModel.go() musicModel.go()
} }
@ -78,61 +109,15 @@ class LoadingFragment : Fragment(R.layout.fragment_loading) {
) == PackageManager.PERMISSION_DENIED ) == PackageManager.PERMISSION_DENIED
} }
private fun onMusicLoadResponse( // Remove the loading indicator and show the error groups
response: MusicLoaderResponse?, private fun showError(binding: FragmentLoadingBinding) {
binding: FragmentLoadingBinding
) {
if (response == MusicLoaderResponse.DONE) {
findNavController().navigate(
LoadingFragmentDirections.actionToMain()
)
} else {
binding.loadingErrorText.text =
if (response == MusicLoaderResponse.NO_MUSIC)
getString(R.string.error_no_music)
else
getString(R.string.error_music_load_failed)
// If the response wasn't a success, then show the specific error message
// depending on which error response was given, along with a retry button
binding.loadingBar.visibility = View.GONE
binding.loadingErrorText.visibility = View.VISIBLE
binding.loadingErrorIcon.visibility = View.VISIBLE
binding.loadingRetryButton.visibility = View.VISIBLE
}
}
private fun onNoPerms(binding: FragmentLoadingBinding) {
// If there are no perms, switch out the view elements as if an error screen was being
// shown, but show the label that Auxio needs to read external storage to function,
// along with a GRANT button
binding.loadingBar.visibility = View.GONE binding.loadingBar.visibility = View.GONE
binding.loadingErrorIcon.visibility = View.VISIBLE binding.loadingErrorIcon.visibility = View.VISIBLE
binding.loadingGrantButton.visibility = View.VISIBLE
binding.loadingErrorText.visibility = View.VISIBLE binding.loadingErrorText.visibility = View.VISIBLE
binding.loadingErrorText.text = getString(R.string.error_no_perms)
}
private fun onRetry(retry: Boolean, binding: FragmentLoadingBinding) {
if (retry) {
wipeViews(binding)
musicModel.doneWithReload()
}
}
private fun onGrant(grant: Boolean, permLauncher: ActivityResultLauncher<String>) {
if (grant) {
permLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
musicModel.doneWithGrant()
}
} }
// Wipe views and switch back to the plain ProgressBar // Wipe views and switch back to the plain ProgressBar
private fun wipeViews(binding: FragmentLoadingBinding) { private fun returnToLoading(binding: FragmentLoadingBinding) {
binding.loadingBar.visibility = View.VISIBLE binding.loadingBar.visibility = View.VISIBLE
binding.loadingErrorText.visibility = View.GONE binding.loadingErrorText.visibility = View.GONE
binding.loadingErrorIcon.visibility = View.GONE binding.loadingErrorIcon.visibility = View.GONE

View file

@ -2,6 +2,10 @@ package org.oxycblt.auxio.music
import android.net.Uri import android.net.Uri
// --- MUSIC MODELS ---
// TODO: Remove parent/child references so that they can be parcelable [Would require genre rework]
// TODO: Dont determine artist/album/song counts on the fly [If possible]
// The base model for all music // The base model for all music
// This is used in a lot of general functions in order to cut down on code // This is used in a lot of general functions in order to cut down on code
sealed class BaseModel { sealed class BaseModel {

View file

@ -17,9 +17,7 @@ import org.oxycblt.auxio.music.processing.MusicLoaderResponse
import org.oxycblt.auxio.music.processing.MusicSorter import org.oxycblt.auxio.music.processing.MusicSorter
// ViewModel for music storage. // ViewModel for music storage.
// FIXME: This system can be improved in multiple ways // TODO: Move genre usage to songs [If there's a way to find songs without a genre]
// - Remove lists/parents from models so that they can be parcelable
// - Move genre usage to songs [If there's a way to find songs without a genre]
class MusicViewModel(private val app: Application) : ViewModel() { class MusicViewModel(private val app: Application) : ViewModel() {
// Coroutine // Coroutine

View file

@ -21,6 +21,8 @@ import java.io.InputStream
const val MOSAIC_BITMAP_SIZE = 512 const val MOSAIC_BITMAP_SIZE = 512
const val MOSAIC_BITMAP_INCREMENT = 256 const val MOSAIC_BITMAP_INCREMENT = 256
// A Fetcher that takes multiple cover uris and turns them into a 2x2 mosaic image.
// TODO: Add 4x4 mosaics?
class MosaicFetcher(private val context: Context) : Fetcher<List<Uri>> { class MosaicFetcher(private val context: Context) : Fetcher<List<Uri>> {
override suspend fun fetch( override suspend fun fetch(
pool: BitmapPool, pool: BitmapPool,
@ -30,8 +32,9 @@ class MosaicFetcher(private val context: Context) : Fetcher<List<Uri>> {
): FetchResult { ): FetchResult {
val streams = mutableListOf<InputStream>() val streams = mutableListOf<InputStream>()
for (uri in data) { // Load the streams.
val stream: InputStream? = context.contentResolver.openInputStream(uri) data.forEach {
val stream: InputStream? = context.contentResolver.openInputStream(it)
if (stream != null) { if (stream != null) {
streams.add(stream) streams.add(stream)
@ -39,7 +42,7 @@ class MosaicFetcher(private val context: Context) : Fetcher<List<Uri>> {
} }
// If so many streams failed that there's not enough images to make a mosaic, then // If so many streams failed that there's not enough images to make a mosaic, then
// just return the first album. // just return the first cover image.
if (streams.size < 4) { if (streams.size < 4) {
streams.forEach { it.close() } streams.forEach { it.close() }

View file

@ -19,6 +19,7 @@ enum class MusicLoaderResponse {
} }
// Class that loads music from the FileSystem. // Class that loads music from the FileSystem.
// TODO: Add custom artist images from the filesystem
class MusicLoader( class MusicLoader(
private val resolver: ContentResolver, private val resolver: ContentResolver,

View file

@ -24,6 +24,8 @@ class CompactPlaybackFragment : Fragment() {
): View? { ): View? {
val binding = FragmentCompactPlaybackBinding.inflate(inflater) val binding = FragmentCompactPlaybackBinding.inflate(inflater)
// --- UI SETUP ---
binding.lifecycleOwner = this binding.lifecycleOwner = this
// Put a placeholder song in the binding & hide the playback fragment initially, // Put a placeholder song in the binding & hide the playback fragment initially,
@ -31,6 +33,9 @@ class CompactPlaybackFragment : Fragment() {
binding.song = musicModel.songs.value!![0] binding.song = musicModel.songs.value!![0]
binding.root.visibility = View.GONE binding.root.visibility = View.GONE
// --- VIEWMODEL SETUP ---
// TODO: Add some kind of animation to when this view becomes visible/invisible.
playbackModel.currentSong.observe(viewLifecycleOwner) { playbackModel.currentSong.observe(viewLifecycleOwner) {
if (it == null) { if (it == null) {
Log.d(this::class.simpleName, "Hiding playback bar due to no song being played.") Log.d(this::class.simpleName, "Hiding playback bar due to no song being played.")

View file

@ -15,7 +15,9 @@ import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.recycler.ClickListener import org.oxycblt.auxio.recycler.ClickListener
// Shared ViewHolders for each ViewModel, providing basic information // Shared ViewHolders for each ViewModel, providing basic information
// All new instances should be created with from(context, listener) instead of direct instantiation. // All new instances should be created with from() instead of direct instantiation.
// TODO: Add indicators to song recycler items when they're being played.
class GenreViewHolder private constructor( class GenreViewHolder private constructor(
listener: ClickListener<Genre>, listener: ClickListener<Genre>,
private val binding: ItemGenreBinding private val binding: ItemGenreBinding
@ -82,6 +84,8 @@ class AlbumViewHolder private constructor(
} }
} }
// TODO: Add indicators to song recycler items when they're being played.
class SongViewHolder private constructor( class SongViewHolder private constructor(
listener: ClickListener<Song>, listener: ClickListener<Song>,
private val binding: ItemSongBinding private val binding: ItemSongBinding

View file

@ -12,7 +12,6 @@ import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.recycler.ClickListener import org.oxycblt.auxio.recycler.ClickListener
import org.oxycblt.auxio.theme.applyDivider import org.oxycblt.auxio.theme.applyDivider
class SongsFragment : Fragment() { class SongsFragment : Fragment() {
private val musicModel: MusicViewModel by activityViewModels { private val musicModel: MusicViewModel by activityViewModels {
MusicViewModel.Factory(requireActivity().application) MusicViewModel.Factory(requireActivity().application)
@ -27,6 +26,9 @@ class SongsFragment : Fragment() {
): View? { ): View? {
val binding = FragmentSongsBinding.inflate(inflater) val binding = FragmentSongsBinding.inflate(inflater)
// TODO: Add option to search songs if LibraryFragment isn't enabled
// TODO: Maybe add fast scrolling or sorting
// --- UI SETUP --- // --- UI SETUP ---
binding.songRecycler.apply { binding.songRecycler.apply {

View file

@ -5,5 +5,7 @@
android:name="androidx.navigation.fragment.NavHostFragment" android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true" app:defaultNavHost="true"
app:navGraph="@navigation/nav_main" app:navGraph="@navigation/nav_main"
tools:context=".MainActivity"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android" /> xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" />

View file

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android" <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools"
tools:context=".detail.AlbumDetailFragment">
<data> <data>

View file

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android" <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools"
tools:context=".detail.ArtistDetailFragment">
<data> <data>

View file

@ -67,12 +67,12 @@
android:singleLine="true" android:singleLine="true"
android:marqueeRepeatLimit="marquee_forever" android:marqueeRepeatLimit="marquee_forever"
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption" android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
android:text="@{@string/format_info(song.album.name, song.album.artist.name)}" android:text="@{@string/format_info(song.album.artist.name, song.album.name)}"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/album_cover" app:layout_constraintStart_toEndOf="@+id/album_cover"
app:layout_constraintTop_toBottomOf="@+id/song_name" app:layout_constraintTop_toBottomOf="@+id/song_name"
tools:text="Artist / Album" /> tools:text="Artist Name / Album Name" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</layout> </layout>

View file

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android" <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools"
tools:context=".detail.GenreDetailFragment">
<data> <data>

View file

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android" <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools"
tools:context=".library.LibraryFragment">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools" <layout xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"> xmlns:android="http://schemas.android.com/apk/res/android"
tools:context=".loading.LoadingFragment">
<data> <data>

View file

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android" <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainFragment">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -16,7 +17,7 @@
android:layout_weight="1" /> android:layout_weight="1" />
<androidx.fragment.app.FragmentContainerView <androidx.fragment.app.FragmentContainerView
android:id="@+id/playback_bar" android:id="@+id/compact_playback"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:name="org.oxycblt.auxio.playback.CompactPlaybackFragment" android:name="org.oxycblt.auxio.playback.CompactPlaybackFragment"

View file

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android" <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools"
tools:context=".songs.SongsFragment">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto" <layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools"
tools:context=".recycler.viewholders.AlbumViewHolder">
<data> <data>
@ -58,6 +59,6 @@
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/album_cover" app:layout_constraintStart_toEndOf="@+id/album_cover"
app:layout_constraintTop_toBottomOf="@+id/album_name" app:layout_constraintTop_toBottomOf="@+id/album_name"
tools:text="10 Songs" /> tools:text="Artist / 10 Songs" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</layout> </layout>

View file

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto" <layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools"
tools:context=".detail.adapters.DetailSongAdapter.ViewHolder">
<data> <data>

View file

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto" <layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools"
tools:context=".recycler.viewholders.ArtistViewHolder">
<data> <data>

View file

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto" <layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools"
tools:context=".detail.adapters.DetailAlbumAdapter.ViewHolder">
<data> <data>

View file

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto" <layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools"
tools:context=".recycler.viewholders.GenreViewHolder">
<data> <data>

View file

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto" <layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools"
tools:context=".detail.adapters.DetailArtistAdapter.ViewHolder">
<data> <data>

View file

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android" <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".recycler.viewholders.HeaderViewHolder">
<data> <data>

View file

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto" <layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools"
tools:context=".recycler.viewholders.SongViewHolder">
<data> <data>

View file

@ -16,7 +16,7 @@
<dimen name="cover_size_compact">44dp</dimen> <dimen name="cover_size_compact">44dp</dimen>
<dimen name="cover_size_normal">56dp</dimen> <dimen name="cover_size_normal">56dp</dimen>
<dimen name="cover_size_large">68dp</dimen> <dimen name="cover_size_large">68dp</dimen>
<dimen name="cover_size_huge">230dp</dimen> <dimen name="cover_size_huge">250dp</dimen>
<dimen name="track_number_width">32dp</dimen> <dimen name="track_number_width">32dp</dimen>
<dimen name="track_number_text_size_max">20sp</dimen> <dimen name="track_number_text_size_max">20sp</dimen>