feat: implement "Resume All" functionality for downloads#8165
feat: implement "Resume All" functionality for downloads#8165bermount wants to merge 4 commits intolibre-tube:masterfrom
Conversation
Adds a "Resume All" button to the downloads screen that allows users to simultaneously resume all incomplete downloads. This is managed by tracking incomplete download IDs in preferences and implementing a new service action that fills available download slots up to the maximum concurrence limit.
Ensure that the download ID is tracked when manually resuming downloads.
edd34de to
c0181cd
Compare
| app:layout_constraintBottom_toTopOf="@+id/delete_all" | ||
| app:layout_constraintEnd_toEndOf="parent" | ||
| tools:targetApi="o" | ||
| tools:visibility="visible" /> |
There was a problem hiding this comment.
I personally think that this button is a bit unintuitive, i.e. the logo is just a download button and not really self-explainable.
Instead, I think it would look better to put the button in the same line as the sort order button.
I know this conflicts with the playlist name, but I think it's still the better solution. E.g. this could look like the following (tested locally):
diff --git a/app/src/main/java/com/github/libretube/ui/fragments/DownloadsFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/DownloadsFragment.kt
index 47123b199..fc38bbc78 100644
--- a/app/src/main/java/com/github/libretube/ui/fragments/DownloadsFragment.kt
+++ b/app/src/main/java/com/github/libretube/ui/fragments/DownloadsFragment.kt
@@ -231,6 +231,7 @@ class DownloadsFragmentPage : DynamicLayoutManagerFragment(R.layout.fragment_dow
}
binding.playlistName.text = playlist.downloadPlaylist.title
+ binding.playlistName.isVisible = true
playlist.downloadVideos.map { it.videoId }
}
diff --git a/app/src/main/res/layout/fragment_download_content.xml b/app/src/main/res/layout/fragment_download_content.xml
index 151e3b0cf..bcb36d962 100644
--- a/app/src/main/res/layout/fragment_download_content.xml
+++ b/app/src/main/res/layout/fragment_download_content.xml
@@ -12,44 +12,64 @@
android:layout_height="match_parent"
android:orientation="vertical">
- <LinearLayout
+ <RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal">
+ android:paddingHorizontal="8dp">
- <TextView
- android:id="@+id/playlist_name"
- android:layout_width="0dp"
- android:layout_weight="1"
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/resume_all"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:maxLines="1"
- android:textSize="18sp"
- android:textStyle="bold"
- android:ellipsize="end"
- android:paddingHorizontal="8dp"
- tools:text="Downloaded Playlist Name"/>
+ android:text="@string/resume_all"
+ android:contentDescription="@string/resume_all"
+ app:icon="@drawable/ic_play"
+ android:tooltipText="@string/resume_all"
+ android:visibility="gone"
+ tools:targetApi="o"
+ tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/sort_type"
style="?materialButtonTonalStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginEnd="8dp"
- android:ellipsize="end"
+ android:layout_marginEnd="8dp"
+ android:ellipsize="end"
android:maxLines="1"
android:paddingHorizontal="10dp"
+ android:layout_alignParentEnd="true"
android:text="@string/sort_by"
app:icon="@drawable/ic_sort"
+ android:gravity="end"
app:iconGravity="textEnd" />
- </LinearLayout>
+ </RelativeLayout>
+
+ <TextView
+ android:id="@+id/playlist_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:maxLines="1"
+ android:textSize="18sp"
+ android:textStyle="bold"
+ android:ellipsize="end"
+ android:paddingHorizontal="8dp"
+ android:visibility="gone"
+ tools:visibility="visible"
+ tools:text="Downloaded Playlist Name"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/downloads_recView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
- android:orientation="vertical"/>
+ android:orientation="vertical" />
+
+ <androidx.fragment.app.FragmentContainerView
+ android:id="@+id/fragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
</LinearLayout>
@@ -78,23 +98,6 @@
android:textStyle="bold" />
</LinearLayout>
- <com.google.android.material.floatingactionbutton.FloatingActionButton
- android:id="@+id/resume_all"
- style="?attr/floatingActionButtonSmallSecondaryStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end"
- android:layout_marginEnd="16dp"
- android:layout_marginBottom="8dp"
- android:contentDescription="@string/resume"
- android:src="@drawable/ic_download"
- android:tooltipText="@string/resume"
- android:visibility="gone"
- app:layout_constraintBottom_toTopOf="@+id/delete_all"
- app:layout_constraintEnd_toEndOf="parent"
- tools:targetApi="o"
- tools:visibility="visible" />
-
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/delete_all"
style="?attr/floatingActionButtonSmallSecondaryStyle"
@@ -127,9 +130,4 @@
tools:targetApi="o"
tools:visibility="visible" />
- <androidx.fragment.app.FragmentContainerView
- android:id="@+id/fragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
</androidx.constraintlayout.widget.ConstraintLayout>
\ 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 0c6fb58cc..1f5ac9cce 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -543,6 +543,7 @@
<string name="include_timestamp_in_filename">Include timestamp in filename</string>
<string name="did_you_mean">Did you mean:</string>
<string name="showing_results_for">Showing results for:</string>
+ <string name="resume_all">Resume all</string>
<!-- Notification channel strings -->
<string name="download_channel_name">Download Service</string>
There was a problem hiding this comment.
@Bnyro I agree that the icon alone is a bit unintuitive. However, as you mentioned, moving it to the top conflicts with the playlist name and makes the header look cluttered.
How about changing it to an button at the bottom with the text "Resume downloads"? I think this makes the action self-explainable while keeping the top layout clean.
There was a problem hiding this comment.
This isn't great, as it would move the location of the other buttons, thus making it somewhat surprising to the user.
I'm not a heavy download user, so I'm not quite sure what's the use case for this is, but if it's something that happens only very rarely, maybe it should be moved to a notification instead (e.g 'You have unfinished downloads' with a resume button?).
There was a problem hiding this comment.
I think that a notification would be very annoying, a button makes more sense imo.
| private fun resumeAll() { | ||
| lifecycleScope.launch(coroutineContext) { | ||
| // Get incomplete items directly using stored IDs | ||
| val incompleteIds = DownloadHelper.getIncompleteDownloadIds() |
There was a problem hiding this comment.
Instead of storing the incomplete download IDs in the preferences, can't we just iterate over all download items in the database and compare their downloadSize to the actual file size?
Or otherwise we should store an isFinished attribute in the DownloadItem table, instead of putting that information into the preferences store.
|
Very good idea, thanks for the PR 👍 |
| return if (idsString.isBlank()) { | ||
| emptySet() | ||
| } else { | ||
| idsString.split(",").mapNotNull { it.toIntOrNull() }.toSet() | ||
| } |
There was a problem hiding this comment.
| return if (idsString.isBlank()) { | |
| emptySet() | |
| } else { | |
| idsString.split(",").mapNotNull { it.toIntOrNull() }.toSet() | |
| } | |
| return idsString.split(",").mapNotNull { it.toIntOrNull() }.toSet() |
split returns an empty list if the string is blank, which will be turned into an empty set
Adds a "Resume All" button to the downloads screen that allows users to simultaneously resume all incomplete downloads. This is managed by tracking incomplete download IDs in preferences and implementing a new service action that fills available download slots up to the maximum concurrence limit. The button is hidden when there is no incomplete download.
Works with #8161 .