diff --git a/src/main/java/ru/touchin/roboswag/core/android/support/v7/util/BatchingListUpdateCallback.java b/src/main/java/ru/touchin/roboswag/core/android/support/v7/util/BatchingListUpdateCallback.java
deleted file mode 100644
index 1b801c6..0000000
--- a/src/main/java/ru/touchin/roboswag/core/android/support/v7/util/BatchingListUpdateCallback.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package ru.touchin.roboswag.core.android.support.v7.util;
-
-/**
- * Wraps a {@link ListUpdateCallback} callback and batches operations that can be merged.
- *
- *
For instance, when 2 add operations comes that adds 2 consecutive elements,
- * BatchingListUpdateCallback merges them and calls the wrapped callback only once.
- *
- *
This is a general purpose class and is also used by
- * {@link DiffUtil.DiffResult DiffResult}
- *
- *
If you use this class to batch updates, you must call {@link #dispatchLastEvent()} when the
- * stream of update events drain.
- */
-@SuppressWarnings({"PMD", "checkstyle:all"})
-public class BatchingListUpdateCallback implements ListUpdateCallback {
-
- private static final int TYPE_NONE = 0;
- private static final int TYPE_ADD = 1;
- private static final int TYPE_REMOVE = 2;
- private static final int TYPE_CHANGE = 3;
-
- private final ListUpdateCallback mWrapped;
-
- private int mLastEventType = TYPE_NONE;
- private int mLastEventPosition = -1;
- private int mLastEventCount = -1;
- private Object mLastEventPayload = null;
-
- public BatchingListUpdateCallback(ListUpdateCallback callback) {
- mWrapped = callback;
- }
-
- /**
- * BatchingListUpdateCallback holds onto the last event to see if it can be merged with the
- * next one. When stream of events finish, you should call this method to dispatch the last
- * event.
- */
- public void dispatchLastEvent() {
- if (mLastEventType == TYPE_NONE) {
- return;
- }
- switch (mLastEventType) {
- case TYPE_ADD:
- mWrapped.onInserted(mLastEventPosition, mLastEventCount);
- break;
- case TYPE_REMOVE:
- mWrapped.onRemoved(mLastEventPosition, mLastEventCount);
- break;
- case TYPE_CHANGE:
- mWrapped.onChanged(mLastEventPosition, mLastEventCount, mLastEventPayload);
- break;
- }
- mLastEventPayload = null;
- mLastEventType = TYPE_NONE;
- }
-
- @Override
- public void onInserted(int position, int count) {
- if (mLastEventType == TYPE_ADD && position >= mLastEventPosition
- && position <= mLastEventPosition + mLastEventCount) {
- mLastEventCount += count;
- mLastEventPosition = Math.min(position, mLastEventPosition);
- return;
- }
- dispatchLastEvent();
- mLastEventPosition = position;
- mLastEventCount = count;
- mLastEventType = TYPE_ADD;
- }
-
- @Override
- public void onRemoved(int position, int count) {
- if (mLastEventType == TYPE_REMOVE && mLastEventPosition >= position &&
- mLastEventPosition <= position + count) {
- mLastEventCount += count;
- mLastEventPosition = position;
- return;
- }
- dispatchLastEvent();
- mLastEventPosition = position;
- mLastEventCount = count;
- mLastEventType = TYPE_REMOVE;
- }
-
- @Override
- public void onMoved(int fromPosition, int toPosition) {
- dispatchLastEvent(); // moves are not merged
- mWrapped.onMoved(fromPosition, toPosition);
- }
-
- @Override
- public void onChanged(int position, int count, Object payload) {
- if (mLastEventType == TYPE_CHANGE &&
- !(position > mLastEventPosition + mLastEventCount
- || position + count < mLastEventPosition || mLastEventPayload != payload)) {
- // take potential overlap into account
- int previousEnd = mLastEventPosition + mLastEventCount;
- mLastEventPosition = Math.min(position, mLastEventPosition);
- mLastEventCount = Math.max(previousEnd, position + count) - mLastEventPosition;
- return;
- }
- dispatchLastEvent();
- mLastEventPosition = position;
- mLastEventCount = count;
- mLastEventPayload = payload;
- mLastEventType = TYPE_CHANGE;
- }
-
-}
diff --git a/src/main/java/ru/touchin/roboswag/core/android/support/v7/util/DiffUtil.java b/src/main/java/ru/touchin/roboswag/core/android/support/v7/util/DiffUtil.java
deleted file mode 100644
index 4bdc364..0000000
--- a/src/main/java/ru/touchin/roboswag/core/android/support/v7/util/DiffUtil.java
+++ /dev/null
@@ -1,783 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package ru.touchin.roboswag.core.android.support.v7.util;
-
-import android.support.annotation.Nullable;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-/**
- * DiffUtil is a utility class that can calculate the difference between two lists and output a
- * list of update operations that converts the first list into the second one.
- *
- *
It can be used to calculate updates for a RecyclerView Adapter.
- *
- *
DiffUtil uses Eugene W. Myers's difference algorithm to calculate the minimal number of updates
- * to convert one list into another. Myers's algorithm does not handle items that are moved so
- * DiffUtil runs a second pass on the result to detect items that were moved.
- *
- *
If the lists are large, this operation may take significant time so you are advised to run this
- * on a background thread, get the {@link DiffResult} then apply it on the RecyclerView on the main
- * thread.
- *
- *
This algorithm is optimized for space and uses O(N) space to find the minimal
- * number of addition and removal operations between the two lists. It has O(N + D^2) expected time
- * performance where D is the length of the edit script.
- *
- *
If move detection is enabled, it takes an additional O(N^2) time where N is the total number of
- * added and removed items. If your lists are already sorted by the same constraint (e.g. a created
- * timestamp for a list of posts), you can disable move detection to improve performance.
- *
- *
The actual runtime of the algorithm significantly depends on the number of changes in the list
- * and the cost of your comparison methods. Below are some average run times for reference:
- * (The areSame list is composed of random UUID Strings and the tests are run on Nexus 5X with M)
- *
- * - 100 items and 10 modifications: avg: 0.39 ms, median: 0.35 ms
- *
- 100 items and 100 modifications: 3.82 ms, median: 3.75 ms
- *
- 100 items and 100 modifications without moves: 2.09 ms, median: 2.06 ms
- *
- 1000 items and 50 modifications: avg: 4.67 ms, median: 4.59 ms
- *
- 1000 items and 50 modifications without moves: avg: 3.59 ms, median: 3.50 ms
- *
- 1000 items and 200 modifications: 27.07 ms, median: 26.92 ms
- *
- 1000 items and 200 modifications without moves: 13.54 ms, median: 13.36 ms
- *
- *
- * Due to implementation constraints, the max size of the list can be 2^26.
- */
-@SuppressWarnings({"PMD", "checkstyle:all"})
-public class DiffUtil {
-
- private DiffUtil() {
- // utility class, no instance.
- }
-
- private static final Comparator SNAKE_COMPARATOR = new Comparator() {
- @Override
- public int compare(Snake o1, Snake o2) {
- int cmpX = o1.x - o2.x;
- return cmpX == 0 ? o1.y - o2.y : cmpX;
- }
- };
-
- // Myers' algorithm uses two lists as axis labels. In DiffUtil's implementation, `x` axis is
- // used for old list and `y` axis is used for new list.
-
- /**
- * Calculates the list of update operations that can covert one list into the other one.
- *
- * @param cb The callback that acts as a gateway to the backing list data
- * @return A DiffResult that contains the information about the edit sequence to convert the
- * old list into the new list.
- */
- public static DiffResult calculateDiff(Callback cb) {
- return calculateDiff(cb, true);
- }
-
- /**
- * Calculates the list of update operations that can covert one list into the other one.
- *
- * If your old and new lists are sorted by the same constraint and items never move (swap
- * positions), you can disable move detection which takes O(N^2) time where
- * N is the number of added, moved, removed items.
- *
- * @param cb The callback that acts as a gateway to the backing list data
- * @param detectMoves True if DiffUtil should try to detect moved items, false otherwise.
- * @return A DiffResult that contains the information about the edit sequence to convert the
- * old list into the new list.
- */
- public static DiffResult calculateDiff(Callback cb, boolean detectMoves) {
- final int oldSize = cb.getOldListSize();
- final int newSize = cb.getNewListSize();
-
- final List snakes = new ArrayList<>();
-
- // instead of a recursive implementation, we keep our own stack to avoid potential stack
- // overflow exceptions
- final List stack = new ArrayList<>();
-
- stack.add(new Range(0, oldSize, 0, newSize));
-
- final int max = oldSize + newSize + Math.abs(oldSize - newSize);
- // allocate forward and backward k-lines. K lines are diagonal lines in the matrix. (see the
- // paper for details)
- // These arrays lines keep the max reachable position for each k-line.
- final int[] forward = new int[max * 2];
- final int[] backward = new int[max * 2];
-
- // We pool the ranges to avoid allocations for each recursive call.
- final List rangePool = new ArrayList<>();
- while (!stack.isEmpty()) {
- final Range range = stack.remove(stack.size() - 1);
- final Snake snake = diffPartial(cb, range.oldListStart, range.oldListEnd,
- range.newListStart, range.newListEnd, forward, backward, max);
- if (snake != null) {
- if (snake.size > 0) {
- snakes.add(snake);
- }
- // offset the snake to convert its coordinates from the Range's area to global
- snake.x += range.oldListStart;
- snake.y += range.newListStart;
-
- // add new ranges for left and right
- final Range left = rangePool.isEmpty() ? new Range() : rangePool.remove(
- rangePool.size() - 1);
- left.oldListStart = range.oldListStart;
- left.newListStart = range.newListStart;
- if (snake.reverse) {
- left.oldListEnd = snake.x;
- left.newListEnd = snake.y;
- } else {
- if (snake.removal) {
- left.oldListEnd = snake.x - 1;
- left.newListEnd = snake.y;
- } else {
- left.oldListEnd = snake.x;
- left.newListEnd = snake.y - 1;
- }
- }
- stack.add(left);
-
- // re-use range for right
- //noinspection UnnecessaryLocalVariable
- final Range right = range;
- if (snake.reverse) {
- if (snake.removal) {
- right.oldListStart = snake.x + snake.size + 1;
- right.newListStart = snake.y + snake.size;
- } else {
- right.oldListStart = snake.x + snake.size;
- right.newListStart = snake.y + snake.size + 1;
- }
- } else {
- right.oldListStart = snake.x + snake.size;
- right.newListStart = snake.y + snake.size;
- }
- stack.add(right);
- } else {
- rangePool.add(range);
- }
-
- }
- // sort snakes
- Collections.sort(snakes, SNAKE_COMPARATOR);
-
- return new DiffResult(cb, snakes, forward, backward, detectMoves);
-
- }
-
- private static Snake diffPartial(Callback cb, int startOld, int endOld,
- int startNew, int endNew, int[] forward, int[] backward, int kOffset) {
- final int oldSize = endOld - startOld;
- final int newSize = endNew - startNew;
-
- if (endOld - startOld < 1 || endNew - startNew < 1) {
- return null;
- }
-
- final int delta = oldSize - newSize;
- final int dLimit = (oldSize + newSize + 1) / 2;
- Arrays.fill(forward, kOffset - dLimit - 1, kOffset + dLimit + 1, 0);
- Arrays.fill(backward, kOffset - dLimit - 1 + delta, kOffset + dLimit + 1 + delta, oldSize);
- final boolean checkInFwd = delta % 2 != 0;
- for (int d = 0; d <= dLimit; d++) {
- for (int k = -d; k <= d; k += 2) {
- // find forward path
- // we can reach k from k - 1 or k + 1. Check which one is further in the graph
- int x;
- final boolean removal;
- if (k == -d || k != d && forward[kOffset + k - 1] < forward[kOffset + k + 1]) {
- x = forward[kOffset + k + 1];
- removal = false;
- } else {
- x = forward[kOffset + k - 1] + 1;
- removal = true;
- }
- // set y based on x
- int y = x - k;
- // move diagonal as long as items match
- while (x < oldSize && y < newSize
- && cb.areItemsTheSame(startOld + x, startNew + y)) {
- x++;
- y++;
- }
- forward[kOffset + k] = x;
- if (checkInFwd && k >= delta - d + 1 && k <= delta + d - 1) {
- if (forward[kOffset + k] >= backward[kOffset + k]) {
- Snake outSnake = new Snake();
- outSnake.x = backward[kOffset + k];
- outSnake.y = outSnake.x - k;
- outSnake.size = forward[kOffset + k] - backward[kOffset + k];
- outSnake.removal = removal;
- outSnake.reverse = false;
- return outSnake;
- }
- }
- }
- for (int k = -d; k <= d; k += 2) {
- // find reverse path at k + delta, in reverse
- final int backwardK = k + delta;
- int x;
- final boolean removal;
- if (backwardK == d + delta || backwardK != -d + delta
- && backward[kOffset + backwardK - 1] < backward[kOffset + backwardK + 1]) {
- x = backward[kOffset + backwardK - 1];
- removal = false;
- } else {
- x = backward[kOffset + backwardK + 1] - 1;
- removal = true;
- }
-
- // set y based on x
- int y = x - backwardK;
- // move diagonal as long as items match
- while (x > 0 && y > 0
- && cb.areItemsTheSame(startOld + x - 1, startNew + y - 1)) {
- x--;
- y--;
- }
- backward[kOffset + backwardK] = x;
- if (!checkInFwd && k + delta >= -d && k + delta <= d) {
- if (forward[kOffset + backwardK] >= backward[kOffset + backwardK]) {
- Snake outSnake = new Snake();
- outSnake.x = backward[kOffset + backwardK];
- outSnake.y = outSnake.x - backwardK;
- outSnake.size =
- forward[kOffset + backwardK] - backward[kOffset + backwardK];
- outSnake.removal = removal;
- outSnake.reverse = true;
- return outSnake;
- }
- }
- }
- }
- throw new IllegalStateException("DiffUtil hit an unexpected case while trying to calculate"
- + " the optimal path. Please make sure your data is not changing during the"
- + " diff calculation.");
- }
-
- /**
- * A Callback class used by DiffUtil while calculating the diff between two lists.
- */
- public abstract static class Callback {
- /**
- * Returns the size of the old list.
- *
- * @return The size of the old list.
- */
- public abstract int getOldListSize();
-
- /**
- * Returns the size of the new list.
- *
- * @return The size of the new list.
- */
- public abstract int getNewListSize();
-
- /**
- * Called by the DiffUtil to decide whether two object represent the same Item.
- *
- * For example, if your items have unique ids, this method should check their id equality.
- *
- * @param oldItemPosition The position of the item in the old list
- * @param newItemPosition The position of the item in the new list
- * @return True if the two items represent the same object or false if they are different.
- */
- public abstract boolean areItemsTheSame(int oldItemPosition, int newItemPosition);
-
- /**
- * Called by the DiffUtil when it wants to check whether two items have the same data.
- * DiffUtil uses this information to detect if the contents of an item has changed.
- *
- * DiffUtil uses this method to check equality instead of {@link Object#equals(Object)}
- * so that you can change its behavior depending on your UI.
- *
- * This method is called only if {@link #areItemsTheSame(int, int)} returns
- * {@code true} for these items.
- *
- * @param oldItemPosition The position of the item in the old list
- * @param newItemPosition The position of the item in the new list which replaces the
- * oldItem
- * @return True if the contents of the items are the same or false if they are different.
- */
- public abstract boolean areContentsTheSame(int oldItemPosition, int newItemPosition);
-
- /**
- * When {@link #areItemsTheSame(int, int)} returns {@code true} for two items and
- * {@link #areContentsTheSame(int, int)} returns false for them, DiffUtil
- * calls this method to get a payload about the change.
- *
- * Default implementation returns {@code null}.
- *
- * @param oldItemPosition The position of the item in the old list
- * @param newItemPosition The position of the item in the new list
- * @return A payload object that represents the change between the two items.
- */
- @Nullable
- public Object getChangePayload(int oldItemPosition, int newItemPosition) {
- return null;
- }
- }
-
- /**
- * Snakes represent a match between two lists. It is optionally prefixed or postfixed with an
- * add or remove operation. See the Myers' paper for details.
- */
- static class Snake {
- /**
- * Position in the old list
- */
- int x;
-
- /**
- * Position in the new list
- */
- int y;
-
- /**
- * Number of matches. Might be 0.
- */
- int size;
-
- /**
- * If true, this is a removal from the original list followed by {@code size} matches.
- * If false, this is an addition from the new list followed by {@code size} matches.
- */
- boolean removal;
-
- /**
- * If true, the addition or removal is at the end of the snake.
- * If false, the addition or removal is at the beginning of the snake.
- */
- boolean reverse;
- }
-
- /**
- * Represents a range in two lists that needs to be solved.
- *
- * This internal class is used when running Myers' algorithm without recursion.
- */
- static class Range {
-
- int oldListStart, oldListEnd;
-
- int newListStart, newListEnd;
-
- public Range() {
- }
-
- public Range(int oldListStart, int oldListEnd, int newListStart, int newListEnd) {
- this.oldListStart = oldListStart;
- this.oldListEnd = oldListEnd;
- this.newListStart = newListStart;
- this.newListEnd = newListEnd;
- }
- }
-
- /**
- * This class holds the information about the result of a
- * {@link DiffUtil#calculateDiff(Callback, boolean)} call.
- *
- * You can consume the updates in a DiffResult via
- * {@link #dispatchUpdatesTo(ListUpdateCallback)}
- */
- public static class DiffResult {
- /**
- * While reading the flags below, keep in mind that when multiple items move in a list,
- * Myers's may pick any of them as the anchor item and consider that one NOT_CHANGED while
- * picking others as additions and removals. This is completely fine as we later detect
- * all moves.
- *
- * Below, when an item is mentioned to stay in the same "location", it means we won't
- * dispatch a move/add/remove for it, it DOES NOT mean the item is still in the same
- * position.
- */
- // item stayed the same.
- private static final int FLAG_NOT_CHANGED = 1;
- // item stayed in the same location but changed.
- private static final int FLAG_CHANGED = FLAG_NOT_CHANGED << 1;
- // Item has moved and also changed.
- private static final int FLAG_MOVED_CHANGED = FLAG_CHANGED << 1;
- // Item has moved but did not change.
- private static final int FLAG_MOVED_NOT_CHANGED = FLAG_MOVED_CHANGED << 1;
- // Ignore this update.
- // If this is an addition from the new list, it means the item is actually removed from an
- // earlier position and its move will be dispatched when we process the matching removal
- // from the old list.
- // If this is a removal from the old list, it means the item is actually added back to an
- // earlier index in the new list and we'll dispatch its move when we are processing that
- // addition.
- private static final int FLAG_IGNORE = FLAG_MOVED_NOT_CHANGED << 1;
-
- // since we are re-using the int arrays that were created in the Myers' step, we mask
- // change flags
- private static final int FLAG_OFFSET = 5;
-
- private static final int FLAG_MASK = (1 << FLAG_OFFSET) - 1;
-
- // The Myers' snakes. At this point, we only care about their diagonal sections.
- private final List mSnakes;
-
- // The list to keep oldItemStatuses. As we traverse old items, we assign flags to them
- // which also includes whether they were a real removal or a move (and its new index).
- private final int[] mOldItemStatuses;
- // The list to keep newItemStatuses. As we traverse new items, we assign flags to them
- // which also includes whether they were a real addition or a move(and its old index).
- private final int[] mNewItemStatuses;
- // The callback that was given to calcualte diff method.
- private final Callback mCallback;
-
- private final int mOldListSize;
-
- private final int mNewListSize;
-
- private final boolean mDetectMoves;
-
- /**
- * @param callback The callback that was used to calculate the diff
- * @param snakes The list of Myers' snakes
- * @param oldItemStatuses An int[] that can be re-purposed to keep metadata
- * @param newItemStatuses An int[] that can be re-purposed to keep metadata
- * @param detectMoves True if this DiffResult will try to detect moved items
- */
- DiffResult(Callback callback, List snakes, int[] oldItemStatuses,
- int[] newItemStatuses, boolean detectMoves) {
- mSnakes = snakes;
- mOldItemStatuses = oldItemStatuses;
- mNewItemStatuses = newItemStatuses;
- Arrays.fill(mOldItemStatuses, 0);
- Arrays.fill(mNewItemStatuses, 0);
- mCallback = callback;
- mOldListSize = callback.getOldListSize();
- mNewListSize = callback.getNewListSize();
- mDetectMoves = detectMoves;
- addRootSnake();
- findMatchingItems();
- }
-
- /**
- * We always add a Snake to 0/0 so that we can run loops from end to beginning and be done
- * when we run out of snakes.
- */
- private void addRootSnake() {
- Snake firstSnake = mSnakes.isEmpty() ? null : mSnakes.get(0);
- if (firstSnake == null || firstSnake.x != 0 || firstSnake.y != 0) {
- Snake root = new Snake();
- root.x = 0;
- root.y = 0;
- root.removal = false;
- root.size = 0;
- root.reverse = false;
- mSnakes.add(0, root);
- }
- }
-
- /**
- * This method traverses each addition / removal and tries to match it to a previous
- * removal / addition. This is how we detect move operations.
- *
- * This class also flags whether an item has been changed or not.
- *
- * DiffUtil does this pre-processing so that if it is running on a big list, it can be moved
- * to background thread where most of the expensive stuff will be calculated and kept in
- * the statuses maps. DiffResult uses this pre-calculated information while dispatching
- * the updates (which is probably being called on the main thread).
- */
- private void findMatchingItems() {
- int posOld = mOldListSize;
- int posNew = mNewListSize;
- // traverse the matrix from right bottom to 0,0.
- for (int i = mSnakes.size() - 1; i >= 0; i--) {
- final Snake snake = mSnakes.get(i);
- final int endX = snake.x + snake.size;
- final int endY = snake.y + snake.size;
- if (mDetectMoves) {
- while (posOld > endX) {
- // this is a removal. Check remaining snakes to see if this was added before
- findAddition(posOld, posNew, i);
- posOld--;
- }
- while (posNew > endY) {
- // this is an addition. Check remaining snakes to see if this was removed
- // before
- findRemoval(posOld, posNew, i);
- posNew--;
- }
- }
- for (int j = 0; j < snake.size; j++) {
- // matching items. Check if it is changed or not
- final int oldItemPos = snake.x + j;
- final int newItemPos = snake.y + j;
- final boolean theSame = mCallback
- .areContentsTheSame(oldItemPos, newItemPos);
- final int changeFlag = theSame ? FLAG_NOT_CHANGED : FLAG_CHANGED;
- mOldItemStatuses[oldItemPos] = (newItemPos << FLAG_OFFSET) | changeFlag;
- mNewItemStatuses[newItemPos] = (oldItemPos << FLAG_OFFSET) | changeFlag;
- }
- posOld = snake.x;
- posNew = snake.y;
- }
- }
-
- private void findAddition(int x, int y, int snakeIndex) {
- if (mOldItemStatuses[x - 1] != 0) {
- return; // already set by a latter item
- }
- findMatchingItem(x, y, snakeIndex, false);
- }
-
- private void findRemoval(int x, int y, int snakeIndex) {
- if (mNewItemStatuses[y - 1] != 0) {
- return; // already set by a latter item
- }
- findMatchingItem(x, y, snakeIndex, true);
- }
-
- /**
- * Finds a matching item that is before the given coordinates in the matrix
- * (before : left and above).
- *
- * @param x The x position in the matrix (position in the old list)
- * @param y The y position in the matrix (position in the new list)
- * @param snakeIndex The current snake index
- * @param removal True if we are looking for a removal, false otherwise
- * @return True if such item is found.
- */
- private boolean findMatchingItem(final int x, final int y, final int snakeIndex,
- final boolean removal) {
- final int myItemPos;
- int curX;
- int curY;
- if (removal) {
- myItemPos = y - 1;
- curX = x;
- curY = y - 1;
- } else {
- myItemPos = x - 1;
- curX = x - 1;
- curY = y;
- }
- for (int i = snakeIndex; i >= 0; i--) {
- final Snake snake = mSnakes.get(i);
- final int endX = snake.x + snake.size;
- final int endY = snake.y + snake.size;
- if (removal) {
- // check removals for a match
- for (int pos = curX - 1; pos >= endX; pos--) {
- if (mCallback.areItemsTheSame(pos, myItemPos)) {
- // found!
- final boolean theSame = mCallback.areContentsTheSame(pos, myItemPos);
- final int changeFlag = theSame ? FLAG_MOVED_NOT_CHANGED
- : FLAG_MOVED_CHANGED;
- mNewItemStatuses[myItemPos] = (pos << FLAG_OFFSET) | FLAG_IGNORE;
- mOldItemStatuses[pos] = (myItemPos << FLAG_OFFSET) | changeFlag;
- return true;
- }
- }
- } else {
- // check for additions for a match
- for (int pos = curY - 1; pos >= endY; pos--) {
- if (mCallback.areItemsTheSame(myItemPos, pos)) {
- // found
- final boolean theSame = mCallback.areContentsTheSame(myItemPos, pos);
- final int changeFlag = theSame ? FLAG_MOVED_NOT_CHANGED
- : FLAG_MOVED_CHANGED;
- mOldItemStatuses[x - 1] = (pos << FLAG_OFFSET) | FLAG_IGNORE;
- mNewItemStatuses[pos] = ((x - 1) << FLAG_OFFSET) | changeFlag;
- return true;
- }
- }
- }
- curX = snake.x;
- curY = snake.y;
- }
- return false;
- }
-
- /**
- * Dispatches update operations to the given Callback.
- *
- * These updates are atomic such that the first update call effects every update call that
- * comes after it (the same as RecyclerView).
- *
- * @param updateCallback The callback to receive the update operations.
- */
- public void dispatchUpdatesTo(ListUpdateCallback updateCallback) {
- final BatchingListUpdateCallback batchingCallback;
- if (updateCallback instanceof BatchingListUpdateCallback) {
- batchingCallback = (BatchingListUpdateCallback) updateCallback;
- } else {
- batchingCallback = new BatchingListUpdateCallback(updateCallback);
- // replace updateCallback with a batching callback and override references to
- // updateCallback so that we don't call it directly by mistake
- //noinspection UnusedAssignment
- updateCallback = batchingCallback;
- }
- // These are add/remove ops that are converted to moves. We track their positions until
- // their respective update operations are processed.
- final List postponedUpdates = new ArrayList<>();
- int posOld = mOldListSize;
- int posNew = mNewListSize;
- for (int snakeIndex = mSnakes.size() - 1; snakeIndex >= 0; snakeIndex--) {
- final Snake snake = mSnakes.get(snakeIndex);
- final int snakeSize = snake.size;
- final int endX = snake.x + snakeSize;
- final int endY = snake.y + snakeSize;
- if (endX < posOld) {
- dispatchRemovals(postponedUpdates, batchingCallback, endX, posOld - endX, endX);
- }
-
- if (endY < posNew) {
- dispatchAdditions(postponedUpdates, batchingCallback, endX, posNew - endY,
- endY);
- }
- for (int i = snakeSize - 1; i >= 0; i--) {
- if ((mOldItemStatuses[snake.x + i] & FLAG_MASK) == FLAG_CHANGED) {
- batchingCallback.onChanged(snake.x + i, 1,
- mCallback.getChangePayload(snake.x + i, snake.y + i));
- }
- }
- posOld = snake.x;
- posNew = snake.y;
- }
- batchingCallback.dispatchLastEvent();
- }
-
- private static PostponedUpdate removePostponedUpdate(List updates,
- int pos, boolean removal) {
- for (int i = updates.size() - 1; i >= 0; i--) {
- final PostponedUpdate update = updates.get(i);
- if (update.posInOwnerList == pos && update.removal == removal) {
- updates.remove(i);
- for (int j = i; j < updates.size(); j++) {
- // offset other ops since they swapped positions
- updates.get(j).currentPos += removal ? 1 : -1;
- }
- return update;
- }
- }
- return null;
- }
-
- private void dispatchAdditions(List postponedUpdates,
- ListUpdateCallback updateCallback, int start, int count, int globalIndex) {
- if (!mDetectMoves) {
- updateCallback.onInserted(start, count);
- return;
- }
- for (int i = count - 1; i >= 0; i--) {
- int status = mNewItemStatuses[globalIndex + i] & FLAG_MASK;
- switch (status) {
- case 0: // real addition
- updateCallback.onInserted(start, 1);
- for (PostponedUpdate update : postponedUpdates) {
- update.currentPos += 1;
- }
- break;
- case FLAG_MOVED_CHANGED:
- case FLAG_MOVED_NOT_CHANGED:
- final int pos = mNewItemStatuses[globalIndex + i] >> FLAG_OFFSET;
- final PostponedUpdate update = removePostponedUpdate(postponedUpdates, pos,
- true);
- // the item was moved from that position
- //noinspection ConstantConditions
- updateCallback.onMoved(update.currentPos, start);
- if (status == FLAG_MOVED_CHANGED) {
- // also dispatch a change
- updateCallback.onChanged(start, 1,
- mCallback.getChangePayload(pos, globalIndex + i));
- }
- break;
- case FLAG_IGNORE: // ignoring this
- postponedUpdates.add(new PostponedUpdate(globalIndex + i, start, false));
- break;
- default:
- throw new IllegalStateException(
- "unknown flag for pos " + (globalIndex + i) + " " + Long
- .toBinaryString(status));
- }
- }
- }
-
- private void dispatchRemovals(List postponedUpdates,
- ListUpdateCallback updateCallback, int start, int count, int globalIndex) {
- if (!mDetectMoves) {
- updateCallback.onRemoved(start, count);
- return;
- }
- for (int i = count - 1; i >= 0; i--) {
- final int status = mOldItemStatuses[globalIndex + i] & FLAG_MASK;
- switch (status) {
- case 0: // real removal
- updateCallback.onRemoved(start + i, 1);
- for (PostponedUpdate update : postponedUpdates) {
- update.currentPos -= 1;
- }
- break;
- case FLAG_MOVED_CHANGED:
- case FLAG_MOVED_NOT_CHANGED:
- final int pos = mOldItemStatuses[globalIndex + i] >> FLAG_OFFSET;
- final PostponedUpdate update = removePostponedUpdate(postponedUpdates, pos,
- false);
- // the item was moved to that position. we do -1 because this is a move not
- // add and removing current item offsets the target move by 1
- //noinspection ConstantConditions
- updateCallback.onMoved(start + i, update.currentPos - 1);
- if (status == FLAG_MOVED_CHANGED) {
- // also dispatch a change
- updateCallback.onChanged(update.currentPos - 1, 1,
- mCallback.getChangePayload(globalIndex + i, pos));
- }
- break;
- case FLAG_IGNORE: // ignoring this
- postponedUpdates.add(new PostponedUpdate(globalIndex + i, start + i, true));
- break;
- default:
- throw new IllegalStateException(
- "unknown flag for pos " + (globalIndex + i) + " " + Long
- .toBinaryString(status));
- }
- }
- }
- }
-
- /**
- * Represents an update that we skipped because it was a move.
- *
- * When an update is skipped, it is tracked as other updates are dispatched until the matching
- * add/remove operation is found at which point the tracked position is used to dispatch the
- * update.
- */
- private static class PostponedUpdate {
-
- int posInOwnerList;
-
- int currentPos;
-
- boolean removal;
-
- public PostponedUpdate(int posInOwnerList, int currentPos, boolean removal) {
- this.posInOwnerList = posInOwnerList;
- this.currentPos = currentPos;
- this.removal = removal;
- }
-
- }
-
-}
diff --git a/src/main/java/ru/touchin/roboswag/core/android/support/v7/util/ListUpdateCallback.java b/src/main/java/ru/touchin/roboswag/core/android/support/v7/util/ListUpdateCallback.java
deleted file mode 100644
index 06d9dc0..0000000
--- a/src/main/java/ru/touchin/roboswag/core/android/support/v7/util/ListUpdateCallback.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package ru.touchin.roboswag.core.android.support.v7.util;
-
-/**
- * An interface that can receive Update operations that are applied to a list.
- *
- *
This class can be used together with DiffUtil to detect changes between two lists.
- */
-@SuppressWarnings({"PMD", "checkstyle:all"})
-public interface ListUpdateCallback {
-
- /**
- * Called when {@code count} number of items are inserted at the given position.
- *
- * @param position The position of the new item.
- * @param count The number of items that have been added.
- */
- void onInserted(int position, int count);
-
- /**
- * Called when {@code count} number of items are removed from the given position.
- *
- * @param position The position of the item which has been removed.
- * @param count The number of items which have been removed.
- */
- void onRemoved(int position, int count);
-
- /**
- * Called when an item changes its position in the list.
- *
- * @param fromPosition The previous position of the item before the move.
- * @param toPosition The new position of the item.
- */
- void onMoved(int fromPosition, int toPosition);
-
- /**
- * Called when {@code count} number of items are updated at the given position.
- *
- * @param position The position of the item which has been updated.
- * @param count The number of items which has changed.
- */
- void onChanged(int position, int count, Object payload);
-
-}
diff --git a/src/main/java/ru/touchin/roboswag/core/observables/collections/ObservableCollection.java b/src/main/java/ru/touchin/roboswag/core/observables/collections/ObservableCollection.java
deleted file mode 100644
index 4001596..0000000
--- a/src/main/java/ru/touchin/roboswag/core/observables/collections/ObservableCollection.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
- *
- * This file is part of RoboSwag library.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package ru.touchin.roboswag.core.observables.collections;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import ru.touchin.roboswag.core.observables.collections.changes.Change;
-import ru.touchin.roboswag.core.observables.collections.changes.CollectionChanges;
-import io.reactivex.Emitter;
-import io.reactivex.Observable;
-
-/**
- * Created by Gavriil Sitnikov on 23/05/16.
- * 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.
- * Methods {@link #size()} and {@link #get(int)} will return only already loaded items info.
- *
- * @param Type of collection's items.
- */
-public abstract class ObservableCollection {
-
- private int changesCount;
- @NonNull
- private transient Observable> changesObservable;
- @NonNull
- private transient Observable> itemsObservable;
- @Nullable
- private transient Emitter super CollectionChanges> changesEmitter;
-
- public ObservableCollection() {
- this.changesObservable = createChangesObservable();
- this.itemsObservable = createItemsObservable();
- }
-
- @NonNull
- private Observable> createChangesObservable() {
- return Observable
- .>create(emitter -> this.changesEmitter = emitter)
- .doOnDispose(() -> this.changesEmitter = null)
- .share();
- }
-
- @NonNull
- private Observable> createItemsObservable() {
- return Observable
- //switchOnNext to calculate getItems() on subscription but not on that method calling moment
- .switchOnNext(Observable.fromCallable(() -> observeChanges().map(changes -> getItems()).startWith(getItems())))
- .replay(1)
- .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 List insertedItems,
- @NonNull final List removedItems,
- @NonNull final Change change) {
- notifyAboutChanges(insertedItems, removedItems, Collections.singleton(change));
- }
-
- /**
- * Method to notify that collection have changed.
- *
- * @param insertedItems Collection of inserted items;
- * @param removedItems Collection of removed items;
- * @param changes Changes of collection.
- */
- protected void notifyAboutChanges(@NonNull final List insertedItems,
- @NonNull final List removedItems,
- @NonNull final Collection changes) {
- if (changes.isEmpty()) {
- return;
- }
- changesCount++;
- if (changesEmitter != null) {
- changesEmitter.onNext(new CollectionChanges<>(changesCount, insertedItems, removedItems, changes));
- }
- }
-
- /**
- * Observes changes so it can be used to update UI based on changes etc.
- *
- * @return List of changes applied to collection.
- */
- @NonNull
- public Observable> 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 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> 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;
- }
-
- private void writeObject(@NonNull final ObjectOutputStream outputStream) throws IOException {
- outputStream.writeInt(changesCount);
- }
-
- private void readObject(@NonNull final ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
- changesCount = inputStream.readInt();
- this.changesObservable = createChangesObservable();
- this.itemsObservable = createItemsObservable();
- }
-
-}
diff --git a/src/main/java/ru/touchin/roboswag/core/observables/collections/ObservableFilteredList.java b/src/main/java/ru/touchin/roboswag/core/observables/collections/ObservableFilteredList.java
deleted file mode 100644
index e92550f..0000000
--- a/src/main/java/ru/touchin/roboswag/core/observables/collections/ObservableFilteredList.java
+++ /dev/null
@@ -1,159 +0,0 @@
-package ru.touchin.roboswag.core.observables.collections;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.Executors;
-
-import ru.touchin.roboswag.core.observables.collections.changes.DefaultCollectionsChangesCalculator;
-import io.reactivex.Scheduler;
-import io.reactivex.disposables.Disposable;
-import io.reactivex.functions.Function;
-import io.reactivex.schedulers.Schedulers;
-import ru.touchin.roboswag.core.log.Lc;
-
-/**
- * Created by Gavriil Sitnikov on 02/06/2016.
- * {@link ObservableCollection} based on simple collection with filter inside.
- * Changing filter or collection will provide changes from {@link #observeChanges()}.
- *
- * @param Type of collection's items.
- */
-public class ObservableFilteredList extends ObservableCollection {
-
- // we need to filter on 1 thread to prevent parallel filtering
- private static final Scheduler FILTER_SCHEDULER = Schedulers.from(Executors.newSingleThreadExecutor());
-
- @NonNull
- private static List filterCollection(@NonNull final Collection sourceCollection,
- @Nullable final Function filter) {
- if (filter == null) {
- return new ArrayList<>(sourceCollection);
- }
- final List result = new ArrayList<>(sourceCollection.size());
- try {
- for (final TItem item : sourceCollection) {
- if (filter.apply(item)) {
- result.add(item);
- }
- }
- } catch (final Exception exception) {
- Lc.assertion(exception);
- }
- return result;
- }
-
- @NonNull
- private List filteredList;
- @NonNull
- private ObservableCollection sourceCollection;
- @Nullable
- private Function filter;
- @Nullable
- private Disposable sourceCollectionSubscription;
-
- public ObservableFilteredList() {
- this(new ArrayList<>(), null);
- }
-
- public ObservableFilteredList(@NonNull final Function filter) {
- this(new ArrayList<>(), filter);
- }
-
- public ObservableFilteredList(@NonNull final Collection sourceCollection, @Nullable final Function filter) {
- this(new ObservableList<>(sourceCollection), filter);
- }
-
- public ObservableFilteredList(@NonNull final ObservableCollection sourceCollection, @Nullable final Function filter) {
- super();
- this.filter = filter;
- this.sourceCollection = sourceCollection;
- this.filteredList = filterCollection(this.sourceCollection.getItems(), this.filter);
- updateInternal();
- }
-
- /**
- * Sets collection of items to filter.
- *
- * @param sourceCollection Collection with items.
- */
- public void setSourceCollection(@Nullable final ObservableCollection sourceCollection) {
- this.sourceCollection = sourceCollection != null ? sourceCollection : new ObservableList<>();
- updateInternal();
- }
-
- /**
- * Sets collection of items to filter.
- *
- * @param sourceCollection Collection with items.
- */
- public void setSourceCollection(@Nullable final Collection sourceCollection) {
- this.sourceCollection = sourceCollection != null ? new ObservableList<>(sourceCollection) : new ObservableList<>();
- updateInternal();
- }
-
- /**
- * 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 Function filter) {
- this.filter = filter;
- updateInternal();
- }
-
- private void updateInternal() {
- if (sourceCollectionSubscription != null) {
- sourceCollectionSubscription.dispose();
- sourceCollectionSubscription = null;
- }
- sourceCollectionSubscription = sourceCollection.observeItems()
- .observeOn(FILTER_SCHEDULER)
- .subscribe(items -> {
- final List oldFilteredList = filteredList;
- filteredList = filterCollection(items, filter);
- final DefaultCollectionsChangesCalculator calculator
- = new DefaultCollectionsChangesCalculator<>(oldFilteredList, filteredList, false);
- notifyAboutChanges(calculator.calculateInsertedItems(), calculator.calculateRemovedItems(), calculator.calculateChanges());
- });
- }
-
- /**
- * Updates collection by current filter. Use it if some item's parameter which is important for filtering have changing.
- */
- public void update() {
- updateInternal();
- }
-
- @Override
- public int size() {
- return filteredList.size();
- }
-
- @NonNull
- @Override
- public TItem get(final int position) {
- return filteredList.get(position);
- }
-
- @NonNull
- @Override
- public Collection getItems() {
- return Collections.unmodifiableCollection(filteredList);
- }
-
- /**
- * Returns source non-filtered observable collection of items.
- *
- * @return Non-filtered collection of items.
- */
- @NonNull
- public ObservableCollection getSourceCollection() {
- return sourceCollection;
- }
-
-}
diff --git a/src/main/java/ru/touchin/roboswag/core/observables/collections/ObservableList.java b/src/main/java/ru/touchin/roboswag/core/observables/collections/ObservableList.java
deleted file mode 100644
index 14dcd19..0000000
--- a/src/main/java/ru/touchin/roboswag/core/observables/collections/ObservableList.java
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
- * Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
- *
- * This file is part of RoboSwag library.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package ru.touchin.roboswag.core.observables.collections;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import ru.touchin.roboswag.core.log.Lc;
-import ru.touchin.roboswag.core.observables.collections.changes.Change;
-import ru.touchin.roboswag.core.observables.collections.changes.ChangePayloadProducer;
-import ru.touchin.roboswag.core.observables.collections.changes.CollectionsChangesCalculator;
-import ru.touchin.roboswag.core.observables.collections.changes.DefaultCollectionsChangesCalculator;
-import ru.touchin.roboswag.core.observables.collections.changes.DiffCollectionsChangesCalculator;
-import ru.touchin.roboswag.core.observables.collections.changes.SameItemsPredicate;
-
-/**
- * Created by Gavriil Sitnikov on 23/05/16.
- * {@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 Type of collection's items.
- */
-public class ObservableList extends ObservableCollection implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- @NonNull
- private List items;
- private boolean detectMoves;
- @Nullable
- private SameItemsPredicate sameItemsPredicate;
- @Nullable
- private ChangePayloadProducer changePayloadProducer;
- @Nullable
- private ObservableList diffUtilsSource;
-
- public ObservableList() {
- super();
- items = new ArrayList<>();
- }
-
- public ObservableList(@NonNull final Collection initialItems) {
- super();
- 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);
- notifyAboutChange(Collections.singletonList(item), Collections.emptyList(), new Change.Inserted(position, 1));
- }
- }
-
- /**
- * Adding items at the end of list.
- *
- * @param itemsToAdd Items to add.
- */
- public void addAll(@NonNull final Collection 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 itemsToAdd) {
- synchronized (this) {
- if (!itemsToAdd.isEmpty()) {
- items.addAll(position, itemsToAdd);
- notifyAboutChange(new ArrayList<>(itemsToAdd), Collections.emptyList(), new Change.Inserted(position, itemsToAdd.size()));
- }
- }
- }
-
- /**
- * Removing item.
- *
- * @param item Item to remove.
- */
- public void remove(@NonNull final TItem item) {
- synchronized (this) {
- final int position = indexOf(item);
- if (position < 0) {
- Lc.assertion("Illegal removing of item " + item);
- return;
- }
- remove(position);
- }
- }
-
- /**
- * 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;
- }
- synchronized (this) {
- final List removedItems = new ArrayList<>(count);
- for (int i = 0; i < count; i++) {
- removedItems.add(items.remove(position));
- }
- notifyAboutChange(Collections.emptyList(), removedItems, new Change.Removed(position, count));
- }
- }
-
- /**
- * Removing all items from list.
- */
- public void clear() {
- synchronized (this) {
- if (!items.isEmpty()) {
- final List removedItems = new ArrayList<>(items);
- items.clear();
- notifyAboutChange(Collections.emptyList(), removedItems, new Change.Removed(0, removedItems.size()));
- }
- }
- }
-
- @NonNull
- @Override
- public TItem get(final int position) {
- synchronized (this) {
- return items.get(position);
- }
- }
-
- @NonNull
- @Override
- public Collection getItems() {
- synchronized (this) {
- return Collections.unmodifiableCollection(new ArrayList<>(items));
- }
- }
-
- /**
- * 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 updatedItems) {
- if (updatedItems.isEmpty()) {
- return;
- }
- int index = position;
- synchronized (this) {
- for (final TItem item : updatedItems) {
- items.set(index, item);
- index++;
- }
- notifyAboutChange(Collections.emptyList(), Collections.emptyList(), new Change.Changed(position, updatedItems.size(), null));
- }
- }
-
- /**
- * Resetting all items in list to new ones.
- *
- * @param newItems New items to set.
- */
- public void set(@NonNull final Collection newItems) {
- synchronized (this) {
- final List oldList = new ArrayList<>(items);
- final List newList = new ArrayList<>(newItems);
- final CollectionsChangesCalculator calculator;
- if (diffUtilsSource != null) {
- if (diffUtilsSource.sameItemsPredicate != null) {
- calculator = new DiffCollectionsChangesCalculator<>(oldList, newList,
- diffUtilsSource.detectMoves, diffUtilsSource.sameItemsPredicate, diffUtilsSource.changePayloadProducer);
- } else {
- calculator = new DefaultCollectionsChangesCalculator<>(oldList, newList, false);
- }
- } else if (sameItemsPredicate != null) {
- calculator = new DiffCollectionsChangesCalculator<>(oldList, newList, detectMoves, sameItemsPredicate, changePayloadProducer);
- } else {
- calculator = new DefaultCollectionsChangesCalculator<>(oldList, newList, false);
- }
- items.clear();
- items.addAll(newItems);
- notifyAboutChanges(calculator.calculateInsertedItems(), calculator.calculateRemovedItems(), calculator.calculateChanges());
- }
- }
-
- @Override
- public int size() {
- synchronized (this) {
- return items.size();
- }
- }
-
- /**
- * Enable diff utils algorithm in collection changes.
- *
- * @param detectMoves The flag that determines whether the {@link Change.Moved} changes will be generated or not;
- * @param sameItemsPredicate Predicate for the determination of the same elements;
- * @param changePayloadProducer Function that calculate change payload when items the same but contents are different.
- */
- public void enableDiffUtils(final boolean detectMoves,
- @NonNull final SameItemsPredicate sameItemsPredicate,
- @Nullable final ChangePayloadProducer changePayloadProducer) {
- this.detectMoves = detectMoves;
- this.sameItemsPredicate = sameItemsPredicate;
- this.changePayloadProducer = changePayloadProducer;
- }
-
- /**
- * Disable diff utils algorithm.
- */
- public void disableDiffUtils() {
- this.sameItemsPredicate = null;
- }
-
- /**
- * Returns enabled flag of diff utils.
- *
- * @return true if diff utils is enabled.
- */
- public boolean diffUtilsIsEnabled() {
- return diffUtilsSource != null ? diffUtilsSource.diffUtilsIsEnabled() : sameItemsPredicate != null;
- }
-
- /**
- * Sets observableCollection as a source of diff utils parameters;
- *
- * @param diffUtilsSource Source of diff utils parameters.
- */
- public void setDiffUtilsSource(@Nullable final ObservableList diffUtilsSource) {
- this.diffUtilsSource = diffUtilsSource;
- }
-
- /**
- * 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);
- }
- }
-
- private void writeObject(@NonNull final ObjectOutputStream outputStream) throws IOException {
- outputStream.writeObject(items);
- }
-
- @SuppressWarnings("unchecked")
- private void readObject(@NonNull final ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
- items = (List) inputStream.readObject();
- }
-
-}
diff --git a/src/main/java/ru/touchin/roboswag/core/observables/collections/changes/Change.java b/src/main/java/ru/touchin/roboswag/core/observables/collections/changes/Change.java
deleted file mode 100644
index a1b1c80..0000000
--- a/src/main/java/ru/touchin/roboswag/core/observables/collections/changes/Change.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
- *
- * This file is part of RoboSwag library.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package ru.touchin.roboswag.core.observables.collections.changes;
-
-import android.support.annotation.Nullable;
-
-/**
- * Created by Gavriil Sitnikov on 23/05/16.
- * Class representing simple change of collection like insertion, remove or replacing/changing items.
- */
-public abstract class Change {
-
- /**
- * Represents a insert operation in collection.
- */
- public static class Inserted extends Change {
-
- private final int position;
- private final int count;
-
- public Inserted(final int position, final int count) {
- super();
- this.position = position;
- this.count = count;
- }
-
- public int getPosition() {
- return position;
- }
-
- public int getCount() {
- return count;
- }
-
- }
-
- /**
- * Represents a remove operation from collection.
- */
- public static class Removed extends Change {
-
- private final int position;
- private final int count;
-
- public Removed(final int position, final int count) {
- super();
- this.position = position;
- this.count = count;
- }
-
- public int getPosition() {
- return position;
- }
-
- public int getCount() {
- return count;
- }
-
- }
-
- /**
- * Represents a move operation in collection.
- */
- public static class Moved extends Change {
-
- private final int fromPosition;
- private final int toPosition;
-
- public Moved(final int fromPosition, final int toPosition) {
- super();
- this.fromPosition = fromPosition;
- this.toPosition = toPosition;
- }
-
- public int getFromPosition() {
- return fromPosition;
- }
-
- public int getToPosition() {
- return toPosition;
- }
-
- }
-
- /**
- * Represents a modification operation in a collection.
- */
- public static class Changed extends Change {
-
- private final int position;
- private final int count;
- @Nullable
- private final Object payload;
-
- public Changed(final int position, final int count, @Nullable final Object payload) {
- super();
- this.position = position;
- this.count = count;
- this.payload = payload;
- }
-
- public int getPosition() {
- return position;
- }
-
- public int getCount() {
- return count;
- }
-
- @Nullable
- public Object getPayload() {
- return payload;
- }
-
- }
-
-}
diff --git a/src/main/java/ru/touchin/roboswag/core/observables/collections/changes/ChangePayloadProducer.java b/src/main/java/ru/touchin/roboswag/core/observables/collections/changes/ChangePayloadProducer.java
deleted file mode 100644
index ffbbb03..0000000
--- a/src/main/java/ru/touchin/roboswag/core/observables/collections/changes/ChangePayloadProducer.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2017 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
- *
- * This file is part of RoboSwag library.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package ru.touchin.roboswag.core.observables.collections.changes;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-/**
- * Functional interface for calculating change payload between two items same type.
- * Payload calculating when items are same {@link SameItemsPredicate}, but content different.
- */
-public interface ChangePayloadProducer {
-
-
- /**
- * Calculate change payload between two items.
- *
- * @param item1 First item;
- * @param item2 Second item;
- * @return Object that represents minimal changes between two items.
- */
- @Nullable
- Object getChangePayload(@NonNull TItem item1, @NonNull TItem item2);
-
-}
diff --git a/src/main/java/ru/touchin/roboswag/core/observables/collections/changes/CollectionChanges.java b/src/main/java/ru/touchin/roboswag/core/observables/collections/changes/CollectionChanges.java
deleted file mode 100644
index f161155..0000000
--- a/src/main/java/ru/touchin/roboswag/core/observables/collections/changes/CollectionChanges.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (c) 2017 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
- *
- * This file is part of RoboSwag library.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package ru.touchin.roboswag.core.observables.collections.changes;
-
-import android.support.annotation.NonNull;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Class which is representing change of collection. There could be multiple changes applied to collection.
- */
-public class CollectionChanges {
-
- private final int number;
- @NonNull
- private final List insertedItems;
- @NonNull
- private final List removedItems;
- @NonNull
- private final Collection changes;
-
- public CollectionChanges(final int number,
- @NonNull final List insertedItems,
- @NonNull final List removedItems,
- @NonNull final Collection changes) {
- this.number = number;
- this.insertedItems = Collections.unmodifiableList(insertedItems);
- this.removedItems = Collections.unmodifiableList(removedItems);
- this.changes = Collections.unmodifiableCollection(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 getChanges() {
- return changes;
- }
-
- /**
- * Returns inserted items in change.
- *
- * @return Inserted items.
- */
- @NonNull
- public List getInsertedItems() {
- return insertedItems;
- }
-
- /**
- * Returns removed items in change.
- *
- * @return Removed items.
- */
- @NonNull
- public List getRemovedItems() {
- return removedItems;
- }
-
-}
diff --git a/src/main/java/ru/touchin/roboswag/core/observables/collections/changes/CollectionsChangesCalculator.java b/src/main/java/ru/touchin/roboswag/core/observables/collections/changes/CollectionsChangesCalculator.java
deleted file mode 100644
index 9f6d026..0000000
--- a/src/main/java/ru/touchin/roboswag/core/observables/collections/changes/CollectionsChangesCalculator.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (c) 2017 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
- *
- * This file is part of RoboSwag library.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package ru.touchin.roboswag.core.observables.collections.changes;
-
-import android.support.annotation.NonNull;
-
-import java.util.List;
-
-/**
- * Interface that represent changes calculator between two collections.
- */
-public interface CollectionsChangesCalculator {
-
- /**
- * Calculate changes between two collection as collection of objects {@link Change}.
- *
- * @return List of changes.
- */
- @NonNull
- List calculateChanges();
-
- /**
- * Calculate changes between two collection as collection of inserted items.
- *
- * @return List of inserted item.
- */
- @NonNull
- List calculateInsertedItems();
-
- /**
- * Calculate changes between two collection as collection of removed items.
- *
- * @return List of removed item.
- */
- @NonNull
- List calculateRemovedItems();
-
-}
diff --git a/src/main/java/ru/touchin/roboswag/core/observables/collections/changes/DefaultCollectionsChangesCalculator.java b/src/main/java/ru/touchin/roboswag/core/observables/collections/changes/DefaultCollectionsChangesCalculator.java
deleted file mode 100644
index 09cd790..0000000
--- a/src/main/java/ru/touchin/roboswag/core/observables/collections/changes/DefaultCollectionsChangesCalculator.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (c) 2017 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
- *
- * This file is part of RoboSwag library.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package ru.touchin.roboswag.core.observables.collections.changes;
-
-import android.support.annotation.NonNull;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * Default calculator between two collections that use equals function.
- */
-public class DefaultCollectionsChangesCalculator implements CollectionsChangesCalculator {
-
- @NonNull
- private final Collection initialCollection;
- @NonNull
- private final Collection modifiedCollection;
- private final boolean shrinkChangesToModifiedSize;
- @NonNull
- private final Collection itemsToAdd = new ArrayList<>();
- private int currentSize;
- private int oldSize;
- private int newSize;
- private int couldBeAdded;
-
- /**
- * Default calculator of 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}.
- */
- public DefaultCollectionsChangesCalculator(@NonNull final Collection initialCollection,
- @NonNull final Collection modifiedCollection,
- final boolean shrinkChangesToModifiedSize) {
- super();
- this.initialCollection = initialCollection;
- this.modifiedCollection = modifiedCollection;
- this.shrinkChangesToModifiedSize = shrinkChangesToModifiedSize;
- }
-
- @NonNull
- @Override
- public List calculateChanges() {
- int initialOffset = 0;
- itemsToAdd.clear();
- currentSize = 0;
- oldSize = initialCollection.size();
- newSize = modifiedCollection.size();
- couldBeAdded = modifiedCollection.size() - initialCollection.size();
- final List result = new ArrayList<>();
- for (final TItem modifiedItem : modifiedCollection) {
- int foundPosition = 0;
- for (final Object initialObject : initialCollection) {
- if (foundPosition >= initialOffset && modifiedItem.equals(initialObject)) {
- if (tryAddSkipped(result) == MethodAction.RETURN
- || tryRemoveRest(result, foundPosition - initialOffset) == MethodAction.RETURN) {
- return result;
- }
- initialOffset = foundPosition + 1;
- currentSize++;
- break;
- }
- foundPosition++;
- }
- // if not found
- if (foundPosition >= initialCollection.size()) {
- itemsToAdd.add(modifiedItem);
- }
- }
-
- if (tryAddSkipped(result) == MethodAction.RETURN) {
- return result;
- }
- tryRemoveRest(result, initialCollection.size() - initialOffset);
- return result;
- }
-
- @NonNull
- @Override
- public List calculateInsertedItems() {
- final List insertedItems = new ArrayList<>();
- for (final TItem newItem : modifiedCollection) {
- if (!initialCollection.contains(newItem)) {
- insertedItems.add(newItem);
- }
- }
- return insertedItems;
- }
-
- @NonNull
- @Override
- public List calculateRemovedItems() {
- final List removedItems = new ArrayList<>();
- for (final TItem oldItem : initialCollection) {
- if (!modifiedCollection.contains(oldItem)) {
- removedItems.add(oldItem);
- }
- }
- return removedItems;
- }
-
- @NonNull
- private MethodAction tryAddSkipped(@NonNull final Collection changes) {
- if (!itemsToAdd.isEmpty()) {
- if (shrinkChangesToModifiedSize && couldBeAdded < itemsToAdd.size()) {
- addSimpleDifferenceChanges(changes);
- return MethodAction.RETURN;
- }
- changes.add(new Change.Inserted(currentSize, itemsToAdd.size()));
- currentSize += itemsToAdd.size();
- couldBeAdded -= itemsToAdd.size();
- itemsToAdd.clear();
- }
- return MethodAction.CONTINUE;
- }
-
- @NonNull
- private MethodAction tryRemoveRest(@NonNull final Collection changes, final int itemsToRemove) {
- if (itemsToRemove > 0) {
- if (shrinkChangesToModifiedSize && couldBeAdded < -itemsToRemove) {
- addSimpleDifferenceChanges(changes);
- return MethodAction.RETURN;
- }
- changes.add(new Change.Removed(currentSize, itemsToRemove));
- }
- return MethodAction.CONTINUE;
- }
-
- private void addSimpleDifferenceChanges(@NonNull final Collection changes) {
- changes.add(new Change.Changed(currentSize, newSize - currentSize, null));
- if (oldSize - newSize > 0) {
- changes.add(new Change.Removed(newSize, oldSize - newSize));
- }
- }
-
- private enum MethodAction {
- RETURN,
- CONTINUE
- }
-
-}
diff --git a/src/main/java/ru/touchin/roboswag/core/observables/collections/changes/DiffCollectionsChangesCalculator.java b/src/main/java/ru/touchin/roboswag/core/observables/collections/changes/DiffCollectionsChangesCalculator.java
deleted file mode 100644
index 7b72772..0000000
--- a/src/main/java/ru/touchin/roboswag/core/observables/collections/changes/DiffCollectionsChangesCalculator.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (c) 2017 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
- *
- * This file is part of RoboSwag library.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package ru.touchin.roboswag.core.observables.collections.changes;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import ru.touchin.roboswag.core.android.support.v7.util.DiffUtil;
-import ru.touchin.roboswag.core.android.support.v7.util.ListUpdateCallback;
-
-/**
- * Implementation of {@link CollectionsChangesCalculator} based on DiffUtils from support library.
- */
-public class DiffCollectionsChangesCalculator extends DiffUtil.Callback implements CollectionsChangesCalculator {
-
- @NonNull
- private final List oldList;
- @NonNull
- private final List newList;
- private final boolean detectMoves;
- @NonNull
- private final SameItemsPredicate sameItemsPredicate;
- @Nullable
- private final ChangePayloadProducer changePayloadProducer;
-
- public DiffCollectionsChangesCalculator(@NonNull final List oldList,
- @NonNull final List newList,
- final boolean detectMoves,
- @NonNull final SameItemsPredicate sameItemsPredicate,
- @Nullable final ChangePayloadProducer changePayloadProducer) {
- super();
- this.oldList = oldList;
- this.newList = newList;
- this.detectMoves = detectMoves;
- this.sameItemsPredicate = sameItemsPredicate;
- this.changePayloadProducer = changePayloadProducer;
- }
-
- @NonNull
- @Override
- public List calculateChanges() {
- final List changes = new ArrayList<>();
- DiffUtil.calculateDiff(this, detectMoves).dispatchUpdatesTo(new ListUpdateCallback() {
- @Override
- public void onInserted(final int position, final int count) {
- changes.add(new Change.Inserted(position, count));
- }
-
- @Override
- public void onRemoved(final int position, final int count) {
- changes.add(new Change.Removed(position, count));
- }
-
- @Override
- public void onMoved(final int fromPosition, final int toPosition) {
- changes.add(new Change.Moved(fromPosition, toPosition));
- }
-
- @Override
- public void onChanged(final int position, final int count, @Nullable final Object payload) {
- changes.add(new Change.Changed(position, count, payload));
- }
- });
- return changes;
- }
-
- @NonNull
- @Override
- public List calculateInsertedItems() {
- final List insertedItems = new ArrayList<>();
- for (final TItem newItem : newList) {
- if (!containsByPredicate(newItem, oldList)) {
- insertedItems.add(newItem);
- }
- }
- return insertedItems;
- }
-
- @NonNull
- @Override
- public List calculateRemovedItems() {
- final List removedItems = new ArrayList<>();
- for (final TItem oldItem : oldList) {
- if (!containsByPredicate(oldItem, newList)) {
- removedItems.add(oldItem);
- }
- }
- return removedItems;
- }
-
- @Override
- public int getOldListSize() {
- return oldList.size();
- }
-
- @Override
- public int getNewListSize() {
- return newList.size();
- }
-
- @Override
- public boolean areItemsTheSame(final int oldItemPosition, final int newItemPosition) {
- return sameItemsPredicate.areSame(oldList.get(oldItemPosition), newList.get(newItemPosition));
- }
-
- @Override
- public boolean areContentsTheSame(final int oldItemPosition, final int newItemPosition) {
- return oldList.get(oldItemPosition).equals(newList.get(newItemPosition));
- }
-
- @Nullable
- @Override
- public Object getChangePayload(final int oldItemPosition, final int newItemPosition) {
- return changePayloadProducer != null
- ? changePayloadProducer.getChangePayload(oldList.get(oldItemPosition), newList.get(newItemPosition)) : null;
- }
-
- private boolean containsByPredicate(@NonNull final TItem searchedItem, @NonNull final List items) {
- for (final TItem item : items) {
- if (sameItemsPredicate.areSame(item, searchedItem)) {
- return true;
- }
- }
- return false;
- }
-
-}
diff --git a/src/main/java/ru/touchin/roboswag/core/observables/collections/changes/SameItemsPredicate.java b/src/main/java/ru/touchin/roboswag/core/observables/collections/changes/SameItemsPredicate.java
deleted file mode 100644
index 611f6a5..0000000
--- a/src/main/java/ru/touchin/roboswag/core/observables/collections/changes/SameItemsPredicate.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2017 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
- *
- * This file is part of RoboSwag library.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package ru.touchin.roboswag.core.observables.collections.changes;
-
-import android.support.annotation.NonNull;
-
-/**
- * Functional interface for determine same objects. Usually this is just the comparison by id.
- *
- * @param Type of objects
- */
-public interface SameItemsPredicate {
-
- /**
- * Function for determine same objects.
- *
- * @param item1 First object;
- * @param item2 Second object;
- * @return True if items are same.
- */
- boolean areSame(@NonNull TItem item1, @NonNull TItem item2);
-
-}
diff --git a/src/main/java/ru/touchin/roboswag/core/observables/collections/loadable/LoadedItems.java b/src/main/java/ru/touchin/roboswag/core/observables/collections/loadable/LoadedItems.java
deleted file mode 100644
index 3099253..0000000
--- a/src/main/java/ru/touchin/roboswag/core/observables/collections/loadable/LoadedItems.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
- *
- * This file is part of RoboSwag library.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package ru.touchin.roboswag.core.observables.collections.loadable;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import java.util.Collection;
-
-/**
- * Created by Gavriil Sitnikov on 23/05/16.
- * Object represents loaded items with reference to load other parts and info of are there more items to load or not.
- *
- * @param Type of items to load;
- * @param Type of reference to load other parts of items.
- */
-public interface LoadedItems {
-
- 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 getItems();
-
- /**
- * Returns reference that could be used to load other parts of items.
- *
- * @return Reference object.
- */
- @Nullable
- TReference getReference();
-
-}
diff --git a/src/main/java/ru/touchin/roboswag/core/observables/collections/loadable/LoadingMoreList.java b/src/main/java/ru/touchin/roboswag/core/observables/collections/loadable/LoadingMoreList.java
deleted file mode 100644
index fd8f374..0000000
--- a/src/main/java/ru/touchin/roboswag/core/observables/collections/loadable/LoadingMoreList.java
+++ /dev/null
@@ -1,402 +0,0 @@
-/*
- * Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
- *
- * This file is part of RoboSwag library.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package ru.touchin.roboswag.core.observables.collections.loadable;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.Executors;
-
-import io.reactivex.Observable;
-import io.reactivex.Scheduler;
-import io.reactivex.Single;
-import io.reactivex.functions.Function;
-import io.reactivex.schedulers.Schedulers;
-import io.reactivex.subjects.BehaviorSubject;
-import ru.touchin.roboswag.core.log.Lc;
-import ru.touchin.roboswag.core.observables.collections.ObservableCollection;
-import ru.touchin.roboswag.core.observables.collections.ObservableList;
-import ru.touchin.roboswag.core.observables.collections.changes.Change;
-import ru.touchin.roboswag.core.observables.collections.changes.CollectionChanges;
-import ru.touchin.roboswag.core.utils.Optional;
-
-/**
- * Created by Gavriil Sitnikov on 23/05/16.
- * {@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.).
- * Use {@link #loadItem(int)} and {@link #loadRange(int, int)} to load items asynchronously.
- *
- * @param Type of collection's items;
- * @param Type of reference object to help rightly loading next block of items;
- * @param Type of loading block of items.
- */
-public class LoadingMoreList>
- extends ObservableCollection {
-
- 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 = Schedulers.from(Executors.newSingleThreadExecutor());
- @NonNull
- private Observable loadingMoreObservable;
- @NonNull
- private final BehaviorSubject moreItemsCount = BehaviorSubject.createDefault(LoadedItems.UNKNOWN_ITEMS_COUNT);
- @NonNull
- private final ObservableList innerList = new ObservableList<>();
- @Nullable
- private LoadedItemsFilter loadedItemsFilter;
- @Nullable
- private TMoreReference moreItemsReference;
-
- public LoadingMoreList(@NonNull final MoreItemsLoader moreMoreItemsLoader) {
- this(moreMoreItemsLoader, null);
- }
-
- @SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
- //ConstructorCallsOverridableMethod: actually it is calling in lambda callback
- public LoadingMoreList(@NonNull final MoreItemsLoader moreMoreItemsLoader,
- @Nullable final LoadedItems initialItems) {
- super();
- this.loadingMoreObservable = Observable
- .switchOnNext(Observable
- .fromCallable(() -> createLoadRequestBasedObservable(this::createActualRequest, moreMoreItemsLoader::load).toObservable()))
- .doOnNext(loadedItems -> onItemsLoaded(loadedItems, size(), false))
- .replay(1)
- .refCount();
-
- if (initialItems != null) {
- innerOnItemsLoaded(initialItems, 0, false);
- }
- }
-
- @Nullable
- public TMoreReference getMoreItemsReference() {
- return moreItemsReference;
- }
-
- @NonNull
- private MoreLoadRequest createActualRequest() {
- return new MoreLoadRequest<>(moreItemsReference, Math.max(0, size()));
- }
-
- @NonNull
- protected Single createLoadRequestBasedObservable(@NonNull final Callable requestCreator,
- @NonNull final Function> observableCreator) {
- return Single
- .fromCallable(requestCreator)
- .flatMap(loadRequest -> observableCreator.apply(loadRequest)
- .subscribeOn(Schedulers.io())
- .observeOn(loaderScheduler)
- .doOnSuccess(ignored -> {
- if (!requestCreator.call().equals(loadRequest)) {
- throw new RequestChangedDuringLoadingException();
- }
- }))
- .retry((number, throwable) ->
- number <= RETRY_LOADING_AFTER_CHANGE_COUNT && throwable instanceof RequestChangedDuringLoadingException);
- }
-
- @NonNull
- protected final Scheduler getLoaderScheduler() {
- return loaderScheduler;
- }
-
- @NonNull
- @Override
- public Observable> observeChanges() {
- return innerList.observeChanges();
- }
-
- @Override
- protected void notifyAboutChanges(@NonNull final List insertedItems,
- @NonNull final List removedItems,
- @NonNull final Collection changes) {
- 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 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 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 observeMoreItemsCount() {
- return moreItemsCount.distinctUntilChanged();
- }
-
- /**
- * 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) DUPLICATES_REMOVER : null;
- }
-
- /**
- * 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 loadedItemsFilter) {
- this.loadedItemsFilter = loadedItemsFilter;
- }
-
- private void innerOnItemsLoaded(@NonNull final LoadedItems loadedItems, final int insertPosition, final boolean reset) {
- final List items = new ArrayList<>(loadedItems.getItems());
- final boolean lastPage = reset || insertPosition > size() - 1;
- if (reset) {
- resetState();
- if (insertPosition != 0) {
- Lc.assertion("Wrong insert position " + insertPosition);
- }
- innerList.set(items);
- } else {
- if (this.loadedItemsFilter != null) {
- filterList(items, this.loadedItemsFilter);
- }
- innerList.addAll(insertPosition, items);
- }
- if (lastPage) {
- moreItemsReference = loadedItems.getReference();
- moreItemsCount.onNext(loadedItems.getMoreItemsCount());
- }
- }
-
- /**
- * 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 filterList(@NonNull final List items, @NonNull final LoadedItemsFilter loadedItemsFilter) {
- for (int i = items.size() - 1; i >= 0; 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);
- }
- if (filterAction == FilterAction.REPLACE_SOURCE_ITEM_WITH_LOADED) {
- innerList.update(j, items.get(i));
- items.remove(i);
- break;
- }
- }
- }
- }
-
- @Override
- public int size() {
- return innerList.size();
- }
-
- @NonNull
- @Override
- public TItem get(final int position) {
- return innerList.get(position);
- }
-
- @NonNull
- @Override
- public Collection getItems() {
- return innerList.getItems();
- }
-
- /**
- * Returns {@link Observable} that is loading new items.
- *
- * @return {@link Observable} that is loading new items.
- */
- @NonNull
- protected Observable getLoadingMoreObservable() {
- return loadingMoreObservable;
- }
-
- /**
- * 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 Single> loadItem(final int position) {
- return Observable.switchOnNext(Observable
- .fromCallable(() -> {
- if (position < size()) {
- return Observable.just(new Optional<>(get(position)));
- } else if (moreItemsCount.getValue() == 0) {
- return Observable.just(new Optional(null));
- } else {
- return loadingMoreObservable.switchMap(ignored -> Observable.>error(new NotLoadedYetException()));
- }
- }))
- .subscribeOn(loaderScheduler)
- .retry((number, throwable) -> throwable instanceof NotLoadedYetException)
- .firstOrError();
- }
-
- /**
- * 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
- @SuppressWarnings("unchecked")
- //unchecked: it's OK for such zip operator
- public Single> loadRange(final int first, final int last) {
- final List>> itemsRequests = new ArrayList<>();
- for (int i = first; i <= last; i++) {
- itemsRequests.add(loadItem(i));
- }
- return Single.zip(itemsRequests,
- items -> {
- final List result = new ArrayList<>();
- for (final Object item : items) {
- final Optional optional = (Optional) item;
- if (optional.get() != null) {
- result.add(optional.get());
- }
- }
- return Collections.unmodifiableCollection(result);
- });
- }
-
- /**
- * 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);
- }
-
- protected void resetState() {
- moreItemsReference = null;
- 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,
- REPLACE_SOURCE_ITEM_WITH_LOADED
- }
-
- /**
- * 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 Type of collection's items.
- */
- public interface LoadedItemsFilter {
-
- /**
- * 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 TItem collectionObject, @NonNull 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 {
- }
-
-}
diff --git a/src/main/java/ru/touchin/roboswag/core/observables/collections/loadable/MoreItemsLoader.java b/src/main/java/ru/touchin/roboswag/core/observables/collections/loadable/MoreItemsLoader.java
deleted file mode 100644
index 8a64530..0000000
--- a/src/main/java/ru/touchin/roboswag/core/observables/collections/loadable/MoreItemsLoader.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
- *
- * This file is part of RoboSwag library.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package ru.touchin.roboswag.core.observables.collections.loadable;
-
-import android.support.annotation.NonNull;
-
-import io.reactivex.Single;
-
-/**
- * Created by Gavriil Sitnikov on 02/06/2016.
- * Object that is loading next part of items by reference or position.
- *
- * @param Type of items to be loaded;
- * @param Type of reference to be used to load next part of items;
- * @param Type of loaded items part.
- */
-public interface MoreItemsLoader> {
-
- /**
- * Returns {@link Single} that could load next part of items.
- *
- * @param moreLoadRequest Request with info inside to load next part of items;
- * @return {@link Single} of loading items.
- */
- @NonNull
- Single load(@NonNull final MoreLoadRequest moreLoadRequest);
-
-}
\ No newline at end of file
diff --git a/src/main/java/ru/touchin/roboswag/core/observables/collections/loadable/MoreLoadRequest.java b/src/main/java/ru/touchin/roboswag/core/observables/collections/loadable/MoreLoadRequest.java
deleted file mode 100644
index 6cac3fc..0000000
--- a/src/main/java/ru/touchin/roboswag/core/observables/collections/loadable/MoreLoadRequest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
- *
- * This file is part of RoboSwag library.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package ru.touchin.roboswag.core.observables.collections.loadable;
-
-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 Type of reference to load next part of items.
- */
-public class MoreLoadRequest {
-
- @Nullable
- private final TMoreReference moreReference;
- private final int nextPosition;
-
- public MoreLoadRequest(@Nullable final TMoreReference moreReference, final int nextPosition) {
- this.moreReference = moreReference;
- 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;
- }
-
- @Override
- public boolean equals(@Nullable final Object object) {
- return object instanceof MoreLoadRequest
- && ObjectUtils.equals(((MoreLoadRequest) object).moreReference, moreReference)
- && ((MoreLoadRequest) object).nextPosition == nextPosition;
- }
-
- @Override
- public int hashCode() {
- return nextPosition + (moreReference != null ? moreReference.hashCode() : 0);
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/ru/touchin/roboswag/core/utils/ObjectUtils.java b/src/main/java/ru/touchin/roboswag/core/utils/ObjectUtils.java
index cb41c00..a8c2baf 100644
--- a/src/main/java/ru/touchin/roboswag/core/utils/ObjectUtils.java
+++ b/src/main/java/ru/touchin/roboswag/core/utils/ObjectUtils.java
@@ -27,8 +27,6 @@ import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
-import ru.touchin.roboswag.core.observables.collections.ObservableCollection;
-
/**
* Created by Gavriil Sitnikov on 04/10/2015.
* Some utilities related to objects.
@@ -163,16 +161,6 @@ public final class ObjectUtils {
return collection == null || collection.isEmpty();
}
- /**
- * Returns true if ObservableCollection is null or empty.
- *
- * @param observableCollection observableCollection to check;
- * @return True if observableCollection is null or empty.
- */
- public static boolean isNullOrEmpty(@Nullable final ObservableCollection> observableCollection) {
- return observableCollection == null || observableCollection.isEmpty();
- }
-
/**
* Returns true if map is null or empty.
*