Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ import com.xwray.groupie.GroupieViewHolder

// TODO(zhuinden): move this into its own artifact later: `groupiex` (or rather, `groupie-ktx`)
operator fun GroupAdapter<out GroupieViewHolder>.plusAssign(element: Group) = this.add(element)
operator fun GroupAdapter<out GroupieViewHolder>.plusAssign(groups: Collection<Group>) = this.addAll(groups)
operator fun GroupAdapter<out GroupieViewHolder>.plusAssign(groups: List<Group>) = this.addAll(groups)
operator fun GroupAdapter<out GroupieViewHolder>.minusAssign(element: Group) = this.remove(element)
operator fun GroupAdapter<out GroupieViewHolder>.minusAssign(groups: Collection<Group>) = this.removeAll(groups)
operator fun GroupAdapter<out GroupieViewHolder>.minusAssign(groups: List<Group>) = this.removeAll(groups)
8 changes: 3 additions & 5 deletions library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ android {
defaultConfig {
minSdkVersion rootProject.minimumSdkVersion
targetSdkVersion rootProject.sdkVersion
versionCode 1
versionName "1.0"
versionCode 3
versionName "2.9.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
signingConfigs {
Expand All @@ -40,6 +40,4 @@ dependencies {
implementation "androidx.recyclerview:recyclerview:1.1.0"
testImplementation "junit:junit:$junit_version"
testImplementation "org.mockito:mockito-core:$mockito_version"
}


}
28 changes: 9 additions & 19 deletions library/src/main/java/com/xwray/groupie/AsyncDiffUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,49 +6,39 @@
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListUpdateCallback;

import java.util.Collection;
import java.util.List;

/**
* A wrapper around {@link DiffUtil} that calculates diff in a background thread
*/
class AsyncDiffUtil {

interface Callback extends ListUpdateCallback {
/**
* Called on the main thread before DiffUtil dispatches the result
*/
@MainThread
void onDispatchAsyncResult(@NonNull Collection<? extends Group> newGroups);
void onDispatchAsyncResult(List<Group> mergedGroups);
}

private final Callback asyncDiffUtilCallback;
final Callback asyncDiffUtilCallback;
private int maxScheduledGeneration;
private Collection<? extends Group> groups;

AsyncDiffUtil(@NonNull Callback callback) {
this.asyncDiffUtilCallback = callback;
}

@NonNull
Callback getAsyncDiffUtilCallback() {
return asyncDiffUtilCallback;
}

@NonNull
Collection<? extends Group> getGroups() {
return groups;
}

int getMaxScheduledGeneration() {
return maxScheduledGeneration;
}

void calculateDiff(@NonNull Collection<? extends Group> newGroups,
@NonNull DiffUtil.Callback diffUtilCallback,
@Nullable OnAsyncUpdateListener onAsyncUpdateListener,
void calculateDiff(@NonNull List<? extends Group> oldGroups,
@NonNull List<? extends Group> newGroups,
@Nullable final OnAsyncUpdateListener onAsyncUpdateListener,
boolean detectMoves) {
groups = newGroups;
// incrementing generation means any currently-running diffs are discarded when they finish
final int runGeneration = ++maxScheduledGeneration;
final DiffCallback diffUtilCallback = new DiffCallback(oldGroups, newGroups);
new DiffTask(this, diffUtilCallback, runGeneration, detectMoves, onAsyncUpdateListener).execute();
}
}
}
62 changes: 51 additions & 11 deletions library/src/main/java/com/xwray/groupie/DiffCallback.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
package com.xwray.groupie;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.DiffUtil;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class DiffCallback extends DiffUtil.Callback {
private final int oldBodyItemCount;
private final int newBodyItemCount;
private final Collection<? extends Group> oldGroups;
private final Collection<? extends Group> newGroups;
private final List<Item> oldList;
private final List<Item> newList;

private final Map<Integer, Integer> changeMap = new HashMap<>();

DiffCallback(Collection<? extends Group> oldGroups, Collection<? extends Group> newGroups) {
this.oldBodyItemCount = GroupUtils.getItemCount(oldGroups);
this.newBodyItemCount = GroupUtils.getItemCount(newGroups);
this.oldGroups = oldGroups;
this.newGroups = newGroups;
this.oldList = flatToItem(oldGroups);
this.newList = flatToItem(newGroups);
}

@Override
Expand All @@ -30,23 +37,56 @@ public int getNewListSize() {

@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
Item oldItem = GroupUtils.getItem(oldGroups, oldItemPosition);
Item newItem = GroupUtils.getItem(newGroups, newItemPosition);
Item oldItem = GroupUtils.getItem(oldList, oldItemPosition);
Item newItem = GroupUtils.getItem(newList, newItemPosition);
return newItem.isSameAs(oldItem);
}

@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
Item oldItem = GroupUtils.getItem(oldGroups, oldItemPosition);
Item newItem = GroupUtils.getItem(newGroups, newItemPosition);
return newItem.hasSameContentAs(oldItem);
Item oldItem = GroupUtils.getItem(oldList, oldItemPosition);
Item newItem = GroupUtils.getItem(newList, newItemPosition);
boolean sameContent = newItem.hasSameContentAs(oldItem);

//false means changed
if (sameContent) {
changeMap.put(newItemPosition, oldItemPosition);
}
return sameContent;
}

@Nullable
@Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
Item oldItem = GroupUtils.getItem(oldGroups, oldItemPosition);
Item newItem = GroupUtils.getItem(newGroups, newItemPosition);
Item oldItem = GroupUtils.getItem(oldList, oldItemPosition);
Item newItem = GroupUtils.getItem(newList, newItemPosition);
return oldItem.getChangePayload(newItem);
}

@NonNull
public List<Group> mergeGroups() {
ArrayList<Group> resultList = new ArrayList<>();

for (int i = 0; i < newList.size(); i++) {
Integer position = changeMap.get(i);

if (position == null) {
resultList.add(newList.get(i));
} else {
resultList.add(oldList.get(position));
}
}
return resultList;
}

private List<Item> flatToItem(Collection<? extends Group> groups) {
List<Item> result = new ArrayList<>();
for (Group group : groups) {
final int size = group.getItemCount();
for (int i = 0; i < size; i++) {
result.add(group.getItem(i));
}
}
return result;
}
}
35 changes: 21 additions & 14 deletions library/src/main/java/com/xwray/groupie/DiffTask.java
Original file line number Diff line number Diff line change
@@ -1,30 +1,32 @@
package com.xwray.groupie;

