Skip to content

Commit bff7ada

Browse files
authored
Merge pull request #8248 from dtcxzyw/fix-readd-to-playlist
Fix inconsistency between user interaction and database commit order when re-adding videos to the playlist
2 parents ed33d1d + 3d5a8af commit bff7ada

3 files changed

Lines changed: 73 additions & 7 deletions

File tree

app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.schabi.newpipe.databinding.FragmentMainBinding;
3939
import org.schabi.newpipe.error.ErrorUtil;
4040
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
41+
import org.schabi.newpipe.local.playlist.LocalPlaylistFragment;
4142
import org.schabi.newpipe.settings.tabs.Tab;
4243
import org.schabi.newpipe.settings.tabs.TabsManager;
4344
import org.schabi.newpipe.util.NavigationHelper;
@@ -217,6 +218,12 @@ private void updateTitleForTab(final int tabPosition) {
217218
setTitle(tabsList.get(tabPosition).getTabName(requireContext()));
218219
}
219220

221+
public void commitPlaylistTabs() {
222+
pagerAdapter.getLocalPlaylistFragments()
223+
.stream()
224+
.forEach(LocalPlaylistFragment::commitChanges);
225+
}
226+
220227
private void updateTabLayoutPosition() {
221228
final ScrollableTabLayout tabLayout = binding.mainTabLayout;
222229
final ViewPager viewPager = binding.pager;
@@ -268,10 +275,18 @@ public void onTabReselected(final TabLayout.Tab tab) {
268275
updateTitleForTab(tab.getPosition());
269276
}
270277

271-
private static final class SelectedTabsPagerAdapter
278+
public static final class SelectedTabsPagerAdapter
272279
extends FragmentStatePagerAdapterMenuWorkaround {
273280
private final Context context;
274281
private final List<Tab> internalTabsList;
282+
/**
283+
* Keep reference to LocalPlaylistFragments, because their data can be modified by the user
284+
* during runtime and changes are not committed immediately. However, in some cases,
285+
* the changes need to be committed immediately by calling
286+
* {@link LocalPlaylistFragment#commitChanges()}.
287+
* The fragments are removed when {@link LocalPlaylistFragment#onDestroy()} is called.
288+
*/
289+
private final List<LocalPlaylistFragment> localPlaylistFragments = new ArrayList<>();
275290

276291
private SelectedTabsPagerAdapter(final Context context,
277292
final FragmentManager fragmentManager,
@@ -298,9 +313,17 @@ public Fragment getItem(final int position) {
298313
((BaseFragment) fragment).useAsFrontPage(true);
299314
}
300315

316+
if (fragment instanceof LocalPlaylistFragment) {
317+
localPlaylistFragments.add((LocalPlaylistFragment) fragment);
318+
}
319+
301320
return fragment;
302321
}
303322

323+
public List<LocalPlaylistFragment> getLocalPlaylistFragments() {
324+
return localPlaylistFragments;
325+
}
326+
304327
@Override
305328
public int getItemPosition(@NonNull final Object object) {
306329
// Causes adapter to reload all Fragments when

app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import androidx.appcompat.widget.Toolbar;
5555
import androidx.coordinatorlayout.widget.CoordinatorLayout;
5656
import androidx.core.content.ContextCompat;
57+
import androidx.fragment.app.Fragment;
5758
import androidx.preference.PreferenceManager;
5859

5960
import com.google.android.exoplayer2.PlaybackException;
@@ -84,11 +85,13 @@
8485
import org.schabi.newpipe.fragments.BackPressable;
8586
import org.schabi.newpipe.fragments.BaseStateFragment;
8687
import org.schabi.newpipe.fragments.EmptyFragment;
88+
import org.schabi.newpipe.fragments.MainFragment;
8789
import org.schabi.newpipe.fragments.list.comments.CommentsFragment;
8890
import org.schabi.newpipe.fragments.list.videos.RelatedItemsFragment;
8991
import org.schabi.newpipe.ktx.AnimationType;
9092
import org.schabi.newpipe.local.dialog.PlaylistDialog;
9193
import org.schabi.newpipe.local.history.HistoryRecordManager;
94+
import org.schabi.newpipe.local.playlist.LocalPlaylistFragment;
9295
import org.schabi.newpipe.player.Player;
9396
import org.schabi.newpipe.player.PlayerService;
9497
import org.schabi.newpipe.player.PlayerType;
@@ -472,10 +475,23 @@ private void setOnClickListeners() {
472475

473476
binding.detailControlsBackground.setOnClickListener(v -> openBackgroundPlayer(false));
474477
binding.detailControlsPopup.setOnClickListener(v -> openPopupPlayer(false));
475-
binding.detailControlsPlaylistAppend.setOnClickListener(makeOnClickListener(info ->
478+
binding.detailControlsPlaylistAppend.setOnClickListener(makeOnClickListener(info -> {
479+
if (getFM() != null && currentInfo != null) {
480+
final Fragment fragment = getParentFragmentManager().
481+
findFragmentById(R.id.fragment_holder);
482+
483+
// commit previous pending changes to database
484+
if (fragment instanceof LocalPlaylistFragment) {
485+
((LocalPlaylistFragment) fragment).commitChanges();
486+
} else if (fragment instanceof MainFragment) {
487+
((MainFragment) fragment).commitPlaylistTabs();
488+
}
489+
476490
disposables.add(PlaylistDialog.createCorrespondingDialog(requireContext(),
477491
List.of(new StreamEntity(info)),
478-
dialog -> dialog.show(getParentFragmentManager(), TAG)))));
492+
dialog -> dialog.show(getParentFragmentManager(), TAG)));
493+
}
494+
}));
479495
binding.detailControlsDownload.setOnClickListener(v -> {
480496
if (PermissionHelper.checkStoragePermissions(activity,
481497
PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE)) {

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

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.schabi.newpipe.error.ErrorInfo;
4242
import org.schabi.newpipe.error.UserAction;
4343
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
44+
import org.schabi.newpipe.fragments.MainFragment;
4445
import org.schabi.newpipe.fragments.list.playlist.PlaylistControlViewHolder;
4546
import org.schabi.newpipe.info_list.dialog.InfoItemDialog;
4647
import org.schabi.newpipe.info_list.dialog.StreamDialogDefaultEntry;
@@ -71,7 +72,7 @@
7172

7273
public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistStreamEntry>, Void>
7374
implements PlaylistControlViewHolder {
74-
// Save the list 10 seconds after the last change occurred
75+
/** Save the list 10 seconds after the last change occurred. */
7576
private static final long SAVE_DEBOUNCE_MILLIS = 10000;
7677
private static final int MINIMUM_INITIAL_DRAG_VELOCITY = 12;
7778
@State
@@ -92,13 +93,20 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
9293
private PublishSubject<Long> debouncedSaveSignal;
9394
private CompositeDisposable disposables;
9495

95-
/* Has the playlist been fully loaded from db */
96+
/** Whether the playlist has been fully loaded from db. */
9697
private AtomicBoolean isLoadingComplete;
97-
/* Has the playlist been modified (e.g. items reordered or deleted) */
98+
/** Whether the playlist has been modified (e.g. items reordered or deleted) */
9899
private AtomicBoolean isModified;
99-
/* Flag to prevent simultaneous rewrites of the playlist */
100+
/** Flag to prevent simultaneous rewrites of the playlist. */
100101
private boolean isRewritingPlaylist = false;
101102

103+
/**
104+
* The pager adapter that the fragment is created from when it is used as frontpage, i.e.
105+
* {@link #useAsFrontPage} is {@link true}.
106+
*/
107+
@Nullable
108+
private MainFragment.SelectedTabsPagerAdapter tabsPagerAdapter = null;
109+
102110
public static LocalPlaylistFragment getInstance(final long playlistId, final String name) {
103111
final LocalPlaylistFragment instance = new LocalPlaylistFragment();
104112
instance.setInitialData(playlistId, name);
@@ -158,6 +166,17 @@ protected ViewBinding getListHeader() {
158166
return headerBinding;
159167
}
160168

169+
/**
170+
* <p>Commit changes immediately if the playlist has been modified.</p>
171+
* Delete operations and other modifications will be committed to ensure that the database
172+
* is up to date, e.g. when the user adds the just deleted stream from another fragment.
173+
*/
174+
public void commitChanges() {
175+
if (isModified != null && isModified.get()) {
176+
saveImmediate();
177+
}
178+
}
179+
161180
@Override
162181
protected void initListeners() {
163182
super.initListeners();
@@ -291,6 +310,9 @@ public void onDestroy() {
291310
if (disposables != null) {
292311
disposables.dispose();
293312
}
313+
if (tabsPagerAdapter != null) {
314+
tabsPagerAdapter.getLocalPlaylistFragments().remove(this);
315+
}
294316

295317
debouncedSaveSignal = null;
296318
playlistManager = null;
@@ -877,5 +899,10 @@ private void createShareConfirmationDialog() {
877899
)
878900
.show();
879901
}
902+
903+
public void setTabsPagerAdapter(
904+
@Nullable final MainFragment.SelectedTabsPagerAdapter tabsPagerAdapter) {
905+
this.tabsPagerAdapter = tabsPagerAdapter;
906+
}
880907
}
881908

0 commit comments

Comments
 (0)