diff --git a/app/build.gradle b/app/build.gradle index 5e0ac84e1..281bc881e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,7 +1,7 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' -apply plugin: "androidx.navigation.safeargs" +apply plugin: 'androidx.navigation.safeargs' android { compileSdkVersion 30 @@ -22,6 +22,10 @@ android { } buildTypes { + debug { + applicationIdSuffix = '.debug' + versionNameSuffix = "-DEBUG" + } release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' diff --git a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt index 018c263c9..7d56901bb 100644 --- a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt +++ b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt @@ -11,9 +11,8 @@ import org.oxycblt.auxio.ui.accent // FIXME: Fix bug where fast navigation will break the animations and // lead to nothing being displayed [Possibly Un-fixable] -// FIXME: Compat issues with Versions 5/6 that cause recyclerview -// dividers not to show and for progress bars to look wonky -// FIXME: Navigation memory leak that is really confusing +// TODO: Landscape UI layouts +// FIXME: Compat issue with Versions 5 that leads to progress bar looking off class MainActivity : AppCompatActivity(R.layout.activity_main) { override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? { diff --git a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt index bcf1e6321..56850aa02 100644 --- a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt @@ -59,9 +59,9 @@ class MainFragment : Fragment() { ) val navController = ( - childFragmentManager.findFragmentById(R.id.explore_nav_host) - as NavHostFragment? - )?.findNavController() + childFragmentManager.findFragmentById(R.id.explore_nav_host) + as NavHostFragment? + )?.findNavController() // --- UI SETUP --- @@ -97,11 +97,11 @@ class MainFragment : Fragment() { if (it) { if (binding.navBar.selectedItemId != R.id.library_fragment || ( - navController!!.currentDestination?.id == R.id.album_detail_fragment && - detailModel.currentAlbum.value == null || - detailModel.currentAlbum.value?.id - != playbackModel.song.value!!.album.id - ) || + navController!!.currentDestination?.id == R.id.album_detail_fragment && + detailModel.currentAlbum.value == null || + detailModel.currentAlbum.value?.id + != playbackModel.song.value!!.album.id + ) || navController.currentDestination?.id == R.id.artist_detail_fragment || navController.currentDestination?.id == R.id.genre_detail_fragment ) { diff --git a/app/src/main/java/org/oxycblt/auxio/database/PlaybackStateDatabase.kt b/app/src/main/java/org/oxycblt/auxio/database/PlaybackStateDatabase.kt index 8f4daba36..65a0269f5 100644 --- a/app/src/main/java/org/oxycblt/auxio/database/PlaybackStateDatabase.kt +++ b/app/src/main/java/org/oxycblt/auxio/database/PlaybackStateDatabase.kt @@ -15,6 +15,7 @@ import android.util.Log */ class PlaybackStateDatabase(context: Context) : SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) { + override fun onCreate(db: SQLiteDatabase) { createTable(db, TABLE_NAME_STATE) createTable(db, TABLE_NAME_QUEUE) @@ -111,7 +112,7 @@ class PlaybackStateDatabase(context: Context) : /** * Read the stored [PlaybackState] from the database, if there is one. - * @return The stored [PlaybackState], null if there isnt one,. + * @return The stored [PlaybackState], null if there isn't one,. * @author OxygenCobalt */ fun readState(): PlaybackState? { @@ -121,7 +122,11 @@ class PlaybackStateDatabase(context: Context) : var stateCursor: Cursor? = null try { - stateCursor = database.query(TABLE_NAME_STATE, null, null, null, null, null, null) + stateCursor = database.query( + TABLE_NAME_STATE, + null, null, null, + null, null, null + ) stateCursor?.use { cursor -> // Don't bother if the cursor [and therefore database] has nothing in it. @@ -227,7 +232,10 @@ class PlaybackStateDatabase(context: Context) : var queueCursor: Cursor? = null try { - queueCursor = database.query(TABLE_NAME_QUEUE, null, null, null, null, null, null) + queueCursor = database.query( + TABLE_NAME_QUEUE, null, null, + null, null, null, null + ) queueCursor?.use { cursor -> if (cursor.count == 0) return@use diff --git a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt index 2f5643c0b..12301aee9 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt @@ -131,7 +131,7 @@ class AlbumDetailFragment : DetailFragment() { binding.albumSongRecycler.post { // Only scroll after UI creation val y = binding.albumSongRecycler.y + - binding.albumSongRecycler.getChildAt(pos).y + binding.albumSongRecycler.getChildAt(pos).y binding.nestedScroll.smoothScrollBy(0, y.toInt()) } diff --git a/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt index eba09351b..1338e0bba 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt @@ -11,7 +11,8 @@ import androidx.navigation.fragment.findNavController * A Base [Fragment] implementing a [OnBackPressedCallback] so that Auxio will navigate upwards * instead of out of the app if a Detail Fragment is currently open. Also carries the * multi-navigation fix. - * // TODO: Merge headers with recyclerview [if possible] + * TODO: Implement a system where the Toolbar will update with some info when + * the main detail header is obscured. * @author OxygenCobalt */ abstract class DetailFragment : Fragment() { @@ -32,6 +33,7 @@ abstract class DetailFragment : Fragment() { super.onPause() callback.isEnabled = false } + private val callback = object : OnBackPressedCallback(false) { override fun handleOnBackPressed() { diff --git a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt index 57f8cb282..9c064c0bd 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt @@ -9,9 +9,6 @@ import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.recycler.SortMode // ViewModel for the Detail Fragments. -// TODO: -// - Implement a system where the Toolbar will update with some info when -// the main detail header is obscured. class DetailViewModel : ViewModel() { private var mIsNavigating = false val isNavigating: Boolean get() = mIsNavigating diff --git a/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailAlbumAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailAlbumAdapter.kt index 714863700..2401b29df 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailAlbumAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailAlbumAdapter.kt @@ -4,6 +4,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView import org.oxycblt.auxio.databinding.ItemArtistAlbumBinding import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.recycler.DiffCallback @@ -11,17 +12,19 @@ import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder class DetailAlbumAdapter( private val doOnClick: (data: Album) -> Unit, - private val doOnLongClick: (data: Album, view: View) -> Unit -) : ListAdapter(DiffCallback()) { + private val doOnLongClick: (data: Album, view: View) -> Unit, +) : ListAdapter(DiffCallback()) { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AlbumViewHolder { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return AlbumViewHolder( ItemArtistAlbumBinding.inflate(LayoutInflater.from(parent.context)) ) } - override fun onBindViewHolder(holder: AlbumViewHolder, position: Int) { - holder.bind(getItem(position)) + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (val item = getItem(position)) { + is Album -> (holder as AlbumViewHolder).bind(item) + } } // Generic ViewHolder for a detail album diff --git a/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt b/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt index 0ed6609ea..fb57cf0ec 100644 --- a/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt @@ -44,7 +44,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { val binding = FragmentLibraryBinding.inflate(inflater) val musicStore = MusicStore.getInstance() diff --git a/app/src/main/java/org/oxycblt/auxio/music/coil/MosaicFetcher.kt b/app/src/main/java/org/oxycblt/auxio/music/coil/MosaicFetcher.kt index fe13a2470..53b814d4c 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/coil/MosaicFetcher.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/coil/MosaicFetcher.kt @@ -107,5 +107,5 @@ class MosaicFetcher(private val context: Context) : Fetcher> { ) } - override fun key(data: List): String? = data.toString() + override fun key(data: List): String = data.toString() } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/CompactPlaybackFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/CompactPlaybackFragment.kt index fed2de892..c1c779158 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/CompactPlaybackFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/CompactPlaybackFragment.kt @@ -6,7 +6,7 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.ImageView +import android.widget.ImageButton import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels @@ -27,8 +27,6 @@ import org.oxycblt.auxio.ui.createToast class CompactPlaybackFragment : Fragment() { private val playbackModel: PlaybackViewModel by activityViewModels() - private var playbackControls: ImageView? = null - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -77,9 +75,6 @@ class CompactPlaybackFragment : Fragment() { binding.playbackProgress.progress = it } - // Use the playback control ImageView for later. - playbackControls = binding.playbackControls - Log.d(this::class.simpleName, "Fragment Created") return binding.root @@ -90,6 +85,10 @@ class CompactPlaybackFragment : Fragment() { playbackModel.disableAnimation() + // Use the caveman method of getting a view as storing the binding will cause a memory + // leak. + val playbackControls = requireView().findViewById(R.id.playback_controls) + // Observe the changes to isPlaying for val iconPauseToPlay = ContextCompat.getDrawable( requireContext(), R.drawable.ic_pause_to_play @@ -122,10 +121,4 @@ class CompactPlaybackFragment : Fragment() { } } } - - override fun onDestroy() { - super.onDestroy() - - playbackControls = null - } } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt index eec28b562..2ae6965f3 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt @@ -32,7 +32,7 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { val binding = FragmentPlaybackBinding.inflate(inflater) // TODO: Add a swipe-to-next-track function using a ViewPager @@ -84,7 +84,6 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener { } // Make marquee scroll work - // TODO: Add nav here as well binding.playbackSong.isSelected = true binding.playbackSeekBar.setOnSeekBarChangeListener(this) diff --git a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt index a76848e03..e7f22c234 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt @@ -1,6 +1,7 @@ package org.oxycblt.auxio.playback.queue import android.annotation.SuppressLint +import android.content.Context import android.util.Log import android.view.LayoutInflater import android.view.MotionEvent @@ -54,7 +55,7 @@ class QueueAdapter( ItemQueueSongBinding.inflate(LayoutInflater.from(parent.context)) ) USER_QUEUE_HEADER_ITEM_tYPE -> UserQueueHeaderViewHolder( - ItemActionHeaderBinding.inflate(LayoutInflater.from(parent.context)) + parent.context, ItemActionHeaderBinding.inflate(LayoutInflater.from(parent.context)) ) else -> error("Someone messed with the ViewHolder item types.") } @@ -97,6 +98,7 @@ class QueueAdapter( // Check for two things: // If the data from the next queue is now entirely empty [Signified by a header at the end] // Or if the data from the last queue is now entirely empty [Signified by there being 2 headers with no items in between] + // If so, remove that item and the removed item in a range. Otherwise just remove the item. if (data[data.lastIndex] is Header) { val lastIndex = data.lastIndex @@ -146,8 +148,16 @@ class QueueAdapter( } inner class UserQueueHeaderViewHolder( + context: Context, private val binding: ItemActionHeaderBinding ) : BaseViewHolder
(binding, null, null) { + + init { + binding.headerButton.contentDescription = context.getString( + R.string.description_clear_user_queue + ) + } + override fun onBind(data: Header) { binding.header = data binding.headerButton.apply { diff --git a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt index fd2c8e543..10d604ab7 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt @@ -28,7 +28,7 @@ class QueueFragment : Fragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { val binding = FragmentQueueBinding.inflate(inflater) val callback = QueueDragCallback(playbackModel) diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt index de877c4e9..6278454df 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt @@ -560,12 +560,12 @@ class PlaybackStateManager private constructor() { // Traverse albums and then album songs instead of just the songs, as its faster. musicStore.albums.find { it.id == item.albumId } ?.songs?.find { it.id == item.songId }?.let { - if (item.isUserQueue) { - mUserQueue.add(it) - } else { - mQueue.add(it) + if (item.isUserQueue) { + mUserQueue.add(it) + } else { + mQueue.add(it) + } } - } } // Get a more accurate index [At least if were not in the user queue] diff --git a/app/src/main/java/org/oxycblt/auxio/recycler/viewholders/ModelHolders.kt b/app/src/main/java/org/oxycblt/auxio/recycler/viewholders/ModelHolders.kt index dc47b4edd..4bf7c8905 100644 --- a/app/src/main/java/org/oxycblt/auxio/recycler/viewholders/ModelHolders.kt +++ b/app/src/main/java/org/oxycblt/auxio/recycler/viewholders/ModelHolders.kt @@ -3,8 +3,6 @@ package org.oxycblt.auxio.recycler.viewholders import android.content.Context import android.view.LayoutInflater import android.view.View -import androidx.annotation.DrawableRes -import org.oxycblt.auxio.databinding.ItemActionHeaderBinding import org.oxycblt.auxio.databinding.ItemAlbumBinding import org.oxycblt.auxio.databinding.ItemArtistBinding import org.oxycblt.auxio.databinding.ItemGenreBinding @@ -149,20 +147,3 @@ class HeaderViewHolder( } } } - -abstract class ActionHeaderViewHolder( - protected val binding: ItemActionHeaderBinding, - @DrawableRes private val iconRes: Int -) : BaseViewHolder
(binding, null, null) { - override fun onBind(data: Header) { - binding.header = data - binding.headerButton.apply { - setImageResource(iconRes) - setOnClickListener { - onActionClick() - } - } - } - - abstract fun onActionClick() -} diff --git a/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt b/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt index facea6e72..400730cd7 100644 --- a/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt @@ -22,7 +22,7 @@ class SongsFragment : Fragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { val binding = FragmentSongsBinding.inflate(inflater) val musicStore = MusicStore.getInstance() diff --git a/app/src/main/res/drawable/ic_pause_to_play.xml b/app/src/main/res/drawable/ic_pause_to_play.xml index cace5f88f..aec896ba4 100644 --- a/app/src/main/res/drawable/ic_pause_to_play.xml +++ b/app/src/main/res/drawable/ic_pause_to_play.xml @@ -1,3 +1,7 @@ + diff --git a/app/src/main/res/drawable/ic_play_large.xml b/app/src/main/res/drawable/ic_play_large.xml index e316addf9..2f0b72806 100644 --- a/app/src/main/res/drawable/ic_play_large.xml +++ b/app/src/main/res/drawable/ic_play_large.xml @@ -1,3 +1,4 @@ + diff --git a/app/src/main/res/drawable/ui_header_dividers.xml b/app/src/main/res/drawable/ui_header_dividers.xml index 8a036f39b..d5470056f 100644 --- a/app/src/main/res/drawable/ui_header_dividers.xml +++ b/app/src/main/res/drawable/ui_header_dividers.xml @@ -9,7 +9,7 @@ https://stackoverflow.com/a/61157571/14143986 android:top="-2dp"> diff --git a/app/src/main/res/drawable/ui_small_unbounded_ripple.xml b/app/src/main/res/drawable/ui_small_unbounded_ripple.xml new file mode 100644 index 000000000..c76c0abd6 --- /dev/null +++ b/app/src/main/res/drawable/ui_small_unbounded_ripple.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_album_detail.xml b/app/src/main/res/layout/fragment_album_detail.xml index 3c7862c33..8f1ede2a6 100644 --- a/app/src/main/res/layout/fragment_album_detail.xml +++ b/app/src/main/res/layout/fragment_album_detail.xml @@ -111,23 +111,23 @@ android:id="@+id/album_song_header" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_marginTop="@dimen/padding_medium" + android:layout_marginTop="@dimen/margin_medium" android:background="@drawable/ui_header_dividers" - android:textColor="?android:attr/textColorPrimary" android:fontFamily="@font/inter_semibold" android:paddingStart="@dimen/padding_medium" android:paddingTop="@dimen/padding_small" android:paddingEnd="@dimen/padding_small" android:paddingBottom="@dimen/padding_small" android:text="@string/label_songs" + android:textColor="?android:attr/textColorPrimary" android:textSize="19sp" - app:layout_constraintTop_toBottomOf="@+id/album_details" /> + app:layout_constraintTop_toBottomOf="@+id/album_details" + app:layout_constraintStart_toStartOf="parent" /> @@ -120,7 +120,6 @@ android:id="@+id/artist_sort_button" android:layout_width="wrap_content" android:layout_height="0dp" - android:layout_marginTop="@dimen/margin_medium" android:background="@drawable/ui_header_dividers" android:contentDescription="@string/description_sort_button" android:onClick="@{() -> detailModel.incrementArtistSortMode()}" @@ -128,9 +127,9 @@ android:paddingTop="@dimen/padding_small" android:paddingEnd="@dimen/margin_medium" android:paddingBottom="@dimen/padding_small" - app:layout_constraintBottom_toTopOf="@+id/artist_album_recycler" + app:layout_constraintBottom_toBottomOf="@+id/artist_album_header" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@+id/artist_counts" + app:layout_constraintTop_toTopOf="@+id/artist_album_header" tools:src="@drawable/ic_sort_numeric_down" /> + app:layout_constraintBottom_toBottomOf="@+id/genre_artist_header" + app:layout_constraintTop_toTopOf="@+id/genre_artist_header" + tools:layout_editor_absoluteX="355dp" + tools:src="@drawable/ic_sort_alpha_down" /> + android:layout_height="wrap_content" + android:background="@drawable/ui_header_dividers"> + app:layout_constraintStart_toStartOf="parent" /> + app:layout_constraintTop_toTopOf="@+id/header_title" + tools:src="@drawable/ic_sort_numeric_down" /> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 579b2c56b..ca35f7d2b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -25,7 +25,6 @@ Add to queue Added to queue Next in Queue - Clear queue Music Playback The music playback service for Auxio. @@ -50,6 +49,7 @@ Default Sort Order Sort from A to Z Sort from Z to A + Clear queue Play Pause Skip to next song