package com.xabber.android.ui.fragment;
import android.app.Activity;
import android.app.Fragment;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.text.util.Linkify;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.xabber.android.R;
import com.xabber.android.data.Application;
import com.xabber.android.data.VcardMaps;
import com.xabber.android.data.account.AccountItem;
import com.xabber.android.data.account.AccountManager;
import com.xabber.android.data.account.StatusMode;
import com.xabber.android.data.account.listeners.OnAccountChangedListener;
import com.xabber.android.data.entity.AccountJid;
import com.xabber.android.data.entity.BaseEntity;
import com.xabber.android.data.entity.UserJid;
import com.xabber.android.data.extension.capability.CapabilitiesManager;
import com.xabber.android.data.extension.capability.ClientInfo;
import com.xabber.android.data.extension.vcard.OnVCardListener;
import com.xabber.android.data.extension.vcard.VCardManager;
import com.xabber.android.data.log.LogManager;
import com.xabber.android.data.roster.OnContactChangedListener;
import com.xabber.android.data.roster.PresenceManager;
import com.xabber.android.data.roster.RosterContact;
import com.xabber.android.data.roster.RosterManager;
import com.xabber.android.ui.activity.AccountInfoEditorActivity;
import com.xabber.android.ui.color.ColorManager;
import com.xabber.xmpp.vcard.AddressProperty;
import com.xabber.xmpp.vcard.AddressType;
import com.xabber.xmpp.vcard.EmailType;
import com.xabber.xmpp.vcard.TelephoneType;
import com.xabber.xmpp.vcard.VCardProperty;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smackx.vcardtemp.packet.VCard;
import org.jivesoftware.smackx.vcardtemp.provider.VCardProvider;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.parts.Resourcepart;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class ContactVcardViewerFragment extends Fragment implements OnContactChangedListener, OnAccountChangedListener, OnVCardListener {
public static final String ARGUMENT_ACCOUNT = "com.xabber.android.ui.fragment.ContactVcardViewerFragment.ARGUMENT_ACCOUNT";
public static final String ARGUMENT_USER = "com.xabber.android.ui.fragment.ContactVcardViewerFragment.ARGUMENT_USER";
private static final String SAVED_VCARD = "com.xabber.android.ui.fragment.ContactVcardViewerFragment.SAVED_VCARD";
private static final String SAVED_VCARD_ERROR = "com.xabber.android.ui.fragment.ContactVcardViewerFragment.SAVED_VCARD_ERROR";
private static final String LOG_TAG = ContactVcardViewerFragment.class.getSimpleName();
public static final int REQUEST_CODE_EDIT_VCARD = 1;
AccountJid account;
UserJid user;
private LinearLayout xmppItems;
private LinearLayout contactInfoItems;
private VCard vCard;
private boolean vCardError;
private View progressBar;
private Listener listener;
private Button editButton;
public interface Listener {
void onVCardReceived();
}
public static ContactVcardViewerFragment newInstance(AccountJid account, UserJid user) {
ContactVcardViewerFragment fragment = new ContactVcardViewerFragment();
Bundle arguments = new Bundle();
arguments.putParcelable(ARGUMENT_ACCOUNT, account);
arguments.putParcelable(ARGUMENT_USER, user);
fragment.setArguments(arguments);
return fragment;
}
public static ContactVcardViewerFragment newInstance(AccountJid account) {
try {
return newInstance(account, UserJid.from(account.getFullJid().asBareJid()));
} catch (UserJid.UserJidCreateException e) {
throw new IllegalStateException("Cannot convert account to user. Account: " + account, e);
}
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
listener = (Listener) activity;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle args = getArguments();
account = args.getParcelable(ARGUMENT_ACCOUNT);
user = args.getParcelable(ARGUMENT_USER);
vCard = null;
vCardError = false;
if (savedInstanceState != null) {
vCardError = savedInstanceState.getBoolean(SAVED_VCARD_ERROR, false);
String xml = savedInstanceState.getString(SAVED_VCARD);
if (xml != null) {
try {
vCard = parseVCard(xml);
} catch (Exception e) {
LogManager.exception(this, e);
}
}
}
}
public static VCard parseVCard(String xml) throws Exception {
XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
parser.setInput(new StringReader(xml));
int eventType = parser.next();
if (eventType != XmlPullParser.START_TAG) {
throw new IllegalStateException(String.valueOf(eventType));
}
if (!VCard.ELEMENT.equals(parser.getName())) {
throw new IllegalStateException(parser.getName());
}
if (!VCard.NAMESPACE.equals(parser.getNamespace())) {
throw new IllegalStateException(parser.getNamespace());
}
return (new VCardProvider()).parse(parser);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.fragment_contact_vcard, container, false);
xmppItems = (LinearLayout) view.findViewById(R.id.xmpp_items);
contactInfoItems = (LinearLayout) view.findViewById(R.id.contact_info_items);
progressBar = view.findViewById(R.id.contact_info_progress_bar);
editButton = (Button) view.findViewById(R.id.contact_info_edit_button);
editButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (vCard != null) {
Intent intent = AccountInfoEditorActivity.createIntent(getActivity(), account,
vCard.getChildElementXML().toString());
startActivityForResult(intent, REQUEST_CODE_EDIT_VCARD);
}
}
});
return view;
}
@Override
public void onResume() {
super.onResume();
if (AccountManager.getInstance().getAccount(account) == null) {
// in case if account was removed
return;
}
Application.getInstance().addUIListener(OnVCardListener.class, this);
Application.getInstance().addUIListener(OnContactChangedListener.class, this);
Application.getInstance().addUIListener(OnAccountChangedListener.class, this);
if (vCard == null && !vCardError) {
requestVCard();
} else {
updateVCard();
}
updateContact(account, user);
}
public void requestVCard() {
progressBar.setVisibility(View.VISIBLE);
VCardManager.getInstance().requestByUser(account, user.getJid());
}
@Override
public void onPause() {
super.onPause();
Application.getInstance().removeUIListener(OnVCardListener.class, this);
Application.getInstance().removeUIListener(OnContactChangedListener.class, this);
Application.getInstance().removeUIListener(OnAccountChangedListener.class, this);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(SAVED_VCARD_ERROR, vCardError);
if (vCard != null) {
outState.putString(SAVED_VCARD, vCard.getChildElementXML().toString());
}
}
@Override
public void onDetach() {
super.onDetach();
listener = null;
}
@Override
public void onVCardReceived(AccountJid account, Jid bareAddress, VCard vCard) {
if (!this.account.equals(account) || !this.user.equals(bareAddress)) {
return;
}
this.vCard = vCard;
this.vCardError = false;
updateVCard();
listener.onVCardReceived();
progressBar.setVisibility(View.GONE);
}
@Override
public void onVCardFailed(AccountJid account, Jid bareAddress) {
if (!this.account.equals(account) || !this.user.equals(bareAddress)) {
return;
}
this.vCard = null;
this.vCardError = true;
progressBar.setVisibility(View.GONE);
}
@Override
public void onContactsChanged(Collection<RosterContact> entities) {
for (BaseEntity entity : entities) {
if (entity.equals(account, user)) {
updateContact(account, user);
break;
}
}
}
@Override
public void onAccountsChanged(Collection<AccountJid> accounts) {
if (accounts.contains(account)) {
updateContact(account, user);
if (account.getFullJid().asBareJid().equals(user.getJid().asBareJid())) {
AccountItem accountItem = AccountManager.getInstance().getAccount(this.account);
if (accountItem != null && accountItem.getFactualStatusMode().isOnline()) {
VCardManager.getInstance().request(this.account, this.account.getFullJid().asBareJid());
}
}
}
}
/**
* @return Concatenated source and value with splitter if necessary.
*/
private String addString(String source, String value, String splitter) {
if (value == null || "".equals(value)) {
return source;
}
if (source == null || "".equals(source)) {
return value;
}
return source + splitter + value;
}
public void updateContact(AccountJid account, UserJid bareAddress) {
this.account = account;
this.user = bareAddress;
if (!isAdded()) {
return;
}
xmppItems.removeAllViews();
List<View> resourcesList = new ArrayList<>();
fillResourceList(account, bareAddress.getJid(), resourcesList);
if (!resourcesList.isEmpty()) {
addHeader(xmppItems, getString(R.string.contact_info_connected_clients_header));
}
addItemGroup(resourcesList, xmppItems, R.drawable.ic_vcard_jabber_24dp, false);
addHeader(xmppItems, getString(R.string.contact_info_visiting_card_header));
View jabberIdView = createItemView(xmppItems, getString(R.string.jabber_id),
bareAddress.toString(), R.drawable.ic_vcard_xmpp_24dp);
if (jabberIdView != null) {
xmppItems.addView(jabberIdView);
}
}
private void addHeader(LinearLayout rootView, String text) {
LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
View contactInfoHeader = inflater.inflate(R.layout.item_contact_info_header, rootView, false);
TextView headerView = (TextView) contactInfoHeader.findViewById(R.id.contact_info_header_text_view);
headerView.setTextColor(ColorManager.getInstance().getAccountPainter().getAccountSendButtonColor(account));
headerView.setText(text);
rootView.addView(contactInfoHeader);
}
private void fillResourceList(AccountJid account, Jid bareAddress, List<View> resourcesList) {
final List<Presence> allPresences = RosterManager.getInstance().getPresences(account, bareAddress);
boolean isAccount = account.getFullJid().asBareJid().equals(user.getBareJid());
Resourcepart accountResource = null;
if (isAccount) {
// TODO: probably not the best way to get own resource
AccountItem accountItem = AccountManager.getInstance().getAccount(account);
if (accountItem != null) {
accountResource = accountItem.getConnection().getConfiguration().getResource();
}
}
PresenceManager.sortPresencesByPriority(allPresences);
for (Presence presence : allPresences) {
Jid fromJid = presence.getFrom();
ClientInfo clientInfo = CapabilitiesManager.getInstance().getCachedClientInfo(fromJid);
String client = "";
if (clientInfo == null) {
client = getString(R.string.please_wait);
CapabilitiesManager.getInstance().requestClientInfoByUser(account, fromJid);
} else if (clientInfo == ClientInfo.INVALID_CLIENT_INFO) {
client = getString(R.string.unknown);
} else {
String name = clientInfo.getName();
if (name != null) {
client = name;
}
String type = clientInfo.getType();
if (type != null) {
if (client.isEmpty()) {
client = type;
} else {
client = client + "/" + type;
}
}
}
int priorityValue = presence.getPriority();
String priorityString;
if (priorityValue == Integer.MIN_VALUE) {
priorityString = getString(R.string.account_priority) + ": " + getString(R.string.unknown);
} else {
priorityString = getString(R.string.account_priority) + ": " + priorityValue;
}
if (!client.isEmpty()) {
client = getString(R.string.contact_viewer_client) + ": " + client;
}
Resourcepart resourceOrNull = fromJid.getResourceOrNull();
String resource = getString(R.string.account_resource) + ": " + resourceOrNull;
final StatusMode statusMode = StatusMode.createStatusMode(presence);
String status = presence.getStatus();
if (TextUtils.isEmpty(status)) {
status = getString(statusMode.getStringID());
}
LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
View resourceView = inflater.inflate(R.layout.item_contact_info, xmppItems, false);
resourceView.findViewById(R.id.contact_info_item_secondary);
((TextView)resourceView.findViewById(R.id.contact_info_item_secondary)).setText(client);
((TextView)resourceView.findViewById(R.id.contact_info_item_main)).setText(status);
((TextView)resourceView.findViewById(R.id.contact_info_item_secondary_second_line)).setText(resource);
resourceView.findViewById(R.id.contact_info_item_secondary_second_line).setVisibility(View.VISIBLE);
((TextView)resourceView.findViewById(R.id.contact_info_item_secondary_third_line)).setText(priorityString);
resourceView.findViewById(R.id.contact_info_item_secondary_third_line).setVisibility(View.VISIBLE);
if (isAccount &&resourceOrNull != null
&& resourceOrNull.equals(accountResource)) {
TextView thisDeviceIndicatorTextView
= (TextView) resourceView.findViewById(R.id.contact_info_item_secondary_forth_line);
thisDeviceIndicatorTextView.setTextColor(ColorManager.getInstance().getAccountPainter().getAccountSendButtonColor(account));
thisDeviceIndicatorTextView.setText(R.string.contact_viewer_this_device);
thisDeviceIndicatorTextView.setVisibility(View.VISIBLE);
}
ImageView statusIcon = (ImageView) resourceView.findViewById(R.id.contact_info_right_icon);
statusIcon.setVisibility(View.VISIBLE);
statusIcon.setImageDrawable(getResources().getDrawable(R.drawable.ic_status));
statusIcon.setImageLevel(statusMode.getStatusLevel());
resourcesList.add(resourceView);
}
}
public void updateVCard() {
if (vCard == null) {
return;
}
if (account.getFullJid().asBareJid().equals(user.getBareJid())) {
editButton.setVisibility(View.VISIBLE);
}
contactInfoItems.removeAllViews();
addNameInfo(vCard);
List<View> birthDayList = new ArrayList<>();
addItem(birthDayList, contactInfoItems, getString(R.string.vcard_birth_date), vCard.getField(VCardProperty.BDAY.toString()));
addItemGroup(birthDayList, contactInfoItems, R.drawable.ic_vcard_birthday_24dp);
addOrganizationInfo(vCard);
List<View> webList = new ArrayList<>();
addItem(webList, contactInfoItems, getString(R.string.vcard_url), vCard.getField(VCardProperty.URL.toString()));
addItemGroup(webList, contactInfoItems, R.drawable.ic_vcard_web_24dp);
addAdditionalInfo(vCard);
addAddresses(vCard);
addPhones(vCard);
addEmails(vCard);
}
private void addEmails(VCard vCard) {
List<View> emailList = new ArrayList<>();
String emailHome = vCard.getEmailHome();
if (!"".equals(emailHome)) {
addItem(emailList, contactInfoItems, getString(VcardMaps.getEmailTypeMap().get(EmailType.HOME)), emailHome);
}
String emailWork = vCard.getEmailWork();
if (!"".equals(emailWork)) {
addItem(emailList, contactInfoItems, getString(VcardMaps.getEmailTypeMap().get(EmailType.WORK)), emailWork);
}
addItemGroup(emailList, contactInfoItems, R.drawable.ic_vcard_email_24dp);
}
private void addPhones(VCard vCard) {
List<View> phoneList = new ArrayList<>();
for (TelephoneType type : TelephoneType.values()) {
String types = getString(VcardMaps.getTelephoneTypeMap().get(TelephoneType.HOME));
String phoneHome = vCard.getPhoneHome(type.name());
if (!"".equals(phoneHome)) {
types = addString(types, getString(VcardMaps.getTelephoneTypeMap().get(type)), ", ");
addItem(phoneList, contactInfoItems, types, phoneHome);
}
}
for (TelephoneType type : TelephoneType.values()) {
String types = getString(VcardMaps.getTelephoneTypeMap().get(TelephoneType.WORK));
String phoneHome = vCard.getPhoneWork(type.name());
if (!"".equals(phoneHome)) {
types = addString(types, getString(VcardMaps.getTelephoneTypeMap().get(type)), ", ");
addItem(phoneList, contactInfoItems, types, phoneHome);
}
}
addItemGroup(phoneList, contactInfoItems, R.drawable.ic_vcard_phone_24dp);
}
private void addAddresses(VCard vCard) {
List<View> addressList = new ArrayList<>();
String homeAddress = null;
for (AddressProperty property : AddressProperty.values()) {
homeAddress = addString(homeAddress, vCard.getAddressFieldHome(property.name()), "\n");
}
addItem(addressList, contactInfoItems, getString(VcardMaps.getAddressTypeMap().get(AddressType.HOME)), homeAddress);
String workAddress = null;
for (AddressProperty property : AddressProperty.values()) {
workAddress = addString(workAddress, vCard.getAddressFieldWork(property.name()), "\n");
}
addItem(addressList, contactInfoItems, getString(VcardMaps.getAddressTypeMap().get(AddressType.WORK)), workAddress);
addItemGroup(addressList, contactInfoItems, R.drawable.ic_vcard_address_24dp);
}
private void addAdditionalInfo(VCard vCard) {
List<View> notesList = new ArrayList<>();
addItem(notesList, contactInfoItems, getString(R.string.vcard_note), vCard.getField(VCardProperty.NOTE.name()));
addItem(notesList, contactInfoItems, getString(R.string.vcard_decsription), vCard.getField(VCardProperty.DESC.name()));
addItemGroup(notesList, contactInfoItems, R.drawable.ic_vcard_notes_24dp);
}
private void addOrganizationInfo(VCard vCard) {
List<View> organizationList = new ArrayList<>();
addItem(organizationList, contactInfoItems, getString(R.string.vcard_title), vCard.getField(VCardProperty.TITLE.toString()));
addItem(organizationList, contactInfoItems, getString(R.string.vcard_role), vCard.getField(VCardProperty.ROLE.toString()));
String organization = vCard.getOrganization();
String unit = vCard.getOrganizationUnit();
addItem(organizationList, contactInfoItems, getString(R.string.vcard_organization), addString(organization, unit, "\n"));
addItemGroup(organizationList, contactInfoItems, R.drawable.ic_vcard_job_title_24dp);
}
private void addNameInfo(VCard vCard) {
List<View> nameList = new ArrayList<>();
addItem(nameList, contactInfoItems, getString(R.string.vcard_nick_name), vCard.getField(VCardProperty.NICKNAME.name()));
addItem(nameList, contactInfoItems, getString(R.string.vcard_formatted_name), vCard.getField(VCardProperty.FN.name()));
addItem(nameList, contactInfoItems, getString(R.string.vcard_prefix_name), vCard.getPrefix());
addItem(nameList, contactInfoItems, getString(R.string.vcard_given_name), vCard.getFirstName());
addItem(nameList, contactInfoItems, getString(R.string.vcard_middle_name), vCard.getMiddleName());
addItem(nameList, contactInfoItems, getString(R.string.vcard_family_name), vCard.getLastName());
addItem(nameList, contactInfoItems, getString(R.string.vcard_suffix_name), vCard.getSuffix());
addItemGroup(nameList, contactInfoItems, R.drawable.ic_vcard_contact_info_24dp);
}
private void addItemGroup(List<View> nameList, LinearLayout itemList, int groupIcon) {
addItemGroup(nameList, itemList, groupIcon, true);
}
private void addItemGroup(List<View> nameList, LinearLayout itemList, int groupIcon, boolean addSeparator) {
if (nameList.isEmpty()) {
return;
}
if (addSeparator) {
addSeparator(itemList);
}
((ImageView) nameList.get(0).findViewById(R.id.contact_info_group_icon)).setImageResource(groupIcon);
for (View view : nameList) {
itemList.addView(view);
}
}
private void addItem(List<View> nameList, ViewGroup rootView, String label, String value) {
View itemView = createItemView(rootView, label, value, null);
if (itemView != null) {
Linkify.addLinks((TextView)itemView.findViewById(R.id.contact_info_item_main), Linkify.ALL);
nameList.add(itemView);
}
}
private void addSeparator(LinearLayout rootView) {
LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
rootView.addView(inflater.inflate(R.layout.contact_info_separator, rootView, false));
}
private View createItemView(ViewGroup rootView, String label, String value, Integer iconResource) {
if (value == null || value.isEmpty() ) {
return null;
}
LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
View contactInfoItem = inflater.inflate(R.layout.item_contact_info, rootView, false);
if (label == null || label.trim().isEmpty()) {
contactInfoItem.findViewById(R.id.contact_info_item_secondary).setVisibility(View.GONE);
} else {
((TextView) contactInfoItem.findViewById(R.id.contact_info_item_secondary)).setText(label);
}
((TextView)contactInfoItem.findViewById(R.id.contact_info_item_main)).setText(value);
if (iconResource != null) {
((ImageView) contactInfoItem.findViewById(R.id.contact_info_group_icon)).setImageResource(iconResource);
}
return contactInfoItem;
}
public VCard getvCard() {
return vCard;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_EDIT_VCARD
&& resultCode == Activity.RESULT_OK) {
requestVCard();
}
}
}