Support list-multi

Stephen Paul Weber created

No open yet, and just the big searchable list mode for now

Change summary

src/main/java/eu/siacs/conversations/entities/Conversation.java | 46 ++
1 file changed, 36 insertions(+), 10 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/entities/Conversation.java 🔗

@@ -47,6 +47,7 @@ import android.util.DisplayMetrics;
 import android.util.LruCache;
 import android.util.Pair;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -88,6 +89,7 @@ import java.time.format.DateTimeParseException;
 import java.time.format.DateTimeFormatter;
 import java.time.format.FormatStyle;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.HashMap;
@@ -1992,14 +1994,16 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
                     super(binding);
                     binding.search.addTextChangedListener(this);
                 }
-                protected Element mValue = null;
+                protected Field field = null;
+                Set<String> filteredValues;
                 List<Option> options = new ArrayList<>();
                 protected ArrayAdapter<Option> adapter;
                 protected boolean open;
+                protected boolean multi;
 
                 @Override
                 public void bind(Item item) {
-                    Field field = (Field) item;
+                    field = (Field) item;
                     setTextOrHide(binding.label, field.getLabel());
                     setTextOrHide(binding.desc, field.getDesc());
 
@@ -2011,23 +2015,40 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
                         binding.desc.setTextAppearance(binding.getRoot().getContext(), R.style.TextAppearance_Conversations_Status);
                     }
 
-                    mValue = field.getValue();
-
                     Element validate = field.el.findChild("validate", "http://jabber.org/protocol/xdata-validate");
                     open = validate != null && validate.findChild("open", "http://jabber.org/protocol/xdata-validate") != null;
                     setupInputType(field.el, binding.search, null);
 
+                    multi = field.getType().equals(Optional.of("list-multi"));
+                    if (multi) {
+                        binding.list.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);
+                    } else {
+                        binding.list.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
+                    }
+
                     options = field.getOptions();
                     binding.list.setOnItemClickListener((parent, view, position, id) -> {
-                        mValue.setContent(adapter.getItem(binding.list.getCheckedItemPosition()).getValue());
-                        if (open) binding.search.setText(mValue.getContent());
+                        Set<String> values = new HashSet<>(field.getValues());
+                        for (final String value : field.getValues()) {
+                            if (filteredValues.contains(value)) {
+                                values.remove(value);
+                            }
+                        }
+
+                        SparseBooleanArray positions = binding.list.getCheckedItemPositions();
+                        for (int i = 0; i < positions.size(); i++) {
+                            values.add(adapter.getItem(positions.keyAt(i)).getValue());
+                        }
+                        field.setValues(values);
+
+                        if (!multi && open) binding.search.setText(String.join("\n", field.getValues()));
                     });
                     search("");
                 }
 
                 @Override
                 public void afterTextChanged(Editable s) {
-                    if (open) mValue.setContent(s.toString());
+                    if (!multi && open) field.setValues(List.of(s.toString()));
                     search(s.toString());
                 }
 
@@ -2045,11 +2066,14 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
                     } else {
                         filteredOptions = options.stream().filter(o -> o.toString().replaceAll("\\W", "").toLowerCase().contains(q)).collect(Collectors.toList());
                     }
+                    filteredValues = filteredOptions.stream().map(o -> o.getValue()).collect(Collectors.toSet());
                     adapter = new ArrayAdapter(binding.getRoot().getContext(), R.layout.simple_list_item, filteredOptions);
                     binding.list.setAdapter(adapter);
 
-                    int checkedPos = filteredOptions.indexOf(new Option(mValue.getContent(), ""));
-                    if (checkedPos >= 0) binding.list.setItemChecked(checkedPos, true);
+                    for (final String value : field.getValues()) {
+                        int checkedPos = filteredOptions.indexOf(new Option(value, ""));
+                        if (checkedPos >= 0) binding.list.setItemChecked(checkedPos, true);
+                    }
                 }
             }
 
@@ -2480,7 +2504,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
                     return value;
                 }
 
-                public void setValues(List<String> values) {
+                public void setValues(Collection<String> values) {
                     for(Element child : el.getChildren()) {
                         if ("value".equals(child.getName()) && "jabber:x:data".equals(child.getNamespace())) {
                             el.removeChild(child);
@@ -2544,6 +2568,8 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
                             } else {
                                 viewType = TYPE_SPINNER_FIELD;
                             }
+                        } else if (fieldType.equals("list-multi")) {
+                            viewType = TYPE_SEARCH_LIST_FIELD;
                         } else {
                             viewType = TYPE_TEXT_FIELD;
                         }