Add a way to display the avatar of a contact.
authorDa Risk <darisk972@gmail.com>
Mon, 01 Nov 2010 23:57:34 +0100
changeset 808 98d220b7fe9d
parent 807 e4b9ec9f30e6
child 809 2655ee22df9f
Add a way to display the avatar of a contact. little quick and dirty but it will be good until the Contact API.
res/layout/contactlistcontact.xml
src/com/beem/project/beem/service/Contact.java
src/com/beem/project/beem/service/RosterAdapter.java
src/com/beem/project/beem/service/XmppConnectionAdapter.java
src/com/beem/project/beem/service/XmppFacade.java
src/com/beem/project/beem/service/aidl/IXmppFacade.aidl
src/com/beem/project/beem/smack/avatar/AvatarListener.java
src/com/beem/project/beem/smack/avatar/AvatarManager.java
src/com/beem/project/beem/ui/ContactList.java
--- a/res/layout/contactlistcontact.xml	Tue Oct 26 00:59:27 2010 +0200
+++ b/res/layout/contactlistcontact.xml	Mon Nov 01 23:57:34 2010 +0100
@@ -1,20 +1,30 @@
 <?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical" android:layout_width="fill_parent"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
     android:layout_height="wrap_content" android:paddingLeft="10px"
     android:paddingRight="10px" android:paddingTop="8dip"
     android:paddingBottom="8dip" >
+    <ImageView android:id="@+id/avatar"
+	android:layout_width="48dip" android:layout_height="48dip"
+	android:layout_alignParentLeft="true"
+	android:layout_alignParentTop="true"
+	/>
     <TextView android:id="@+id/contactlistpseudo"
 	android:layout_width="fill_parent" android:layout_height="wrap_content"
+	android:layout_alignParentTop="true"
+	android:layout_toRightOf="@id/avatar"
 	android:singleLine="true"
 	android:maxLines="1" android:textColor="@color/white"
 	android:drawablePadding="12px"
+	android:paddingLeft="10dip"
 	android:textSize="18sp" android:textStyle="bold" />
 
     <TextView android:id="@+id/contactlistmsgperso"
 	android:layout_width="fill_parent" android:layout_height="wrap_content"
+	android:layout_below="@id/contactlistpseudo"
+	android:layout_toRightOf="@id/avatar"
 	android:paddingLeft="30px" android:singleLine="true"
 	android:maxLines="1" android:linksClickable="false" android:autoLink="all"
 	android:scrollHorizontally="true" android:textColorLink="@color/white"
 	android:textSize="12sp" />
-</LinearLayout>
+</RelativeLayout>
--- a/src/com/beem/project/beem/service/Contact.java	Tue Oct 26 00:59:27 2010 +0200
+++ b/src/com/beem/project/beem/service/Contact.java	Mon Nov 01 23:57:34 2010 +0100
@@ -85,6 +85,7 @@
     private List<String> mRes;
     private final List<String> mGroups = new ArrayList<String>();
     private String mName;
+    private String mAvatarId;
 
     /**
      * Construct a contact from a parcel.
@@ -97,6 +98,7 @@
 	mSelectedRes = in.readString();
 	mName = in.readString();
 	mMsgState = in.readString();
+	mAvatarId = in.readString();
 	mRes = new ArrayList<String>();
 	in.readStringList(mRes);
 	in.readStringList(mGroups);
@@ -148,6 +150,7 @@
 	dest.writeString(mSelectedRes);
 	dest.writeString(mName);
 	dest.writeString(mMsgState);
+	dest.writeString(mAvatarId);
 	dest.writeStringList(getMRes());
 	dest.writeStringList(getGroups());
     }
@@ -258,6 +261,10 @@
 	return mStatus;
     }
 
+    public String getAvatarId() {
+	return mAvatarId;
+    }
+
     /**
      * Set the groups the contact is in.
      * @param groups list of groups
@@ -286,6 +293,10 @@
 	mID = mid;
     }
 
+    public void setAvatarId(String avatarId) {
+	mAvatarId = avatarId;
+    }
+
     /**
      * Set the resource of the contact.
      * @param resource to set.
--- a/src/com/beem/project/beem/service/RosterAdapter.java	Tue Oct 26 00:59:27 2010 +0200
+++ b/src/com/beem/project/beem/service/RosterAdapter.java	Mon Nov 01 23:57:34 2010 +0100
@@ -66,6 +66,10 @@
 import com.beem.project.beem.R;
 import com.beem.project.beem.service.aidl.IBeemRosterListener;
 import com.beem.project.beem.utils.Status;
+import com.beem.project.beem.smack.avatar.AvatarMetadataExtension;
+import com.beem.project.beem.smack.avatar.AvatarMetadataExtension.Info;
+import com.beem.project.beem.smack.avatar.AvatarManager;
+import com.beem.project.beem.smack.avatar.AvatarListener;
 
 /**
  * This class implement a Roster adapter for BEEM.
@@ -78,6 +82,8 @@
 	new RemoteCallbackList<IBeemRosterListener>();
     private final Map<Integer, String> mDefaultStatusMessages;
     private final RosterListenerAdapter mRosterListener = new RosterListenerAdapter();
+    private Map<String, String> mAvatarIdmap = new HashMap<String, String>();
+    private AvatarManager mAvatarManager;
 
     /**
      * Constructor.
@@ -91,6 +97,21 @@
     }
 
     /**
+     * Constructor.
+     * @param roster The roster to adapt.
+     * @param context The context of the RosterAdapter.
+     */
+    public RosterAdapter(final Roster roster, final Context context, final AvatarManager avatarMgr) {
+	mAdaptee = roster;
+	roster.addRosterListener(mRosterListener);
+	mDefaultStatusMessages = createDefaultStatusMessagesMap(context);
+	mAvatarManager = avatarMgr;
+	if (mAvatarManager != null)
+	    mAvatarManager.addAvatarListener(new AvatarEventListener());
+    }
+
+
+    /**
      * {@inheritDoc}
      */
     @Override
