3535import org .schabi .newpipe .local .holder .RemoteBookmarkPlaylistItemHolder ;
3636import org .schabi .newpipe .local .playlist .LocalPlaylistManager ;
3737import org .schabi .newpipe .local .playlist .RemotePlaylistManager ;
38+ import org .schabi .newpipe .util .DebounceSavable ;
39+ import org .schabi .newpipe .util .DebounceSaver ;
3840import org .schabi .newpipe .util .NavigationHelper ;
3941import org .schabi .newpipe .util .OnClickGesture ;
4042
4143import java .util .ArrayList ;
4244import java .util .HashMap ;
4345import java .util .List ;
4446import java .util .Map ;
45- import java .util .concurrent .TimeUnit ;
4647import java .util .concurrent .atomic .AtomicBoolean ;
4748
4849import icepick .State ;
4950import io .reactivex .rxjava3 .android .schedulers .AndroidSchedulers ;
5051import io .reactivex .rxjava3 .core .Flowable ;
5152import io .reactivex .rxjava3 .disposables .CompositeDisposable ;
5253import io .reactivex .rxjava3 .disposables .Disposable ;
53- import io .reactivex .rxjava3 .subjects .PublishSubject ;
5454
55- public final class BookmarkFragment extends BaseLocalListFragment <List <PlaylistLocalItem >, Void > {
55+ public final class BookmarkFragment extends BaseLocalListFragment <List <PlaylistLocalItem >, Void >
56+ implements DebounceSavable {
5657
57- // Save the list 10 seconds after the last change occurred
58- private static final long SAVE_DEBOUNCE_MILLIS = 10000 ;
5958 private static final int MINIMUM_INITIAL_DRAG_VELOCITY = 12 ;
6059 @ State
6160 protected Parcelable itemsListState ;
@@ -66,12 +65,10 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL
6665 private RemotePlaylistManager remotePlaylistManager ;
6766 private ItemTouchHelper itemTouchHelper ;
6867
69- private PublishSubject <Long > debouncedSaveSignal ;
70-
71- /* Has the playlist been fully loaded from db */
68+ /* Have the bookmarked playlists been fully loaded from db */
7269 private AtomicBoolean isLoadingComplete ;
73- /* Has the playlist been modified (e.g. items reordered or deleted) */
74- private AtomicBoolean isModified ;
70+
71+ private DebounceSaver debounceSaver ;
7572
7673 // Map from (uid, local/remote item) to the saved display index in the database.
7774 private Map <Pair <Long , LocalItem .LocalItemType >, Long > displayIndexInDatabase ;
@@ -91,9 +88,8 @@ public void onCreate(final Bundle savedInstanceState) {
9188 remotePlaylistManager = new RemotePlaylistManager (database );
9289 disposables = new CompositeDisposable ();
9390
94- debouncedSaveSignal = PublishSubject .create ();
9591 isLoadingComplete = new AtomicBoolean ();
96- isModified = new AtomicBoolean ( );
92+ debounceSaver = new DebounceSaver ( 10000 , this );
9793
9894 displayIndexInDatabase = new HashMap <>();
9995 }
@@ -183,9 +179,11 @@ public void drag(final LocalItem selectedItem,
183179 public void startLoading (final boolean forceLoad ) {
184180 super .startLoading (forceLoad );
185181
186- disposables .add (getDebouncedSaver ());
182+ if (debounceSaver != null ) {
183+ disposables .add (debounceSaver .getDebouncedSaver ());
184+ debounceSaver .setIsModified (false );
185+ }
187186 isLoadingComplete .set (false );
188- isModified .set (false );
189187
190188 Flowable .combineLatest (localPlaylistManager .getPlaylists (),
191189 remotePlaylistManager .getPlaylists (), PlaylistLocalItem ::merge )
@@ -225,21 +223,20 @@ public void onDestroyView() {
225223 @ Override
226224 public void onDestroy () {
227225 super .onDestroy ();
228- if (debouncedSaveSignal != null ) {
229- debouncedSaveSignal .onComplete ();
226+ if (debounceSaver != null ) {
227+ debounceSaver . getDebouncedSaveSignal () .onComplete ();
230228 }
231229 if (disposables != null ) {
232230 disposables .dispose ();
233231 }
234232
235- debouncedSaveSignal = null ;
233+ debounceSaver = null ;
236234 disposables = null ;
237235 localPlaylistManager = null ;
238236 remotePlaylistManager = null ;
239237 itemsListState = null ;
240238
241239 isLoadingComplete = null ;
242- isModified = null ;
243240 displayIndexInDatabase = null ;
244241 }
245242
@@ -263,7 +260,7 @@ public void onSubscribe(final Subscription s) {
263260
264261 @ Override
265262 public void onNext (final List <PlaylistLocalItem > subscriptions ) {
266- if (isModified == null || !isModified . get ()) {
263+ if (debounceSaver == null || !debounceSaver . getIsModified ()) {
267264 checkDisplayIndexModified (subscriptions );
268265 handleResult (subscriptions );
269266 isLoadingComplete .set (true );
@@ -346,20 +343,21 @@ private void deleteItem(final PlaylistLocalItem item) {
346343 }
347344 itemListAdapter .removeItem (item );
348345
349- saveChanges ();
346+ debounceSaver . saveChanges ();
350347 }
351348
352349 private void checkDisplayIndexModified (@ NonNull final List <PlaylistLocalItem > result ) {
353- if (isModified != null && isModified . get ()) {
350+ if (debounceSaver != null && debounceSaver . getIsModified ()) {
354351 return ;
355352 }
356353
357354 displayIndexInDatabase .clear ();
358355
359356 // If the display index does not match actual index in the list, update the display index.
360357 // This may happen when a new list is created
361- // or on the first run after database update
362- // or displayIndex is not continuous for some reason.
358+ // or on the first run after database migration
359+ // or display index is not continuous for some reason
360+ // or the user changes the display index.
363361 boolean isDisplayIndexModified = false ;
364362 for (int i = 0 ; i < result .size (); i ++) {
365363 final PlaylistLocalItem item = result .get (i );
@@ -388,40 +386,19 @@ private void checkDisplayIndexModified(@NonNull final List<PlaylistLocalItem> re
388386 }
389387
390388 if (isDisplayIndexModified ) {
391- saveChanges ();
392- }
393- }
394-
395- private void saveChanges () {
396- if (isModified == null || debouncedSaveSignal == null ) {
397- return ;
389+ debounceSaver .saveChanges ();
398390 }
399-
400- isModified .set (true );
401- debouncedSaveSignal .onNext (System .currentTimeMillis ());
402391 }
403392
404- private Disposable getDebouncedSaver () {
405- if (debouncedSaveSignal == null ) {
406- return Disposable .empty ();
407- }
408-
409- return debouncedSaveSignal
410- .debounce (SAVE_DEBOUNCE_MILLIS , TimeUnit .MILLISECONDS )
411- .observeOn (AndroidSchedulers .mainThread ())
412- .subscribe (ignored -> saveImmediate (), throwable ->
413- showError (new ErrorInfo (throwable , UserAction .SOMETHING_ELSE ,
414- "Debounced saver" )));
415- }
416-
417- private void saveImmediate () {
393+ @ Override
394+ public void saveImmediate () {
418395 if (itemListAdapter == null ) {
419396 return ;
420397 }
421398
422399 // List must be loaded and modified in order to save
423- if (isLoadingComplete == null || isModified == null
424- || !isLoadingComplete .get () || !isModified . get ()) {
400+ if (isLoadingComplete == null || debounceSaver == null
401+ || !isLoadingComplete .get () || !debounceSaver . getIsModified ()) {
425402 Log .w (TAG , "Attempting to save playlists in bookmark when bookmark "
426403 + "is not loaded or playlists not modified" );
427404 return ;
@@ -485,8 +462,8 @@ private void saveImmediate() {
485462 remoteItemsUpdate , remoteItemsDeleteUid )
486463 .observeOn (AndroidSchedulers .mainThread ())
487464 .subscribe (() -> {
488- if (isModified != null ) {
489- isModified . set (false );
465+ if (debounceSaver != null ) {
466+ debounceSaver . setIsModified (false );
490467 }
491468 },
492469 throwable -> showError (new ErrorInfo (throwable ,
@@ -544,7 +521,7 @@ public boolean onMove(@NonNull final RecyclerView recyclerView,
544521 final int targetIndex = target .getBindingAdapterPosition ();
545522 final boolean isSwapped = itemListAdapter .swapItems (sourceIndex , targetIndex );
546523 if (isSwapped ) {
547- saveChanges ();
524+ debounceSaver . saveChanges ();
548525 }
549526 return isSwapped ;
550527 }
0 commit comments