Skip to content

Commit 8cf2c1e

Browse files
committed
Migrate to ListAdapter [1/2]
Signed-off-by: Muntashir Al-Islam <muntashirakon@riseup.net>
1 parent 4a25c3f commit 8cf2c1e

38 files changed

Lines changed: 1683 additions & 1406 deletions

app/src/main/java/io/github/muntashirakon/AppManager/apk/whatsnew/WhatsNewDialogFragment.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
7171
recyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
7272
mAdapter = new WhatsNewRecyclerAdapter(requireContext(), mNewPkgInfo.packageName);
7373
recyclerView.setAdapter(mAdapter);
74-
viewModel.getChangesLiveData().observe(this, mAdapter::setAdapterList);
74+
viewModel.getChangesLiveData().observe(this, mAdapter::submitList);
7575
viewModel.loadChanges(mNewPkgInfo, mOldPkgInfo);
7676
}
7777

app/src/main/java/io/github/muntashirakon/AppManager/apk/whatsnew/WhatsNewFragment.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
5050
recyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
5151
WhatsNewRecyclerAdapter adapter = new WhatsNewRecyclerAdapter(requireContext(), newPkgInfo.packageName);
5252
recyclerView.setAdapter(adapter);
53-
viewModel.getChangesLiveData().observe(getViewLifecycleOwner(), adapter::setAdapterList);
53+
viewModel.getChangesLiveData().observe(getViewLifecycleOwner(), adapter::submitList);
5454
viewModel.loadChanges(newPkgInfo, oldPkgInfo);
5555
}
5656
}

app/src/main/java/io/github/muntashirakon/AppManager/apk/whatsnew/WhatsNewRecyclerAdapter.java

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,41 @@
1010
import android.view.ViewGroup;
1111

1212
import androidx.annotation.NonNull;
13+
import androidx.recyclerview.widget.DiffUtil;
1314

1415
import com.google.android.material.textview.MaterialTextView;
1516

16-
import java.util.ArrayList;
17-
import java.util.List;
17+
import java.util.Objects;
1818

1919
import io.github.muntashirakon.AppManager.R;
20-
import io.github.muntashirakon.util.AdapterUtils;
2120
import io.github.muntashirakon.AppManager.utils.UIUtils;
2221
import io.github.muntashirakon.AppManager.utils.appearance.ColorCodes;
2322
import io.github.muntashirakon.widget.RecyclerView;
2423

