Twelve: Add play all button on albums and playlists

Change-Id: Iccfbed51f937ed24d166542a1433a88d412a10e9
diff --git a/app/src/main/java/org/lineageos/twelve/fragments/AlbumFragment.kt b/app/src/main/java/org/lineageos/twelve/fragments/AlbumFragment.kt
index f242671..b6880a9 100644
--- a/app/src/main/java/org/lineageos/twelve/fragments/AlbumFragment.kt
+++ b/app/src/main/java/org/lineageos/twelve/fragments/AlbumFragment.kt
@@ -27,6 +27,7 @@
 import coil3.load
 import com.google.android.material.appbar.MaterialToolbar
 import com.google.android.material.card.MaterialCardView
+import com.google.android.material.floatingactionbutton.FloatingActionButton
 import com.google.android.material.progressindicator.LinearProgressIndicator
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.collectLatest
@@ -35,6 +36,7 @@
 import org.lineageos.twelve.ext.getParcelable
 import org.lineageos.twelve.ext.getViewProperty
 import org.lineageos.twelve.ext.setProgressCompat
+import org.lineageos.twelve.ext.updateMargin
 import org.lineageos.twelve.ext.updatePadding
 import org.lineageos.twelve.models.RequestStatus
 import org.lineageos.twelve.ui.recyclerview.SimpleListAdapter
@@ -60,6 +62,7 @@
     private val infoNestedScrollView by getViewProperty<NestedScrollView?>(R.id.infoNestedScrollView)
     private val linearProgressIndicator by getViewProperty<LinearProgressIndicator>(R.id.linearProgressIndicator)
     private val noElementsNestedScrollView by getViewProperty<NestedScrollView>(R.id.noElementsNestedScrollView)
+    private val playAllFloatingActionButton by getViewProperty<FloatingActionButton>(R.id.playAllFloatingActionButton)
     private val recyclerView by getViewProperty<RecyclerView>(R.id.recyclerView)
     private val thumbnailImageView by getViewProperty<ImageView>(R.id.thumbnailImageView)
     private val toolbar by getViewProperty<MaterialToolbar>(R.id.toolbar)
