diff --git a/src/cheogram/java/com/cheogram/android/Util.java b/src/cheogram/java/com/cheogram/android/Util.java
new file mode 100644
index 0000000000000000000000000000000000000000..8683a801397b6e0fe63208032410185b642ad65b
--- /dev/null
+++ b/src/cheogram/java/com/cheogram/android/Util.java
@@ -0,0 +1,28 @@
+package com.cheogram.android;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ListView;
+import android.widget.ListAdapter;
+
+public class Util {
+ public static void justifyListViewHeightBasedOnChildren (ListView listView) {
+ ListAdapter adapter = listView.getAdapter();
+
+ if (adapter == null) {
+ return;
+ }
+ ViewGroup vg = listView;
+ int totalHeight = 0;
+ for (int i = 0; i < adapter.getCount(); i++) {
+ View listItem = adapter.getView(i, null, vg);
+ listItem.measure(0, 0);
+ totalHeight += listItem.getMeasuredHeight();
+ }
+
+ ViewGroup.LayoutParams par = listView.getLayoutParams();
+ par.height = totalHeight + (listView.getDividerHeight() * (adapter.getCount() - 1));
+ listView.setLayoutParams(par);
+ listView.requestLayout();
+ }
+}
diff --git a/src/cheogram/res/drawable/business_black.xml b/src/cheogram/res/drawable/business_black.xml
new file mode 100644
index 0000000000000000000000000000000000000000..092f51b9a457baf57252ebd337aeb5884e7fb50c
--- /dev/null
+++ b/src/cheogram/res/drawable/business_black.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/src/cheogram/res/drawable/business_white.xml b/src/cheogram/res/drawable/business_white.xml
new file mode 100644
index 0000000000000000000000000000000000000000..23ee37d0e0f60da8d097d0888ad40e05ad482e2e
--- /dev/null
+++ b/src/cheogram/res/drawable/business_white.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/src/cheogram/res/drawable/email_black.xml b/src/cheogram/res/drawable/email_black.xml
new file mode 100644
index 0000000000000000000000000000000000000000..0fcdb4bce12e1a69cabc41f3fc4b9dbf2c125596
--- /dev/null
+++ b/src/cheogram/res/drawable/email_black.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/src/cheogram/res/drawable/email_white.xml b/src/cheogram/res/drawable/email_white.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6943b4cedf275cf147349c902cfed68ca60571cd
--- /dev/null
+++ b/src/cheogram/res/drawable/email_white.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/src/cheogram/res/drawable/ic_chat_black_24dp.xml b/src/cheogram/res/drawable/ic_chat_black_24dp.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3abf998ea74d165d61c073141a740556c74fb41c
--- /dev/null
+++ b/src/cheogram/res/drawable/ic_chat_black_24dp.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/src/cheogram/res/drawable/jabber.xml b/src/cheogram/res/drawable/jabber.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8392c2f7b6633cd70e5368fa9e0b8049f83e7a10
--- /dev/null
+++ b/src/cheogram/res/drawable/jabber.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/cheogram/res/drawable/link_black.xml b/src/cheogram/res/drawable/link_black.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5e6eefa3622d30773fd3a61470bc3bee904a145a
--- /dev/null
+++ b/src/cheogram/res/drawable/link_black.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/src/cheogram/res/drawable/link_white.xml b/src/cheogram/res/drawable/link_white.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2c0a73f7ee385d6f5ac25323c67ba0d0bc46c195
--- /dev/null
+++ b/src/cheogram/res/drawable/link_white.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/src/cheogram/res/values/attrs.xml b/src/cheogram/res/values/attrs.xml
new file mode 100644
index 0000000000000000000000000000000000000000..525c1c33a06d0d3c1720297fc8cc38cc6f03d848
--- /dev/null
+++ b/src/cheogram/res/values/attrs.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/src/cheogram/res/values/themes.xml b/src/cheogram/res/values/themes.xml
index 62f863331a9ababd7f1b5e5ceb6dc9ab258feb77..e5268df51a0dfef1943e443f72c383fa84e1ef46 100644
--- a/src/cheogram/res/values/themes.xml
+++ b/src/cheogram/res/values/themes.xml
@@ -97,6 +97,10 @@
- @drawable/ic_backup_black_48dp
- @drawable/ic_help_black_48dp
+ - @drawable/link_black
+ - @drawable/email_black
+ - @drawable/business_black
+ - @drawable/ic_chat_black_24dp
- @drawable/ic_group_add_white_24dp
- @drawable/ic_person_add_white_24dp
- @drawable/ic_cancel_black_24dp
@@ -252,6 +256,10 @@
- @drawable/ic_backup_white_48dp
- @drawable/ic_help_white_48dp
+ - @drawable/link_white
+ - @drawable/email_white
+ - @drawable/business_white
+ - @drawable/ic_chat_white_24dp
- @drawable/ic_group_add_white_24dp
- @drawable/ic_person_add_white_24dp
- @drawable/ic_cancel_white_24dp
diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
index bd4cd01750a8084779316f639d763f3f9fde0e75..9cf7e9161b7c3d495a340322a1a3745c0e3f6db9 100644
--- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
@@ -135,6 +135,12 @@ public class IqGenerator extends AbstractGenerator {
return packet;
}
+ public IqPacket retrieveVcard4(final Jid jid) {
+ final IqPacket packet = retrieve("urn:xmpp:vcard4", null);
+ packet.setTo(jid);
+ return packet;
+ }
+
public IqPacket retrieveBookmarks() {
return retrieve(Namespace.BOOKMARKS2, null);
}
diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
index 0d758119b8d8c79a09a1e8741c65ee779fa1b219..c4e7e4f6fb8daab7af161d951843f7311f5f8bd1 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -4152,6 +4152,35 @@ public class XmppConnectionService extends Service {
}
}
+ public void fetchVcard4(Account account, final Contact contact, final Consumer callback) {
+ IqPacket packet = this.mIqGenerator.retrieveVcard4(contact.getJid());
+ sendIqPacket(account, packet, (a, result) -> {
+ if (result.getType() == IqPacket.TYPE.RESULT) {
+ final Element item = mIqParser.getItem(result);
+ if (item != null) {
+ final Element vcard4 = item.findChild("vcard", Namespace.VCARD4);
+ if (vcard4 != null) {
+ if (callback != null) {
+ callback.accept(vcard4);
+ }
+ return;
+ }
+ }
+ } else {
+ Element error = result.findChild("error");
+ if (error == null) {
+ Log.d(Config.LOGTAG, "fetchVcard4 (server error)");
+ } else {
+ Log.d(Config.LOGTAG, "fetchVcard4 " + error.toString());
+ }
+ }
+ if (callback != null) {
+ callback.accept(null);
+ }
+
+ });
+ }
+
public void deleteContactOnServer(Contact contact) {
contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
contact.resetOption(Contact.Options.DIRTY_PUSH);
diff --git a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
index 9a04d6138d33abce52410073246504acc0253637..8e05ae20a04a15d04de4337c5f738e8c8ff14b19 100644
--- a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
@@ -6,6 +6,7 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -16,11 +17,13 @@ import android.provider.ContactsContract.Intents;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.RelativeSizeSpan;
+import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
+import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
@@ -32,6 +35,8 @@ import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.databinding.DataBindingUtil;
+import com.cheogram.android.Util;
+
import org.openintents.openpgp.util.OpenPgpUtils;
import java.util.ArrayList;
@@ -48,6 +53,7 @@ import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.crypto.axolotl.FingerprintStatus;
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
import eu.siacs.conversations.databinding.ActivityContactDetailsBinding;
+import eu.siacs.conversations.databinding.CommandRowBinding;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Bookmark;
import eu.siacs.conversations.entities.Contact;
@@ -62,6 +68,7 @@ import eu.siacs.conversations.ui.util.AvatarWorkerTask;
import eu.siacs.conversations.ui.util.GridManager;
import eu.siacs.conversations.ui.util.JidDialog;
import eu.siacs.conversations.ui.util.MenuDoubleTabUtil;
+import eu.siacs.conversations.ui.util.ShareUtil;
import eu.siacs.conversations.utils.AccountUtils;
import eu.siacs.conversations.utils.Compatibility;
import eu.siacs.conversations.utils.Emoticons;
@@ -69,6 +76,7 @@ import eu.siacs.conversations.utils.IrregularUnicodeDetector;
import eu.siacs.conversations.utils.PhoneNumberUtilWrapper;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.utils.XmppUri;
+import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
@@ -622,6 +630,47 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
xmppConnectionService.getAttachments(account, contact.getJid().asBareJid(), limit, this);
this.binding.showMedia.setOnClickListener((v) -> MediaBrowserActivity.launch(this, contact));
}
+
+ final VcardAdapter items = new VcardAdapter();
+ binding.profileItems.setAdapter(items);
+ binding.profileItems.setOnItemClickListener((a0, v, pos, a3) -> {
+ final Uri uri = items.getUri(pos);
+ if (uri == null) return;
+
+ if (uri.getScheme().equals("xmpp")) {
+ switchToConversation(xmppConnectionService.findOrCreateConversation(account, Jid.of(uri.getSchemeSpecificPart()), false, true));
+ } else {
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ startActivity(intent);
+ }
+ });
+ binding.profileItems.setOnItemLongClickListener((a0, v, pos, a3) -> {
+ String toCopy = null;
+ final Uri uri = items.getUri(pos);
+ if (uri != null) toCopy = uri.toString();
+ if (toCopy == null) {
+ toCopy = items.getItem(pos).findChildContent("text", Namespace.VCARD4);
+ }
+
+ if (toCopy == null) return false;
+ if (ShareUtil.copyTextToClipboard(ContactDetailsActivity.this, toCopy, R.string.message)) {
+ Toast.makeText(ContactDetailsActivity.this, R.string.message_copied_to_clipboard, Toast.LENGTH_SHORT).show();
+ }
+ return true;
+ });
+ xmppConnectionService.fetchVcard4(account, contact, (vcard4) -> {
+ if (vcard4 == null) return;
+
+ runOnUiThread(() -> {
+ for (Element el : vcard4.getChildren()) {
+ if (el.findChildEnsureSingle("uri", Namespace.VCARD4) != null || el.findChildEnsureSingle("text", Namespace.VCARD4) != null) {
+ items.add(el);
+ }
+ }
+ Util.justifyListViewHeightBasedOnChildren(binding.profileItems);
+ });
+ });
+
populateView();
}
}
@@ -651,4 +700,67 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
});
}
+
+ class VcardAdapter extends ArrayAdapter {
+ VcardAdapter() { super(ContactDetailsActivity.this, 0); }
+
+ private Drawable getDrawable(int attr) {
+ final TypedValue typedvalueattr = new TypedValue();
+ getTheme().resolveAttribute(attr, typedvalueattr, true);
+ return getResources().getDrawable(typedvalueattr.resourceId);
+ }
+
+ @Override
+ public View getView(int position, View view, @NonNull ViewGroup parent) {
+ final CommandRowBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.command_row, parent, false);
+ final Element item = getItem(position);
+
+ if (item.getName().equals("org")) {
+ binding.command.setCompoundDrawablesRelativeWithIntrinsicBounds(getDrawable(R.attr.icon_org), null, null, null);
+ binding.command.setCompoundDrawablePadding(20);
+ } else if (item.getName().equals("impp")) {
+ binding.command.setCompoundDrawablesRelativeWithIntrinsicBounds(getDrawable(R.attr.icon_chat), null, null, null);
+ binding.command.setCompoundDrawablePadding(20);
+ } else if (item.getName().equals("url")) {
+ binding.command.setCompoundDrawablesRelativeWithIntrinsicBounds(getDrawable(R.attr.icon_link), null, null, null);
+ binding.command.setCompoundDrawablePadding(20);
+ }
+
+ final Uri uri = getUri(position);
+ if (uri != null) {
+ if (uri.getScheme().equals("xmpp")) {
+ binding.command.setText(uri.getSchemeSpecificPart());
+ binding.command.setCompoundDrawablesRelativeWithIntrinsicBounds(getResources().getDrawable(R.drawable.jabber), null, null, null);
+ binding.command.setCompoundDrawablePadding(20);
+ } else if (uri.getScheme().equals("tel")) {
+ binding.command.setText(uri.getSchemeSpecificPart());
+ binding.command.setCompoundDrawablesRelativeWithIntrinsicBounds(getDrawable(R.attr.ic_make_audio_call), null, null, null);
+ binding.command.setCompoundDrawablePadding(20);
+ } else if (uri.getScheme().equals("mailto")) {
+ binding.command.setText(uri.getSchemeSpecificPart());
+ binding.command.setCompoundDrawablesRelativeWithIntrinsicBounds(getDrawable(R.attr.icon_email), null, null, null);
+ binding.command.setCompoundDrawablePadding(20);
+ } else if (uri.getScheme().equals("http") || uri.getScheme().equals("https")) {
+ binding.command.setText(uri.toString());
+ binding.command.setCompoundDrawablesRelativeWithIntrinsicBounds(getDrawable(R.attr.icon_link), null, null, null);
+ binding.command.setCompoundDrawablePadding(20);
+ } else {
+ binding.command.setText(uri.toString());
+ }
+ } else {
+ final String text = item.findChildContent("text", Namespace.VCARD4);
+ binding.command.setText(text);
+ }
+
+ return binding.getRoot();
+ }
+
+ public Uri getUri(int pos) {
+ final Element item = getItem(pos);
+ final String uriS = item.findChildContent("uri", Namespace.VCARD4);
+ if (uriS != null) return Uri.parse(uriS).normalizeScheme();
+ if (item.getName().equals("email")) return Uri.parse("mailto:" + item.findChildContent("text", Namespace.VCARD4));
+ return null;
+ }
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/xml/Namespace.java b/src/main/java/eu/siacs/conversations/xml/Namespace.java
index 4570033e496cd2f6857293a476d69b44326d55fa..fdf14728c03e53693509a7d4431d3d245b98f5e0 100644
--- a/src/main/java/eu/siacs/conversations/xml/Namespace.java
+++ b/src/main/java/eu/siacs/conversations/xml/Namespace.java
@@ -66,4 +66,5 @@ public final class Namespace {
public static final String EASY_ONBOARDING_INVITE = "urn:xmpp:invite#invite";
public static final String OMEMO_DTLS_SRTP_VERIFICATION = "http://gultsch.de/xmpp/drafts/omemo/dlts-srtp-verification";
public static final String UNIFIED_PUSH = "http://gultsch.de/xmpp/drafts/unified-push";
+ public static final String VCARD4 = "urn:ietf:params:xml:ns:vcard-4.0";
}
diff --git a/src/main/res/layout/activity_contact_details.xml b/src/main/res/layout/activity_contact_details.xml
index d64963685f28b7f222672087ce4898fe0112df86..a50f417d21e621e25d1bac75d45124ec755fe168 100644
--- a/src/main/res/layout/activity_contact_details.xml
+++ b/src/main/res/layout/activity_contact_details.xml
@@ -128,6 +128,24 @@
+
+
+
+
+
+