import android.os.AsyncTask;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.DiffUtil;

import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.List;

/**
* An async task implementation that runs {@link DiffUtil#calculateDiff(DiffUtil.Callback)}
* in a background thread. This task will call {@link AsyncDiffUtil.Callback#onDispatchAsyncResult(Collection)}
* passing the new list just before dispatching the diff result to the provided
* {@link DiffUtil.Callback} so that the new list.
* <p>This task is executed via {@link AsyncDiffUtil#calculateDiff(Collection, DiffUtil.Callback, OnAsyncUpdateListener, boolean)}.
* in a background thread.
* <p>This task is executed via {@link AsyncDiffUtil#calculateDiff(List, List, OnAsyncUpdateListener, boolean)}.
*/
class DiffTask extends AsyncTask<Void, Void, DiffUtil.DiffResult> {
@NonNull private final DiffUtil.Callback diffCallback;
@NonNull
private final DiffCallback diffCallback;
private final WeakReference<AsyncDiffUtil> asyncListDiffer;
private final int runGeneration;
private final boolean detectMoves;
@Nullable private WeakReference<OnAsyncUpdateListener> onAsyncUpdateListener;
@Nullable
private WeakReference<OnAsyncUpdateListener> onAsyncUpdateListener;
private Exception backgroundException = null;

DiffTask(@NonNull AsyncDiffUtil asyncDiffUtil,
@NonNull DiffUtil.Callback callback,
@NonNull DiffCallback callback,
int runGeneration,
boolean detectMoves,
@Nullable OnAsyncUpdateListener onAsyncUpdateListener) {
Expand Down Expand Up @@ -54,16 +56,21 @@ protected void onPostExecute(@Nullable DiffUtil.DiffResult diffResult) {
throw new RuntimeException(backgroundException);
}
AsyncDiffUtil async = asyncListDiffer.get();
if (shouldDispatchResult(diffResult, async)) {
async.getAsyncDiffUtilCallback().onDispatchAsyncResult(async.getGroups());
diffResult.dispatchUpdatesTo(async.getAsyncDiffUtilCallback());
if (onAsyncUpdateListener != null && onAsyncUpdateListener.get() != null) {
onAsyncUpdateListener.get().onUpdateComplete();
}

if (!shouldDispatchResult(diffResult, async)) {
return;
}

AsyncDiffUtil.Callback asyncDiffUtilCallback = async.asyncDiffUtilCallback;
asyncDiffUtilCallback.onDispatchAsyncResult(diffCallback.mergeGroups());

diffResult.dispatchUpdatesTo(asyncDiffUtilCallback);
if (onAsyncUpdateListener != null && onAsyncUpdateListener.get() != null) {
onAsyncUpdateListener.get().onUpdateComplete();
}
}

private boolean shouldDispatchResult(@Nullable DiffUtil.DiffResult diffResult, AsyncDiffUtil async) {
return diffResult != null && async != null && runGeneration == async.getMaxScheduledGeneration();
}
}
}
Loading