@@ -81,11 +84,7 @@
                 view.setOnClickListener {
                     when (val item = item) {
                         is AlbumViewModel.AlbumContent.AudioItem -> {
-                            val audios = currentList.mapNotNull {
-                                (it as? AlbumViewModel.AlbumContent.AudioItem)?.audio
-                            }
-
-                            viewModel.playAudio(audios, audios.indexOf(item.audio))
+                            viewModel.playAlbum(item.audio)
 
                             findNavController().navigate(
                                 R.id.action_albumFragment_to_fragment_now_playing
@@ -216,10 +215,27 @@
             windowInsets
         }
 
+        ViewCompat.setOnApplyWindowInsetsListener(playAllFloatingActionButton) { v, windowInsets ->
+            val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
+
+            v.updateMargin(
+                insets,
+                bottom = true,
+            )
+
+            windowInsets
+        }
+
         toolbar.setupWithNavController(findNavController())
 
         recyclerView.adapter = adapter
 
+        playAllFloatingActionButton.setOnClickListener {
+            viewModel.playAlbum()
+
+            findNavController().navigate(R.id.action_albumFragment_to_fragment_now_playing)
+        }
+
         viewModel.loadAlbum(albumUri)
 
         viewLifecycleOwner.lifecycleScope.launch {
@@ -318,6 +334,10 @@
                     val isEmpty = it.isEmpty()
                     recyclerView.isVisible = !isEmpty
                     noElementsNestedScrollView.isVisible = isEmpty
+                    when (isEmpty) {
+                        true -> playAllFloatingActionButton.hide()
+                        false -> playAllFloatingActionButton.show()
+                    }
                 }
             }
 
diff --git a/app/src/main/java/org/lineageos/twelve/fragments/PlaylistFragment.kt b/app/src/main/java/org/lineageos/twelve/fragments/PlaylistFragment.kt
index 5c9d8f4..28ff7db 100644
--- a/app/src/main/java/org/lineageos/twelve/fragments/PlaylistFragment.kt
+++ b/app/src/main/java/org/lineageos/twelve/fragments/PlaylistFragment.kt
@@ -25,6 +25,7 @@
 import androidx.recyclerview.widget.RecyclerView
 import com.google.android.material.appbar.MaterialToolbar
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.google.android.material.floatingactionbutton.FloatingActionButton
 import com.google.android.material.progressindicator.LinearProgressIndicator
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.launch
@@ -32,6 +33,7 @@
 import org.lineageos.twelve.ext.getParcelable
 import org.lineageos.twelve.ext.getViewProperty
 import org.lineageos.twelve.ext.setProgressCompat
+import org.lineageos.twelve.ext.updateMargin
 import org.lineageos.twelve.ext.updatePadding
 import org.lineageos.twelve.models.Audio
 import org.lineageos.twelve.models.RequestStatus
@@ -55,6 +57,7 @@
     private val infoNestedScrollView by getViewProperty<NestedScrollView?>(R.id.infoNestedScrollView)
     private val linearProgressIndicator by getViewProperty<LinearProgressIndicator>(R.id.linearProgressIndicator)
     private val noElementsNestedScrollView by getViewProperty<NestedScrollView>(R.id.noElementsNestedScrollView)
+    private val playAllFloatingActionButton by getViewProperty<FloatingActionButton>(R.id.playAllFloatingActionButton)
     private val playlistNameTextView by getViewProperty<TextView>(R.id.playlistNameTextView)
     private val recyclerView by getViewProperty<RecyclerView>(R.id.recyclerView)
     private val toolbar by getViewProperty<MaterialToolbar>(R.id.toolbar)
@@ -70,7 +73,7 @@
                 view.setLeadingIconImage(R.drawable.ic_music_note)
                 view.setOnClickListener {
                     item?.let {
-                        viewModel.playAudio(currentList, bindingAdapterPosition)
+                        viewModel.playPlaylist(bindingAdapterPosition)
 
                         findNavController().navigate(
                             R.id.action_playlistFragment_to_fragment_now_playing
@@ -162,6 +165,17 @@
             windowInsets
         }
 
+        ViewCompat.setOnApplyWindowInsetsListener(playAllFloatingActionButton) { v, windowInsets ->
+            val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
+
+            v.updateMargin(
+                insets,
+                bottom = true,
+            )
+
+            windowInsets
+        }
+
         toolbar.setupWithNavController(findNavController())
         toolbar.inflateMenu(R.menu.fragment_podcast_toolbar)
         toolbar.setOnMenuItemClickListener {
@@ -182,6 +196,12 @@
 
         recyclerView.adapter = adapter
 
+        playAllFloatingActionButton.setOnClickListener {
+            viewModel.playPlaylist()
+
+            findNavController().navigate(R.id.action_playlistFragment_to_fragment_now_playing)
+        }
+
         viewModel.loadPlaylist(playlistUri)
 
         viewLifecycleOwner.lifecycleScope.launch {
@@ -239,6 +259,10 @@
                     val isEmpty = audios.isEmpty()
                     recyclerView.isVisible = !isEmpty
                     noElementsNestedScrollView.isVisible = isEmpty
+                    when (isEmpty) {
+                        true -> playAllFloatingActionButton.hide()
+                        false -> playAllFloatingActionButton.show()
+                    }
                 }
 
                 is RequestStatus.Error -> {
@@ -251,6 +275,7 @@
 
                     recyclerView.isVisible = false
                     noElementsNestedScrollView.isVisible = true
+                    playAllFloatingActionButton.isVisible = false
 
                     if (it.type == RequestStatus.Error.Type.NOT_FOUND) {
                         // Get out of here
diff --git a/app/src/main/java/org/lineageos/twelve/viewmodels/AlbumViewModel.kt b/app/src/main/java/org/lineageos/twelve/viewmodels/AlbumViewModel.kt
index 18010e6..9faabb0 100644
--- a/app/src/main/java/org/lineageos/twelve/viewmodels/AlbumViewModel.kt
+++ b/app/src/main/java/org/lineageos/twelve/viewmodels/AlbumViewModel.kt
@@ -139,4 +139,12 @@
     fun loadAlbum(albumUri: Uri) {
         this.albumUri.value = albumUri
     }
+
+    fun playAlbum(startFrom: Audio? = null) {
+        albumContent.value.mapNotNull {
+            (it as? AlbumContent.AudioItem)?.audio
+        }.takeUnless { it.isEmpty() }?.let { audios ->
+            playAudio(audios, startFrom?.let { audios.indexOf(it) } ?: 0)
+        }
+    }
 }
diff --git a/app/src/main/java/org/lineageos/twelve/viewmodels/PlaylistViewModel.kt b/app/src/main/java/org/lineageos/twelve/viewmodels/PlaylistViewModel.kt
index 31e8563..3a4269e 100644
--- a/app/src/main/java/org/lineageos/twelve/viewmodels/PlaylistViewModel.kt
+++ b/app/src/main/java/org/lineageos/twelve/viewmodels/PlaylistViewModel.kt
@@ -50,4 +50,12 @@
             mediaRepository.deletePlaylist(playlistUri)
         }
     }
+
+    fun playPlaylist(position: Int = 0) {
+        (playlist.value as? RequestStatus.Success)?.data?.second?.takeUnless {
+            it.isEmpty()
+        }?.mapNotNull { it }?.let {
+            playAudio(it, position)
+        }
+    }
 }
diff --git a/app/src/main/res/layout-land/fragment_album.xml b/app/src/main/res/layout-land/fragment_album.xml
index ce15464..9bfa55a 100644
--- a/app/src/main/res/layout-land/fragment_album.xml
+++ b/app/src/main/res/layout-land/fragment_album.xml
@@ -95,4 +95,23 @@
         android:indeterminate="true"
         app:layout_behavior="@string/appbar_scrolling_view_behavior" />
 
+    <FrameLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="24dp"
+        android:paddingBottom="16dp"
+        app:layout_anchor="@+id/recyclerView"
+        app:layout_anchorGravity="bottom|end"
+        app:layout_behavior="@string/hide_bottom_view_on_scroll_behavior">
+
+        <com.google.android.material.floatingactionbutton.FloatingActionButton
+            android:id="@+id/playAllFloatingActionButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:visibility="gone"
+            app:srcCompat="@drawable/ic_play_arrow"
+            tools:visibility="visible" />
+
+    </FrameLayout>
+
 </androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/app/src/main/res/layout-land/fragment_playlist.xml b/app/src/main/res/layout-land/fragment_playlist.xml
index 82291f6..fb3ec0e 100644
--- a/app/src/main/res/layout-land/fragment_playlist.xml
+++ b/app/src/main/res/layout-land/fragment_playlist.xml
@@ -95,4 +95,23 @@
         android:indeterminate="true"
         app:layout_behavior="@string/appbar_scrolling_view_behavior" />
 
+    <FrameLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="24dp"
+        android:paddingBottom="16dp"
+        app:layout_anchor="@+id/recyclerView"
+        app:layout_anchorGravity="bottom|end"
+        app:layout_behavior="@string/hide_bottom_view_on_scroll_behavior">
+
+        <com.google.android.material.floatingactionbutton.FloatingActionButton
+            android:id="@+id/playAllFloatingActionButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:visibility="gone"
+            app:srcCompat="@drawable/ic_play_arrow"
+            tools:visibility="visible" />
+
+    </FrameLayout>
+
 </androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/app/src/main/res/layout/fragment_album.xml b/app/src/main/res/layout/fragment_album.xml
index 540f4f9..2aa8c9a 100644
--- a/app/src/main/res/layout/fragment_album.xml
+++ b/app/src/main/res/layout/fragment_album.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
      SPDX-FileCopyrightText: 2024 The LineageOS Project
      SPDX-License-Identifier: Apache-2.0
 -->
@@ -96,4 +95,23 @@
         android:indeterminate="true"
         app:layout_behavior="@string/appbar_scrolling_view_behavior" />
 
+    <FrameLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="24dp"
+        android:paddingBottom="16dp"
+        app:layout_anchor="@+id/recyclerView"
+        app:layout_anchorGravity="bottom|end"
+        app:layout_behavior="@string/hide_bottom_view_on_scroll_behavior">
+
+        <com.google.android.material.floatingactionbutton.FloatingActionButton
+            android:id="@+id/playAllFloatingActionButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:visibility="gone"
+            app:srcCompat="@drawable/ic_play_arrow"
+            tools:visibility="visible" />
+
+    </FrameLayout>
+
 </androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/app/src/main/res/layout/fragment_playlist.xml b/app/src/main/res/layout/fragment_playlist.xml
index b222084..d2a124b 100644
--- a/app/src/main/res/layout/fragment_playlist.xml
+++ b/app/src/main/res/layout/fragment_playlist.xml
@@ -97,4 +97,23 @@
         android:indeterminate="true"
         app:layout_behavior="@string/appbar_scrolling_view_behavior" />
 
+    <FrameLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="24dp"
+        android:paddingBottom="16dp"
+        app:layout_anchor="@+id/recyclerView"
+        app:layout_anchorGravity="bottom|end"
+        app:layout_behavior="@string/hide_bottom_view_on_scroll_behavior">
+
+        <com.google.android.material.floatingactionbutton.FloatingActionButton
+            android:id="@+id/playAllFloatingActionButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:visibility="gone"
+            app:srcCompat="@drawable/ic_play_arrow"
+            tools:visibility="visible" />
+
+    </FrameLayout>
+
 </androidx.coordinatorlayout.widget.CoordinatorLayout>