fetch missing avatars from server

iNPUTmice created

Change summary

src/eu/siacs/conversations/entities/Contact.java               | 20 +
src/eu/siacs/conversations/entities/Conversation.java          | 19 +
src/eu/siacs/conversations/generator/IqGenerator.java          | 17 +
src/eu/siacs/conversations/parser/AbstractParser.java          | 12 +
src/eu/siacs/conversations/parser/IqParser.java                | 12 +
src/eu/siacs/conversations/parser/MessageParser.java           | 19 +
src/eu/siacs/conversations/persistance/FileBackend.java        |  5 
src/eu/siacs/conversations/services/XmppConnectionService.java | 16 +
src/eu/siacs/conversations/ui/ConversationActivity.java        |  3 
src/eu/siacs/conversations/utils/UIHelper.java                 |  3 
src/eu/siacs/conversations/xmpp/pep/Avatar.java                | 41 ++++
11 files changed, 152 insertions(+), 15 deletions(-)

Detailed changes

src/eu/siacs/conversations/entities/Contact.java 🔗

@@ -8,12 +8,14 @@ import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 
+import eu.siacs.conversations.persistance.FileBackend;
 import eu.siacs.conversations.utils.UIHelper;
 import eu.siacs.conversations.xml.Element;
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
 import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 
 public class Contact implements ListItem {
 	public static final String TABLENAME = "contacts";
@@ -34,6 +36,7 @@ public class Contact implements ListItem {
 	protected int subscription = 0;
 	protected String systemAccount;
 	protected String photoUri;
+	protected String avatar;
 	protected JSONObject keys = new JSONObject();
 	protected Presences presences = new Presences();
 
@@ -316,7 +319,20 @@ public class Contact implements ListItem {
 	}
 
 	@Override
-	public Bitmap getImage(int dpSize, Context context) {
-		return UIHelper.getContactPicture(this, dpSize, context, false);
+	public Bitmap getImage(int size, Context context) {
+		if (this.avatar!=null) {
+			Bitmap bm = BitmapFactory.decodeFile(FileBackend.getAvatarPath(context, avatar));
+			if (bm==null) {
+				return UIHelper.getContactPicture(this, size, context, false);
+			} else {
+				return bm;
+			}
+		} else {
+			return UIHelper.getContactPicture(this, size, context, false);
+		}
+	}
+
+	public void setAvatar(String filename) {
+		this.avatar = filename;
 	}
 }

src/eu/siacs/conversations/entities/Conversation.java 🔗

@@ -4,6 +4,8 @@ import java.security.interfaces.DSAPublicKey;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
 
+import eu.siacs.conversations.utils.UIHelper;
+
 import net.java.otr4j.OtrException;
 import net.java.otr4j.crypto.OtrCryptoEngineImpl;
 import net.java.otr4j.crypto.OtrCryptoException;
@@ -13,7 +15,7 @@ import net.java.otr4j.session.SessionStatus;
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
-import android.net.Uri;
+import android.graphics.Bitmap;
 
 public class Conversation extends AbstractEntity {
 	public static final String TABLENAME = "conversations";
@@ -174,13 +176,6 @@ public class Conversation extends AbstractEntity {
 		return this.contactJid;
 	}
 
-	public Uri getProfilePhotoUri() {
-		if (this.getProfilePhotoString() != null) {
-			return Uri.parse(this.getProfilePhotoString());
-		}
-		return null;
-	}
-
 	public int getStatus() {
 		return this.status;
 	}
@@ -396,4 +391,12 @@ public class Conversation extends AbstractEntity {
 	public Bookmark getBookmark() {
 		return this.bookmark;
 	}
+
+	public Bitmap getImage(Context context, int size) {
+		if (mode==MODE_SINGLE) {
+			return getContact().getImage(size, context);
+		} else {
+			return UIHelper.getContactPicture(this, size, context, false);
+		}
+	}
 }

src/eu/siacs/conversations/generator/IqGenerator.java 🔗

@@ -39,6 +39,15 @@ public class IqGenerator extends AbstractGenerator {
 		return packet;
 	}
 	
+	protected IqPacket retrieve(String node, Element item) {
+		IqPacket packet = new IqPacket(IqPacket.TYPE_GET);
+		Element pubsub = packet.addChild("pubsub", "http://jabber.org/protocol/pubsub");
+		Element items = pubsub.addChild("items");
+		items.setAttribute("node", node);
+		items.addChild(item);
+		return packet;
+	}
+	
 	public IqPacket publishAvatar(Avatar avatar) {
 		Element item = new Element("item");
 		item.setAttribute("id", avatar.sha1sum);
@@ -59,4 +68,12 @@ public class IqGenerator extends AbstractGenerator {
 		info.setAttribute("type", avatar.type);
 		return publish("urn:xmpp:avatar:metadata",item);
 	}
+	
+	public IqPacket retrieveAvatar(Avatar avatar) {
+		Element item = new Element("item");
+		item.setAttribute("id", avatar.sha1sum);
+		IqPacket packet = retrieve("urn:xmpp:avatar:data", item);
+		packet.setTo(avatar.owner);
+		return packet;
+	}
 }

src/eu/siacs/conversations/parser/AbstractParser.java 🔗

@@ -73,4 +73,16 @@ public abstract class AbstractParser {
 			}
 		}
 	}
+	
+	protected String avatarData(Element items) {
+		Element item = items.findChild("item");
+		if (item==null) {
+			return null;
+		}
+		Element data = item.findChild("data","urn:xmpp:avatar:data");
+		if (data==null) {
+			return null;
+		}
+		return data.getContent();
+	}
 }

