From 1e5fe19eeb4ad514e391f2e63c246534d70d0969 Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Wed, 22 Mar 2023 21:57:18 -0500 Subject: [PATCH] Fetch and render vcard4 --- .../java/com/cheogram/android/Util.java | 28 +++++ src/cheogram/res/drawable/business_black.xml | 10 ++ src/cheogram/res/drawable/business_white.xml | 10 ++ src/cheogram/res/drawable/email_black.xml | 10 ++ src/cheogram/res/drawable/email_white.xml | 10 ++ .../res/drawable/ic_chat_black_24dp.xml | 11 ++ src/cheogram/res/drawable/jabber.xml | 36 ++++++ src/cheogram/res/drawable/link_black.xml | 10 ++ src/cheogram/res/drawable/link_white.xml | 10 ++ src/cheogram/res/values/attrs.xml | 7 ++ src/cheogram/res/values/themes.xml | 8 ++ .../conversations/generator/IqGenerator.java | 6 + .../services/XmppConnectionService.java | 29 +++++ .../ui/ContactDetailsActivity.java | 112 ++++++++++++++++++ .../eu/siacs/conversations/xml/Namespace.java | 1 + .../res/layout/activity_contact_details.xml | 18 +++ 16 files changed, 316 insertions(+) create mode 100644 src/cheogram/java/com/cheogram/android/Util.java create mode 100644 src/cheogram/res/drawable/business_black.xml create mode 100644 src/cheogram/res/drawable/business_white.xml create mode 100644 src/cheogram/res/drawable/email_black.xml create mode 100644 src/cheogram/res/drawable/email_white.xml create mode 100644 src/cheogram/res/drawable/ic_chat_black_24dp.xml create mode 100644 src/cheogram/res/drawable/jabber.xml create mode 100644 src/cheogram/res/drawable/link_black.xml create mode 100644 src/cheogram/res/drawable/link_white.xml create mode 100644 src/cheogram/res/values/attrs.xml 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 @@ + + + + + +