Detailed changes
@@ -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();
+ }
+}
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@color/black54"
+ android:pathData="M12,7L12,3L2,3v18h20L22,7L12,7zM6,19L4,19v-2h2v2zM6,15L4,15v-2h2v2zM6,11L4,11L4,9h2v2zM6,7L4,7L4,5h2v2zM10,19L8,19v-2h2v2zM10,15L8,15v-2h2v2zM10,11L8,11L8,9h2v2zM10,7L8,7L8,5h2v2zM20,19h-8v-2h2v-2h-2v-2h2v-2h-2L12,9h8v10zM18,11h-2v2h2v-2zM18,15h-2v2h2v-2z"/>
+</vector>
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M12,7L12,3L2,3v18h20L22,7L12,7zM6,19L4,19v-2h2v2zM6,15L4,15v-2h2v2zM6,11L4,11L4,9h2v2zM6,7L4,7L4,5h2v2zM10,19L8,19v-2h2v2zM10,15L8,15v-2h2v2zM10,11L8,11L8,9h2v2zM10,7L8,7L8,5h2v2zM20,19h-8v-2h2v-2h-2v-2h2v-2h-2L12,9h8v10zM18,11h-2v2h2v-2zM18,15h-2v2h2v-2z"/>
+</vector>
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@color/black54"
+ android:pathData="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM20,8l-8,5 -8,-5L4,6l8,5 8,-5v2z"/>
+</vector>
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM20,8l-8,5 -8,-5L4,6l8,5 8,-5v2z"/>
+</vector>
@@ -0,0 +1,11 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal"
+ android:autoMirrored="true">
+ <path
+ android:fillColor="@color/black54"
+ android:pathData="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM6,9h12v2L6,11L6,9zM14,14L6,14v-2h8v2zM18,8L6,8L6,6h12v2z"/>
+</vector>
@@ -0,0 +1,36 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
+ android:viewportWidth="54.67484"
+ android:viewportHeight="69.64137"
+ android:width="18.8422dp"
+ android:height="24dp">
+ <path
+ android:pathData="M-69.123113 1078.1423Z"
+ android:fillColor="#000000"
+ android:strokeMiterLimit="10" />
+ <group
+ android:scaleX="0.02688812"
+ android:scaleY="-0.02688812"
+ android:translateX="-4.373626"
+ android:translateY="98.6128">
+ <path
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@color/black54"
+ android:pathData="M3.9,12c0,-1.71 1.39,-3.1 3.1,-3.1h4L11,7L7,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5h4v-1.9L7,15.1c-1.71,0 -3.1,-1.39 -3.1,-3.1zM8,13h8v-2L8,11v2zM17,7h-4v1.9h4c1.71,0 3.1,1.39 3.1,3.1s-1.39,3.1 -3.1,3.1h-4L13,17h4c2.76,0 5,-2.24 5,-5s-2.24,-5 -5,-5z"/>
+</vector>
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M3.9,12c0,-1.71 1.39,-3.1 3.1,-3.1h4L11,7L7,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5h4v-1.9L7,15.1c-1.71,0 -3.1,-1.39 -3.1,-3.1zM8,13h8v-2L8,11v2zM17,7h-4v1.9h4c1.71,0 3.1,1.39 3.1,3.1s-1.39,3.1 -3.1,3.1h-4L13,17h4c2.76,0 5,-2.24 5,-5s-2.24,-5 -5,-5z"/>
+</vector>
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <attr name="icon_email" format="reference" />
+ <attr name="icon_link" format="reference" />
+ <attr name="icon_org" format="reference" />
+ <attr name="icon_chat" format="reference" />
+</resources>
@@ -97,6 +97,10 @@
<item name="media_preview_backup" type="reference">@drawable/ic_backup_black_48dp</item>
<item name="media_preview_unknown" type="reference">@drawable/ic_help_black_48dp</item>
+ <item name="icon_link" type="reference">@drawable/link_black</item>
+ <item name="icon_email" type="reference">@drawable/email_black</item>
+ <item name="icon_org" type="reference">@drawable/business_black</item>
+ <item name="icon_chat" type="reference">@drawable/ic_chat_black_24dp</item>
<item name="icon_add_group" type="reference">@drawable/ic_group_add_white_24dp</item>
<item name="icon_add_person" type="reference">@drawable/ic_person_add_white_24dp</item>
<item name="icon_cancel" type="reference">@drawable/ic_cancel_black_24dp</item>
@@ -252,6 +256,10 @@
<item name="media_preview_backup" type="reference">@drawable/ic_backup_white_48dp</item>
<item name="media_preview_unknown" type="reference">@drawable/ic_help_white_48dp</item>
+ <item name="icon_link" type="reference">@drawable/link_white</item>
+ <item name="icon_email" type="reference">@drawable/email_white</item>
+ <item name="icon_org" type="reference">@drawable/business_white</item>
+ <item name="icon_chat" type="reference">@drawable/ic_chat_white_24dp</item>
<item name="icon_add_group" type="reference">@drawable/ic_group_add_white_24dp</item>
<item name="icon_add_person" type="reference">@drawable/ic_person_add_white_24dp</item>
<item name="icon_cancel" type="reference">@drawable/ic_cancel_white_24dp</item>
@@ -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);
}
@@ -4152,6 +4152,35 @@ public class XmppConnectionService extends Service {
}
}
+ public void fetchVcard4(Account account, final Contact contact, final Consumer<Element> 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);
@@ -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<Element> {
+ 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;
+ }
+ }
}
@@ -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";
}
@@ -128,6 +128,24 @@
</RelativeLayout>
</androidx.cardview.widget.CardView>
+ <androidx.cardview.widget.CardView
+ android:id="@+id/profile"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/activity_vertical_margin"
+ android:layout_marginLeft="@dimen/activity_horizontal_margin"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:layout_marginTop="@dimen/activity_vertical_margin">
+
+ <ListView
+ android:id="@+id/profile_items"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:divider="@android:color/transparent"
+ android:dividerHeight="0dp"></ListView>
+
+ </androidx.cardview.widget.CardView>
+
<androidx.cardview.widget.CardView
android:id="@+id/media_wrapper"
android:layout_width="fill_parent"