@@ -242,7 +263,7 @@
 	try {
 	    c.setGroups(entry.getGroups());
 	} catch (NullPointerException e) {
-	    Log.d(TAG, "Group list not ready");
+	    Log.d(TAG, "Group list not ready", e);
 	}
 	Iterator<Presence> iPres = mAdaptee.getPresences(user);
 	while (iPres.hasNext()) {
@@ -251,6 +272,7 @@
 		c.addRes(StringUtils.parseResource(p.getFrom()));
 	}
 	c.setName(entry.getName());
+	c.setAvatarId(mAvatarIdmap.get(user));
 	return c;
     }
 
@@ -357,6 +379,8 @@
 	    for (int i = 0; i < n; i++) {
 		IBeemRosterListener listener = mRemoteRosListeners.getBroadcastItem(i);
 		try {
+		    if (!presence.isAvailable())
+			mAvatarIdmap.remove(StringUtils.parseBareAddress(presence.getFrom()));
 		    if (presence.getStatus() == null || "".equals(presence.getStatus())) {
 			presence.setStatus(mDefaultStatusMessages.get(Status.getStatusFromPresence(presence)));
 		    }
@@ -368,4 +392,21 @@
 	    mRemoteRosListeners.finishBroadcast();
 	}
     }
+
+    /**
+     * Listener on avatar metadata event.
+     *
+     */
+    private class AvatarEventListener implements AvatarListener {
+	
+	@Override
+	public void onAvatarChange(String from, String avatarId, List<Info> avatarInfos) {
+	    String bare = StringUtils.parseBareAddress(from);
+	    if (avatarId == null)
+		mAvatarIdmap.remove(bare);
+	    else {
+		mAvatarIdmap.put(bare, avatarId);
+	    }
+	}
+    }
 }
--- a/src/com/beem/project/beem/service/XmppConnectionAdapter.java	Tue Oct 26 00:59:27 2010 +0200
+++ b/src/com/beem/project/beem/service/XmppConnectionAdapter.java	Mon Nov 01 23:57:34 2010 +0100
@@ -81,6 +81,7 @@
 import com.beem.project.beem.ui.Subscription;
 import com.beem.project.beem.utils.BeemBroadcastReceiver;
 import com.beem.project.beem.utils.Status;
+import com.beem.project.beem.smack.pep.PepSubManager;
 import com.beem.project.beem.smack.avatar.AvatarCache;
 import com.beem.project.beem.smack.avatar.FileAvatarCache;
 import com.beem.project.beem.smack.avatar.AvatarManager;
@@ -112,6 +113,8 @@
     private ChatStateManager mChatStateManager;
     private final BeemService mService;
     private BeemApplication mApplication;
+    private AvatarManager mAvatarManager;
+    private PepSubManager mPepManager;
     private final RemoteCallbackList<IBeemConnectionListener> mRemoteConnListeners =
 	new RemoteCallbackList<IBeemConnectionListener>();
     private final SubscribePacketListener mSubscribePacketListener = new SubscribePacketListener();
@@ -239,6 +242,7 @@
 	    mService.initJingle(mAdaptee);
 	    discoverServerFeatures();
 
+	    mRoster = new RosterAdapter(mAdaptee.getRoster(), mService, mAvatarManager);
 	    mApplication.setConnected(true);
 	    changeStatus(Status.CONTACT_STATUS_AVAILABLE, mService.getServicePreference().getString("status_text", ""));
 	    return true;
@@ -319,6 +323,10 @@
 	changeStatusAndPriority(status, msg, mPreviousPriority);
     }
 