src/eu/siacs/conversations/parser/IqParser.java 🔗

@@ -40,6 +40,18 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
 		}
 		mXmppConnectionService.updateRosterUi();
 	}
+	
+	public String avatarData(IqPacket packet) {
+		Element pubsub = packet.findChild("pubsub", "http://jabber.org/protocol/pubsub");
+		if (pubsub==null) {
+			return null;
+		}
+		Element items = pubsub.findChild("items");
+		if (items==null) {
+			return null;
+		}
+		return super.avatarData(items);
+	}
 
 	@Override
 	public void onIqPacketReceived(Account account, IqPacket packet) {

src/eu/siacs/conversations/parser/MessageParser.java 🔗

@@ -5,12 +5,14 @@ import android.util.Log;
 import net.java.otr4j.session.Session;
 import net.java.otr4j.session.SessionStatus;
 import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Contact;
 import eu.siacs.conversations.entities.Conversation;
 import eu.siacs.conversations.entities.Message;
 import eu.siacs.conversations.services.XmppConnectionService;
 import eu.siacs.conversations.utils.CryptoHelper;
 import eu.siacs.conversations.xml.Element;
 import eu.siacs.conversations.xmpp.OnMessagePacketReceived;
+import eu.siacs.conversations.xmpp.pep.Avatar;
 import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
 
 public class MessageParser extends AbstractParser implements
@@ -263,7 +265,22 @@ public class MessageParser extends AbstractParser implements
 		Element items = event.findChild("items");
 		String node = items.getAttribute("node");
 		if (node!=null) {
-			Log.d("xmppService",account.getJid()+": "+node+" from "+from);
+			if (node.equals("urn:xmpp:avatar:metadata")) {
+				Avatar avatar = Avatar.parseMetadata(items);
+				avatar.owner = from;
+				if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) {
+					if (account.getJid().equals(from)) {
+						account.setAvatar(avatar.getFilename());
+					} else {
+						Contact contact = account.getRoster().getContact(from);
+						contact.setAvatar(avatar.getFilename());
+					}
+				} else {
+					mXmppConnectionService.fetchAvatar(account, avatar);
+				}
+			} else {
+				Log.d("xmppService",account.getJid()+": "+node+" from "+from);
+			}
 		} else {
 			Log.d("xmppService",event.toString());
 		}

src/eu/siacs/conversations/persistance/FileBackend.java 🔗

@@ -248,6 +248,11 @@ public class FileBackend {
 		}
 	}
 	
