Skip to content

Commit fd8e92c

Browse files
authored
Merge pull request TeamNewPipe#9523 from Jared234/9468_permanently_set_thumbnail
Allow the user to permanently set a thumbnail
2 parents 062570c + 2679a4b commit fd8e92c

12 files changed

Lines changed: 879 additions & 33 deletions

File tree

app/schemas/org.schabi.newpipe.database.AppDatabase/6.json

Lines changed: 737 additions & 0 deletions
Large diffs are not rendered by default.

app/src/androidTest/java/org/schabi/newpipe/database/DatabaseMigrationTest.kt

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ class DatabaseMigrationTest {
3333
@get:Rule
3434
val testHelper = MigrationTestHelper(
3535
InstrumentationRegistry.getInstrumentation(),
36-
AppDatabase::class.java.canonicalName, FrameworkSQLiteOpenHelperFactory()
36+
AppDatabase::class.java.canonicalName,
37+
FrameworkSQLiteOpenHelperFactory()
3738
)
3839

3940
@Test
@@ -42,7 +43,8 @@ class DatabaseMigrationTest {
4243

4344
databaseInV2.run {
4445
insert(
45-
"streams", SQLiteDatabase.CONFLICT_FAIL,
46+
"streams",
47+
SQLiteDatabase.CONFLICT_FAIL,
4648
ContentValues().apply {
4749
put("service_id", DEFAULT_SERVICE_ID)
4850
put("url", DEFAULT_URL)
@@ -54,14 +56,16 @@ class DatabaseMigrationTest {
5456
}
5557
)
5658
insert(
57-
"streams", SQLiteDatabase.CONFLICT_FAIL,
59+
"streams",
60+
SQLiteDatabase.CONFLICT_FAIL,
5861
ContentValues().apply {
5962
put("service_id", DEFAULT_SECOND_SERVICE_ID)
6063
put("url", DEFAULT_SECOND_URL)
6164
}
6265
)
6366
insert(
64-
"streams", SQLiteDatabase.CONFLICT_FAIL,
67+
"streams",
68+
SQLiteDatabase.CONFLICT_FAIL,
6569
ContentValues().apply {
6670
put("service_id", DEFAULT_SERVICE_ID)
6771
}
@@ -70,18 +74,31 @@ class DatabaseMigrationTest {
7074
}
7175

7276
testHelper.runMigrationsAndValidate(
73-
AppDatabase.DATABASE_NAME, Migrations.DB_VER_3,
74-
true, Migrations.MIGRATION_2_3
77+
AppDatabase.DATABASE_NAME,
78+
Migrations.DB_VER_3,
79+
true,
80+
Migrations.MIGRATION_2_3
7581
)
7682

7783
testHelper.runMigrationsAndValidate(
78-
AppDatabase.DATABASE_NAME, Migrations.DB_VER_4,
79-
true, Migrations.MIGRATION_3_4
84+
AppDatabase.DATABASE_NAME,
85+
Migrations.DB_VER_4,
86+
true,
87+
Migrations.MIGRATION_3_4
8088
)
8189

8290
testHelper.runMigrationsAndValidate(
83-
AppDatabase.DATABASE_NAME, Migrations.DB_VER_5,
84-
true, Migrations.MIGRATION_4_5
91+
AppDatabase.DATABASE_NAME,
92+
Migrations.DB_VER_5,
93+
true,
94+
Migrations.MIGRATION_4_5
95+
)
96+
97+
testHelper.runMigrationsAndValidate(
98+
AppDatabase.DATABASE_NAME,
99+
Migrations.DB_VER_6,
100+
true,
101+
Migrations.MIGRATION_5_6
85102
)
86103

87104
val migratedDatabaseV3 = getMigratedDatabase()
@@ -121,7 +138,8 @@ class DatabaseMigrationTest {
121138
private fun getMigratedDatabase(): AppDatabase {
122139
val database: AppDatabase = Room.databaseBuilder(
123140
ApplicationProvider.getApplicationContext(),
124-
AppDatabase::class.java, AppDatabase.DATABASE_NAME
141+
AppDatabase::class.java,
142+
AppDatabase.DATABASE_NAME
125143
)
126144
.build()
127145
testHelper.closeWhenFinished(database)

app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import static org.schabi.newpipe.database.Migrations.MIGRATION_2_3;
66
import static org.schabi.newpipe.database.Migrations.MIGRATION_3_4;
77
import static org.schabi.newpipe.database.Migrations.MIGRATION_4_5;
8+
import static org.schabi.newpipe.database.Migrations.MIGRATION_5_6;
89

910
import android.content.Context;
1011
import android.database.Cursor;
@@ -24,7 +25,8 @@ private NewPipeDatabase() {
2425
private static AppDatabase getDatabase(final Context context) {
2526
return Room
2627
.databaseBuilder(context.getApplicationContext(), AppDatabase.class, DATABASE_NAME)
27-
.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5)
28+
.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5,
29+
MIGRATION_5_6)
2830
.build();
2931
}
3032

app/src/main/java/org/schabi/newpipe/database/AppDatabase.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package org.schabi.newpipe.database;
22

3-
import static org.schabi.newpipe.database.Migrations.DB_VER_5;
3+
import static org.schabi.newpipe.database.Migrations.DB_VER_6;
44

55
import androidx.room.Database;
66
import androidx.room.RoomDatabase;
@@ -38,7 +38,7 @@
3838
FeedEntity.class, FeedGroupEntity.class, FeedGroupSubscriptionEntity.class,
3939
FeedLastUpdatedEntity.class
4040
},
41-
version = DB_VER_5
41+
version = DB_VER_6
4242
)
4343
public abstract class AppDatabase extends RoomDatabase {
4444
public static final String DATABASE_NAME = "newpipe.db";

app/src/main/java/org/schabi/newpipe/database/Migrations.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public final class Migrations {
2323
public static final int DB_VER_3 = 3;
2424
public static final int DB_VER_4 = 4;
2525
public static final int DB_VER_5 = 5;
26+
public static final int DB_VER_6 = 6;
2627

2728
private static final String TAG = Migrations.class.getName();
2829
public static final boolean DEBUG = MainActivity.DEBUG;
@@ -188,6 +189,14 @@ public void migrate(@NonNull final SupportSQLiteDatabase database) {
188189
}
189190
};
190191

192+
public static final Migration MIGRATION_5_6 = new Migration(DB_VER_5, DB_VER_6) {
193+
@Override
194+
public void migrate(@NonNull final SupportSQLiteDatabase database) {
195+
database.execSQL("ALTER TABLE `playlists` ADD COLUMN `is_thumbnail_permanent` "
196+
+ "INTEGER NOT NULL DEFAULT 0");
197+
}
198+
};
199+
191200
private Migrations() {
192201
}
193202
}

app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistStreamDAO.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.PLAYLIST_STREAM_JOIN_TABLE;
2626
import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_ID;
2727
import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_TABLE;
28+
import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_THUMBNAIL_URL;
2829
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.JOIN_STREAM_ID_ALIAS;
2930
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_PROGRESS_MILLIS;
3031
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE;
@@ -53,6 +54,15 @@ default Flowable<List<PlaylistStreamEntity>> listByService(final int serviceId)
5354
+ " WHERE " + JOIN_PLAYLIST_ID + " = :playlistId")
5455
Flowable<Integer> getMaximumIndexOf(long playlistId);
5556

57+
@Query("SELECT CASE WHEN COUNT(*) != 0 then " + STREAM_THUMBNAIL_URL + " ELSE :defaultUrl END"
58+
+ " FROM " + STREAM_TABLE
59+
+ " LEFT JOIN " + PLAYLIST_STREAM_JOIN_TABLE
60+
+ " ON " + STREAM_ID + " = " + JOIN_STREAM_ID
61+
+ " WHERE " + JOIN_PLAYLIST_ID + " = :playlistId "
62+
+ " LIMIT 1"
63+
)
64+
Flowable<String> getAutomaticThumbnailUrl(long playlistId, String defaultUrl);
65+
5666
@RewriteQueriesToDropUnusedColumns
5767
@Transaction
5868
@Query("SELECT * FROM " + STREAM_TABLE + " INNER JOIN "

app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistEntity.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public class PlaylistEntity {
1515
public static final String PLAYLIST_ID = "uid";
1616
public static final String PLAYLIST_NAME = "name";
1717
public static final String PLAYLIST_THUMBNAIL_URL = "thumbnail_url";
18+
public static final String PLAYLIST_THUMBNAIL_PERMANENT = "is_thumbnail_permanent";
1819

1920
@PrimaryKey(autoGenerate = true)
2021
@ColumnInfo(name = PLAYLIST_ID)
@@ -26,9 +27,14 @@ public class PlaylistEntity {
2627
@ColumnInfo(name = PLAYLIST_THUMBNAIL_URL)
2728
private String thumbnailUrl;
2829

29-
public PlaylistEntity(final String name, final String thumbnailUrl) {
30+
@ColumnInfo(name = PLAYLIST_THUMBNAIL_PERMANENT)
31+
private boolean isThumbnailPermanent;
32+
33+
public PlaylistEntity(final String name, final String thumbnailUrl,
34+
final boolean isThumbnailPermanent) {
3035
this.name = name;
3136
this.thumbnailUrl = thumbnailUrl;
37+
this.isThumbnailPermanent = isThumbnailPermanent;
3238
}
3339

3440
public long getUid() {
@@ -54,4 +60,13 @@ public String getThumbnailUrl() {
5460
public void setThumbnailUrl(final String thumbnailUrl) {
5561
this.thumbnailUrl = thumbnailUrl;
5662
}
63+
64+
public boolean getIsThumbnailPermanent() {
65+
return isThumbnailPermanent;
66+
}
67+
68+
public void setIsThumbnailPermanent(final boolean isThumbnailSet) {
69+
this.isThumbnailPermanent = isThumbnailSet;
70+
}
71+
5772
}

app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.schabi.newpipe.local.bookmark;
22

3+
import android.content.DialogInterface;
34
import android.os.Bundle;
45
import android.os.Parcelable;
56
import android.text.InputType;
@@ -31,6 +32,7 @@
3132
import org.schabi.newpipe.util.NavigationHelper;
3233
import org.schabi.newpipe.util.OnClickGesture;
3334

35+
import java.util.ArrayList;
3436
import java.util.List;
3537

3638
import icepick.State;
@@ -256,6 +258,41 @@ private void showRemoteDeleteDialog(final PlaylistRemoteEntity item) {
256258
}
257259

258260
private void showLocalDialog(final PlaylistMetadataEntry selectedItem) {
261+
final String rename = getString(R.string.rename);
262+
final String delete = getString(R.string.delete);
263+
final String unsetThumbnail = getString(R.string.unset_playlist_thumbnail);
264+
final boolean isThumbnailPermanent = localPlaylistManager
265+
.getIsPlaylistThumbnailPermanent(selectedItem.uid);
266+
267+
final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
268+
269+
final ArrayList<String> items = new ArrayList<>();
270+
items.add(rename);
271+
items.add(delete);
272+
if (isThumbnailPermanent) {
273+
items.add(unsetThumbnail);
274+
}
275+
276+
final DialogInterface.OnClickListener action = (d, index) -> {
277+
if (items.get(index).equals(rename)) {
278+
showRenameDialog(selectedItem);
279+
} else if (items.get(index).equals(delete)) {
280+
showDeleteDialog(selectedItem.name,
281+
localPlaylistManager.deletePlaylist(selectedItem.uid));
282+
} else if (isThumbnailPermanent && items.get(index).equals(unsetThumbnail)) {
283+
final String thumbnailUrl = localPlaylistManager
284+
.getAutomaticPlaylistThumbnail(selectedItem.uid);
285+
localPlaylistManager
286+
.changePlaylistThumbnail(selectedItem.uid, thumbnailUrl, false)
287+
.observeOn(AndroidSchedulers.mainThread())
288+
.subscribe();
289+
}
290+
};
291+
292+
builder.setItems(items.toArray(new String[0]), action).create().show();
293+
}
294+
295+
private void showRenameDialog(final PlaylistMetadataEntry selectedItem) {
259296
final DialogEditTextBinding dialogBinding =
260297
DialogEditTextBinding.inflate(getLayoutInflater());
261298
dialogBinding.dialogEditText.setHint(R.string.name);
@@ -269,11 +306,6 @@ private void showLocalDialog(final PlaylistMetadataEntry selectedItem) {
269306
selectedItem.uid,
270307
dialogBinding.dialogEditText.getText().toString()))
271308
.setNegativeButton(R.string.cancel, null)
272-
.setNeutralButton(R.string.delete, (dialog, which) -> {
273-
showDeleteDialog(selectedItem.name,
274-
localPlaylistManager.deletePlaylist(selectedItem.uid));
275-
dialog.dismiss();
276-
})
277309
.create()
278310
.show();
279311
}

app/src/main/java/org/schabi/newpipe/local/dialog/PlaylistAppendDialog.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ private void onPlaylistSelected(@NonNull final LocalPlaylistManager manager,
134134
if (playlist.thumbnailUrl
135135
.equals("drawable://" + R.drawable.placeholder_thumbnail_playlist)) {
136136
playlistDisposables.add(manager
137-
.changePlaylistThumbnail(playlist.uid, streams.get(0).getThumbnailUrl())
137+
.changePlaylistThumbnail(playlist.uid, streams.get(0).getThumbnailUrl(), false)
138138
.observeOn(AndroidSchedulers.mainThread())
139139
.subscribe(ignored -> successToast.show()));
140140
}

app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,8 @@ public void removeWatchedStreams(final boolean removePartiallyWatched) {
405405
.zipWith(historyIdsMaybe, (playlist, historyStreamIds) -> {
406406
// Remove Watched, Functionality data
407407
final List<PlaylistStreamEntry> notWatchedItems = new ArrayList<>();
408+
final boolean isThumbnailPermanent = playlistManager
409+
.getIsPlaylistThumbnailPermanent(playlistId);
408410
boolean thumbnailVideoRemoved = false;
409411

410412
if (removePartiallyWatched) {
@@ -414,7 +416,7 @@ public void removeWatchedStreams(final boolean removePartiallyWatched) {
414416

415417
if (indexInHistory < 0) {
416418
notWatchedItems.add(playlistItem);
417-
} else if (!thumbnailVideoRemoved
419+
} else if (!isThumbnailPermanent && !thumbnailVideoRemoved
418420
&& playlistManager.getPlaylistThumbnail(playlistId)
419421
.equals(playlistItem.getStreamEntity().getThumbnailUrl())) {
420422
thumbnailVideoRemoved = true;
@@ -435,7 +437,7 @@ public void removeWatchedStreams(final boolean removePartiallyWatched) {
435437
if (indexInHistory < 0 || (streamStateEntity != null
436438
&& !streamStateEntity.isFinished(duration))) {
437439
notWatchedItems.add(playlistItem);
438-
} else if (!thumbnailVideoRemoved
440+
} else if (!isThumbnailPermanent && !thumbnailVideoRemoved
439441
&& playlistManager.getPlaylistThumbnail(playlistId)
440442
.equals(playlistItem.getStreamEntity().getThumbnailUrl())) {
441443
thumbnailVideoRemoved = true;
@@ -585,8 +587,9 @@ private void changePlaylistName(final String title) {
585587
disposables.add(disposable);
586588
}
587589

588-
private void changeThumbnailUrl(final String thumbnailUrl) {
589-
if (playlistManager == null) {
590+
private void changeThumbnailUrl(final String thumbnailUrl, final boolean isPermanent) {
591+
if (playlistManager == null || (!isPermanent && playlistManager
592+
.getIsPlaylistThumbnailPermanent(playlistId))) {
590593
return;
591594
}
592595

@@ -600,7 +603,7 @@ private void changeThumbnailUrl(final String thumbnailUrl) {
600603
}
601604

602605
final Disposable disposable = playlistManager
603-
.changePlaylistThumbnail(playlistId, thumbnailUrl)
606+
.changePlaylistThumbnail(playlistId, thumbnailUrl, isPermanent)
604607
.observeOn(AndroidSchedulers.mainThread())
605608
.subscribe(ignore -> successToast.show(), throwable ->
606609
showError(new ErrorInfo(throwable, UserAction.REQUESTED_BOOKMARK,
@@ -609,6 +612,10 @@ private void changeThumbnailUrl(final String thumbnailUrl) {
609612
}
610613

611614
private void updateThumbnailUrl() {
615+
if (playlistManager.getIsPlaylistThumbnailPermanent(playlistId)) {
616+
return;
617+
}
618+
612619
final String newThumbnailUrl;
613620

614621
if (!itemListAdapter.getItemsList().isEmpty()) {
@@ -618,7 +625,7 @@ private void updateThumbnailUrl() {
618625
newThumbnailUrl = "drawable://" + R.drawable.placeholder_thumbnail_playlist;
619626
}
620627

621-
changeThumbnailUrl(newThumbnailUrl);
628+
changeThumbnailUrl(newThumbnailUrl, false);
622629
}
623630

624631
private void deleteItem(final PlaylistStreamEntry item) {
@@ -786,7 +793,8 @@ context, getPlayQueueStartingAt(item), true))
786793
.setAction(
787794
StreamDialogDefaultEntry.SET_AS_PLAYLIST_THUMBNAIL,
788795
(f, i) ->
789-
changeThumbnailUrl(item.getStreamEntity().getThumbnailUrl()))
796+
changeThumbnailUrl(item.getStreamEntity().getThumbnailUrl(),
797+
true))
790798
.setAction(
791799
StreamDialogDefaultEntry.DELETE,
792800
(f, i) -> deleteItem(item))

0 commit comments

Comments
 (0)