+    public AvatarManager getAvatarManager() {
+	return mAvatarManager;
+    }
+
     /**
      * get the previous status.
      * @return previous status.
@@ -388,7 +396,7 @@
 	Roster adap = mAdaptee.getRoster();
 	if (adap == null)
 	    return null;
-	mRoster = new RosterAdapter(adap, mService);
+	mRoster = new RosterAdapter(adap, mService, mAvatarManager);
 	return mRoster;
     }
 
@@ -485,10 +493,11 @@
 	// Enable pep sending
 	// API 8
 	// mService.getExternalCacheDir()
+	mPepManager = new PepSubManager(mAdaptee);
 	File cacheDir = Environment.getExternalStorageDirectory();
 	cacheDir = new File(cacheDir, "/Android/data/com.beem.project.beem/cache/");
 	AvatarCache avatarCache = new FileAvatarCache(cacheDir);
-	new AvatarManager(mAdaptee, avatarCache, true);
+	mAvatarManager = new AvatarManager(mAdaptee, mPepManager, avatarCache, true);
     }
 
     /**
--- a/src/com/beem/project/beem/service/XmppFacade.java	Tue Oct 26 00:59:27 2010 +0200
+++ b/src/com/beem/project/beem/service/XmppFacade.java	Mon Nov 01 23:57:34 2010 +0100
@@ -55,6 +55,7 @@
 import com.beem.project.beem.service.aidl.IXmppConnection;
 import com.beem.project.beem.service.aidl.IXmppFacade;
 import com.beem.project.beem.utils.PresenceType;
+import com.beem.project.beem.smack.avatar.AvatarManager;
 
 /**
  * This class is a facade for the Beem Service.
@@ -150,19 +151,12 @@
     public void call(String jid) throws RemoteException {
     }
 
-    /* (non-Javadoc)
-     * @see com.beem.project.beem.service.aidl.IXmppFacade#getVcardAvatar(java.lang.String)
-     */
     @Override
-    public byte[] getVcardAvatar(String jid) throws RemoteException {
-	VCard vcard = new VCard();
+    public byte[] getAvatar(String avatarId) throws RemoteException {
+	AvatarManager mgr = mConnexion.getAvatarManager();
+	if (mgr == null)
+	    return null;
 
-	try {
-	    vcard.load(mConnexion.getAdaptee(), jid);
-	    return vcard.getAvatar();
-	} catch (XMPPException e) {
-	    e.printStackTrace();
-	}
-	return null;
+	return mgr.getAvatar(avatarId);
     }
 }
--- a/src/com/beem/project/beem/service/aidl/IXmppFacade.aidl	Tue Oct 26 00:59:27 2010 +0200
+++ b/src/com/beem/project/beem/service/aidl/IXmppFacade.aidl	Mon Nov 01 23:57:34 2010 +0100
@@ -97,10 +97,10 @@
      void call(in String jid);
 
      /**
-      * get the user vcard avatar
-      * @param jid the user jid
+      * get the an avatar
+      * @param id the id of the avatar
       */
-     byte[] getVcardAvatar(in String jid);
+     byte[] getAvatar(in String id);
 
      IPrivacyListManager getPrivacyListManager();
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/beem/project/beem/smack/avatar/AvatarListener.java	Mon Nov 01 23:57:34 2010 +0100
@@ -0,0 +1,10 @@
+package com.beem.project.beem.smack.avatar;
+
+import java.util.List;
+import com.beem.project.beem.smack.avatar.AvatarMetadataExtension.Info;
+
+public interface AvatarListener {
+
+    void onAvatarChange(String from, String avatarId, List<Info> avatarInfos);
+
+}
--- a/src/com/beem/project/beem/smack/avatar/AvatarManager.java	Tue Oct 26 00:59:27 2010 +0200
+++ b/src/com/beem/project/beem/smack/avatar/AvatarManager.java	Mon Nov 01 23:57:34 2010 +0100
@@ -50,6 +50,7 @@
 import java.io.IOException;
 
 import java.util.List;
+import java.util.LinkedList;
 
 import org.jivesoftware.smack.Connection;
 import org.jivesoftware.smack.packet.PacketExtension;
@@ -67,6 +68,7 @@
     private Connection mCon;
     private boolean mAutoDownload;
     private AvatarCache mCache;
+    private final List<AvatarListener> mListeners = new LinkedList<AvatarListener>();
 
     /**
      * Create an AvatarManager.
@@ -75,13 +77,12 @@
      * @param cache the cache which will store the avatars
      * @param autoDownload true to enable auto download of avatars
      */
