Skip to content

Commit ca038d6

Browse files
committed
Fix single choice and multiple choice dialogs
Signed-off-by: Muntashir Al-Islam <muntashirakon@riseup.net>
1 parent 770f1c9 commit ca038d6

2 files changed

Lines changed: 90 additions & 54 deletions

File tree

libcore/ui/src/main/java/io/github/muntashirakon/dialog/SearchableMultiChoiceDialogBuilder.java

Lines changed: 64 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,14 @@ static class MultiChoiceItem<E> {
7676
this.isSelected = false;
7777
this.isDisabled = false;
7878
}
79+
80+
MultiChoiceItem(@NonNull MultiChoiceItem<E> other) {
81+
this.id = other.id;
82+
this.name = other.name;
83+
this.rawItem = other.rawItem;
84+
this.isSelected = other.isSelected;
85+
this.isDisabled = other.isDisabled;
86+
}
7987
}
8088

8189
public SearchableMultiChoiceDialogBuilder(@NonNull Context context, @NonNull List<T> items, @ArrayRes int itemNames) {
@@ -285,6 +293,8 @@ class SearchableRecyclerViewAdapter extends ListAdapter<MultiChoiceItem<T>, Sear
285293
final List<MultiChoiceItem<T>> mMasterList = new ArrayList<>();
286294
@NonNull
287295
final List<T> mNotFoundItems = new ArrayList<>();
296+
@NonNull
297+
List<MultiChoiceItem<T>> mCurrentVisibleList = new ArrayList<>();
288298
@LayoutRes
289299
private final int mLayoutId;
290300
@Nullable
@@ -308,21 +318,20 @@ void setFilteredItems(CharSequence constraint) {
308318

309319
private void dispatchFilteredList() {
310320
if (mCurrentQuery == null || mCurrentQuery.length() == 0) {
311-
submitList(new ArrayList<>(mMasterList));
312-
return;
313-
}
314-
315-
Locale locale = Locale.getDefault();
316-
String query = mCurrentQuery.toString().toLowerCase(locale);
317-
List<MultiChoiceItem<T>> filteredList = new ArrayList<>();
318-
319-
for (MultiChoiceItem<T> item : mMasterList) {
320-
if (item.name.toString().toLowerCase(locale).contains(query)
321-
|| item.rawItem.toString().toLowerCase(Locale.ROOT).contains(query)) {
322-
filteredList.add(item);
321+
mCurrentVisibleList = new ArrayList<>(mMasterList);
322+
} else {
323+
Locale locale = Locale.getDefault();
324+
String query = mCurrentQuery.toString().toLowerCase(locale);
325+
List<MultiChoiceItem<T>> filteredList = new ArrayList<>();
326+
for (MultiChoiceItem<T> item : mMasterList) {
327+
if (item.name.toString().toLowerCase(locale).contains(query)
328+
|| item.rawItem.toString().toLowerCase(Locale.ROOT).contains(query)) {
329+
filteredList.add(item);
330+
}
323331
}
332+
mCurrentVisibleList = filteredList;
324333
}
325-
submitList(filteredList);
334+
submitList(mCurrentVisibleList);
326335
}
327336

328337
ArrayList<T> getSelectedItems() {
@@ -337,20 +346,29 @@ ArrayList<T> getSelectedItems() {
337346

338347
void addSelectedItems(@Nullable List<T> selectedItems) {
339348
if (selectedItems == null) return;
349+
boolean listUpdated = false;
340350
for (T item : selectedItems) {
341351
boolean found = false;
342-
for (MultiChoiceItem<T> wrapper : mMasterList) {
352+
for (int i = 0; i < mMasterList.size(); i++) {
353+
MultiChoiceItem<T> wrapper = mMasterList.get(i);
343354
if (Objects.equals(wrapper.rawItem, item)) {
344-
wrapper.isSelected = true;
355+
if (!wrapper.isSelected) {
356+
MultiChoiceItem<T> newItem = new MultiChoiceItem<>(wrapper);
357+
newItem.isSelected = true;
358+
mMasterList.set(i, newItem);
359+
listUpdated = true;
360+
}
345361
found = true;
346362
break;
347363
}
348364
}
349-
if (!found) {
365+
if (!found && !mNotFoundItems.contains(item)) {
350366
mNotFoundItems.add(item);
351367
}
352368
}
353-
dispatchFilteredList();
369+
if (listUpdated) {
370+
dispatchFilteredList();
371+
}
354372
}
355373

356374
void addSelectedIndexes(@Nullable int[] selectedIndexes) {
@@ -393,35 +411,44 @@ void addDisabledItems(@Nullable List<T> disabledItems) {
393411
}
394412

395413
void selectAll() {
396-
List<MultiChoiceItem<T>> visibleItems = getCurrentList();
397-
for (MultiChoiceItem<T> item : visibleItems) {
414+
boolean listUpdated = false;
415+
for (MultiChoiceItem<T> item : mCurrentVisibleList) {
398416
if (!item.isSelected) {
399-
item.isSelected = true;
400-
triggerMultiChoiceClickListener(item.id, true);
417+
MultiChoiceItem<T> newItem = new MultiChoiceItem<>(item);
418+
newItem.isSelected = true;
419+
mMasterList.set(newItem.id, newItem);
420+
triggerMultiChoiceClickListener(newItem.id, true);
421+
listUpdated = true;
401422
}
402423
}
403-
checkSelections();
404-
dispatchFilteredList();
424+
if (listUpdated) {
425+
dispatchFilteredList();
426+
checkSelections();
427+
}
405428
}
406429

407430
void deselectAll() {
408-
List<MultiChoiceItem<T>> visibleItems = getCurrentList();
409-
for (MultiChoiceItem<T> item : visibleItems) {
431+
boolean listUpdated = false;
432+
for (MultiChoiceItem<T> item : mCurrentVisibleList) {
410433
if (item.isSelected) {
411-
item.isSelected = false;
412-
triggerMultiChoiceClickListener(item.id, false);
434+
MultiChoiceItem<T> newItem = new MultiChoiceItem<>(item);
435+
newItem.isSelected = false;
436+
mMasterList.set(newItem.id, newItem);
437+
triggerMultiChoiceClickListener(newItem.id, false);
438+
listUpdated = true;
413439
}
414440
}
415-
checkSelections();
416-
dispatchFilteredList();
441+
if (listUpdated) {
442+
dispatchFilteredList();
443+
checkSelections();
444+
}
417445
}
418446

419447
boolean areAllSelected() {
420-
List<MultiChoiceItem<T>> visibleItems = getCurrentList();
421-
if (visibleItems.isEmpty()) {
448+
if (mCurrentVisibleList.isEmpty()) {
422449
return false;
423450
}
424-
for (MultiChoiceItem<T> item : visibleItems) {
451+
for (MultiChoiceItem<T> item : mCurrentVisibleList) {
425452
if (!item.isSelected) {
426453
return false;
427454
}
@@ -439,17 +466,17 @@ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
439466
@Override
440467
public void onBindViewHolder(@NonNull SearchableRecyclerViewAdapter.ViewHolder holder, int position) {
441468
MultiChoiceItem<T> item = getItem(position);
442-
443469
holder.item.setText(item.name);
444470
holder.item.setTextIsSelectable(mIsTextSelectable);
445471
holder.item.setEnabled(!item.isDisabled);
446472
holder.item.setChecked(item.isSelected);
447-
448473
holder.item.setOnClickListener(v -> {
449-
item.isSelected = !item.isSelected;
450-
holder.item.setChecked(item.isSelected);
474+
MultiChoiceItem<T> newItem = new MultiChoiceItem<>(item);
475+
newItem.isSelected = !item.isSelected;
476+
mMasterList.set(newItem.id, newItem);
477+
dispatchFilteredList();
451478
checkSelections();
452-
triggerMultiChoiceClickListener(item.id, item.isSelected);
479+
triggerMultiChoiceClickListener(newItem.id, newItem.isSelected);
453480
});
454481
}
455482

libcore/ui/src/main/java/io/github/muntashirakon/dialog/SearchableSingleChoiceDialogBuilder.java

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,14 @@ static class SingleChoiceItem<E> {
7878
this.isSelected = false;
7979
this.isDisabled = false;
8080
}
81+
82+
SingleChoiceItem(@NonNull SingleChoiceItem<E> other) {
83+
this.id = other.id;
84+
this.name = other.name;
85+
this.rawItem = other.rawItem;
86+
this.isSelected = other.isSelected;
87+
this.isDisabled = other.isDisabled;
88+
}
8189
}
8290

8391
public SearchableSingleChoiceDialogBuilder(@NonNull Context context, @NonNull List<T> items, @ArrayRes int itemNames) {
@@ -329,11 +337,14 @@ void setSelection(@Nullable T selectedItem) {
329337
return;
330338
}
331339
mSelectedRawItem = selectedItem;
332-
for (SingleChoiceItem<T> item : mMasterList) {
340+
for (int i = 0; i < mMasterList.size(); i++) {
341+
SingleChoiceItem<T> item = mMasterList.get(i);
333342
boolean targetSelectionState = Objects.equals(item.rawItem, selectedItem);
334343
if (item.isSelected != targetSelectionState) {
335-
item.isSelected = targetSelectionState;
336-
triggerSingleChoiceClickListener(item.id, targetSelectionState);
344+
SingleChoiceItem<T> newItem = new SingleChoiceItem<>(item);
345+
newItem.isSelected = targetSelectionState;
346+
mMasterList.set(i, newItem);
347+
triggerSingleChoiceClickListener(newItem.id, targetSelectionState);
337348
}
338349
}
339350
dispatchFilteredList();
@@ -350,15 +361,22 @@ void setSelectedIndex(int selectedIndex) {
350361

351362
void addDisabledItems(@Nullable List<T> disabledItems) {
352363
if (disabledItems == null) return;
364+
boolean listUpdated = false;
353365
for (T item : disabledItems) {
354-
for (SingleChoiceItem<T> wrapper : mMasterList) {
355-
if (Objects.equals(wrapper.rawItem, item)) {
356-
wrapper.isDisabled = true;
366+
for (int i = 0; i < mMasterList.size(); i++) {
367+
SingleChoiceItem<T> wrapper = mMasterList.get(i);
368+
if (Objects.equals(wrapper.rawItem, item) && !wrapper.isDisabled) {
369+
SingleChoiceItem<T> newItem = new SingleChoiceItem<>(wrapper);
370+
newItem.isDisabled = true;
371+
mMasterList.set(i, newItem);
372+
listUpdated = true;
357373
break;
358374
}
359375
}
360376
}
361-
dispatchFilteredList();
377+
if (listUpdated) {
378+
dispatchFilteredList();
379+
}
362380
}
363381

364382
@NonNull
@@ -380,16 +398,7 @@ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
380398
// Already selected, do nothing
381399
return;
382400
}
383-
mSelectedRawItem = item.rawItem;
384-
for (SingleChoiceItem<T> target : mMasterList) {
385-
boolean wasSelected = target.isSelected;
386-
target.isSelected = (target.id == item.id);
387-
if (wasSelected != target.isSelected) {
388-
// Trigger only if this item wasn't selected before
389-
triggerSingleChoiceClickListener(target.id, target.isSelected);
390-
}
391-
}
392-
dispatchFilteredList();
401+
setSelection(item.rawItem);
393402
});
394403
}
395404

0 commit comments

Comments
 (0)