Skip to content

Commit 3ff4b71

Browse files
authored
Merge branch 'dev' into 9458_faulty_playlist_thumbnail_update
2 parents 6809756 + ca421c2 commit 3ff4b71

41 files changed

Lines changed: 343 additions & 28 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/build.gradle

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import com.android.tools.profgen.ArtProfileKt
2+
import com.android.tools.profgen.ArtProfileSerializer
3+
import com.android.tools.profgen.DexFile
4+
15
plugins {
26
id "com.android.application"
37
id "kotlin-android"
@@ -308,3 +312,24 @@ static String getGitWorkingBranch() {
308312
return ""
309313
}
310314
}
315+
316+
project.afterEvaluate {
317+
tasks.compileReleaseArtProfile.doLast {
318+
outputs.files.each { file ->
319+
if (file.toString().endsWith(".profm")) {
320+
println("Sorting ${file} ...")
321+
def version = ArtProfileSerializer.valueOf("METADATA_0_0_2")
322+
def profile = ArtProfileKt.ArtProfile(file)
323+
def keys = new ArrayList(profile.profileData.keySet())
324+
def sortedData = new LinkedHashMap()
325+
Collections.sort keys, new DexFile.Companion()
326+
keys.each { key -> sortedData[key] = profile.profileData[key] }
327+
new FileOutputStream(file).with {
328+
write(version.magicBytes$profgen)
329+
write(version.versionBytes$profgen)
330+
version.write$profgen(it, sortedData, "")
331+
}
332+
}
333+
}
334+
}
335+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.schabi.newpipe.database.playlist;
2+
3+
import androidx.room.ColumnInfo;
4+
5+
/**
6+
* This class adds a field to {@link PlaylistMetadataEntry} that contains an integer representing
7+
* how many times a specific stream is already contained inside a local playlist. Used to be able
8+
* to grey out playlists which already contain the current stream in the playlist append dialog.
9+
* @see org.schabi.newpipe.local.playlist.LocalPlaylistManager#getPlaylistDuplicates(String)
10+
*/
11+
public class PlaylistDuplicatesEntry extends PlaylistMetadataEntry {
12+
public static final String PLAYLIST_TIMES_STREAM_IS_CONTAINED = "timesStreamIsContained";
13+
@ColumnInfo(name = PLAYLIST_TIMES_STREAM_IS_CONTAINED)
14+
public final long timesStreamIsContained;
15+
16+
public PlaylistDuplicatesEntry(final long uid,
17+
final String name,
18+
final String thumbnailUrl,
19+
final long streamCount,
20+
final long timesStreamIsContained) {
21+
super(uid, name, thumbnailUrl, streamCount);
22+
this.timesStreamIsContained = timesStreamIsContained;
23+
}
24+
}

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import androidx.room.Transaction;
77

88
import org.schabi.newpipe.database.BasicDAO;
9+
import org.schabi.newpipe.database.playlist.PlaylistDuplicatesEntry;
910
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
1011
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
1112
import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity;
@@ -14,6 +15,7 @@
1415

1516
import io.reactivex.rxjava3.core.Flowable;
1617

18+
import static org.schabi.newpipe.database.playlist.PlaylistDuplicatesEntry.PLAYLIST_TIMES_STREAM_IS_CONTAINED;
1719
import static org.schabi.newpipe.database.playlist.PlaylistMetadataEntry.PLAYLIST_STREAM_COUNT;
1820
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.DEFAULT_THUMBNAIL;
1921
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_ID;
@@ -28,6 +30,7 @@
2830
import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_ID;
2931
import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_TABLE;
3032
import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_THUMBNAIL_URL;
33+
import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_URL;
3134
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.JOIN_STREAM_ID_ALIAS;
3235
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_PROGRESS_MILLIS;
3336
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE;
@@ -103,4 +106,24 @@ default Flowable<List<PlaylistStreamEntity>> listByService(final int serviceId)
103106
+ " GROUP BY " + PLAYLIST_ID
104107
+ " ORDER BY " + PLAYLIST_NAME + " COLLATE NOCASE ASC")
105108
Flowable<List<PlaylistMetadataEntry>> getPlaylistMetadata();
109+
110+
@Transaction
111+
@Query("SELECT " + PLAYLIST_TABLE + "." + PLAYLIST_ID + ", "
112+
+ PLAYLIST_NAME + ", "
113+
+ PLAYLIST_TABLE + "." + PLAYLIST_THUMBNAIL_URL + ", "
114+
+ "COALESCE(COUNT(" + JOIN_PLAYLIST_ID + "), 0) AS " + PLAYLIST_STREAM_COUNT + ", "
115+
+ "COALESCE(SUM(" + STREAM_URL + " = :streamUrl), 0) AS "
116+
+ PLAYLIST_TIMES_STREAM_IS_CONTAINED
117+
118+
+ " FROM " + PLAYLIST_TABLE
119+
+ " LEFT JOIN " + PLAYLIST_STREAM_JOIN_TABLE
120+
+ " ON " + PLAYLIST_TABLE + "." + PLAYLIST_ID + " = " + JOIN_PLAYLIST_ID
121+
122+
+ " LEFT JOIN " + STREAM_TABLE
123+
+ " ON " + STREAM_TABLE + "." + STREAM_ID + " = " + JOIN_STREAM_ID
124+
+ " AND :streamUrl = :streamUrl"
125+
126+
+ " GROUP BY " + JOIN_PLAYLIST_ID
127+
+ " ORDER BY " + PLAYLIST_NAME + " COLLATE NOCASE ASC")
128+
Flowable<List<PlaylistDuplicatesEntry>> getPlaylistDuplicatesMetadata(String streamUrl);
106129
}

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

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import android.view.LayoutInflater;
55
import android.view.View;
66
import android.view.ViewGroup;
7+
import android.widget.TextView;
78
import android.widget.Toast;
89

