mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-31 15:23:00 +00:00 
			
		
		
		
	Reuse DebounceSaver
This commit is contained in:
		| @@ -17,15 +17,15 @@ public interface PlaylistLocalItem extends LocalItem { | ||||
|             final List<PlaylistMetadataEntry> localPlaylists, | ||||
|             final List<PlaylistRemoteEntity> remotePlaylists) { | ||||
|  | ||||
|         // Merge localPlaylists and remotePlaylists by displayIndex. | ||||
|         // If two items have the same displayIndex, sort them in CASE_INSENSITIVE_ORDER. | ||||
|         // Merge localPlaylists and remotePlaylists by display index. | ||||
|         // If two items have the same display index, sort them in CASE_INSENSITIVE_ORDER. | ||||
|         // This algorithm is similar to the merge operation in merge sort. | ||||
|  | ||||
|         final List<PlaylistLocalItem> result = new ArrayList<>( | ||||
|                 localPlaylists.size() + remotePlaylists.size()); | ||||
|         final List<PlaylistLocalItem> itemsWithSameIndex = new ArrayList<>(); | ||||
|  | ||||
|         // The data from database may not be in the displayIndex order | ||||
|         // The data from database may not be in the display index order | ||||
|         Collections.sort(localPlaylists, | ||||
|                 Comparator.comparingLong(PlaylistMetadataEntry::getDisplayIndex)); | ||||
|         Collections.sort(remotePlaylists, | ||||
| @@ -58,7 +58,7 @@ public interface PlaylistLocalItem extends LocalItem { | ||||
|                         final List<PlaylistLocalItem> itemsWithSameIndex) { | ||||
|         if (!itemsWithSameIndex.isEmpty() | ||||
|                 && itemsWithSameIndex.get(0).getDisplayIndex() != item.getDisplayIndex()) { | ||||
|             // The new item has a different displayIndex, add previous items with same | ||||
|             // The new item has a different display index, add previous items with same | ||||
|             // index to the result. | ||||
|             addItemsWithSameIndex(result, itemsWithSameIndex); | ||||
|             itemsWithSameIndex.clear(); | ||||
|   | ||||
| @@ -35,6 +35,8 @@ import org.schabi.newpipe.local.holder.LocalBookmarkPlaylistItemHolder; | ||||
| import org.schabi.newpipe.local.holder.RemoteBookmarkPlaylistItemHolder; | ||||
| import org.schabi.newpipe.local.playlist.LocalPlaylistManager; | ||||
| import org.schabi.newpipe.local.playlist.RemotePlaylistManager; | ||||
| import org.schabi.newpipe.util.DebounceSavable; | ||||
| import org.schabi.newpipe.util.DebounceSaver; | ||||
| import org.schabi.newpipe.util.NavigationHelper; | ||||
| import org.schabi.newpipe.util.OnClickGesture; | ||||
|  | ||||
| @@ -42,7 +44,6 @@ import java.util.ArrayList; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.concurrent.TimeUnit; | ||||
| import java.util.concurrent.atomic.AtomicBoolean; | ||||
|  | ||||
| import icepick.State; | ||||
| @@ -50,12 +51,10 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; | ||||
| import io.reactivex.rxjava3.core.Flowable; | ||||
| import io.reactivex.rxjava3.disposables.CompositeDisposable; | ||||
| import io.reactivex.rxjava3.disposables.Disposable; | ||||
| import io.reactivex.rxjava3.subjects.PublishSubject; | ||||
|  | ||||
| public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistLocalItem>, Void> { | ||||
| public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistLocalItem>, Void> | ||||
|         implements DebounceSavable { | ||||
|  | ||||
|     // Save the list 10 seconds after the last change occurred | ||||
|     private static final long SAVE_DEBOUNCE_MILLIS = 10000; | ||||
|     private static final int MINIMUM_INITIAL_DRAG_VELOCITY = 12; | ||||
|     @State | ||||
|     protected Parcelable itemsListState; | ||||
| @@ -66,12 +65,10 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL | ||||
|     private RemotePlaylistManager remotePlaylistManager; | ||||
|     private ItemTouchHelper itemTouchHelper; | ||||
|  | ||||
|     private PublishSubject<Long> debouncedSaveSignal; | ||||
|  | ||||
|     /* Has the playlist been fully loaded from db */ | ||||
|     /* Have the bookmarked playlists been fully loaded from db */ | ||||
|     private AtomicBoolean isLoadingComplete; | ||||
|     /* Has the playlist been modified (e.g. items reordered or deleted) */ | ||||
|     private AtomicBoolean isModified; | ||||
|  | ||||
|     private DebounceSaver debounceSaver; | ||||
|  | ||||
|     // Map from (uid, local/remote item) to the saved display index in the database. | ||||
|     private Map<Pair<Long, LocalItem.LocalItemType>, Long> displayIndexInDatabase; | ||||
| @@ -91,9 +88,8 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL | ||||
|         remotePlaylistManager = new RemotePlaylistManager(database); | ||||
|         disposables = new CompositeDisposable(); | ||||
|  | ||||
|         debouncedSaveSignal = PublishSubject.create(); | ||||
|         isLoadingComplete = new AtomicBoolean(); | ||||
|         isModified = new AtomicBoolean(); | ||||
|         debounceSaver = new DebounceSaver(10000, this); | ||||
|  | ||||
|         displayIndexInDatabase = new HashMap<>(); | ||||
|     } | ||||
| @@ -183,9 +179,11 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL | ||||
|     public void startLoading(final boolean forceLoad) { | ||||
|         super.startLoading(forceLoad); | ||||
|  | ||||
|         disposables.add(getDebouncedSaver()); | ||||
|         if (debounceSaver != null) { | ||||
|             disposables.add(debounceSaver.getDebouncedSaver()); | ||||
|             debounceSaver.setIsModified(false); | ||||
|         } | ||||
|         isLoadingComplete.set(false); | ||||
|         isModified.set(false); | ||||
|  | ||||
|         Flowable.combineLatest(localPlaylistManager.getPlaylists(), | ||||
|                 remotePlaylistManager.getPlaylists(), PlaylistLocalItem::merge) | ||||
| @@ -225,21 +223,20 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL | ||||
|     @Override | ||||
|     public void onDestroy() { | ||||
|         super.onDestroy(); | ||||
|         if (debouncedSaveSignal != null) { | ||||
|             debouncedSaveSignal.onComplete(); | ||||
|         if (debounceSaver != null) { | ||||
|             debounceSaver.getDebouncedSaveSignal().onComplete(); | ||||
|         } | ||||
|         if (disposables != null) { | ||||
|             disposables.dispose(); | ||||
|         } | ||||
|  | ||||
|         debouncedSaveSignal = null; | ||||
|         debounceSaver = null; | ||||
|         disposables = null; | ||||
|         localPlaylistManager = null; | ||||
|         remotePlaylistManager = null; | ||||
|         itemsListState = null; | ||||
|  | ||||
|         isLoadingComplete = null; | ||||
|         isModified = null; | ||||
|         displayIndexInDatabase = null; | ||||
|     } | ||||
|  | ||||
| @@ -263,7 +260,7 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL | ||||
|  | ||||
|             @Override | ||||
|             public void onNext(final List<PlaylistLocalItem> subscriptions) { | ||||
|                 if (isModified == null || !isModified.get()) { | ||||
|                 if (debounceSaver == null || !debounceSaver.getIsModified()) { | ||||
|                     checkDisplayIndexModified(subscriptions); | ||||
|                     handleResult(subscriptions); | ||||
|                     isLoadingComplete.set(true); | ||||
| @@ -346,11 +343,11 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL | ||||
|         } | ||||
|         itemListAdapter.removeItem(item); | ||||
|  | ||||
|         saveChanges(); | ||||
|         debounceSaver.saveChanges(); | ||||
|     } | ||||
|  | ||||
|     private void checkDisplayIndexModified(@NonNull final List<PlaylistLocalItem> result) { | ||||
|         if (isModified != null && isModified.get()) { | ||||
|         if (debounceSaver != null && debounceSaver.getIsModified()) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| @@ -358,8 +355,9 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL | ||||
|  | ||||
|         // If the display index does not match actual index in the list, update the display index. | ||||
|         // This may happen when a new list is created | ||||
|         // or on the first run after database update | ||||
|         // or displayIndex is not continuous for some reason. | ||||
|         // or on the first run after database migration | ||||
|         // or display index is not continuous for some reason | ||||
|         // or the user changes the display index. | ||||
|         boolean isDisplayIndexModified = false; | ||||
|         for (int i = 0; i < result.size(); i++) { | ||||
|             final PlaylistLocalItem item = result.get(i); | ||||
| @@ -388,40 +386,19 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL | ||||
|         } | ||||
|  | ||||
|         if (isDisplayIndexModified) { | ||||
|             saveChanges(); | ||||
|             debounceSaver.saveChanges(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void saveChanges() { | ||||
|         if (isModified == null || debouncedSaveSignal == null) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         isModified.set(true); | ||||
|         debouncedSaveSignal.onNext(System.currentTimeMillis()); | ||||
|     } | ||||
|  | ||||
|     private Disposable getDebouncedSaver() { | ||||
|         if (debouncedSaveSignal == null) { | ||||
|             return Disposable.empty(); | ||||
|         } | ||||
|  | ||||
|         return debouncedSaveSignal | ||||
|                 .debounce(SAVE_DEBOUNCE_MILLIS, TimeUnit.MILLISECONDS) | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .subscribe(ignored -> saveImmediate(), throwable -> | ||||
|                         showError(new ErrorInfo(throwable, UserAction.SOMETHING_ELSE, | ||||
|                                 "Debounced saver"))); | ||||
|     } | ||||
|  | ||||
|     private void saveImmediate() { | ||||
|     @Override | ||||
|     public void saveImmediate() { | ||||
|         if (itemListAdapter == null) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // List must be loaded and modified in order to save | ||||
|         if (isLoadingComplete == null || isModified == null | ||||
|                 || !isLoadingComplete.get() || !isModified.get()) { | ||||
|         if (isLoadingComplete == null || debounceSaver == null | ||||
|                 || !isLoadingComplete.get() || !debounceSaver.getIsModified()) { | ||||
|             Log.w(TAG, "Attempting to save playlists in bookmark when bookmark " | ||||
|                     + "is not loaded or playlists not modified"); | ||||
|             return; | ||||
| @@ -485,8 +462,8 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL | ||||
|                         remoteItemsUpdate, remoteItemsDeleteUid) | ||||
|                                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                                 .subscribe(() -> { | ||||
|                                             if (isModified != null) { | ||||
|                                                 isModified.set(false); | ||||
|                                             if (debounceSaver != null) { | ||||
|                                                 debounceSaver.setIsModified(false); | ||||
|                                             } | ||||
|                                         }, | ||||
|                                         throwable -> showError(new ErrorInfo(throwable, | ||||
| @@ -544,7 +521,7 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL | ||||
|                 final int targetIndex = target.getBindingAdapterPosition(); | ||||
|                 final boolean isSwapped = itemListAdapter.swapItems(sourceIndex, targetIndex); | ||||
|                 if (isSwapped) { | ||||
|                     saveChanges(); | ||||
|                     debounceSaver.saveChanges(); | ||||
|                 } | ||||
|                 return isSwapped; | ||||
|             } | ||||
|   | ||||
| @@ -46,6 +46,8 @@ import org.schabi.newpipe.local.history.HistoryRecordManager; | ||||
| import org.schabi.newpipe.player.MainPlayer.PlayerType; | ||||
| import org.schabi.newpipe.player.playqueue.PlayQueue; | ||||
| import org.schabi.newpipe.player.playqueue.SinglePlayQueue; | ||||
| import org.schabi.newpipe.util.DebounceSavable; | ||||
| import org.schabi.newpipe.util.DebounceSaver; | ||||
| import org.schabi.newpipe.util.Localization; | ||||
| import org.schabi.newpipe.util.NavigationHelper; | ||||
| import org.schabi.newpipe.util.OnClickGesture; | ||||
| @@ -55,7 +57,6 @@ import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.Iterator; | ||||
| import java.util.List; | ||||
| import java.util.concurrent.TimeUnit; | ||||
| import java.util.concurrent.atomic.AtomicBoolean; | ||||
|  | ||||
| import icepick.State; | ||||
| @@ -64,11 +65,9 @@ import io.reactivex.rxjava3.core.Flowable; | ||||
| import io.reactivex.rxjava3.disposables.CompositeDisposable; | ||||
| import io.reactivex.rxjava3.disposables.Disposable; | ||||
| import io.reactivex.rxjava3.schedulers.Schedulers; | ||||
| import io.reactivex.rxjava3.subjects.PublishSubject; | ||||
|  | ||||
| public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistStreamEntry>, Void> { | ||||
|     // Save the list 10 seconds after the last change occurred | ||||
|     private static final long SAVE_DEBOUNCE_MILLIS = 10000; | ||||
| public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistStreamEntry>, Void> | ||||
|         implements DebounceSavable { | ||||
|     private static final int MINIMUM_INITIAL_DRAG_VELOCITY = 12; | ||||
|     @State | ||||
|     protected Long playlistId; | ||||
| @@ -85,13 +84,13 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | ||||
|     private LocalPlaylistManager playlistManager; | ||||
|     private Subscription databaseSubscription; | ||||
|  | ||||
|     private PublishSubject<Long> debouncedSaveSignal; | ||||
|     private CompositeDisposable disposables; | ||||
|  | ||||
|     /* Has the playlist been fully loaded from db */ | ||||
|     private AtomicBoolean isLoadingComplete; | ||||
|     /* Has the playlist been modified (e.g. items reordered or deleted) */ | ||||
|     private AtomicBoolean isModified; | ||||
|  | ||||
|     private DebounceSaver debounceSaver; | ||||
|  | ||||
|     /* Is the playlist currently being processed to remove watched videos */ | ||||
|     private boolean isRemovingWatched = false; | ||||
|  | ||||
| @@ -109,12 +108,11 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | ||||
|     public void onCreate(final Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         playlistManager = new LocalPlaylistManager(NewPipeDatabase.getInstance(requireContext())); | ||||
|         debouncedSaveSignal = PublishSubject.create(); | ||||
|  | ||||
|         disposables = new CompositeDisposable(); | ||||
|  | ||||
|         isLoadingComplete = new AtomicBoolean(); | ||||
|         isModified = new AtomicBoolean(); | ||||
|         debounceSaver = new DebounceSaver(10000, this); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -220,10 +218,13 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | ||||
|         if (disposables != null) { | ||||
|             disposables.clear(); | ||||
|         } | ||||
|         disposables.add(getDebouncedSaver()); | ||||
|  | ||||
|         if (debounceSaver != null) { | ||||
|             disposables.add(debounceSaver.getDebouncedSaver()); | ||||
|             debounceSaver.setIsModified(false); | ||||
|         } | ||||
|  | ||||
|         isLoadingComplete.set(false); | ||||
|         isModified.set(false); | ||||
|  | ||||
|         playlistManager.getPlaylistStreams(playlistId) | ||||
|                 .onBackpressureLatest() | ||||
| @@ -285,19 +286,18 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | ||||
|     @Override | ||||
|     public void onDestroy() { | ||||
|         super.onDestroy(); | ||||
|         if (debouncedSaveSignal != null) { | ||||
|             debouncedSaveSignal.onComplete(); | ||||
|         if (debounceSaver != null) { | ||||
|             debounceSaver.getDebouncedSaveSignal().onComplete(); | ||||
|         } | ||||
|         if (disposables != null) { | ||||
|             disposables.dispose(); | ||||
|         } | ||||
|  | ||||
|         debouncedSaveSignal = null; | ||||
|         debounceSaver = null; | ||||
|         playlistManager = null; | ||||
|         disposables = null; | ||||
|  | ||||
|         isLoadingComplete = null; | ||||
|         isModified = null; | ||||
|     } | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
| @@ -321,7 +321,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | ||||
|             @Override | ||||
|             public void onNext(final List<PlaylistStreamEntry> streams) { | ||||
|                 // Skip handling the result after it has been modified | ||||
|                 if (isModified == null || !isModified.get()) { | ||||
|                 if (debounceSaver == null || !debounceSaver.getIsModified()) { | ||||
|                     handleResult(streams); | ||||
|                     isLoadingComplete.set(true); | ||||
|                 } | ||||
| @@ -441,7 +441,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | ||||
|  | ||||
|                     itemListAdapter.clearStreamItemList(); | ||||
|                     itemListAdapter.addItems(notWatchedItems); | ||||
|                     saveChanges(); | ||||
|                     debounceSaver.saveChanges(); | ||||
|  | ||||
|  | ||||
|                     if (thumbnailVideoRemoved) { | ||||
| @@ -609,39 +609,18 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | ||||
|         } | ||||
|  | ||||
|         setVideoCount(itemListAdapter.getItemsList().size()); | ||||
|         saveChanges(); | ||||
|         debounceSaver.saveChanges(); | ||||
|     } | ||||
|  | ||||
|     private void saveChanges() { | ||||
|         if (isModified == null || debouncedSaveSignal == null) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         isModified.set(true); | ||||
|         debouncedSaveSignal.onNext(System.currentTimeMillis()); | ||||
|     } | ||||
|  | ||||
|     private Disposable getDebouncedSaver() { | ||||
|         if (debouncedSaveSignal == null) { | ||||
|             return Disposable.empty(); | ||||
|         } | ||||
|  | ||||
|         return debouncedSaveSignal | ||||
|                 .debounce(SAVE_DEBOUNCE_MILLIS, TimeUnit.MILLISECONDS) | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .subscribe(ignored -> saveImmediate(), throwable -> | ||||
|                         showError(new ErrorInfo(throwable, UserAction.SOMETHING_ELSE, | ||||
|                                 "Debounced saver"))); | ||||
|     } | ||||
|  | ||||
|     private void saveImmediate() { | ||||
|     @Override | ||||
|     public void saveImmediate() { | ||||
|         if (playlistManager == null || itemListAdapter == null) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // List must be loaded and modified in order to save | ||||
|         if (isLoadingComplete == null || isModified == null | ||||
|                 || !isLoadingComplete.get() || !isModified.get()) { | ||||
|         if (isLoadingComplete == null || debounceSaver == null | ||||
|                 || !isLoadingComplete.get() || !debounceSaver.getIsModified()) { | ||||
|             Log.w(TAG, "Attempting to save playlist when local playlist " | ||||
|                     + "is not loaded or not modified: playlist id=[" + playlistId + "]"); | ||||
|             return; | ||||
| @@ -664,8 +643,8 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .subscribe( | ||||
|                         () -> { | ||||
|                             if (isModified != null) { | ||||
|                                 isModified.set(false); | ||||
|                             if (debounceSaver != null) { | ||||
|                                 debounceSaver.setIsModified(false); | ||||
|                             } | ||||
|                         }, | ||||
|                         throwable -> showError(new ErrorInfo(throwable, | ||||
| @@ -708,7 +687,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt | ||||
|                 final int targetIndex = target.getBindingAdapterPosition(); | ||||
|                 final boolean isSwapped = itemListAdapter.swapItems(sourceIndex, targetIndex); | ||||
|                 if (isSwapped) { | ||||
|                     saveChanges(); | ||||
|                     debounceSaver.saveChanges(); | ||||
|                 } | ||||
|                 return isSwapped; | ||||
|             } | ||||
|   | ||||
| @@ -0,0 +1,15 @@ | ||||
| package org.schabi.newpipe.util; | ||||
|  | ||||
| import org.schabi.newpipe.error.ErrorInfo; | ||||
|  | ||||
| public interface DebounceSavable { | ||||
|  | ||||
|     /** | ||||
|      * Execute operations to save the data. <br> | ||||
|      * Must set {@link DebounceSaver#setIsModified(boolean)} false in this method manually | ||||
|      * after the data has been saved. | ||||
|      */ | ||||
|     void saveImmediate(); | ||||
|  | ||||
|     void showError(ErrorInfo errorInfo); | ||||
| } | ||||
							
								
								
									
										68
									
								
								app/src/main/java/org/schabi/newpipe/util/DebounceSaver.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								app/src/main/java/org/schabi/newpipe/util/DebounceSaver.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| package org.schabi.newpipe.util; | ||||
|  | ||||
| import org.schabi.newpipe.error.ErrorInfo; | ||||
| import org.schabi.newpipe.error.UserAction; | ||||
|  | ||||
| import java.util.concurrent.TimeUnit; | ||||
| import java.util.concurrent.atomic.AtomicBoolean; | ||||
|  | ||||
| import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; | ||||
| import io.reactivex.rxjava3.disposables.Disposable; | ||||
| import io.reactivex.rxjava3.subjects.PublishSubject; | ||||
|  | ||||
| public class DebounceSaver { | ||||
|  | ||||
|     private final long saveDebounceMillis; | ||||
|  | ||||
|     private final PublishSubject<Long> debouncedSaveSignal; | ||||
|  | ||||
|     private final DebounceSavable debounceSavable; | ||||
|  | ||||
|     // Has the object been modified | ||||
|     private final AtomicBoolean isModified; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Creates a new {@code DebounceSaver}. | ||||
|      * | ||||
|      * @param saveDebounceMillis    Save the object milliseconds later after the last change | ||||
|      *                              occurred. | ||||
|      * @param debounceSavable       The object containing data to be saved. | ||||
|      */ | ||||
|     public DebounceSaver(final long saveDebounceMillis, final DebounceSavable debounceSavable) { | ||||
|         this.saveDebounceMillis = saveDebounceMillis; | ||||
|         debouncedSaveSignal = PublishSubject.create(); | ||||
|         this.debounceSavable = debounceSavable; | ||||
|         this.isModified = new AtomicBoolean(); | ||||
|     } | ||||
|  | ||||
|     public boolean getIsModified() { | ||||
|         return isModified.get(); | ||||
|     } | ||||
|  | ||||
|     public void setIsModified(final boolean isModified) { | ||||
|         this.isModified.set(isModified); | ||||
|     } | ||||
|  | ||||
|     public PublishSubject<Long> getDebouncedSaveSignal() { | ||||
|         return debouncedSaveSignal; | ||||
|     } | ||||
|  | ||||
|     public Disposable getDebouncedSaver() { | ||||
|         return debouncedSaveSignal | ||||
|                 .debounce(saveDebounceMillis, TimeUnit.MILLISECONDS) | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .subscribe(ignored -> debounceSavable.saveImmediate(), throwable -> | ||||
|                         debounceSavable.showError(new ErrorInfo(throwable, | ||||
|                                 UserAction.SOMETHING_ELSE, "Debounced saver"))); | ||||
|     } | ||||
|  | ||||
|     public void saveChanges() { | ||||
|         if (isModified == null || debouncedSaveSignal == null) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         isModified.set(true); | ||||
|         debouncedSaveSignal.onNext(System.currentTimeMillis()); | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 GGAutomaton
					GGAutomaton