/**
* Copyright (c) 2013, Redsolution LTD. All rights reserved.
*
* This file is part of Xabber project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License, Version 3.
*
* Xabber is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License,
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.xabber.android.data.extension.capability;
import android.content.Context;
import android.support.annotation.Nullable;
import com.xabber.android.BuildConfig;
import com.xabber.android.R;
import com.xabber.android.data.Application;
import com.xabber.android.data.account.AccountItem;
import com.xabber.android.data.account.AccountManager;
import com.xabber.android.data.entity.AccountJid;
import com.xabber.android.data.log.LogManager;
import com.xabber.android.data.roster.OnContactChangedListener;
import com.xabber.android.data.roster.RosterContact;
import com.xabber.android.data.roster.RosterManager;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smackx.caps.EntityCapsManager;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
import org.jxmpp.jid.Jid;
import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Provide information about entity capabilities.
*
* @author alexander.ivanov
*/
public class CapabilitiesManager {
@SuppressWarnings("WeakerAccess")
static final String LOG_TAG = CapabilitiesManager.class.getSimpleName();
private static CapabilitiesManager instance;
// cache for jids does not supporting Entity Caps
@SuppressWarnings("WeakerAccess")
Map<Jid, DiscoverInfo> discoverInfoCache;
private Map<Jid, ClientInfo> clientInfoCache;
public static CapabilitiesManager getInstance() {
if (instance == null) {
instance = new CapabilitiesManager();
}
return instance;
}
private CapabilitiesManager() {
Context applicationContext = Application.getInstance().getApplicationContext();
EntityCapsManager.setDefaultEntityNode(applicationContext.getString(R.string.caps_entity_node));
EntityCapsManager.setPersistentCache(new EntityCapsCache());
setServiceDiscoveryClientIdentity(applicationContext);
discoverInfoCache = new ConcurrentHashMap<>();
clientInfoCache = new ConcurrentHashMap<>();
}
private void setServiceDiscoveryClientIdentity(Context applicationContext) {
String identityName = applicationContext.getString(R.string.application_title_full)
+ " Android "
+ BuildConfig.VERSION_NAME;
String identityCategory = "client";
String identityType = "phone";
ServiceDiscoveryManager.setDefaultIdentity(
new DiscoverInfo.Identity(identityCategory, identityName, identityType));
}
@Nullable
public ClientInfo getCachedClientInfo(final Jid jid) {
ClientInfo clientInfo = clientInfoCache.get(jid);
if (clientInfo != null) {
return clientInfo;
}
DiscoverInfo discoverInfoByUser = EntityCapsManager.getDiscoverInfoByUser(jid);
if (discoverInfoByUser == null) {
discoverInfoByUser = discoverInfoCache.get(jid);
}
if (discoverInfoByUser != null) {
clientInfo = ClientInfo.fromDiscoveryInfo(discoverInfoByUser);
clientInfoCache.put(jid, clientInfo);
}
return clientInfo;
}
public void onPresence(final AccountJid accountJid, final Presence presence) {
final Jid from = presence.getFrom();
discoverInfoCache.remove(from);
clientInfoCache.remove(from);
DiscoverInfo discoverInfoByUser = EntityCapsManager.getDiscoverInfoByUser(from);
if (discoverInfoByUser != null) {
return;
}
Application.getInstance().runInBackground(new Runnable() {
@Override
public void run() {
updateClientInfo(accountJid, from);
}
});
}
public void requestClientInfoByUser(final AccountJid account, final Jid jid) {
Application.getInstance().runInBackgroundUserRequest(new Runnable() {
@Override
public void run() {
updateClientInfo(account, jid);
}
});
}
@SuppressWarnings("WeakerAccess")
void updateClientInfo(final AccountJid account, final Jid jid) {
DiscoverInfo discoverInfo = EntityCapsManager.getDiscoverInfoByUser(jid);
if (discoverInfo != null) {
return;
}
AccountItem accountItem = AccountManager.getInstance().getAccount(account);
if (accountItem == null) {
return;
}
try {
discoverInfo = ServiceDiscoveryManager.getInstanceFor(accountItem.getConnection())
.discoverInfo(jid);
EntityCapsManager.NodeVerHash nodeVerHashByJid = EntityCapsManager.getNodeVerHashByJid(jid);
if (nodeVerHashByJid == null) {
discoverInfoCache.put(jid, discoverInfo);
}
if (discoverInfo != null) {
clientInfoCache.put(jid, ClientInfo.fromDiscoveryInfo(discoverInfo));
}
} catch (SmackException.NoResponseException | XMPPException.XMPPErrorException | InterruptedException | SmackException.NotConnectedException e) {
LogManager.exception(this, e);
clientInfoCache.put(jid, ClientInfo.INVALID_CLIENT_INFO);
}
RosterContact rosterContact = RosterManager.getInstance().getRosterContact(account, jid.asBareJid());
if (rosterContact != null) {
final ArrayList<RosterContact> rosterContacts = new ArrayList<>();
rosterContacts.add(rosterContact);
Application.getInstance().runOnUiThread(new Runnable() {
@Override
public void run() {
for (OnContactChangedListener onContactChangedListener
: Application.getInstance().getUIListeners(OnContactChangedListener.class)) {
onContactChangedListener.onContactsChanged(rosterContacts);
}
}
});
}
}
public boolean isFeatureSupported(Jid jid, String namespace) {
DiscoverInfo discoverInfo = getDiscoverInfo(jid);
return discoverInfo != null && discoverInfo.containsFeature(namespace);
}
@Nullable
private DiscoverInfo getDiscoverInfo(final Jid jid) {
DiscoverInfo discoverInfoByUser = EntityCapsManager.getDiscoverInfoByUser(jid);
if (discoverInfoByUser == null) {
discoverInfoByUser = discoverInfoCache.get(jid);
}
return discoverInfoByUser;
}
}