/*
* Copyright 2015 Jacek Marchwicki <jacek.marchwicki@gmail.com>
*
* 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 com.appunite.detector;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import javax.annotation.Nonnull;
import static com.appunite.rx.internal.Preconditions.checkNotNull;
/**
* Use: com.jacekmarchwicki.changesdetector.ChangesDetector from https://github.com/jacek-marchwicki/recyclerview-changes-detector
*/
@Deprecated
public class ChangesDetector<T, H> {
private static final int ACTION_NONE = 0;
private static final int ACTION_INSERT = 1;
private static final int ACTION_REMOVE = 2;
private static final int ACTION_CHANGED = 3;
/**
* Interface that is already implemented by RecyclerView
*/
public interface ChangesAdapter {
void notifyItemRangeInserted(int start, int count);
void notifyItemRangeChanged(int start, int count);
void notifyItemRangeRemoved(int start, int count);
void notifyItemMoved(int fromPosition, int toPosition);
}
@SuppressWarnings("unchecked")
@Nonnull
private H[] mItems = (H[])new Object[0];
@Nonnull
private final Detector<T, H> mDetector;
/**
* Created {@link ChangesDetector} with {@link ChangesDetector.Detector}
* @param detector detector
*/
public ChangesDetector(@Nonnull Detector<T, H> detector) {
mDetector = checkNotNull(detector);
}
/**
* Use: com.jacekmarchwicki.changesdetector.ChangesDetector#Detector from https://github.com/jacek-marchwicki/recyclerview-changes-detector
*/
@Deprecated
public interface Detector<T, H> {
@Nonnull
H apply(@Nonnull T item);
boolean matches(@Nonnull H item, @Nonnull H newOne);
boolean same(@Nonnull H item, @Nonnull H newOne);
}
private int indexOfItem(@Nonnull List<H> list,
@Nonnull H search) {
int counter = 0;
for (H item : list) {
if (mDetector.matches(item, search)) {
return counter;
}
counter += 1;
}
return -1;
}
/**
* Inform adapter about new data
* @param adapter adapter to be informed about changes
* @param values items for adapter
* @param force true if you need to force all data reload
*/
public void newData(@Nonnull ChangesAdapter adapter,
@Nonnull List<T> values,
boolean force) {
checkNotNull(adapter);
checkNotNull(values);
final H[] list = apply(values);
final LinkedList<H> objects = new LinkedList<>();
Collections.addAll(objects, mItems);
clearState();
int successPosition = 0;
for (; successPosition < list.length;) {
final H item = list[successPosition];
final int i = indexOfItem(objects, item);
if (i < 0) {
executeAction(adapter, ACTION_INSERT, successPosition);
successPosition += 1;
} else if (i == 0) {
if (force || !mDetector.same(item, objects.get(0))) {
executeAction(adapter, ACTION_CHANGED, successPosition);
} else {
executeAction(adapter, ACTION_NONE, successPosition);
}
objects.remove(0);
successPosition += 1;
} else {
final H first = objects.get(0);
if (existInList(list, successPosition + 1, first)) {
// changed order
adapter.notifyItemMoved(i + successPosition, successPosition);
if (force || !mDetector.same(item, objects.get(i))) {
executeAction(adapter, ACTION_CHANGED, successPosition);
} else {
executeAction(adapter, ACTION_NONE, successPosition);
}
objects.remove(i);
successPosition += 1;
} else {
executeAction(adapter, ACTION_REMOVE, successPosition);
objects.remove(0);
}
}
}
executeAction(adapter, ACTION_NONE, successPosition);
if (objects.size() > 0) {
adapter.notifyItemRangeRemoved(successPosition, objects.size());
objects.clear();
}
mItems = list;
}
private int lastPosition;
private int count;
private int lastAction;
private void clearState() {
lastPosition = 0;
count = 0;
lastAction = ACTION_NONE;
}
private void executeAction(@Nonnull ChangesAdapter adapter, int currentAction, int currentPosition) {
if (lastAction != currentAction) {
if (lastAction == ACTION_REMOVE) {
adapter.notifyItemRangeRemoved(lastPosition, count);
} else if (lastAction == ACTION_CHANGED) {
adapter.notifyItemRangeChanged(lastPosition, count);
} else if (lastAction == ACTION_INSERT) {
adapter.notifyItemRangeInserted(lastPosition, count);
}
lastAction = currentAction;
lastPosition = currentPosition;
count = 1;
} else {
count += 1;
}
}
private boolean existInList(H[] list, int start, H search) {
for (int i = start, listLength = list.length; i < listLength; i++) {
final H item = list[i];
if (mDetector.matches(item, search)) {
return true;
}
}
return false;
}
@Nonnull
private H[] apply(@Nonnull List<T> values) {
@SuppressWarnings("unchecked")
final H[] result = (H[])new Object[values.size()];
for (int i = 0; i < values.size(); i++) {
T value = values.get(i);
result[i] = mDetector.apply(value);
}
return result;
}
}