+	public boolean isAvatarCached(Avatar avatar) {
+		File file = new File(getAvatarPath(context, avatar.getFilename()));
+		return file.exists();
+	}
+	
 	public void save(Avatar avatar) {
 		File file = new File(getAvatarPath(context, avatar.getFilename()));
 		file.getParentFile().mkdirs();

src/eu/siacs/conversations/services/XmppConnectionService.java 🔗

@@ -1231,6 +1231,22 @@ public class XmppConnectionService extends Service {
 		}
 	}
 	
+	public void fetchAvatar(Account account, final Avatar avatar) {
+		IqPacket packet = this.mIqGenerator.retrieveAvatar(avatar);
+		sendIqPacket(account, packet, new OnIqPacketReceived() {
+			
+			@Override
+			public void onIqPacketReceived(Account account, IqPacket result) {
+				avatar.image = mIqParser.avatarData(result);
+				if (avatar.image!=null) {
+					getFileBackend().save(avatar);
+					Contact contact = account.getRoster().getContact(avatar.owner);
+					contact.setAvatar(avatar.getFilename());
+				}
+			}
+		});
+	}
+	
 	public void deleteContactOnServer(Contact contact) {
 		contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
 		contact.resetOption(Contact.Options.DIRTY_PUSH);

src/eu/siacs/conversations/ui/ConversationActivity.java 🔗

@@ -229,8 +229,7 @@ public class ConversationActivity extends XmppActivity {
 
 				ImageView profilePicture = (ImageView) view
 						.findViewById(R.id.conversation_image);
-				profilePicture.setImageBitmap(UIHelper.getContactPicture(conv,
-						56, activity.getApplicationContext(), false));
+				profilePicture.setImageBitmap(conv.getImage(getApplicationContext(),56)); //;UIHelper.getContactPicture(conv,56, activity.getApplicationContext(), false));
 
 				return view;
 			}

src/eu/siacs/conversations/utils/UIHelper.java 🔗

@@ -488,8 +488,7 @@ public class UIHelper {
 			long id = Long.parseLong(systemAccount[0]);
 			badge.assignContactUri(Contacts.getLookupUri(id, systemAccount[1]));
 		}
-		badge.setImageBitmap(UIHelper.getContactPicture(contact, 72, context,
-				false));
+		badge.setImageBitmap(contact.getImage(72, context));
 	}
 
 	public static AlertDialog getVerifyFingerprintDialog(

src/eu/siacs/conversations/xmpp/pep/Avatar.java 🔗

@@ -1,5 +1,6 @@
 package eu.siacs.conversations.xmpp.pep;
 
+import eu.siacs.conversations.xml.Element;
 import android.util.Base64;
 
 public class Avatar {
@@ -9,6 +10,7 @@ public class Avatar {
 	public int height;
 	public int width;
 	public long size;
+	public String owner;
 	public byte[] getImageAsBytes() {
 		return Base64.decode(image, Base64.DEFAULT);
 	}
@@ -23,4 +25,43 @@ public class Avatar {
 			return sha1sum;
 		}
 	}
+	public static Avatar parseMetadata(Element items) {
+		Element item = items.findChild("item");
+		if (item==null) {
+			return null;
+		}
+		Element metadata = item.findChild("metadata");
+		if (metadata==null) {
+			return null;
+		}
+		String primaryId = item.getAttribute("id");
+		if (primaryId==null) {
+			return null;
+		}
+		for(Element child : metadata.getChildren()) {
+			if (child.getName().equals("info") && primaryId.equals(child.getAttribute("id"))) {
+				Avatar avatar = new Avatar();
+				String height = child.getAttribute("height");
+				String width = child.getAttribute("width");
+				String size = child.getAttribute("bytes");
+				try {
+					if (height!=null) {
+						avatar.height = Integer.parseInt(height);
+					}
+					if (width!=null) {
+						avatar.width = Integer.parseInt(width);
+					}
+					if (size!=null) {
+						avatar.size = Long.parseLong(size);
+					}
+				} catch (NumberFormatException e) {
+					return null;
+				}
+				avatar.type = child.getAttribute("type");
+				avatar.sha1sum = child.getAttribute("id");
+				return avatar;
+			}
+		}
+		return null;
+	}
 }