-    public AvatarManager(final Connection con, final AvatarCache cache, final boolean autoDownload) {
+    public AvatarManager(final Connection con, final PepSubManager pepMgr, final AvatarCache cache, final boolean autoDownload) {
 	mCon = con;
-	mPep = new PepSubManager(mCon);
+	mPep = pepMgr;
 	mAutoDownload = autoDownload;
 	mCache = cache;
-	if (mAutoDownload)
-	    mPep.addPEPListener(new Listener());
+	mPep.addPEPListener(new Listener());
     }
 
     /**
@@ -91,6 +92,8 @@
      * @return the avatar or null if it cannot be retrieved from the cache
      */
     public byte[] getAvatar(String avatarId) {
+	if (avatarId == null)
+	    return null;
 	try {
 	    return mCache.get(avatarId);
 	} catch (IOException e) {
@@ -98,6 +101,16 @@
 	}
     }
 
+    public void addAvatarListener(AvatarListener listener) {
+	if (!mListeners.contains(listener))
+	    mListeners.add(listener);
+    }
+
+    public void removeAvatarListener(AvatarListener listener){
+	mListeners.remove(listener);
+    }
+
+
     /**
      * Select the avatar to download.
      * Subclass should override this method to take control over the selection process.
@@ -110,21 +123,28 @@
 	return available.get(0);
     }
 
+    private void fireListeners(String from, String avatarId, List<Info> avatarInfos) {
+	for(AvatarListener l : mListeners)
+	    l.onAvatarChange(from, avatarId, avatarInfos);
+    }
+
     /**
-     * Doawload an avatar.
+     * Download an avatar.
      *
      * @param from The jid of the user
      * @param info the metadata information of the avatar to download
      */
-    private void downloadAvatar(String from, Info info) {
+    public boolean downloadAvatar(String from, String avatarId, Info info) {
 	try {
 	    AvatarRetriever retriever = AvatarRetrieverFactory.getRetriever(mCon, from, info);
 	    byte[] avatar = retriever.getAvatar();
 	// TODO check the hash before store
-	    mCache.put(info.getId(), avatar);
+	    mCache.put(avatarId, avatar);
+	    return true;
 	} catch (IOException e) {
 	    System.err.println("Error while downloading avatar");
 	    e.printStackTrace();
+	    return false;
 	}
     }
 
@@ -141,15 +161,22 @@
 
 	@Override
 	public void eventReceived(String from, String node, List<Item> items) {
+	    if (!node.equals("urn:xmpp:avatar:metadata"))
+		return;
 	    Item i = items.get(0);
 	    if (i instanceof PayloadItem) {
 		PayloadItem<PacketExtension> pi = (PayloadItem<PacketExtension>) i;
 		PacketExtension ex = pi.getPayload();
 		if (ex instanceof AvatarMetadataExtension) {
 		    AvatarMetadataExtension ext = (AvatarMetadataExtension) ex;
-		    Info info = selectAvatar(ext.getInfos());
-		    if (!mCache.contains(info.getId()))
-			downloadAvatar(from, info);
+		    String id = i.getId();
+		    List<Info> infos = ext.getInfos();
+		    if (infos.size() > 0 && mAutoDownload) {
+			Info info = selectAvatar(infos);
+			if (!mCache.contains(id))
+			    downloadAvatar(from, id, info);
+		    }
+		    fireListeners(from, id, infos);
 		}
 	    }
 	}
--- a/src/com/beem/project/beem/ui/ContactList.java	Tue Oct 26 00:59:27 2010 +0200
+++ b/src/com/beem/project/beem/ui/ContactList.java	Mon Nov 01 23:57:34 2010 +0100
@@ -49,6 +49,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.io.ByteArrayInputStream;
 
 import org.jivesoftware.smack.util.StringUtils;
 
@@ -81,9 +82,11 @@
 import android.widget.Filter;
 import android.widget.Filterable;
 import android.widget.Gallery;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.TextView;
+import android.graphics.drawable.Drawable;
 
 import com.beem.project.beem.R;
 import com.beem.project.beem.service.Contact;
@@ -660,6 +663,23 @@
 		v.setText(curContact.getName());
 		v = (TextView) view.findViewById(R.id.contactlistmsgperso);
 		v.setText(curContact.getMsgState());
+		Drawable d = null;
+		try {
+		    String avatarId = curContact.getAvatarId();
+		    Log.d(TAG, "Avatar id = " + avatarId );
+		    byte[] avatar = mXmppFacade.getAvatar(avatarId);
+		    if (avatar != null) {
+			ByteArrayInputStream in = new ByteArrayInputStream(avatar);
+			d = Drawable.createFromStream(in, avatarId);
+		    }
+		} catch (RemoteException e) {
+		    Log.e(TAG, "Error while setting the avatar", e);
+		}
+		ImageView img = (ImageView) view.findViewById(R.id.avatar);
+		if (d != null)
+		    img.setImageDrawable(d);
+		else
+		    img.setImageResource(R.drawable.beem_launcher_icon_silver);
 	    }
 	}