M build.gradle => build.gradle +1 -0
@@ 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
}
A src/cheogram/java/com/cheogram/android/TagEditorView.java => src/cheogram/java/com/cheogram/android/TagEditorView.java +57 -0
@@ 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<ListItem.Tag> {
+ 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();
+ }
+}
M src/main/java/eu/siacs/conversations/entities/Contact.java => src/main/java/eu/siacs/conversations/entities/Contact.java +12 -2
@@ 189,12 189,18 @@ public class Contact implements ListItem, Blockable {
return jid;
}
- @Override
- public List<Tag> getTags(Context context) {
+ public List<Tag> getGroupTags() {
final ArrayList<Tag> tags = new ArrayList<>();
for (final String group : getGroups(true)) {
tags.add(new Tag(group, UIHelper.getColorForName(group)));
}
+ return tags;
+ }
+
+ @Override
+ public List<Tag> getTags(Context context) {
+ final ArrayList<Tag> 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<String> groups) {
+ this.groups = new JSONArray(groups);
+ }
+
private Collection<String> getGroups(final boolean unique) {
final Collection<String> groups = unique ? new HashSet<>() : new ArrayList<>();
for (int i = 0; i < this.groups.length(); ++i) {
M src/main/java/eu/siacs/conversations/entities/ListItem.java => src/main/java/eu/siacs/conversations/entities/ListItem.java +2 -1
@@ 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<ListItem>, AvatarService.Avatarable
List<Tag> getTags(Context context);
- final class Tag {
+ final class Tag implements Serializable {
private final String name;
private final int color;
M src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java => src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java +36 -0
@@ 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<ListItem.Tag> 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<Map.Entry<ListItem.Tag,Integer>> sortTagsBy = Map.Entry.comparingByValue(Comparator.reverseOrder());
+ sortTagsBy = sortTagsBy.thenComparing(entry -> entry.getKey().getName());
+
+ ArrayAdapter<ListItem.Tag> 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();
M src/main/res/layout/activity_contact_details.xml => src/main/res/layout/activity_contact_details.xml +9 -0
@@ 67,6 67,15 @@
android:layout_marginTop="4dp"
android:orientation="horizontal"></com.wefika.flowlayout.FlowLayout>
+ <com.cheogram.android.TagEditorView
+ android:id="@+id/edit_tags"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginLeft="-4dp"
+ android:layout_marginTop="-4dp" />
+
<TextView
android:id="@+id/details_lastseen"
android:layout_width="wrap_content"