25-
class WhatsNewRecyclerAdapter extends RecyclerView.Adapter<WhatsNewRecyclerAdapter.ViewHolder> {
26-
private final List<ApkWhatsNewFinder.Change> mAdapterList = new ArrayList<>();
24+
class WhatsNewRecyclerAdapter extends RecyclerView.ListAdapter<ApkWhatsNewFinder.Change, WhatsNewRecyclerAdapter.ViewHolder> {
2725
private final int mColorAdd;
2826
private final int mColorRemove;
2927
private final int mColorNeutral;
3028
private final Typeface mTypefaceNormal;
3129
private final Typeface mTypefaceMedium;
3230
private final String mPackageName;
3331

32+
private static final DiffUtil.ItemCallback<ApkWhatsNewFinder.Change> DIFF_CALLBACK =
33+
new DiffUtil.ItemCallback<ApkWhatsNewFinder.Change>() {
34+
@Override
35+
public boolean areItemsTheSame(@NonNull ApkWhatsNewFinder.Change oldItem, @NonNull ApkWhatsNewFinder.Change newItem) {
36+
return oldItem.changeType == newItem.changeType && Objects.equals(oldItem.value, newItem.value);
37+
}
38+
39+
@Override
40+
public boolean areContentsTheSame(@NonNull ApkWhatsNewFinder.Change oldItem, @NonNull ApkWhatsNewFinder.Change newItem) {
41+
// Nothing to do
42+
return true;
43+
}
44+
};
45+
3446
WhatsNewRecyclerAdapter(Context context, @NonNull String packageName) {
47+
super(DIFF_CALLBACK);
3548
mPackageName = packageName;
3649
mColorAdd = ColorCodes.getWhatsNewPlusIndicatorColor(context);
3750
mColorRemove = ColorCodes.getWhatsNewMinusIndicatorColor(context);
@@ -40,10 +53,6 @@ class WhatsNewRecyclerAdapter extends RecyclerView.Adapter<WhatsNewRecyclerAdapt
4053
mTypefaceMedium = Typeface.create("sans-serif-medium", Typeface.NORMAL);
4154
}
4255

43-
void setAdapterList(List<ApkWhatsNewFinder.Change> list) {
44-
AdapterUtils.notifyDataSetChanged(this, mAdapterList, list);
45-
}
46-
4756
@NonNull
4857
@Override
4958
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@@ -59,40 +68,36 @@ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
5968

6069
@Override
6170
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
62-
ApkWhatsNewFinder.Change change = mAdapterList.get(position);
63-
if (change.value.startsWith(mPackageName)) {
64-
change.value = change.value.replaceFirst(mPackageName, "");
71+
ApkWhatsNewFinder.Change change = getItem(position);
72+
String displayValue = change.value;
73+
if (displayValue.startsWith(mPackageName)) {
74+
displayValue = displayValue.replaceFirst(mPackageName, "");
6575
}
6676
switch (change.changeType) {
6777
case ApkWhatsNewFinder.CHANGE_ADD:
6878
holder.changeSign.setText("+");
6979
holder.changeSign.setTextColor(mColorAdd);
70-
holder.textView.setText(change.value);
80+
holder.textView.setText(displayValue);
7181
holder.textView.setTextColor(mColorAdd);
7282
break;
7383
case ApkWhatsNewFinder.CHANGE_INFO:
74-
holder.textView.setText(change.value);
84+
holder.textView.setText(displayValue);
7585
holder.textView.setTextColor(mColorNeutral);
7686
holder.textView.setTypeface(mTypefaceMedium);
7787
holder.textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
7888
break;
7989
case ApkWhatsNewFinder.CHANGE_REMOVED:
8090
holder.changeSign.setText("-");
8191
holder.changeSign.setTextColor(mColorRemove);
82-
holder.textView.setText(change.value);
92+
holder.textView.setText(displayValue);
8393
holder.textView.setTextColor(mColorRemove);
8494
break;
8595
}
8696
}
8797

88-
@Override
89-
public int getItemCount() {
90-
return mAdapterList.size();
91-
}
92-
9398
@Override
9499
public int getItemViewType(int position) {
95-
return mAdapterList.get(position).changeType;
100+
return getItem(position).changeType;
96101
}
97102

98103
static class ViewHolder extends RecyclerView.ViewHolder {

app/src/main/java/io/github/muntashirakon/AppManager/changelog/ChangelogRecyclerAdapter.java

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,36 +19,58 @@
1919
import androidx.annotation.StringRes;
2020
import androidx.annotation.StyleRes;
2121
import androidx.core.widget.TextViewCompat;
22+
import androidx.recyclerview.widget.DiffUtil;
23+
import androidx.recyclerview.widget.ListAdapter;
2224
import androidx.recyclerview.widget.RecyclerView;
2325

2426
import com.google.android.material.chip.ChipDrawable;
2527

26-
import java.util.ArrayList;
27-
import java.util.List;
28+
import java.util.Objects;
2829

2930
import io.github.muntashirakon.AppManager.R;
30-
import io.github.muntashirakon.util.AdapterUtils;
3131
import io.github.muntashirakon.util.UiUtils;
3232

33+
public class ChangelogRecyclerAdapter extends ListAdapter<ChangelogItem, ChangelogRecyclerAdapter.ViewHolder> {
34+
private static final DiffUtil.ItemCallback<ChangelogItem> DIFF_CALLBACK = new DiffUtil.ItemCallback<ChangelogItem>() {
35+
@Override
36+
public boolean areItemsTheSame(@NonNull ChangelogItem oldItem, @NonNull ChangelogItem newItem) {
37+
// Check type, title, and text
38+
if (oldItem.type != newItem.type) {
39+
return false;
40+
}
41+
return Objects.equals(oldItem.getChangeText().toString(), newItem.getChangeText().toString())
42+
&& Objects.equals(oldItem.getChangeTitle(), newItem.getChangeTitle());
43+
}
3344

34-
public class ChangelogRecyclerAdapter extends RecyclerView.Adapter<ChangelogRecyclerAdapter.ViewHolder> {
35-
private final List<ChangelogItem> mAdapterList = new ArrayList<>();
45+
@Override
46+
public boolean areContentsTheSame(@NonNull ChangelogItem oldItem, @NonNull ChangelogItem newItem) {
47+
// Check structural modifications
48+
if (oldItem.isBulletedList() != newItem.isBulletedList()
49+
|| oldItem.isSubtext() != newItem.isSubtext()
50+
|| oldItem.getChangeTextType() != newItem.getChangeTextType()) {
51+
return false;
52+
}
3653

37-
public ChangelogRecyclerAdapter() {
38-
}
54+
// Check for ChangelogHeader
55+
if (oldItem.type == ChangelogItem.HEADER && oldItem instanceof ChangelogHeader && newItem instanceof ChangelogHeader) {
56+
ChangelogHeader oldHeader = (ChangelogHeader) oldItem;
57+
ChangelogHeader newHeader = (ChangelogHeader) newItem;
58+
return Objects.equals(oldHeader.getReleaseType(), newHeader.getReleaseType())
59+
&& Objects.equals(oldHeader.getReleaseDate(), newHeader.getReleaseDate());
60+
}
3961

40-
public void setAdapterList(@NonNull List<ChangelogItem> list) {
41-
synchronized (mAdapterList) {
42-
AdapterUtils.notifyDataSetChanged(this, mAdapterList, list);
62+
return true;
4363
}
64+
};
65+
66+
public ChangelogRecyclerAdapter() {
67+
super(DIFF_CALLBACK);
4468
}
4569

4670
@ChangelogItem.ChangelogType
4771
@Override
4872
public int getItemViewType(int position) {
49-
synchronized (mAdapterList) {
50-
return mAdapterList.get(position).type;
51-
}
73+
return getItem(position).type;
5274
}
5375

5476
@NonNull
@@ -65,10 +87,7 @@ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, @ChangelogItem.C
6587

6688
@Override
6789
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
68-
ChangelogItem changelogItem;
69-
synchronized (mAdapterList) {
70-
changelogItem = mAdapterList.get(position);
71-
}
90+
ChangelogItem changelogItem = getItem(position);
7291
Context context = holder.itemView.getContext();
7392
switch (changelogItem.type) {
7493
case ChangelogItem.HEADER:
@@ -91,13 +110,6 @@ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
91110
}
92111
}
93112

94-
@Override
95-
public int getItemCount() {
96-
synchronized (mAdapterList) {
97-
return mAdapterList.size();
98-
}
99-
}
100-
101113
@NonNull
102114
private CharSequence getChangeText(@NonNull Context context, @NonNull ChangelogItem item) {
103115
SpannableStringBuilder sb = new SpannableStringBuilder();
@@ -166,7 +178,7 @@ private CharSequence getChangeText(@NonNull Context context, @NonNull ChangelogI
166178

167179
@StyleRes
168180
public static int getChangeTextAppearance(@ChangelogItem.ChangeTextType int type) {
169-
switch(type) {
181+
switch (type) {
170182
default:
171183
case ChangelogItem.TEXT_MEDIUM:
172184
return com.google.android.material.R.style.TextAppearance_Material3_BodyMedium;
@@ -179,7 +191,7 @@ public static int getChangeTextAppearance(@ChangelogItem.ChangeTextType int type
179191

180192
@StyleRes
181193
public static int getTitleTextAppearance(@ChangelogItem.ChangeTextType int type) {
182-
switch(type) {
194+
switch (type) {
183195
default:
184196
case ChangelogItem.TEXT_MEDIUM:
185197
return com.google.android.material.R.style.TextAppearance_Material3_TitleMedium;

app/src/main/java/io/github/muntashirakon/AppManager/debloat/BloatwareDetailsDialog.java

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,21 @@
2424
import androidx.lifecycle.AndroidViewModel;
2525
import androidx.lifecycle.MutableLiveData;
2626
import androidx.lifecycle.ViewModelProvider;
27+
import androidx.recyclerview.widget.DiffUtil;
2728
import androidx.recyclerview.widget.LinearLayoutManager;
2829

2930
import com.google.android.material.button.MaterialButton;
3031
import com.google.android.material.chip.Chip;
3132
import com.google.android.material.textview.MaterialTextView;
3233

33-
import java.util.ArrayList;
3434
import java.util.Arrays;
35-
import java.util.Collections;
3635
import java.util.List;
36+
import java.util.Objects;
3737

3838
import io.github.muntashirakon.AppManager.R;
3939
import io.github.muntashirakon.AppManager.StaticDataset;
4040
import io.github.muntashirakon.AppManager.db.utils.AppDb;
4141
import io.github.muntashirakon.AppManager.details.AppDetailsActivity;
42-
import io.github.muntashirakon.util.AdapterUtils;
4342
import io.github.muntashirakon.AppManager.utils.ThreadUtils;
4443
import io.github.muntashirakon.AppManager.utils.UIUtils;
4544
import io.github.muntashirakon.AppManager.utils.appearance.ColorCodes;
@@ -180,7 +179,7 @@ private void updateDialog(@Nullable List<SuggestionObject> suggestionObjects) {
180179
return;
181180
}
182181
mSuggestionContainer.setVisibility(View.VISIBLE);
183-
mAdapter.setList(suggestionObjects);
182+
mAdapter.submitList(suggestionObjects);
184183
}
185184

186185
@NonNull
@@ -254,14 +253,26 @@ public void findDebloatObject(@NonNull String packageName) {
254253
}
255254
}
256255

257-
private class SuggestionsAdapter extends RecyclerView.Adapter<SuggestionsAdapter.SuggestionViewHolder> {
258-
private final List<SuggestionObject> mSuggestions = Collections.synchronizedList(new ArrayList<>());
256+
static class SuggestionsAdapter extends RecyclerView.ListAdapter<SuggestionObject, SuggestionsAdapter.SuggestionViewHolder> {
257+
private static final DiffUtil.ItemCallback<SuggestionObject> DIFF_CALLBACK = new DiffUtil.ItemCallback<SuggestionObject>() {
258+
@Override
259+
public boolean areItemsTheSame(@NonNull SuggestionObject oldItem, @NonNull SuggestionObject newItem) {
260+
// Uniquely identified by server-supplied suggestion identifier
261+
return Objects.equals(oldItem.suggestionId, newItem.suggestionId);
262+
}
259263

260-
public SuggestionsAdapter() {
261-
}
264+
@Override
265+
public boolean areContentsTheSame(@NonNull SuggestionObject oldItem, @NonNull SuggestionObject newItem) {
266+
return Objects.equals(oldItem.packageName, newItem.packageName)
267+
&& Objects.equals(oldItem.getLabel(), newItem.getLabel())
268+
&& Objects.equals(oldItem.getReason(), newItem.getReason())
269+
&& Objects.equals(oldItem.getRepo(), newItem.getRepo())
270+
&& Arrays.equals(oldItem.getUsers(), newItem.getUsers()); // Deep primitive array structural check
271+
}
272+
};
262273

263-
public void setList(@NonNull List<SuggestionObject> suggestions) {
264-
AdapterUtils.notifyDataSetChanged(this, mSuggestions, suggestions);
274+
public SuggestionsAdapter() {
275+
super(DIFF_CALLBACK);
265276
}
266277

267278
@NonNull
@@ -273,17 +284,16 @@ public SuggestionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int vi
273284

274285
@Override
275286
public void onBindViewHolder(@NonNull SuggestionViewHolder holder, int position) {
276-
SuggestionObject suggestion = mSuggestions.get(position);
287+
SuggestionObject suggestion = getItem(position);
277288
holder.labelView.setText(suggestion.getLabel());
278289
holder.packageNameView.setText(suggestion.packageName);
279290
int[] users = suggestion.getUsers();
280291
if (users != null && users.length > 0) {
281292
MaterialButton appInfoButton = holder.marketOrAppInfoButton;
282293
appInfoButton.setIconResource(io.github.muntashirakon.ui.R.drawable.ic_information);
283294
appInfoButton.setOnClickListener(v -> {
284-
Intent appDetailsIntent = AppDetailsActivity.getIntent(requireContext(), suggestion.packageName,
285-
users[0]);
286-
startActivity(appDetailsIntent);
295+
Intent appDetailsIntent = AppDetailsActivity.getIntent(v.getContext(), suggestion.packageName, users[0]);
296+
v.getContext().startActivity(appDetailsIntent);
287297
});
288298
} else {
289299
MaterialButton marketButton = holder.marketOrAppInfoButton;
@@ -292,7 +302,7 @@ public void onBindViewHolder(@NonNull SuggestionViewHolder holder, int position)
292302
Intent appDetailsIntent = suggestion.getMarketLink();
293303
appDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
294304
try {
295-
startActivity(appDetailsIntent);
305+
v.getContext().startActivity(appDetailsIntent);
296306
} catch (Throwable th) {
297307
UIUtils.displayLongToast("Error: " + th.getMessage());
298308
}
@@ -305,17 +315,12 @@ public void onBindViewHolder(@NonNull SuggestionViewHolder holder, int position)
305315
holder.repoView.setText(sb);
306316
}
307317

308-
@Override
309-
public int getItemCount() {
310-
return mSuggestions.size();
311-
}
312-
313318
@Override
314319
public long getItemId(int position) {
315-
return mSuggestions.get(position).hashCode();
320+
return getItem(position).suggestionId.hashCode();
316321
}
317322

318-
private class SuggestionViewHolder extends RecyclerView.ViewHolder {
323+
static class SuggestionViewHolder extends RecyclerView.ViewHolder {
319324
final TextView labelView;
320325
final TextView packageNameView;
321326
final TextView repoView;

0 commit comments

Comments
 (0)