Observable lists in release candidate state
This commit is contained in:
parent
6c68ccfdc5
commit
f5950841b4
|
|
@ -26,8 +26,24 @@ import java.util.Collection;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 23/05/16.
|
||||
* Class representing simple change of collection like insertion, remove or replacing/changing items.
|
||||
*
|
||||
* @param <TItem> Type of changing collection's items.
|
||||
*/
|
||||
public class Change<TItem> {
|
||||
|
||||
/**
|
||||
* Method to calculate changes between two collections.
|
||||
*
|
||||
* @param initialCollection Initial collection;
|
||||
* @param modifiedCollection Changed collection;
|
||||
* @param shrinkChangesToModifiedSize Flag to make position of changed items be less then modified collection size.
|
||||
* It is needed sometimes to not get exceptions like {@link ArrayIndexOutOfBoundsException}.
|
||||
* @param <TItem> Type of collections items.
|
||||
* @return Changes between collections.
|
||||
*/
|
||||
@NonNull
|
||||
public static <TItem> Collection<Change<TItem>> calculateCollectionChanges(@NonNull final Collection<TItem> initialCollection,
|
||||
@NonNull final Collection<TItem> modifiedCollection,
|
||||
|
|
@ -49,20 +65,40 @@ public class Change<TItem> {
|
|||
this.count = changedItems.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns type of change.
|
||||
*
|
||||
* @return Type of change.
|
||||
*/
|
||||
@NonNull
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns collection of items which this change applied to.
|
||||
*
|
||||
* @return Changed items.
|
||||
*/
|
||||
@NonNull
|
||||
public Collection<TItem> getChangedItems() {
|
||||
return changedItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns first index of changed item.
|
||||
*
|
||||
* @return Start of change.
|
||||
*/
|
||||
public int getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns count of changed items.
|
||||
*
|
||||
* @return Count of changed items.
|
||||
*/
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
|
@ -72,6 +108,9 @@ public class Change<TItem> {
|
|||
return type + " change of " + start + ":" + count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of change.
|
||||
*/
|
||||
public enum Type {
|
||||
INSERTED,
|
||||
CHANGED,
|
||||
|
|
|
|||
|
|
@ -36,7 +36,12 @@ import rx.Subscriber;
|
|||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 23/05/16.
|
||||
* TODO: description
|
||||
* Class to represent collection which is providing it's inner changes in Rx observable way.
|
||||
* Use {@link #observeChanges()} and {@link #observeItems()} to observe collection changes.
|
||||
* Use {@link #loadItem(int)} to load item asynchronously.
|
||||
* Methods {@link #size()} and {@link #get(int)} will return only already loaded items info.
|
||||
*
|
||||
* @param <TItem> Type of collection's items.
|
||||
*/
|
||||
public abstract class ObservableCollection<TItem> implements Serializable {
|
||||
|
||||
|
|
@ -73,14 +78,29 @@ public abstract class ObservableCollection<TItem> implements Serializable {
|
|||
.refCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return changes count number since collection creation.
|
||||
*
|
||||
* @return Changes count.
|
||||
*/
|
||||
public int getChangesCount() {
|
||||
return changesCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to notify that collection have changed.
|
||||
*
|
||||
* @param change Change of collection.
|
||||
*/
|
||||
protected void notifyAboutChange(@NonNull final Change<TItem> change) {
|
||||
notifyAboutChanges(Collections.singleton(change));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to notify that collection have changed.
|
||||
*
|
||||
* @param changes Changes of collection.
|
||||
*/
|
||||
protected void notifyAboutChanges(@NonNull final Collection<Change<TItem>> changes) {
|
||||
changesCount++;
|
||||
if (changesSubscriber != null) {
|
||||
|
|
@ -88,31 +108,80 @@ public abstract class ObservableCollection<TItem> implements Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes changes so it can be used to update UI based on changes etc.
|
||||
*
|
||||
* @return List of changes applied to collection.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<CollectionChange<TItem>> observeChanges() {
|
||||
return changesObservable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns already loaded item by position.
|
||||
* Use it carefully for collections which are loading asynchronously.
|
||||
*
|
||||
* @param position Position of item to get;
|
||||
* @return Item in collection by position.
|
||||
*/
|
||||
@NonNull
|
||||
public abstract TItem get(int position);
|
||||
|
||||
/**
|
||||
* Returns already loaded items.
|
||||
* Use it carefully for collections which are loading asynchronously.
|
||||
*
|
||||
* @return Collection of items.
|
||||
*/
|
||||
@NonNull
|
||||
public abstract Collection<TItem> getItems();
|
||||
|
||||
/**
|
||||
* Returns {@link Observable} to observe items collection.
|
||||
* Collection returned in onNext is not inner collection but it's copy, actually so you can't modify it.
|
||||
*
|
||||
* @return Collection's {@link Observable}.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<Collection<TItem>> observeItems() {
|
||||
return itemsObservable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns size of already loaded items.
|
||||
*
|
||||
* @return Size.
|
||||
*/
|
||||
public abstract int size();
|
||||
|
||||
/**
|
||||
* Returns if already loaded items are empty or not.
|
||||
*
|
||||
* @return True if items are empty.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link Observable} which is loading item by position.
|
||||
* It could return null in onNext callback if there is no item to load for such position.
|
||||
*
|
||||
* @param position Position to load item;
|
||||
* @return {@link Observable} to load item.
|
||||
*/
|
||||
@NonNull
|
||||
public abstract Observable<TItem> loadItem(int position);
|
||||
|
||||
/**
|
||||
* Returns {@link Observable} which is loading item by range.
|
||||
* It will return collection of loaded items in onNext callback.
|
||||
*
|
||||
* @param first First position of item to load;
|
||||
* @param last Last position of item to load;
|
||||
* @return {@link Observable} to load items.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<Collection<TItem>> loadRange(final int first, final int last) {
|
||||
final List<Observable<TItem>> itemsRequests = new ArrayList<>();
|
||||
|
|
@ -135,6 +204,11 @@ public abstract class ObservableCollection<TItem> implements Serializable {
|
|||
this.itemsObservable = createItemsObservable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Class which is representing change of collection. There could be multiple changes applied to collection.
|
||||
*
|
||||
* @param <TItem> Type of collection's items.
|
||||
*/
|
||||
public static class CollectionChange<TItem> {
|
||||
|
||||
private final int number;
|
||||
|
|
@ -146,10 +220,20 @@ public abstract class ObservableCollection<TItem> implements Serializable {
|
|||
this.changes = changes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number of change.
|
||||
*
|
||||
* @return Number of change.
|
||||
*/
|
||||
public int getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns collection of changes.
|
||||
*
|
||||
* @return Collection of changes.
|
||||
*/
|
||||
@NonNull
|
||||
public Collection<Change<TItem>> getChanges() {
|
||||
return changes;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,10 @@ import rx.functions.Func1;
|
|||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 02/06/2016.
|
||||
* TODO: fill description
|
||||
* {@link ObservableCollection} based on simple collection with filter inside.
|
||||
* Changing filter or collection will provide changes from {@link #observeChanges()}.
|
||||
*
|
||||
* @param <TItem> Type of collection's items.
|
||||
*/
|
||||
public class ObservableFilteredList<TItem> extends ObservableCollection<TItem> {
|
||||
|
||||
|
|
@ -44,7 +47,7 @@ public class ObservableFilteredList<TItem> extends ObservableCollection<TItem> {
|
|||
|
||||
public ObservableFilteredList(@NonNull final Collection<TItem> sourceCollection) {
|
||||
super();
|
||||
this.sourceCollection = sourceCollection;
|
||||
this.sourceCollection = new ArrayList<>(sourceCollection);
|
||||
this.filteredList = new ArrayList<>(sourceCollection);
|
||||
}
|
||||
|
||||
|
|
@ -56,16 +59,26 @@ public class ObservableFilteredList<TItem> extends ObservableCollection<TItem> {
|
|||
public ObservableFilteredList(@NonNull final Collection<TItem> sourceCollection,
|
||||
@NonNull final Func1<TItem, Boolean> filter) {
|
||||
super();
|
||||
this.sourceCollection = sourceCollection;
|
||||
this.sourceCollection = new ArrayList<>(sourceCollection);
|
||||
this.filter = filter;
|
||||
filteredList = filterCollection(this.sourceCollection, this.filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets collection of items to filter.
|
||||
*
|
||||
* @param sourceCollection Collection with items.
|
||||
*/
|
||||
public void setSourceCollection(@Nullable final Collection<TItem> sourceCollection) {
|
||||
this.sourceCollection = sourceCollection;
|
||||
this.sourceCollection = sourceCollection != null ? new ArrayList<>(sourceCollection) : null;
|
||||
updateCollections();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets filter that should return false as result of call to filter item.
|
||||
*
|
||||
* @param filter Function to filter item. True - item will stay, false - item will be filtered.
|
||||
*/
|
||||
public void setFilter(@Nullable final Func1<TItem, Boolean> filter) {
|
||||
this.filter = filter;
|
||||
updateCollections();
|
||||
|
|
|
|||
|
|
@ -34,7 +34,11 @@ import rx.Observable;
|
|||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 23/05/16.
|
||||
* TODO: description
|
||||
* {@link ObservableCollection} that is based on list.
|
||||
* So it is providing similar List's methods like adding/removing/clearing etc.
|
||||
* But! You can observe it's changes.
|
||||
*
|
||||
* @param <TItem> Type of collection's items.
|
||||
*/
|
||||
public class ObservableList<TItem> extends ObservableCollection<TItem> implements Serializable {
|
||||
|
||||
|
|
@ -53,10 +57,21 @@ public class ObservableList<TItem> extends ObservableCollection<TItem> implement
|
|||
items = new ArrayList<>(initialItems);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adding item at the end of list.
|
||||
*
|
||||
* @param item Item to add.
|
||||
*/
|
||||
public void add(@NonNull final TItem item) {
|
||||
add(items.size(), item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adding item at specific list position.
|
||||
*
|
||||
* @param position Position to add item to;
|
||||
* @param item Item to add.
|
||||
*/
|
||||
public void add(final int position, @NonNull final TItem item) {
|
||||
synchronized (this) {
|
||||
items.add(position, item);
|
||||
|
|
@ -64,10 +79,21 @@ public class ObservableList<TItem> extends ObservableCollection<TItem> implement
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adding items at the end of list.
|
||||
*
|
||||
* @param itemsToAdd Items to add.
|
||||
*/
|
||||
public void addAll(@NonNull final Collection<TItem> itemsToAdd) {
|
||||
addAll(items.size(), itemsToAdd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adding items at specific list position.
|
||||
*
|
||||
* @param position Position to add items to;
|
||||
* @param itemsToAdd Items to add.
|
||||
*/
|
||||
public void addAll(final int position, @NonNull final Collection<TItem> itemsToAdd) {
|
||||
synchronized (this) {
|
||||
if (!itemsToAdd.isEmpty()) {
|
||||
|
|
@ -77,10 +103,21 @@ public class ObservableList<TItem> extends ObservableCollection<TItem> implement
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removing item by position.
|
||||
*
|
||||
* @param position Position to remove item from.
|
||||
*/
|
||||
public void remove(final int position) {
|
||||
remove(position, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removing items by position.
|
||||
*
|
||||
* @param position Position to remove items from;
|
||||
* @param count Count of items to remove.
|
||||
*/
|
||||
public void remove(final int position, final int count) {
|
||||
if (count == 0) {
|
||||
return;
|
||||
|
|
@ -95,6 +132,9 @@ public class ObservableList<TItem> extends ObservableCollection<TItem> implement
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removing all items from list.
|
||||
*/
|
||||
public void clear() {
|
||||
synchronized (this) {
|
||||
final Change<TItem> change = new Change<>(Change.Type.REMOVED, items, 0);
|
||||
|
|
@ -121,10 +161,22 @@ public class ObservableList<TItem> extends ObservableCollection<TItem> implement
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace item at specific position.
|
||||
*
|
||||
* @param position Position to replace item;
|
||||
* @param item Item to place.
|
||||
*/
|
||||
public void update(final int position, @NonNull final TItem item) {
|
||||
update(position, Collections.singleton(item));
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace items at specific position.
|
||||
*
|
||||
* @param position Position to replace items;
|
||||
* @param updatedItems Items to place.
|
||||
*/
|
||||
public void update(final int position, @NonNull final Collection<TItem> updatedItems) {
|
||||
if (updatedItems.isEmpty()) {
|
||||
return;
|
||||
|
|
@ -139,6 +191,11 @@ public class ObservableList<TItem> extends ObservableCollection<TItem> implement
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resetting all items in list to new ones.
|
||||
*
|
||||
* @param newItems New items to set.
|
||||
*/
|
||||
public void set(@NonNull final Collection<TItem> newItems) {
|
||||
synchronized (this) {
|
||||
final Collection<Change<TItem>> changes = Change.calculateCollectionChanges(items, newItems, false);
|
||||
|
|
@ -157,6 +214,12 @@ public class ObservableList<TItem> extends ObservableCollection<TItem> implement
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns position of item in list.
|
||||
*
|
||||
* @param item Item to find index of;
|
||||
* @return Position of item in list or -1 if item not found.
|
||||
*/
|
||||
public int indexOf(@NonNull final TItem item) {
|
||||
synchronized (this) {
|
||||
return items.indexOf(item);
|
||||
|
|
|
|||
|
|
@ -26,17 +26,35 @@ import java.util.Collection;
|
|||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 23/05/16.
|
||||
* TODO: description
|
||||
* Object represents loaded items with reference to load other parts and info of are there more items to load or not.
|
||||
*
|
||||
* @param <TItem> Type of items to load;
|
||||
* @param <TReference> Type of reference to load other parts of items.
|
||||
*/
|
||||
public interface LoadedItems<TItem, TReference> {
|
||||
|
||||
int UNKNOWN_ITEMS_COUNT = -1;
|
||||
|
||||
/**
|
||||
* Returns count of items that could be loaded more.
|
||||
*
|
||||
* @return Count of items to load more or UNKNOWN_ITEMS_COUNT if it's unknown info.
|
||||
*/
|
||||
int getMoreItemsCount();
|
||||
|
||||
/**
|
||||
* Returns loaded items.
|
||||
*
|
||||
* @return Loaded items.
|
||||
*/
|
||||
@NonNull
|
||||
Collection<TItem> getItems();
|
||||
|
||||
/**
|
||||
* Returns reference that could be used to load other parts of items.
|
||||
*
|
||||
* @return Reference object.
|
||||
*/
|
||||
@Nullable
|
||||
TReference getReference();
|
||||
|
||||
|
|
|
|||
|
|
@ -23,12 +23,26 @@ import android.support.annotation.Nullable;
|
|||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 23/05/16.
|
||||
* TODO: description
|
||||
* Object represents loaded items with reference to load other parts and info of are there more items to load or not.
|
||||
*
|
||||
* @param <TItem> Type of items to load;
|
||||
* @param <TReference> Type of reference to load other parts of items;
|
||||
* @param <TNewerReference> Type of reference to load newer parts of items.
|
||||
*/
|
||||
public interface LoadedRenewableItems<TItem, TReference, TNewerReference> extends LoadedItems<TItem, TReference> {
|
||||
|
||||
/**
|
||||
* Returns count of new items other than loaded.
|
||||
*
|
||||
* @return Count of new items other than loaded.
|
||||
*/
|
||||
int getNewerItemsCount();
|
||||
|
||||
/**
|
||||
* Returns reference to load newer items from this loaded part.
|
||||
*
|
||||
* @return Reference to load newer items.
|
||||
*/
|
||||
@Nullable
|
||||
TNewerReference getNewerReference();
|
||||
|
||||
|
|
|
|||
|
|
@ -43,13 +43,22 @@ import rx.subjects.BehaviorSubject;
|
|||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 23/05/16.
|
||||
* TODO: description
|
||||
* {@link ObservableCollection} which is loading items more and more by paging/limit-offset/reference-based mechanisms.
|
||||
* To use this collection {@link MoreItemsLoader} should be created.
|
||||
* {@link MoreItemsLoader} is an object to load next block of items by info from previous loaded block (last loaded item/reference etc.).
|
||||
*
|
||||
* @param <TItem> Type of collection's items;
|
||||
* @param <TMoreReference> Type of reference object to help rightly loading next block of items;
|
||||
* @param <TLoadedItems> Type of loading block of items.
|
||||
*/
|
||||
public class LoadingMoreList<TItem, TMoreReference, TLoadedItems extends LoadedItems<TItem, TMoreReference>>
|
||||
extends ObservableCollection<TItem> {
|
||||
|
||||
private static final int RETRY_LOADING_AFTER_CHANGE_COUNT = 5;
|
||||
|
||||
private static final LoadedItemsFilter<?> DUPLICATES_REMOVER = (collectionObject, loadedItemsObject) ->
|
||||
collectionObject.equals(loadedItemsObject) ? FilterAction.REMOVE_FROM_LOADED_ITEMS : FilterAction.DO_NOTHING;
|
||||
|
||||
@NonNull
|
||||
private final Scheduler loaderScheduler = RxAndroidUtils.createLooperScheduler();
|
||||
@NonNull
|
||||
|
|
@ -58,7 +67,8 @@ public class LoadingMoreList<TItem, TMoreReference, TLoadedItems extends LoadedI
|
|||
private final BehaviorSubject<Integer> moreItemsCount = BehaviorSubject.create(LoadedItems.UNKNOWN_ITEMS_COUNT);
|
||||
@NonNull
|
||||
private final ObservableList<TItem> innerList = new ObservableList<>();
|
||||
private boolean removeDuplicates;
|
||||
@Nullable
|
||||
private LoadedItemsFilter<TItem> loadedItemsFilter;
|
||||
@Nullable
|
||||
private TMoreReference moreItemsReference;
|
||||
|
||||
|
|
@ -128,39 +138,75 @@ public class LoadingMoreList<TItem, TMoreReference, TLoadedItems extends LoadedI
|
|||
Lc.assertion("Illegal operation. Modify getInnerList()");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link ObservableList} of already loaded items so you can modify it.
|
||||
*
|
||||
* @return {@link ObservableList} of already loaded items.
|
||||
*/
|
||||
@NonNull
|
||||
protected ObservableList<TItem> getInnerList() {
|
||||
return innerList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if there are more items to load.
|
||||
*
|
||||
* @return True if there are more items to load.
|
||||
*/
|
||||
public boolean hasMoreItems() {
|
||||
return moreItemsCount.getValue() != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link Observable} which is providing status of if is there are more items to load or not.
|
||||
*
|
||||
* @return {@link Observable} of more items availability status.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<Boolean> observeHasMoreItems() {
|
||||
return moreItemsCount.map(count -> count != 0).distinctUntilChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link Observable} which is providing count of more items to load.
|
||||
*
|
||||
* @return {@link Observable} of more items availability status.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<Integer> observeMoreItemsCount() {
|
||||
return moreItemsCount.distinctUntilChanged();
|
||||
}
|
||||
|
||||
public boolean isRemoveDuplicates() {
|
||||
return removeDuplicates;
|
||||
/**
|
||||
* Sets if duplicates (compared by {@link #equals(Object)}) should be removed from loaded part of items right after loading.
|
||||
*
|
||||
* @param removeDuplicates True if duplicates should be removed.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
//unchecked: it's OK as we are using private static filter
|
||||
public void setRemoveDuplicates(final boolean removeDuplicates) {
|
||||
if (this.loadedItemsFilter != null && this.loadedItemsFilter != DUPLICATES_REMOVER) {
|
||||
Lc.assertion("Remove old filter manually first");
|
||||
return;
|
||||
}
|
||||
this.loadedItemsFilter = removeDuplicates ? (LoadedItemsFilter<TItem>) DUPLICATES_REMOVER : null;
|
||||
}
|
||||
|
||||
public void setRemoveDuplicates(final boolean removeDuplicates) {
|
||||
this.removeDuplicates = removeDuplicates;
|
||||
/**
|
||||
* Sets specific filter object which will remove items from already loaded part or from new loaded items right after loading.
|
||||
*
|
||||
* @param loadedItemsFilter {@link LoadedItemsFilter} to make decision of removing items.
|
||||
*/
|
||||
public void setLoadedItemsFilter(@Nullable final LoadedItemsFilter<TItem> loadedItemsFilter) {
|
||||
this.loadedItemsFilter = loadedItemsFilter;
|
||||
}
|
||||
|
||||
private void innerOnItemsLoaded(@NonNull final TLoadedItems loadedItems, final int insertPosition, final boolean reset) {
|
||||
final List<TItem> items = new ArrayList<>(loadedItems.getItems());
|
||||
final boolean lastPage = insertPosition > size() - 1;
|
||||
if (!reset) {
|
||||
if (removeDuplicates) {
|
||||
removeDuplicatesFromList(items);
|
||||
if (this.loadedItemsFilter != null) {
|
||||
removeDuplicatesFromList(items, this.loadedItemsFilter);
|
||||
}
|
||||
innerList.addAll(insertPosition, items);
|
||||
} else {
|
||||
|
|
@ -173,17 +219,28 @@ public class LoadingMoreList<TItem, TMoreReference, TLoadedItems extends LoadedI
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when any new items part loaded.
|
||||
*
|
||||
* @param loadedItems Loaded items;
|
||||
* @param insertPosition Position to insert loaded items;
|
||||
* @param reset Flag to reset previously loaded items or not.
|
||||
*/
|
||||
protected void onItemsLoaded(@NonNull final TLoadedItems loadedItems, final int insertPosition, final boolean reset) {
|
||||
innerOnItemsLoaded(loadedItems, insertPosition, reset);
|
||||
}
|
||||
|
||||
private void removeDuplicatesFromList(@NonNull final List<TItem> items) {
|
||||
private void removeDuplicatesFromList(@NonNull final List<TItem> items, @NonNull final LoadedItemsFilter<TItem> loadedItemsFilter) {
|
||||
for (int i = items.size() - 1; i >= 0; i--) {
|
||||
for (int j = 0; j < innerList.size(); j++) {
|
||||
if (innerList.get(j).equals(items.get(i))) {
|
||||
for (int j = innerList.size() - 1; j >= 0; j--) {
|
||||
final FilterAction filterAction = loadedItemsFilter.decideFilterAction(innerList.get(j), items.get(i));
|
||||
if (filterAction == FilterAction.REMOVE_FROM_LOADED_ITEMS) {
|
||||
items.remove(i);
|
||||
break;
|
||||
}
|
||||
if (filterAction == FilterAction.REMOVE_FROM_COLLECTION) {
|
||||
innerList.remove(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -205,6 +262,11 @@ public class LoadingMoreList<TItem, TMoreReference, TLoadedItems extends LoadedI
|
|||
return innerList.getItems();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link Observable} that is loading new items.
|
||||
*
|
||||
* @return {@link Observable} that is loading new items.
|
||||
*/
|
||||
@NonNull
|
||||
protected Observable<TLoadedItems> getLoadingMoreObservable() {
|
||||
return loadingMoreObservable;
|
||||
|
|
@ -229,11 +291,19 @@ public class LoadingMoreList<TItem, TMoreReference, TLoadedItems extends LoadedI
|
|||
.retry((number, throwable) -> throwable instanceof NotLoadedYetException);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all loaded items and resets collection's state.
|
||||
*/
|
||||
public void reset() {
|
||||
innerList.clear();
|
||||
resetState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all loaded items and resets collection's state but sets some initial items.
|
||||
*
|
||||
* @param initialItems initial items to be set after reset.
|
||||
*/
|
||||
public void reset(@NonNull final TLoadedItems initialItems) {
|
||||
onItemsLoaded(initialItems, 0, true);
|
||||
}
|
||||
|
|
@ -243,9 +313,44 @@ public class LoadingMoreList<TItem, TMoreReference, TLoadedItems extends LoadedI
|
|||
moreItemsCount.onNext(LoadedItems.UNKNOWN_ITEMS_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to do with some items while new part of items have loaded.
|
||||
*/
|
||||
public enum FilterAction {
|
||||
DO_NOTHING,
|
||||
REMOVE_FROM_COLLECTION,
|
||||
REMOVE_FROM_LOADED_ITEMS
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that is representing object to decide what to do with some items from already loaded and newly loaded part.
|
||||
* It should remove duplicates or items with changed data.
|
||||
*
|
||||
* @param <TItem> Type of collection's items.
|
||||
*/
|
||||
public interface LoadedItemsFilter<TItem> {
|
||||
|
||||
/**
|
||||
* Returns action to do based on items: do nothing, remove already loaded item or remove newly loaded item.
|
||||
*
|
||||
* @param collectionObject Item from collection of already loaded items;
|
||||
* @param loadedItemsObject Item from collection of newly loaded items part;
|
||||
* @return Action to do with items.
|
||||
*/
|
||||
@NonNull
|
||||
FilterAction decideFilterAction(@NonNull final TItem collectionObject, @NonNull final TItem loadedItemsObject);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper exception happens if {@link #loadItem(int)} called with big index and latest loading items part still not reached such item.
|
||||
*/
|
||||
protected static class NotLoadedYetException extends Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception happens if loading request changed during loading so loaded items are not actual anymore.
|
||||
*/
|
||||
protected static class RequestChangedDuringLoadingException extends Exception {
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import android.support.annotation.Nullable;
|
|||
import java.util.NoSuchElementException;
|
||||
|
||||
import ru.touchin.roboswag.core.log.Lc;
|
||||
import ru.touchin.roboswag.core.observables.collections.ObservableCollection;
|
||||
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||
import rx.Observable;
|
||||
import rx.exceptions.OnErrorThrowable;
|
||||
|
|
@ -33,7 +34,14 @@ import rx.subjects.BehaviorSubject;
|
|||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 23/05/16.
|
||||
* TODO: description
|
||||
* {@link ObservableCollection} which is loading items more and more by paging/limit-offset/reference-based mechanisms but also it is providing
|
||||
* interface to load newer items and info about it's loading availability.
|
||||
* To use this collection {@link MoreItemsLoader} and {@link NewerItemsLoader} should be created.
|
||||
*
|
||||
* @param <TItem> Type of collection's items;
|
||||
* @param <TReference> Type of reference object to help rightly loading next block of items;
|
||||
* @param <TNewerReference> Type of reference object to help rightly loading block of newer items;
|
||||
* @param <TLoadedItems> Type of loading block of items.
|
||||
*/
|
||||
public class LoadingRenewableList<TItem, TReference, TNewerReference,
|
||||
TLoadedItems extends LoadedRenewableItems<TItem, TReference, TNewerReference>>
|
||||
|
|
@ -49,8 +57,6 @@ public class LoadingRenewableList<TItem, TReference, TNewerReference,
|
|||
@NonNull
|
||||
private final Observable<TLoadedItems> loadingNewestObservable;
|
||||
|
||||
@SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
|
||||
//TODO
|
||||
public LoadingRenewableList(@NonNull final MoreItemsLoader<TItem, TReference, TLoadedItems> moreMoreItemsLoader,
|
||||
@NonNull final NewerItemsLoader<TItem, TReference, TNewerReference, TLoadedItems> newerItemsLoader) {
|
||||
super(moreMoreItemsLoader);
|
||||
|
|
@ -58,8 +64,6 @@ public class LoadingRenewableList<TItem, TReference, TNewerReference,
|
|||
this.loadingNewestObservable = createLoadingNewerObservable(newerItemsLoader, true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
|
||||
//TODO
|
||||
public LoadingRenewableList(@NonNull final MoreItemsLoader<TItem, TReference, TLoadedItems> moreMoreItemsLoader,
|
||||
@NonNull final NewerItemsLoader<TItem, TReference, TNewerReference, TLoadedItems> newerItemsLoader,
|
||||
@Nullable final TLoadedItems initialItems) {
|
||||
|
|
@ -113,15 +117,30 @@ public class LoadingRenewableList<TItem, TReference, TNewerReference,
|
|||
.refCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if there are new items to load.
|
||||
*
|
||||
* @return True if there are more items to load.
|
||||
*/
|
||||
public boolean hasNewerItems() {
|
||||
return newerItemsCount.getValue() != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link Observable} which is providing status of if is there are new items to load or not.
|
||||
*
|
||||
* @return {@link Observable} of more items availability status.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<Boolean> observeHasNewerItems() {
|
||||
return newerItemsCount.map(count -> count != 0).distinctUntilChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link Observable} which is providing count of new items to load.
|
||||
*
|
||||
* @return {@link Observable} of new items availability status.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<Integer> observeNewerItemsCount() {
|
||||
return newerItemsCount.distinctUntilChanged();
|
||||
|
|
@ -135,6 +154,12 @@ public class LoadingRenewableList<TItem, TReference, TNewerReference,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when newer items part loaded.
|
||||
*
|
||||
* @param loadedItems Loaded items;
|
||||
* @param renew Flag indicates is it loading just to load some new items or to load specific part of new items.
|
||||
*/
|
||||
protected void onNewerItemsLoaded(@NonNull final TLoadedItems loadedItems, final boolean renew) {
|
||||
updateNewerReference(loadedItems);
|
||||
}
|
||||
|
|
@ -146,11 +171,22 @@ public class LoadingRenewableList<TItem, TReference, TNewerReference,
|
|||
newerItemsCount.onNext(LoadedItems.UNKNOWN_ITEMS_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link Observable} that will load newer items by count returned by last loaded items part.
|
||||
*
|
||||
* @return {@link Observable} to load newer items.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<TLoadedItems> loadNewer() {
|
||||
return loadingNewerObservable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link Observable} that will load some newer limited by maximum pages loading results.
|
||||
*
|
||||
* @param maxPageDeep Limit to load pages;
|
||||
* @return Returns {@link Observable} to limited load newer items.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<TLoadedItems> loadNewest(final int maxPageDeep) {
|
||||
return loadingNewerObservable
|
||||
|
|
@ -163,6 +199,11 @@ public class LoadingRenewableList<TItem, TReference, TNewerReference,
|
|||
.last();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link Observable} that tries to load some newer items even if there are no info about count of them.
|
||||
*
|
||||
* @return {@link Observable} to load newer items.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<TLoadedItems> renew() {
|
||||
return loadingNewestObservable;
|
||||
|
|
|
|||
|
|
@ -23,8 +23,22 @@ import android.support.annotation.NonNull;
|
|||
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 02/06/2016.
|
||||
* Object that is loading next part of items by reference or position.
|
||||
*
|
||||
* @param <TItem> Type of items to be loaded;
|
||||
* @param <TMoreReference> Type of reference to be used to load next part of items;
|
||||
* @param <TLoadedItems> Type of loaded items part.
|
||||
*/
|
||||
public interface MoreItemsLoader<TItem, TMoreReference, TLoadedItems extends LoadedItems<TItem, TMoreReference>> {
|
||||
|
||||
/**
|
||||
* Returns {@link Observable} that could load next part of items.
|
||||
*
|
||||
* @param moreLoadRequest Request with info inside to load next part of items;
|
||||
* @return {@link Observable} of loading items.
|
||||
*/
|
||||
@NonNull
|
||||
Observable<TLoadedItems> load(@NonNull final MoreLoadRequest<TMoreReference> moreLoadRequest);
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,12 @@ import android.support.annotation.Nullable;
|
|||
|
||||
import ru.touchin.roboswag.core.utils.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 02/06/2016.
|
||||
* Request represents request to load next part of items.
|
||||
*
|
||||
* @param <TMoreReference> Type of reference to load next part of items.
|
||||
*/
|
||||
public class MoreLoadRequest<TMoreReference> {
|
||||
|
||||
@Nullable
|
||||
|
|
@ -34,11 +40,21 @@ public class MoreLoadRequest<TMoreReference> {
|
|||
this.nextPosition = nextPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns reference to be used to load next part of items.
|
||||
*
|
||||
* @return Reference object.
|
||||
*/
|
||||
@Nullable
|
||||
public TMoreReference getReference() {
|
||||
return moreReference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns position of next item to load.
|
||||
*
|
||||
* @return Position of next item.
|
||||
*/
|
||||
public int getNextPosition() {
|
||||
return nextPosition;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,14 @@ import android.support.annotation.NonNull;
|
|||
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 02/06/2016.
|
||||
* Object that is loading new part of items by reference.
|
||||
*
|
||||
* @param <TItem> Type of items to be loaded;
|
||||
* @param <TNewerReference> Type of reference to be used to load new part of items;
|
||||
* @param <TLoadedItems> Type of loaded items part.
|
||||
*/
|
||||
public interface NewerItemsLoader<TItem, TReference, TNewerReference,
|
||||
TLoadedItems extends LoadedRenewableItems<TItem, TReference, TNewerReference>> {
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,12 @@ import android.support.annotation.Nullable;
|
|||
|
||||
import ru.touchin.roboswag.core.utils.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 02/06/2016.
|
||||
* Request represents request to load new part of items.
|
||||
*
|
||||
* @param <TNewerReference> Type of reference to load new part of items.
|
||||
*/
|
||||
public class NewerLoadRequest<TNewerReference> {
|
||||
|
||||
@Nullable
|
||||
|
|
@ -34,11 +40,21 @@ public class NewerLoadRequest<TNewerReference> {
|
|||
this.newerItemsCount = newerItemsCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns reference to be used to load new part of items.
|
||||
*
|
||||
* @return Reference object.
|
||||
*/
|
||||
@Nullable
|
||||
public TNewerReference getNewerReference() {
|
||||
return newerReference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count of newer items to load.
|
||||
*
|
||||
* @return Count of newer items to load.
|
||||
*/
|
||||
public int getNewerItemsCount() {
|
||||
return newerItemsCount;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ public class Storable<TKey, TObject, TStoreObject> {
|
|||
|
||||
/**
|
||||
* Creates observable which is async setting value to store.
|
||||
* It could emit only completed and errors events.
|
||||
* NOTE: It could emit ONLY completed and errors events. It is not providing onNext event!
|
||||
* Errors won't be emitted if {@link #getStore()} implements {@link SafeStore} and {@link #getConverter()} implements {@link SafeConverter}.
|
||||
*
|
||||
* @param newValue Value to set;
|
||||
|
|
|
|||
Loading…
Reference in New Issue