diff --git a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt index 34d86020f..e3a247c2c 100644 --- a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt @@ -14,8 +14,6 @@ import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.findNavController import org.oxycblt.auxio.databinding.FragmentMainBinding import org.oxycblt.auxio.detail.DetailViewModel -import org.oxycblt.auxio.music.Album -import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.playback.PlaybackViewModel @@ -95,12 +93,10 @@ class MainFragment : Fragment() { if (it != null && navController != null) { val curDest = navController.currentDestination?.id - val isOk = when (it) { - is Song -> (detailModel.currentAlbum.value?.id == it.album.id) magic (curDest != R.id.album_detail_fragment) - is Album -> (detailModel.currentAlbum.value?.id == it.id) magic (curDest != R.id.album_detail_fragment) - is Artist -> (detailModel.currentArtist.value?.id == it.id) magic (curDest != R.id.artist_detail_fragment) + var isOk = false - else -> false + if (curDest == R.id.songs_fragment || curDest == R.id.settings_fragment) { + isOk = true } if (isOk) { @@ -116,17 +112,6 @@ class MainFragment : Fragment() { return binding.root } - /** - * Magic boolean logic that gets navigation working. - * true true -> true | - * true false -> false | - * false true -> true | - * false false -> false | - */ - private infix fun Boolean.magic(other: Boolean): Boolean { - return if (!this && !other) false else !(this && !other) - } - /** * Custom navigator code that has proper animations, unlike BottomNavigationView.setupWithNavController(). */ 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 78f7ed98e..cd85e2d64 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt @@ -10,6 +10,7 @@ import org.oxycblt.auxio.R import org.oxycblt.auxio.detail.adapters.AlbumDetailAdapter import org.oxycblt.auxio.logD import org.oxycblt.auxio.music.Album +import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.BaseModel import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.music.Song @@ -82,28 +83,41 @@ class AlbumDetailFragment : DetailFragment() { detailAdapter.submitList(data) } - detailModel.doneWithNavToParent() - - detailModel.navToParent.observe(viewLifecycleOwner) { - if (it) { - findNavController().navigate( - AlbumDetailFragmentDirections.actionShowParentArtist( - detailModel.currentAlbum.value!!.artist.id - ) - ) - - detailModel.doneWithNavToParent() - } - } - detailModel.navToItem.observe(viewLifecycleOwner) { if (it != null) { - if (it is Song) { - scrollToPlayingItem() - } + logD(it.name) + when (it) { + is Song -> { + if (detailModel.currentAlbum.value!!.id == it.album.id) { + scrollToItem(it.id) - if (it is Album && it.id == detailModel.currentAlbum.value!!.id) { - detailModel.doneWithNavToItem() + detailModel.doneWithNavToItem() + } else { + findNavController().navigate( + AlbumDetailFragmentDirections.actionShowAlbum(it.album.id) + ) + } + } + + is Album -> { + if (detailModel.currentAlbum.value!!.id == it.id) { + binding.detailRecycler.scrollToPosition(0) + detailModel.doneWithNavToItem() + } else { + findNavController().navigate( + AlbumDetailFragmentDirections.actionShowAlbum(it.id) + ) + } + } + + is Artist -> { + logD("Hello?") + findNavController().navigate( + AlbumDetailFragmentDirections.actionShowArtist(it.id) + ) + } + + else -> {} } } } @@ -144,14 +158,11 @@ class AlbumDetailFragment : DetailFragment() { } } - /** - * Scroll to the currently playing item. - */ - private fun scrollToPlayingItem() { + private fun scrollToItem(id: Long) { // Calculate where the item for the currently played song is val pos = detailModel.albumSortMode.value!!.getSortedSongList( detailModel.currentAlbum.value!!.songs - ).indexOf(playbackModel.song.value) + ).indexOfFirst { it.id == id } if (pos != -1) { binding.detailRecycler.post { @@ -167,8 +178,6 @@ class AlbumDetailFragment : DetailFragment() { binding.detailAppbar.isLifted = true } } - - detailModel.doneWithNavToItem() } } } diff --git a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt index 7e6a06583..8f927b42b 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt @@ -12,6 +12,7 @@ import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.BaseModel import org.oxycblt.auxio.music.MusicStore +import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.playback.state.PlaybackMode import org.oxycblt.auxio.ui.ActionMenu import org.oxycblt.auxio.ui.requireCompatActivity @@ -77,8 +78,22 @@ class ArtistDetailFragment : DetailFragment() { } detailModel.navToItem.observe(viewLifecycleOwner) { - if (it != null && it is Artist) { - detailModel.doneWithNavToItem() + if (it != null) { + if (it is Artist) { + if (it.id == detailModel.currentArtist.value!!.id) { + detailModel.doneWithNavToItem() + } else { + findNavController().navigate( + ArtistDetailFragmentDirections.actionShowArtist(it.id) + ) + } + } else { + val albumId = if (it is Song) it.album.id else it.id + + findNavController().navigate( + ArtistDetailFragmentDirections.actionShowAlbum(albumId) + ) + } } } 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 3ce15f393..d51d0caf9 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt @@ -13,6 +13,7 @@ import androidx.recyclerview.widget.RecyclerView import org.oxycblt.auxio.databinding.FragmentDetailBinding import org.oxycblt.auxio.music.BaseModel import org.oxycblt.auxio.playback.PlaybackViewModel +import org.oxycblt.auxio.ui.fixAnimationInfoMemoryLeak import org.oxycblt.auxio.ui.isLandscape import org.oxycblt.auxio.ui.memberBinding @@ -44,6 +45,12 @@ abstract class DetailFragment : Fragment() { callback.isEnabled = false } + override fun onDestroyView() { + super.onDestroyView() + + fixAnimationInfoMemoryLeak() + } + /** * Shortcut method for doing setup of the detail toolbar. */ 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 e2ce1a406..3905035fa 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt @@ -36,16 +36,10 @@ class DetailViewModel : ViewModel() { private val mCurrentAlbum = MutableLiveData() val currentAlbum: LiveData get() = mCurrentAlbum - // Navigation flags + // Primary navigation flag. private val mNavToItem = MutableLiveData() val navToItem: LiveData get() = mNavToItem - private val mNavToParent = MutableLiveData() - val navToParent: LiveData get() = mNavToParent - - private val mNavToChild = MutableLiveData() - val navToChild: LiveData get() = mNavToChild - /** * Update the current navigation status * @param value Whether the current [DetailFragment] is navigating or not. @@ -113,23 +107,4 @@ class DetailViewModel : ViewModel() { fun doneWithNavToItem() { mNavToItem.value = null } - - /** Mark that parent navigation should occur */ - fun navToParent() { - mNavToParent.value = true - } - - /** Mark that the UI is done with the parent navigation */ - fun doneWithNavToParent() { - mNavToParent.value = false - } - - /** Navigate to some child item (Primarily used by GenreDetailFragment) */ - fun navToChild(child: BaseModel) { - mNavToChild.value = child - } - - fun doneWithNavToChild() { - mNavToChild.value = null - } } diff --git a/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt index 4f97f3baf..b7229afd2 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt @@ -12,6 +12,7 @@ import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.BaseModel import org.oxycblt.auxio.music.MusicStore +import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.playback.state.PlaybackMode import org.oxycblt.auxio.ui.ActionMenu import org.oxycblt.auxio.ui.requireCompatActivity @@ -69,19 +70,23 @@ class GenreDetailFragment : DetailFragment() { detailAdapter.submitList(data) } - detailModel.navToChild.observe(viewLifecycleOwner) { + detailModel.navToItem.observe(viewLifecycleOwner) { if (it != null) { - if (it is Artist) { - findNavController().navigate( - GenreDetailFragmentDirections.actionGoArtist(it.id) + when (it) { + is Artist -> findNavController().navigate( + GenreDetailFragmentDirections.actionShowArtist(it.id) ) - } else if (it is Album) { - findNavController().navigate( - GenreDetailFragmentDirections.actionGoAlbum(it.id) - ) - } - detailModel.doneWithNavToChild() + is Album -> findNavController().navigate( + GenreDetailFragmentDirections.actionShowAlbum(it.id) + ) + + is Song -> findNavController().navigate( + GenreDetailFragmentDirections.actionShowAlbum(it.album.id) + ) + + else -> {} + } } } diff --git a/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt b/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt index 2598c11d4..e8c56bd7f 100644 --- a/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt @@ -14,8 +14,8 @@ import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.GridLayoutManager import org.oxycblt.auxio.R import org.oxycblt.auxio.databinding.FragmentSearchBinding +import org.oxycblt.auxio.detail.DetailViewModel import org.oxycblt.auxio.logD -import org.oxycblt.auxio.logE import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.BaseModel @@ -26,6 +26,7 @@ import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.settings.SettingsManager import org.oxycblt.auxio.ui.ActionMenu import org.oxycblt.auxio.ui.accent +import org.oxycblt.auxio.ui.fixAnimationInfoMemoryLeak import org.oxycblt.auxio.ui.getLandscapeSpans import org.oxycblt.auxio.ui.isLandscape import org.oxycblt.auxio.ui.requireCompatActivity @@ -40,6 +41,7 @@ class SearchFragment : Fragment() { // SearchViewModel only scoped to this Fragment private val searchModel: SearchViewModel by viewModels() private val playbackModel: PlaybackViewModel by activityViewModels() + private val detailModel: DetailViewModel by activityViewModels() override fun onCreateView( inflater: LayoutInflater, @@ -113,25 +115,27 @@ class SearchFragment : Fragment() { } } + detailModel.navToItem.observe(viewLifecycleOwner) { + if (it != null) { + findNavController().navigate( + when (it) { + is Song -> SearchFragmentDirections.actionShowAlbum(it.album.id) + is Album -> SearchFragmentDirections.actionShowAlbum(it.id) + is Artist -> SearchFragmentDirections.actionShowArtist(it.id) + + else -> return@observe + } + ) + } + } + return binding.root } override fun onDestroyView() { super.onDestroyView() - try { - // Use reflection to fix a memory leak in the fragment source code that occurs - // from leaving an EditText focused when exiting the view. - // I cant believe I have to do this. - Fragment::class.java.getDeclaredMethod("setFocusedView", View::class.java).apply { - isAccessible = true - invoke(this@SearchFragment, null) - } - } catch (e: Exception) { - logE("Hacky reflection leak fix failed.") - - e.printStackTrace() - } + fixAnimationInfoMemoryLeak() } override fun onResume() { diff --git a/app/src/main/java/org/oxycblt/auxio/ui/ActionMenu.kt b/app/src/main/java/org/oxycblt/auxio/ui/ActionMenu.kt index 1be12518e..35aa17139 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/ActionMenu.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/ActionMenu.kt @@ -137,13 +137,13 @@ class ActionMenu( R.id.action_go_album -> { if (data is Song) { - determineWhereToNavWithSong(data.album) + detailModel.navToItem(data.album) } } R.id.action_go_artist -> { if (data is Song) { - determineWhereToNavWithSong(data.album.artist) + detailModel.navToItem(data.album.artist) } else if (data is Album) { detailModel.navToItem(data.artist) } @@ -151,14 +151,6 @@ class ActionMenu( } } - private fun determineWhereToNavWithSong(parent: BaseModel) { - when (flag) { - FLAG_NONE -> detailModel.navToItem(parent) - FLAG_IN_ALBUM -> detailModel.navToParent() - FLAG_IN_GENRE -> detailModel.navToChild(parent) - } - } - companion object { /** No Flags **/ const val FLAG_NONE = -1 diff --git a/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt b/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt index beb1e40c4..c326d7ae5 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt @@ -9,6 +9,7 @@ import android.graphics.Point import android.os.Build import android.text.Spanned import android.util.DisplayMetrics +import android.view.View import android.view.WindowManager import android.widget.ImageButton import android.widget.TextView @@ -20,6 +21,7 @@ import androidx.core.text.HtmlCompat import androidx.fragment.app.Fragment import com.google.android.material.button.MaterialButton import org.oxycblt.auxio.R +import org.oxycblt.auxio.logE // --- VIEW CONFIGURATION --- @@ -139,6 +141,7 @@ fun Activity.isIrregularLandscape(): Boolean { * Check if the system bars are on the bottom. * @return If the system bars are on the bottom, false if no. */ +@Suppress("DEPRECATION") private fun isSystemBarOnBottom(activity: Activity): Boolean { val realPoint = Point() val metrics = DisplayMetrics() @@ -170,3 +173,21 @@ private fun isSystemBarOnBottom(activity: Activity): Boolean { return (!canMove || width < height) } + +// --- HACKY NIGHTMARES --- + +/** + * Use R E F L E C T I O N to fix a memory leak where mAnimationInfo will keep a reference to + * its focused view. + * I can't believe I have to do this. + */ +fun Fragment.fixAnimationInfoMemoryLeak() { + try { + Fragment::class.java.getDeclaredMethod("setFocusedView", View::class.java).let { + it.isAccessible = true + it.invoke(this, null) + } + } catch (e: Exception) { + logE("mAnimationInfo leak fix failed.") + } +} diff --git a/app/src/main/res/layout-land/fragment_playback.xml b/app/src/main/res/layout-land/fragment_playback.xml index 39f5a5d96..177467c74 100644 --- a/app/src/main/res/layout-land/fragment_playback.xml +++ b/app/src/main/res/layout-land/fragment_playback.xml @@ -24,7 +24,6 @@ android:id="@+id/playback_layout" android:layout_width="match_parent" android:layout_height="match_parent" - android:animateLayoutChanges="true" android:background="@color/background" android:fitsSystemWindows="true"> diff --git a/app/src/main/res/layout-land/item_album_header.xml b/app/src/main/res/layout-land/item_album_header.xml index ecd272f17..f0e59a7dc 100644 --- a/app/src/main/res/layout-land/item_album_header.xml +++ b/app/src/main/res/layout-land/item_album_header.xml @@ -59,7 +59,7 @@ android:background="@drawable/ui_ripple" android:clickable="true" android:focusable="true" - android:onClick="@{() -> detailModel.navToParent()}" + android:onClick="@{() -> detailModel.navToItem(album.artist)}" android:text="@{album.artist.name}" android:textAppearance="?android:attr/textAppearanceListItem" android:textColor="?android:attr/textColorSecondary" diff --git a/app/src/main/res/layout-large-land/fragment_playback.xml b/app/src/main/res/layout-large-land/fragment_playback.xml index fb6d51f5d..c01c418e1 100644 --- a/app/src/main/res/layout-large-land/fragment_playback.xml +++ b/app/src/main/res/layout-large-land/fragment_playback.xml @@ -24,7 +24,6 @@ android:id="@+id/playback_layout" android:layout_width="match_parent" android:layout_height="match_parent" - android:animateLayoutChanges="true" android:background="@color/background" android:fitsSystemWindows="true"> diff --git a/app/src/main/res/layout/fragment_playback.xml b/app/src/main/res/layout/fragment_playback.xml index 4ba50dcc9..859181fad 100644 --- a/app/src/main/res/layout/fragment_playback.xml +++ b/app/src/main/res/layout/fragment_playback.xml @@ -24,7 +24,6 @@ android:id="@+id/playback_layout" android:layout_width="match_parent" android:layout_height="match_parent" - android:animateLayoutChanges="true" android:background="@color/background" android:fitsSystemWindows="true"> diff --git a/app/src/main/res/layout/item_album_header.xml b/app/src/main/res/layout/item_album_header.xml index dbc0b1886..890dfe02b 100644 --- a/app/src/main/res/layout/item_album_header.xml +++ b/app/src/main/res/layout/item_album_header.xml @@ -55,7 +55,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="@dimen/margin_medium" - android:onClick="@{() -> detailModel.navToParent()}" + android:onClick="@{() -> detailModel.navToItem(album.artist)}" android:text="@{album.artist.name}" android:textAppearance="?android:attr/textAppearanceListItem" android:textColor="?android:attr/textColorSecondary" diff --git a/app/src/main/res/navigation/nav_explore.xml b/app/src/main/res/navigation/nav_explore.xml index 27e138a86..e39b151d7 100644 --- a/app/src/main/res/navigation/nav_explore.xml +++ b/app/src/main/res/navigation/nav_explore.xml @@ -39,12 +39,18 @@ + @@ -57,12 +63,19 @@ android:name="albumId" app:argType="long" /> + - + \ No newline at end of file