910
import androidx.annotation.NonNull;
@@ -13,8 +14,8 @@
1314

1415
import org.schabi.newpipe.NewPipeDatabase;
1516
import org.schabi.newpipe.R;
16-
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
1717
import org.schabi.newpipe.database.playlist.model.PlaylistEntity;
18+
import org.schabi.newpipe.database.playlist.PlaylistDuplicatesEntry;
1819
import org.schabi.newpipe.database.stream.model.StreamEntity;
1920
import org.schabi.newpipe.local.LocalItemListAdapter;
2021
import org.schabi.newpipe.local.playlist.LocalPlaylistManager;
@@ -29,6 +30,7 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
2930

3031
private RecyclerView playlistRecyclerView;
3132
private LocalItemListAdapter playlistAdapter;
33+
private TextView playlistDuplicateIndicator;
3234

3335
private final CompositeDisposable playlistDisposables = new CompositeDisposable();
3436

@@ -64,19 +66,23 @@ public void onViewCreated(@NonNull final View view, @Nullable final Bundle saved
6466
playlistAdapter = new LocalItemListAdapter(getActivity());
6567
playlistAdapter.setSelectedListener(selectedItem -> {
6668
final List<StreamEntity> entities = getStreamEntities();
67-
if (selectedItem instanceof PlaylistMetadataEntry && entities != null) {
68-
onPlaylistSelected(playlistManager, (PlaylistMetadataEntry) selectedItem, entities);
69+
if (selectedItem instanceof PlaylistDuplicatesEntry && entities != null) {
70+
onPlaylistSelected(playlistManager,
71+
(PlaylistDuplicatesEntry) selectedItem, entities);
6972
}
7073
});
7174

7275
playlistRecyclerView = view.findViewById(R.id.playlist_list);
7376
playlistRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
7477
playlistRecyclerView.setAdapter(playlistAdapter);
7578

79+
playlistDuplicateIndicator = view.findViewById(R.id.playlist_duplicate);
80+
7681
final View newPlaylistButton = view.findViewById(R.id.newPlaylist);
7782
newPlaylistButton.setOnClickListener(ignored -> openCreatePlaylistDialog());
7883

79-
playlistDisposables.add(playlistManager.getPlaylists()
84+
playlistDisposables.add(playlistManager
85+
.getPlaylistDuplicates(getStreamEntities().get(0).getUrl())
8086
.observeOn(AndroidSchedulers.mainThread())
8187
.subscribe(this::onPlaylistsReceived));
8288
}
@@ -118,19 +124,36 @@ public void openCreatePlaylistDialog() {
118124
requireDialog().dismiss();
119125
}
120126

121-
private void onPlaylistsReceived(@NonNull final List<PlaylistMetadataEntry> playlists) {
122-
if (playlistAdapter != null && playlistRecyclerView != null) {
127+
private void onPlaylistsReceived(@NonNull final List<PlaylistDuplicatesEntry> playlists) {
128+
if (playlistAdapter != null
129+
&& playlistRecyclerView != null
130+
&& playlistDuplicateIndicator != null) {
123131
playlistAdapter.clearStreamItemList();
124132
playlistAdapter.addItems(playlists);
125133
playlistRecyclerView.setVisibility(View.VISIBLE);
134+
playlistDuplicateIndicator.setVisibility(
135+
anyPlaylistContainsDuplicates(playlists) ? View.VISIBLE : View.GONE);
126136
}
127137
}
128138

139+
private boolean anyPlaylistContainsDuplicates(final List<PlaylistDuplicatesEntry> playlists) {
140+
return playlists.stream()
141+
.anyMatch(playlist -> playlist.timesStreamIsContained > 0);
142+
}
143+
129144
private void onPlaylistSelected(@NonNull final LocalPlaylistManager manager,
130-
@NonNull final PlaylistMetadataEntry playlist,
145+
@NonNull final PlaylistDuplicatesEntry playlist,
131146
@NonNull final List<StreamEntity> streams) {
132-
final Toast successToast = Toast.makeText(getContext(),
133-
R.string.playlist_add_stream_success, Toast.LENGTH_SHORT);
147+
148+
final String toastText;
149+
if (playlist.timesStreamIsContained > 0) {
150+
toastText = getString(R.string.playlist_add_stream_success_duplicate,
151+
playlist.timesStreamIsContained);
152+
} else {
153+
toastText = getString(R.string.playlist_add_stream_success);
154+
}
155+
156+
final Toast successToast = Toast.makeText(getContext(), toastText, Toast.LENGTH_SHORT);
134157

135158
playlistDisposables.add(manager.appendToPlaylist(playlist.uid, streams)
136159
.observeOn(AndroidSchedulers.mainThread())

app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import android.view.ViewGroup;
55

66
import org.schabi.newpipe.database.LocalItem;
7+
import org.schabi.newpipe.database.playlist.PlaylistDuplicatesEntry;
78
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
89
import org.schabi.newpipe.local.LocalItemBuilder;
910
import org.schabi.newpipe.local.history.HistoryRecordManager;
@@ -13,6 +14,9 @@
1314
import java.time.format.DateTimeFormatter;
1415

1516
public class LocalPlaylistItemHolder extends PlaylistItemHolder {
17+
18+
private static final float GRAYED_OUT_ALPHA = 0.6f;
19+
1620
public LocalPlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final ViewGroup parent) {
1721
super(infoItemBuilder, parent);
1822
}
@@ -38,6 +42,13 @@ public void updateFromItem(final LocalItem localItem,
3842

3943
PicassoHelper.loadPlaylistThumbnail(item.thumbnailUrl).into(itemThumbnailView);
4044

45+
if (item instanceof PlaylistDuplicatesEntry
46+
&& ((PlaylistDuplicatesEntry) item).timesStreamIsContained > 0) {
47+
itemView.setAlpha(GRAYED_OUT_ALPHA);
48+
} else {
49+
itemView.setAlpha(1.0f);
50+
}
51+
4152
super.updateFromItem(localItem, historyRecordManager, dateTimeFormatter);
4253
}
4354
}

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import androidx.annotation.Nullable;
44

55
import org.schabi.newpipe.database.AppDatabase;
6+
import org.schabi.newpipe.database.playlist.PlaylistDuplicatesEntry;
67
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
78
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
89
import org.schabi.newpipe.database.playlist.dao.PlaylistDAO;
@@ -92,6 +93,18 @@ public Flowable<List<PlaylistMetadataEntry>> getPlaylists() {
9293
return playlistStreamTable.getPlaylistMetadata().subscribeOn(Schedulers.io());
9394
}
9495

96+
/**
97+
* Get playlists with attached information about how many times the provided stream is already
98+
* contained in each playlist.
99+
*
100+
* @param streamUrl the stream url for which to check for duplicates
101+
* @return a list of {@link PlaylistDuplicatesEntry}
102+
*/
103+
public Flowable<List<PlaylistDuplicatesEntry>> getPlaylistDuplicates(final String streamUrl) {
104+
return playlistStreamTable.getPlaylistDuplicatesMetadata(streamUrl)
105+
.subscribeOn(Schedulers.io());
106+
}
107+
95108
public Flowable<List<PlaylistStreamEntry>> getPlaylistStreams(final long playlistId) {
96109
return playlistStreamTable.getOrderedStreamsOf(playlistId).subscribeOn(Schedulers.io());
97110
}

app/src/main/res/layout/dialog_playlists.xml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,26 @@
3434
tools:ignore="RtlHardcoded" />
3535
</RelativeLayout>
3636

37+
<org.schabi.newpipe.views.NewPipeTextView
38+
android:id="@+id/playlist_duplicate"
39+
android:layout_width="match_parent"
40+
android:layout_height="wrap_content"
41+
android:layout_below="@+id/newPlaylist"
42+
android:layout_marginHorizontal="@dimen/video_item_search_padding"
43+
android:layout_marginBottom="@dimen/video_item_search_padding"
44+
android:gravity="center"
45+
android:text="@string/duplicate_in_playlist"
46+
android:textAppearance="?android:attr/textAppearanceMedium"
47+
android:textSize="13sp"
48+
android:visibility="gone"
49+
tools:text="@tools:sample/lorem[20]"
50+
tools:visibility="visible" />
51+
3752
<View
3853
android:id="@+id/separator"
3954
android:layout_width="match_parent"
4055
android:layout_height="1dp"
41-
android:layout_below="@+id/newPlaylist"
56+
android:layout_below="@+id/playlist_duplicate"
4257
android:layout_marginLeft="@dimen/video_item_search_padding"
4358
android:layout_marginRight="@dimen/video_item_search_padding"
4459
android:background="?attr/separator_color" />

app/src/main/res/values-ar/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,4 +780,6 @@
780780
<string name="import_subscriptions_hint">استيراد الاشتراكات أو تصديرها من القائمة المكونة من 3 نقاط</string>
781781
<string name="night_theme_available">هذا الخيار متاح فقط إذا تم تحديد %s للسمة</string>
782782
<string name="unset_playlist_thumbnail">إلغاء تعيين الصورة المصغرة الدائمة</string>
783+
<string name="msg_failed_to_copy">فشل النسخ إلى الحافظة</string>
784+
<string name="card">البطاقة</string>
783785
</resources>

0 commit comments

Comments
 (0)