diff --git a/build.gradle b/build.gradle index 99b0cc8393379094d240bae42b963076e1ae4344..b07919716ca6ee627f8650d2d30ccb353482d0c9 100644 --- a/build.gradle +++ b/build.gradle @@ -109,6 +109,7 @@ dependencies { implementation 'io.github.nishkarsh:android-permissions:2.1.6' implementation 'androidx.recyclerview:recyclerview:1.1.0' implementation 'com.github.ipld:java-cid:v1.3.1' + implementation 'com.splitwise:tokenautocomplete:3.0.2' implementation urlFile('https://gateway.pinata.cloud/ipfs/QmeqMiLxHi8AAjXobxr3QTfa1bSSLyAu86YviAqQnjxCjM/libwebrtc.aar', 'libwebrtc.aar') // INSERT } diff --git a/src/cheogram/java/com/cheogram/android/TagEditorView.java b/src/cheogram/java/com/cheogram/android/TagEditorView.java new file mode 100644 index 0000000000000000000000000000000000000000..5405dedecd875915138cf7f2481c8bb379190236 --- /dev/null +++ b/src/cheogram/java/com/cheogram/android/TagEditorView.java @@ -0,0 +1,57 @@ +package com.cheogram.android; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.tokenautocomplete.TokenCompleteTextView; + +import eu.siacs.conversations.R; +import eu.siacs.conversations.entities.ListItem; +import eu.siacs.conversations.utils.UIHelper; + +public class TagEditorView extends TokenCompleteTextView { + public TagEditorView(Context context, AttributeSet attrs) { + super(context, attrs); + setTokenClickStyle(TokenCompleteTextView.TokenClickStyle.Delete); + setThreshold(1); + performBestGuess(false); + allowCollapse(false); + } + + public void clearSync() { + for (ListItem.Tag tag : getObjects()) { + removeObjectSync(tag); + } + } + + @Override + protected View getViewForObject(ListItem.Tag tag) { + LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Activity.LAYOUT_INFLATER_SERVICE); + final TextView tv = (TextView) inflater.inflate(R.layout.list_item_tag, (ViewGroup) getParent(), false); + tv.setText(tag.getName()); + tv.setBackgroundColor(tag.getColor()); + return tv; + } + + @Override + protected ListItem.Tag defaultObject(String completionText) { + return new ListItem.Tag(completionText, UIHelper.getColorForName(completionText)); + } + + @Override + public boolean shouldIgnoreToken(ListItem.Tag tag) { + return getObjects().contains(tag); + } + + @Override + public void onFocusChanged(boolean hasFocus, int direction, Rect previous) { + super.onFocusChanged(hasFocus, direction, previous); + performCompletion(); + } +} diff --git a/src/main/java/eu/siacs/conversations/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java index bcac60f933f9545b53d7e5646bfad0d8a214c862..42232c560f784d1b3cc13c9c325586eee82613ae 100644 --- a/src/main/java/eu/siacs/conversations/entities/Contact.java +++ b/src/main/java/eu/siacs/conversations/entities/Contact.java @@ -189,12 +189,18 @@ public class Contact implements ListItem, Blockable { return jid; } - @Override - public List getTags(Context context) { + public List getGroupTags() { final ArrayList tags = new ArrayList<>(); for (final String group : getGroups(true)) { tags.add(new Tag(group, UIHelper.getColorForName(group))); } + return tags; + } + + @Override + public List getTags(Context context) { + final ArrayList tags = new ArrayList<>(); + tags.addAll(getGroupTags()); for (final String tag : getSystemTags(true)) { tags.add(new Tag(tag, UIHelper.getColorForName(tag))); } @@ -344,6 +350,10 @@ public class Contact implements ListItem, Blockable { this.systemAccount = lookupUri; } + public void setGroups(List groups) { + this.groups = new JSONArray(groups); + } + private Collection getGroups(final boolean unique) { final Collection groups = unique ? new HashSet<>() : new ArrayList<>(); for (int i = 0; i < this.groups.length(); ++i) { diff --git a/src/main/java/eu/siacs/conversations/entities/ListItem.java b/src/main/java/eu/siacs/conversations/entities/ListItem.java index ee27dff5da4005b903951af7832a88bfc6361a80..83caaf431557f803678cd7a5095a5206e15b87e1 100644 --- a/src/main/java/eu/siacs/conversations/entities/ListItem.java +++ b/src/main/java/eu/siacs/conversations/entities/ListItem.java @@ -2,6 +2,7 @@ package eu.siacs.conversations.entities; import android.content.Context; +import java.io.Serializable; import java.util.List; import java.util.Locale; @@ -16,7 +17,7 @@ public interface ListItem extends Comparable, AvatarService.Avatarable List getTags(Context context); - final class Tag { + final class Tag implements Serializable { private final String name; private final int color; diff --git a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java index eb213cb2d3cada654f7deaa9bad2172e92046e76..7d89184fe360c16667c75c9592cdb314e192f55a 100644 --- a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java @@ -21,6 +21,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; +import android.widget.ArrayAdapter; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.EditText; @@ -33,9 +34,13 @@ import androidx.databinding.DataBindingUtil; import org.openintents.openpgp.util.OpenPgpUtils; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; @@ -44,6 +49,7 @@ import eu.siacs.conversations.crypto.axolotl.FingerprintStatus; import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession; import eu.siacs.conversations.databinding.ActivityContactDetailsBinding; import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.entities.Bookmark; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.ListItem; import eu.siacs.conversations.services.AbstractQuickConversationsService; @@ -266,9 +272,11 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp } protected void saveEdits() { + binding.editTags.setVisibility(View.GONE); if (edit != null) { EditText text = edit.getActionView().findViewById(R.id.search_field); contact.setServerName(text.getText().toString()); + contact.setGroups(binding.editTags.getObjects().stream().map(tag -> tag.getName()).collect(Collectors.toList())); ContactDetailsActivity.this.xmppConnectionService.pushContactToServer(contact); populateView(); edit.collapseActionView(); @@ -313,6 +321,34 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp }); text.setText(contact.getServerName()); text.requestFocus(); + binding.tags.setVisibility(View.GONE); + binding.editTags.clearSync(); + for (final ListItem.Tag group : contact.getGroupTags()) { + binding.editTags.addObjectSync(group); + } + ArrayList tags = new ArrayList<>(); + for (final Account account : xmppConnectionService.getAccounts()) { + for (Contact contact : account.getRoster().getContacts()) { + tags.addAll(contact.getTags(this)); + } + for (Bookmark bookmark : account.getBookmarks()) { + tags.addAll(bookmark.getTags(this)); + } + } + Comparator> sortTagsBy = Map.Entry.comparingByValue(Comparator.reverseOrder()); + sortTagsBy = sortTagsBy.thenComparing(entry -> entry.getKey().getName()); + + ArrayAdapter adapter = new ArrayAdapter<>( + this, + android.R.layout.simple_list_item_1, + tags.stream() + .collect(Collectors.toMap((x) -> x, (t) -> 1, (c1, c2) -> c1 + c2)) + .entrySet().stream() + .sorted(sortTagsBy) + .map(e -> e.getKey()).collect(Collectors.toList()) + ); + binding.editTags.setAdapter(adapter); + binding.editTags.setVisibility(View.VISIBLE); if (save != null) save.setVisible(true); } else { menuItem.collapseActionView(); diff --git a/src/main/res/layout/activity_contact_details.xml b/src/main/res/layout/activity_contact_details.xml index 1c2ce4359701619e1e469b02d8b529c25d9c255a..de3051127b8ef962beb85dc1fe9d9b9d72331192 100644 --- a/src/main/res/layout/activity_contact_details.xml +++ b/src/main/res/layout/activity_contact_details.xml @@ -67,6 +67,15 @@ android:layout_marginTop="4dp" android:orientation="horizontal"> + +