summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTarun Gupta <tarun1995gupta@gmail.com>2015-06-09 10:08:25 (GMT)
committerTarun Gupta <tarun1995gupta@gmail.com>2015-06-14 13:30:22 (GMT)
commit2180f81bd45045021d98c3a2e0649f10680596f9 (patch)
tree872db3fdeb890a232b5a1dc3b823a1e6692b5aa0 /src/com/isode/stroke/avatars
parent9518f8b9d6924e95e4ad839f40866560bd7a7878 (diff)
downloadstroke-2180f81bd45045021d98c3a2e0649f10680596f9.zip
stroke-2180f81bd45045021d98c3a2e0649f10680596f9.tar.bz2
Add Avatar Functionality.
Adds AvatarManager, AvatarManagerImpl, AvatarStorage and different AvatarProviders including DummyAvatarProvider, NullAvatarProvider and OfflineAvatarProvider. Also adds VCardAvatarManager and VCardUpdateAvatarManager. Updates VCard Element, so that it does not return null on calling getPhoto() and thereby produce Null Pointer Exceptions. License: This patch is BSD-licensed, see Documentation/Licenses/BSD-simplified.txt for details. Test-Information: Tests are added for AvatarManagerImpl, VCardAvatarManager and VCardUpdateAvatarManager, which passes. Tests for CombinedAvatarProvider will be added soon. Change-Id: Ia6c0f82ae496427dc0cd11841487f6c53fd0fe1c
Diffstat (limited to 'src/com/isode/stroke/avatars')
-rwxr-xr-xsrc/com/isode/stroke/avatars/AvatarManager.java24
-rwxr-xr-xsrc/com/isode/stroke/avatars/AvatarManagerImpl.java84
-rwxr-xr-xsrc/com/isode/stroke/avatars/AvatarMemoryStorage.java57
-rwxr-xr-xsrc/com/isode/stroke/avatars/AvatarProvider.java21
-rwxr-xr-xsrc/com/isode/stroke/avatars/AvatarStorage.java27
-rwxr-xr-xsrc/com/isode/stroke/avatars/CombinedAvatarProvider.java75
-rwxr-xr-xsrc/com/isode/stroke/avatars/DummyAvatarManager.java36
-rwxr-xr-xsrc/com/isode/stroke/avatars/NullAvatarManager.java28
-rwxr-xr-xsrc/com/isode/stroke/avatars/OfflineAvatarManager.java36
-rwxr-xr-xsrc/com/isode/stroke/avatars/VCardAvatarManager.java88
-rwxr-xr-xsrc/com/isode/stroke/avatars/VCardUpdateAvatarManager.java140
11 files changed, 616 insertions, 0 deletions
diff --git a/src/com/isode/stroke/avatars/AvatarManager.java b/src/com/isode/stroke/avatars/AvatarManager.java
new file mode 100755
index 0000000..8bb8e71
--- /dev/null
+++ b/src/com/isode/stroke/avatars/AvatarManager.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2010 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+/*
+ * Copyright (c) 2015 Tarun Gupta.
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+package com.isode.stroke.avatars;
+
+import java.nio.file.Path;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.signals.Signal1;
+
+public interface AvatarManager {
+
+ public ByteArray getAvatar(JID jid);
+ public Path getAvatarPath(JID jid);
+ public Signal1<JID> onAvatarChanged = new Signal1<JID>();
+} \ No newline at end of file
diff --git a/src/com/isode/stroke/avatars/AvatarManagerImpl.java b/src/com/isode/stroke/avatars/AvatarManagerImpl.java
new file mode 100755
index 0000000..b324b3e
--- /dev/null
+++ b/src/com/isode/stroke/avatars/AvatarManagerImpl.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2010-2013 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+/*
+ * Copyright (c) 2015 Tarun Gupta.
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+package com.isode.stroke.avatars;
+
+import com.isode.stroke.avatars.AvatarManager;
+import com.isode.stroke.avatars.CombinedAvatarProvider;
+import com.isode.stroke.muc.MUCRegistry;
+import com.isode.stroke.avatars.AvatarStorage;
+import com.isode.stroke.client.StanzaChannel;
+import com.isode.stroke.vcards.VCardManager;
+import com.isode.stroke.avatars.VCardUpdateAvatarManager;
+import com.isode.stroke.avatars.VCardAvatarManager;
+import com.isode.stroke.avatars.OfflineAvatarManager;
+import com.isode.stroke.crypto.CryptoProvider;
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.signals.Slot1;
+import java.nio.file.*;
+import com.isode.stroke.signals.SignalConnection;
+
+public class AvatarManagerImpl implements AvatarManager {
+
+ private CombinedAvatarProvider combinedAvatarProvider = new CombinedAvatarProvider();
+ private AvatarStorage avatarStorage;
+ private VCardUpdateAvatarManager vcardUpdateAvatarManager;
+ private VCardAvatarManager vcardAvatarManager;
+ private OfflineAvatarManager offlineAvatarManager;
+ private SignalConnection onAvatarChangedConnection;
+
+ public AvatarManagerImpl(VCardManager vcardManager, StanzaChannel stanzaChannel, AvatarStorage avatarStorage, CryptoProvider crypto) {
+ this(vcardManager, stanzaChannel, avatarStorage, crypto, null);
+ }
+
+ public AvatarManagerImpl(VCardManager vcardManager, StanzaChannel stanzaChannel, AvatarStorage avatarStorage, CryptoProvider crypto, MUCRegistry mucRegistry) {
+ this.avatarStorage = avatarStorage;
+ vcardUpdateAvatarManager = new VCardUpdateAvatarManager(vcardManager, stanzaChannel, avatarStorage, crypto, mucRegistry);
+ combinedAvatarProvider.addProvider(vcardUpdateAvatarManager);
+
+ vcardAvatarManager = new VCardAvatarManager(vcardManager, avatarStorage, crypto, mucRegistry);
+ combinedAvatarProvider.addProvider(vcardAvatarManager);
+
+ offlineAvatarManager = new OfflineAvatarManager(avatarStorage);
+ combinedAvatarProvider.addProvider(offlineAvatarManager);
+
+ onAvatarChangedConnection = combinedAvatarProvider.onAvatarChanged.connect(new Slot1<JID>() {
+
+ public void call(JID p1) {
+ handleCombinedAvatarChanged(p1);
+ }
+ });
+ }
+
+ public Path getAvatarPath(JID jid) {
+ String hash = combinedAvatarProvider.getAvatarHash(jid);
+ if (hash != null && hash.length() != 0) {
+ return avatarStorage.getAvatarPath(hash);
+ }
+ return Paths.get("");
+ }
+
+ public ByteArray getAvatar(JID jid) {
+ String hash = combinedAvatarProvider.getAvatarHash(jid);
+ if (hash != null && hash.length() != 0) {
+ return avatarStorage.getAvatar(hash);
+ }
+ return new ByteArray();
+ }
+
+ private void handleCombinedAvatarChanged(JID jid) {
+ String hash = combinedAvatarProvider.getAvatarHash(jid);
+ assert(hash != null);
+ offlineAvatarManager.setAvatar(jid, hash);
+ onAvatarChanged.emit(jid);
+ }
+} \ No newline at end of file
diff --git a/src/com/isode/stroke/avatars/AvatarMemoryStorage.java b/src/com/isode/stroke/avatars/AvatarMemoryStorage.java
new file mode 100755
index 0000000..a3ad6f0
--- /dev/null
+++ b/src/com/isode/stroke/avatars/AvatarMemoryStorage.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2010 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+/*
+ * Copyright (c) 2015 Tarun Gupta.
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+package com.isode.stroke.avatars;
+
+import com.isode.stroke.avatars.AvatarStorage;
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.jid.JID;
+import java.nio.file.*;
+import java.io.File;
+import java.util.*;
+
+public class AvatarMemoryStorage implements AvatarStorage {
+
+ private Map<String, ByteArray> avatars = new HashMap<String, ByteArray>();
+ private Map<JID, String> jidAvatars = new HashMap<JID, String>();
+
+ public boolean hasAvatar(String hash) {
+ return avatars.containsKey(hash);
+ }
+
+ public void addAvatar(String hash, ByteArray avatar) {
+ avatars.put(hash, avatar);
+ }
+
+ public ByteArray getAvatar(String hash) {
+ if(avatars.containsKey(hash)) {
+ return avatars.get(hash);
+ } else {
+ return new ByteArray();
+ }
+ }
+
+ public Path getAvatarPath(String hash) {
+ return (Paths.get("/avatars" + File.separator + hash)).toAbsolutePath();
+ }
+
+ public void setAvatarForJID(JID jid, String hash) {
+ jidAvatars.put(jid, hash);
+ }
+
+ public String getAvatarForJID(JID jid) {
+ if(jidAvatars.containsKey(jid)) {
+ return jidAvatars.get(jid);
+ } else {
+ return "";
+ }
+ }
+} \ No newline at end of file
diff --git a/src/com/isode/stroke/avatars/AvatarProvider.java b/src/com/isode/stroke/avatars/AvatarProvider.java
new file mode 100755
index 0000000..31fabb4
--- /dev/null
+++ b/src/com/isode/stroke/avatars/AvatarProvider.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2010 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+/*
+ * Copyright (c) 2015 Tarun Gupta.
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+package com.isode.stroke.avatars;
+
+import com.isode.stroke.signals.Signal1;
+import com.isode.stroke.jid.JID;
+
+public interface AvatarProvider {
+
+ public String getAvatarHash(JID jid);
+ public Signal1<JID> onAvatarChanged = new Signal1<JID>();
+} \ No newline at end of file
diff --git a/src/com/isode/stroke/avatars/AvatarStorage.java b/src/com/isode/stroke/avatars/AvatarStorage.java
new file mode 100755
index 0000000..98f7e6f
--- /dev/null
+++ b/src/com/isode/stroke/avatars/AvatarStorage.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2010 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+/*
+ * Copyright (c) 2015 Tarun Gupta.
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+package com.isode.stroke.avatars;
+
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.jid.JID;
+import java.nio.file.Path;
+
+public interface AvatarStorage {
+
+ public boolean hasAvatar(String hash);
+ public void addAvatar(String hash, ByteArray avatar);
+ public ByteArray getAvatar(String hash);
+ public Path getAvatarPath(String hash);
+
+ public void setAvatarForJID(JID jid, String hash);
+ public String getAvatarForJID(JID jid);
+} \ No newline at end of file
diff --git a/src/com/isode/stroke/avatars/CombinedAvatarProvider.java b/src/com/isode/stroke/avatars/CombinedAvatarProvider.java
new file mode 100755
index 0000000..0426bd6
--- /dev/null
+++ b/src/com/isode/stroke/avatars/CombinedAvatarProvider.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2010 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+/*
+ * Copyright (c) 2015 Tarun Gupta.
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+package com.isode.stroke.avatars;
+
+import com.isode.stroke.avatars.AvatarProvider;
+import com.isode.stroke.signals.SignalConnection;
+import com.isode.stroke.signals.Slot1;
+import com.isode.stroke.jid.JID;
+import java.util.logging.Logger;
+import java.util.*;
+
+public class CombinedAvatarProvider implements AvatarProvider {
+
+ private Vector<AvatarProvider> providers = new Vector<AvatarProvider>();
+ private Map<JID, String> avatars = new HashMap<JID, String>();
+ private SignalConnection onAvatarChangedConnection_;
+ private Logger logger_ = Logger.getLogger(this.getClass().getName());
+
+ public String getAvatarHash(JID jid) {
+ return getCombinedAvatarAndCache(jid);
+ }
+
+ public void addProvider(AvatarProvider provider) {
+ onAvatarChangedConnection_ = provider.onAvatarChanged.connect(new Slot1<JID>() {
+
+ public void call(JID p1) {
+ handleAvatarChanged(p1);
+ }
+ });
+ providers.add(provider);
+ }
+
+ public void removeProvider(AvatarProvider provider) {
+ while(providers.contains(provider)) {
+ providers.remove(provider);
+ onAvatarChangedConnection_.disconnect();
+ }
+ }
+
+ private void handleAvatarChanged(JID jid) {
+ String oldHash = new String();
+ if(avatars.containsKey(jid)) {
+ oldHash = avatars.get(jid);
+ }
+ String newHash = getCombinedAvatarAndCache(jid);
+ if (newHash != null && !newHash.equals(oldHash)) {
+ logger_.fine("Avatar changed: " + jid + ": " + oldHash + " -> " + ((newHash != null) ? newHash : "NULL") + "\n");
+ onAvatarChanged.emit(jid);
+ }
+ }
+
+ private String getCombinedAvatarAndCache(JID jid) {
+ logger_.fine("JID: " + jid + "\n");
+ String hash = null;
+ for (int i = 0; i < providers.size() && (hash==null); ++i) {
+ hash = providers.get(i).getAvatarHash(jid);
+ logger_.fine("Provider " + providers.get(i) + ": " + ((hash != null) ? hash : "NULL") + "\n");
+ }
+ if (hash != null) {
+ avatars.put(jid, hash);
+ } else {
+ avatars.put(jid, "");
+ }
+ return hash;
+ }
+} \ No newline at end of file
diff --git a/src/com/isode/stroke/avatars/DummyAvatarManager.java b/src/com/isode/stroke/avatars/DummyAvatarManager.java
new file mode 100755
index 0000000..f1dfe41
--- /dev/null
+++ b/src/com/isode/stroke/avatars/DummyAvatarManager.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2010 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+/*
+ * Copyright (c) 2015 Tarun Gupta.
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+package com.isode.stroke.avatars;
+
+import com.isode.stroke.avatars.AvatarManager;
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.jid.JID;
+import java.nio.file.*;
+import java.io.File;
+import java.util.*;
+
+public class DummyAvatarManager implements AvatarManager {
+
+ private Map<JID, ByteArray> avatars = new HashMap<JID, ByteArray>();
+
+ public Path getAvatarPath(JID j) {
+ return (Paths.get("/avatars" + File.separator + j.toString())).toAbsolutePath();
+ }
+
+ public ByteArray getAvatar(JID jid) {
+ if(avatars.containsKey(jid)) {
+ return avatars.get(jid);
+ } else {
+ return new ByteArray();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/com/isode/stroke/avatars/NullAvatarManager.java b/src/com/isode/stroke/avatars/NullAvatarManager.java
new file mode 100755
index 0000000..e69ed46
--- /dev/null
+++ b/src/com/isode/stroke/avatars/NullAvatarManager.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2010 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+/*
+ * Copyright (c) 2015 Tarun Gupta.
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+package com.isode.stroke.avatars;
+
+import com.isode.stroke.avatars.AvatarManager;
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.jid.JID;
+import java.nio.file.*;
+
+public class NullAvatarManager implements AvatarManager {
+
+ public Path getAvatarPath(JID j) {
+ return Paths.get("");
+ }
+
+ public ByteArray getAvatar(JID jid) {
+ return new ByteArray();
+ }
+} \ No newline at end of file
diff --git a/src/com/isode/stroke/avatars/OfflineAvatarManager.java b/src/com/isode/stroke/avatars/OfflineAvatarManager.java
new file mode 100755
index 0000000..fe5de08
--- /dev/null
+++ b/src/com/isode/stroke/avatars/OfflineAvatarManager.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2010 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+/*
+ * Copyright (c) 2015 Tarun Gupta.
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+package com.isode.stroke.avatars;
+
+import com.isode.stroke.avatars.AvatarProvider;
+import com.isode.stroke.avatars.AvatarStorage;
+import com.isode.stroke.jid.JID;
+
+public class OfflineAvatarManager implements AvatarProvider {
+
+ private AvatarStorage avatarStorage;
+
+ public OfflineAvatarManager(AvatarStorage avatarStorage) {
+ this.avatarStorage = avatarStorage;
+ }
+
+ public String getAvatarHash(JID jid) {
+ return avatarStorage.getAvatarForJID(jid);
+ }
+
+ public void setAvatar(JID jid, String hash) {
+ if (!getAvatarHash(jid).equals(hash)) {
+ avatarStorage.setAvatarForJID(jid, hash);
+ onAvatarChanged.emit(jid);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/com/isode/stroke/avatars/VCardAvatarManager.java b/src/com/isode/stroke/avatars/VCardAvatarManager.java
new file mode 100755
index 0000000..b2efcf2
--- /dev/null
+++ b/src/com/isode/stroke/avatars/VCardAvatarManager.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2010-2013 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+/*
+ * Copyright (c) 2015 Tarun Gupta.
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+package com.isode.stroke.avatars;
+
+import com.isode.stroke.avatars.AvatarProvider;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.elements.VCard;
+import com.isode.stroke.crypto.CryptoProvider;
+import com.isode.stroke.stringcodecs.Hexify;
+import com.isode.stroke.avatars.AvatarStorage;
+import com.isode.stroke.muc.MUCRegistry;
+import com.isode.stroke.vcards.VCardManager;
+import com.isode.stroke.signals.Slot2;
+import java.util.logging.Logger;
+import com.isode.stroke.signals.SignalConnection;
+
+public class VCardAvatarManager implements AvatarProvider {
+
+ private VCardManager vcardManager_;
+ private AvatarStorage avatarStorage_;
+ private CryptoProvider crypto_;
+ private MUCRegistry mucRegistry_;
+ private SignalConnection onVCardChangedConnection_;
+ private Logger logger_ = Logger.getLogger(this.getClass().getName());
+
+ public VCardAvatarManager(VCardManager vcardManager, AvatarStorage avatarStorage, CryptoProvider crypto) {
+ this(vcardManager, avatarStorage, crypto, null);
+ }
+
+ public VCardAvatarManager(VCardManager vcardManager, AvatarStorage avatarStorage, CryptoProvider crypto, MUCRegistry mucRegistry) {
+ this.vcardManager_ = vcardManager;
+ this.avatarStorage_ = avatarStorage;
+ this.crypto_ = crypto;
+ this.mucRegistry_ = mucRegistry;
+ onVCardChangedConnection_ = vcardManager.onVCardChanged.connect(new Slot2<JID, VCard>() {
+
+ public void call(JID p1, VCard vcard) {
+ handleVCardChanged(p1);
+ }
+ });
+ }
+
+ public String getAvatarHash(JID jid) {
+ JID avatarJID = getAvatarJID(jid);
+ String hash = vcardManager_.getPhotoHash(avatarJID);
+ if(hash.length() != 0) {
+ if (!avatarStorage_.hasAvatar(hash)) {
+ VCard vCard = vcardManager_.getVCard(avatarJID);
+ if (vCard != null) {
+ String newHash = Hexify.hexify(crypto_.getSHA1Hash(vCard.getPhoto()));
+ if (!newHash.equals(hash)) {
+ // Shouldn't happen, but sometimes seem to. Might be fixed if we
+ // move to a safer backend.
+ logger_.warning("Inconsistent vCard photo hash cache");
+ hash = newHash;
+ }
+ avatarStorage_.addAvatar(hash, vCard.getPhoto());
+ }
+ else {
+ // Can happen if the cache is inconsistent.
+ hash = "";
+ }
+ }
+ }
+ return hash;
+ }
+
+ private void handleVCardChanged(JID from) {
+ // We don't check whether the avatar actually changed. Direct use of this
+ // manager could cause unnecessary updates, but in practice, this will be
+ // caught by the wrapping CombinedAvatarManager anyway.
+ onAvatarChanged.emit(from);
+ }
+
+ private JID getAvatarJID(JID jid) {
+ JID bareFrom = jid.toBare();
+ return (mucRegistry_ != null && mucRegistry_.isMUC(bareFrom)) ? jid : bareFrom;
+ }
+}
diff --git a/src/com/isode/stroke/avatars/VCardUpdateAvatarManager.java b/src/com/isode/stroke/avatars/VCardUpdateAvatarManager.java
new file mode 100755
index 0000000..ce61892
--- /dev/null
+++ b/src/com/isode/stroke/avatars/VCardUpdateAvatarManager.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2010-2014 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+/*
+ * Copyright (c) 2015 Tarun Gupta.
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+package com.isode.stroke.avatars;
+
+import java.util.Map;
+import java.util.HashMap;
+import com.isode.stroke.avatars.AvatarProvider;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.elements.VCard;
+import com.isode.stroke.elements.Presence;
+import com.isode.stroke.elements.ErrorPayload;
+import com.isode.stroke.elements.VCardUpdate;
+import com.isode.stroke.client.StanzaChannel;
+import com.isode.stroke.vcards.GetVCardRequest;
+import com.isode.stroke.crypto.CryptoProvider;
+import com.isode.stroke.stringcodecs.Hexify;
+import com.isode.stroke.avatars.AvatarStorage;
+import com.isode.stroke.muc.MUCRegistry;
+import com.isode.stroke.vcards.VCardManager;
+import com.isode.stroke.signals.Slot2;
+import com.isode.stroke.signals.Slot1;
+import java.util.logging.Logger;
+import com.isode.stroke.signals.SignalConnection;
+
+public class VCardUpdateAvatarManager implements AvatarProvider {
+
+ private VCardManager vcardManager_;
+ private AvatarStorage avatarStorage_;
+ private CryptoProvider crypto_;
+ private MUCRegistry mucRegistry_;
+ private Map<JID, String> avatarHashes_ = new HashMap<JID, String>();
+ private SignalConnection onPresenceReceivedConnection;
+ private SignalConnection onAvailableChangedConnection;
+ private SignalConnection onVCardChangedConnection;
+ private Logger logger_ = Logger.getLogger(this.getClass().getName());
+
+ public VCardUpdateAvatarManager(VCardManager vcardManager, StanzaChannel stanzaChannel, AvatarStorage avatarStorage, CryptoProvider crypto) {
+ this(vcardManager, stanzaChannel, avatarStorage, crypto, null);
+ }
+
+ public VCardUpdateAvatarManager(VCardManager vcardManager, StanzaChannel stanzaChannel, AvatarStorage avatarStorage, CryptoProvider crypto, MUCRegistry mucRegistry) {
+ this.vcardManager_ = vcardManager;
+ this.avatarStorage_ = avatarStorage;
+ this.crypto_ = crypto;
+ this.mucRegistry_ = mucRegistry;
+ onPresenceReceivedConnection = stanzaChannel.onPresenceReceived.connect(new Slot1<Presence>() {
+
+ public void call(Presence p1) {
+ handlePresenceReceived(p1);
+ }
+ });
+ onAvailableChangedConnection = stanzaChannel.onAvailableChanged.connect(new Slot1<Boolean>() {
+
+ public void call(Boolean b) {
+ handleStanzaChannelAvailableChanged(b);
+ }
+ });
+ onVCardChangedConnection = vcardManager_.onVCardChanged.connect(new Slot2<JID, VCard>() {
+
+ public void call(JID p1, VCard vcard) {
+ handleVCardChanged(p1, vcard);
+ }
+ });
+ }
+
+ public String getAvatarHash(JID jid) {
+ if(avatarHashes_.containsKey(jid)) {
+ return avatarHashes_.get(jid);
+ } else {
+ return null;
+ }
+ }
+
+ private void handlePresenceReceived(Presence presence) {
+ VCardUpdate update = presence.getPayload(new VCardUpdate());
+ if (update == null || presence.getPayload(new ErrorPayload()) != null) {
+ return;
+ }
+ JID from = getAvatarJID(presence.getFrom());
+ if (update.getPhotoHash().equals(getAvatarHash(from))) {
+ return;
+ }
+ logger_.fine("Updated hash: " + from + "-> " + update.getPhotoHash() + "\n");
+ if (avatarStorage_.hasAvatar(update.getPhotoHash())) {
+ setAvatarHash(from, update.getPhotoHash());
+ }
+ else {
+ vcardManager_.requestVCard(from);
+ }
+ }
+
+ private void handleStanzaChannelAvailableChanged(boolean available) {
+ if (available) {
+ Map<JID, String> oldAvatarHashes = new HashMap<JID, String>();
+ oldAvatarHashes.putAll(avatarHashes_);
+ avatarHashes_.clear();
+ for (Map.Entry<JID, String> entry : oldAvatarHashes.entrySet()) {
+ onAvatarChanged.emit(entry.getKey());
+ }
+ }
+ }
+
+ private void handleVCardChanged(JID from, VCard vCard) {
+ if (vCard == null) {
+ logger_.fine("Missing element: " + from + ": null vcard payload\n");
+ return;
+ }
+
+ if (vCard.getPhoto().isEmpty()) {
+ setAvatarHash(from, "");
+ }
+ else {
+ String hash = Hexify.hexify(crypto_.getSHA1Hash(vCard.getPhoto()));
+ if (!avatarStorage_.hasAvatar(hash)) {
+ avatarStorage_.addAvatar(hash, vCard.getPhoto());
+ }
+ setAvatarHash(from, hash);
+ }
+ }
+
+ private void setAvatarHash(JID from, String hash) {
+ logger_.fine("Updating hash: " + from + " -> " + hash + "\n");
+ avatarHashes_.put(from, hash);
+ onAvatarChanged.emit(from);
+ }
+
+ private JID getAvatarJID(JID jid) {
+ JID bareFrom = jid.toBare();
+ return (mucRegistry_ != null && mucRegistry_.isMUC(bareFrom)) ? jid : bareFrom;
+ }
+}