diff --git a/briar-android/build.gradle b/briar-android/build.gradle
index f982a86bec0ad01b5297ae2201d0324449c40e7c..4ead68e29f986ba6a82f592616ace455699b2f7b 100644
--- a/briar-android/build.gradle
+++ b/briar-android/build.gradle
@@ -17,6 +17,7 @@ dependencies {
     compile "org.roboguice:roboguice:2.0"
     compile "info.guardianproject.panic:panic:0.5"
     compile "info.guardianproject.trustedintents:trustedintents:0.2"
+    compile "de.hdodenhof:circleimageview:2.0.0"
 }
 
 dependencyVerification {
diff --git a/briar-android/res/layout/author_view.xml b/briar-android/res/layout/author_view.xml
new file mode 100644
index 0000000000000000000000000000000000000000..92c863c374784ace09c8c75ef5bb64c18d05e197
--- /dev/null
+++ b/briar-android/res/layout/author_view.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout
+	xmlns:android="http://schemas.android.com/apk/res/android"
+	android:layout_width="match_parent"
+	android:layout_height="match_parent">
+
+	<de.hdodenhof.circleimageview.CircleImageView
+		android:id="@+id/avatarView"
+		android:layout_width="@dimen/listitem_picture_size"
+		android:layout_height="@dimen/listitem_picture_size"
+		android:layout_centerVertical="true"
+		android:layout_marginLeft="@dimen/listitem_horizontal_margin"
+		android:layout_marginStart="@dimen/listitem_horizontal_margin"/>
+
+	<ImageView
+		android:id="@+id/statusView"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_alignParentEnd="true"
+		android:layout_alignParentRight="true"
+		android:layout_centerVertical="true"
+		android:src="@drawable/identity_anonymous"/>
+
+	<TextView
+		android:id="@+id/nameView"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_alignParentLeft="true"
+		android:layout_alignParentStart="true"
+		android:layout_centerVertical="true"
+		android:layout_marginEnd="@dimen/margin_small"
+		android:layout_marginLeft="@dimen/listitem_text_left_margin"
+		android:layout_marginRight="@dimen/margin_small"
+		android:layout_marginStart="@dimen/listitem_text_left_margin"
+		android:layout_toLeftOf="@id/statusView"
+		android:layout_toStartOf="@id/statusView"
+		android:ellipsize="end"
+		android:singleLine="true"
+		android:text="@string/anonymous"
+		android:textSize="@dimen/text_size_medium"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/briar-android/res/layout/dropdown_author.xml b/briar-android/res/layout/dropdown_author.xml
new file mode 100644
index 0000000000000000000000000000000000000000..01a9b3f5330f363b672becbc15ce8b1cd3e90d5c
--- /dev/null
+++ b/briar-android/res/layout/dropdown_author.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+	xmlns:android="http://schemas.android.com/apk/res/android"
+	xmlns:tools="http://schemas.android.com/tools"
+	android:layout_width="match_parent"
+	android:layout_height="match_parent"
+	android:gravity="center_vertical">
+
+	<de.hdodenhof.circleimageview.CircleImageView
+		android:id="@+id/avatarView"
+		android:layout_width="@dimen/dropdown_picture_size"
+		android:layout_height="@dimen/dropdown_picture_size"/>
+
+	<TextView
+		android:id="@+id/nameView"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_marginLeft="@dimen/margin_medium"
+		android:layout_marginStart="@dimen/margin_medium"
+		android:ellipsize="end"
+		android:singleLine="true"
+		android:textSize="@dimen/text_size_medium"
+		tools:text="This is a name of an author. It can be quite long."/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/briar-android/res/layout/list_item_contact.xml b/briar-android/res/layout/list_item_contact.xml
index 69ec0383e31bda3ec65b01e7fea2faab620448b2..8259e1b3b4011a16bc656ce89fa30de922c0a415 100644
--- a/briar-android/res/layout/list_item_contact.xml
+++ b/briar-android/res/layout/list_item_contact.xml
@@ -2,49 +2,71 @@
 <LinearLayout
 	xmlns:android="http://schemas.android.com/apk/res/android"
 	xmlns:tools="http://schemas.android.com/tools"
-	android:orientation="vertical"
 	android:layout_width="match_parent"
-	android:layout_height="wrap_content">
+	android:layout_height="wrap_content"
+	android:orientation="vertical">
 
-	<LinearLayout
-		android:orientation="horizontal"
+	<RelativeLayout
 		android:layout_width="match_parent"
-		android:layout_height="wrap_content"
-		android:background="?attr/selectableItemBackground"
-		android:padding="12dp">
-
-	<ImageView
-		android:id="@+id/bulbView"
-		android:layout_width="wrap_content"
-		android:layout_height="wrap_content"
-		android:layout_marginRight="@dimen/margin_medium"
-		android:layout_marginEnd="@dimen/margin_medium"
-		android:layout_gravity="center_vertical"
-		tools:src="@drawable/contact_disconnected"/>
-
-	<TextView
-		android:id="@+id/nameView"
-		android:layout_width="0dp"
-		android:layout_height="wrap_content"
-		android:layout_weight="1"
-		android:layout_gravity="center_vertical"
-		android:layout_marginRight="@dimen/margin_small"
-		android:layout_marginEnd="@dimen/margin_small"
-		android:textSize="@dimen/text_size_medium"
-		android:gravity="center_vertical"
-		tools:text="This is a name of a contact. It can be quite long."/>
-
-	<TextView
-		android:id="@+id/dateView"
-		android:layout_width="wrap_content"
-		android:layout_height="match_parent"
-		android:layout_marginRight="@dimen/margin_small"
-		android:layout_marginEnd="@dimen/margin_small"
-		android:gravity="center_vertical"
-		android:textColor="@color/no_private_messages"
-		tools:text="Dec 24"/>
-
-	</LinearLayout>
+		android:layout_height="@dimen/listitem_height_one_line_avatar"
+		android:background="?attr/selectableItemBackground">
+
+		<de.hdodenhof.circleimageview.CircleImageView
+			android:id="@+id/avatarView"
+			android:layout_width="@dimen/listitem_picture_size"
+			android:layout_height="@dimen/listitem_picture_size"
+			android:layout_alignParentLeft="true"
+			android:layout_alignParentStart="true"
+			android:layout_centerVertical="true"
+			android:layout_marginLeft="@dimen/listitem_horizontal_margin"
+			android:layout_marginStart="@dimen/listitem_horizontal_margin"/>
+
+		<LinearLayout
+			android:id="@+id/bulbHolder"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_alignParentEnd="true"
+			android:layout_alignParentRight="true"
+			android:layout_centerVertical="true"
+			android:layout_marginEnd="@dimen/listitem_horizontal_margin"
+			android:layout_marginRight="@dimen/listitem_horizontal_margin"
+			android:gravity="right"
+			android:orientation="vertical">
+
+			<ImageView
+				android:id="@+id/bulbView"
+				android:layout_width="wrap_content"
+				android:layout_height="wrap_content"
+				tools:src="@drawable/contact_disconnected"/>
+
+			<TextView
+				android:id="@+id/dateView"
+				android:layout_width="wrap_content"
+				android:layout_height="wrap_content"
+				android:textColor="@color/no_private_messages"
+				tools:text="Dec 24"/>
+
+		</LinearLayout>
+
+		<TextView
+			android:id="@+id/nameView"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_alignParentLeft="true"
+			android:layout_alignParentStart="true"
+			android:layout_centerVertical="true"
+			android:layout_marginEnd="@dimen/margin_small"
+			android:layout_marginLeft="@dimen/listitem_text_left_margin"
+			android:layout_marginRight="@dimen/margin_small"
+			android:layout_marginStart="@dimen/listitem_text_left_margin"
+			android:layout_toLeftOf="@id/bulbHolder"
+			android:layout_toStartOf="@id/bulbHolder"
+			android:gravity="center_vertical"
+			android:maxLines="2"
+			android:textSize="@dimen/text_size_medium"
+			tools:text="This is a name of a contact. It can be quite long."/>
+
+	</RelativeLayout>
 
 	<View style="@style/Divider.Horizontal"/>
 
diff --git a/briar-android/res/layout/list_item_msg_in.xml b/briar-android/res/layout/list_item_msg_in.xml
index d93d269e7796bbbc767bc020dd647888b9b1927c..50ccd007720f43d5f23d90dd96c22f112f90fe3b 100644
--- a/briar-android/res/layout/list_item_msg_in.xml
+++ b/briar-android/res/layout/list_item_msg_in.xml
@@ -4,12 +4,20 @@
 	xmlns:tools="http://schemas.android.com/tools"
 	android:layout_width="match_parent"
 	android:layout_height="wrap_content"
-	android:orientation="vertical"
+	android:orientation="horizontal"
 	android:paddingRight="@dimen/margin_medium"
 	android:paddingEnd="@dimen/margin_medium"
 	android:paddingTop="@dimen/margin_small"
 	android:paddingBottom="@dimen/margin_small">
 
+	<de.hdodenhof.circleimageview.CircleImageView
+		android:id="@+id/msgAvatar"
+		android:layout_width="@dimen/listitem_picture_size"
+		android:layout_height="@dimen/listitem_picture_size"
+		android:layout_marginLeft="@dimen/listitem_horizontal_margin"
+		android:layout_marginStart="@dimen/listitem_horizontal_margin"
+		/>
+
 	<RelativeLayout
 		android:id="@+id/msgLayout"
 		android:layout_width="wrap_content"
diff --git a/briar-android/res/values/dimens.xml b/briar-android/res/values/dimens.xml
index e417b0897aa47a6ad3824c2cfdd7e7c9bd74d19a..8c11314aba15cb52a0573d074eafbb1b55ab4d63 100644
--- a/briar-android/res/values/dimens.xml
+++ b/briar-android/res/values/dimens.xml
@@ -20,4 +20,12 @@
 	<dimen name="nav_drawer_width">300dp</dimen>
 	<dimen name="nav_seperator_height">1dp</dimen>
 
+	<dimen name="listitem_horizontal_margin">16dp</dimen>
+	<dimen name="listitem_picture_size">40dp</dimen>
+	<dimen name="listitem_text_left_margin">72dp</dimen>
+
+	<dimen name="listitem_height_one_line_avatar">56dp</dimen>
+
+	<dimen name="dropdown_picture_size">32dp</dimen>
+
 </resources>
diff --git a/briar-android/src/im/delight/android/identicons/AsymmetricIdenticon.java b/briar-android/src/im/delight/android/identicons/AsymmetricIdenticon.java
new file mode 100644
index 0000000000000000000000000000000000000000..b1d98d9b35aad0f9294afcb912a07b174510fff8
--- /dev/null
+++ b/briar-android/src/im/delight/android/identicons/AsymmetricIdenticon.java
@@ -0,0 +1,84 @@
+package im.delight.android.identicons;
+
+/**
+ * Copyright 2014 www.delight.im <info@delight.im>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import android.content.Context;
+import android.graphics.Color;
+import android.util.AttributeSet;
+
+import org.briarproject.api.crypto.CryptoComponent;
+
+import javax.inject.Inject;
+
+import roboguice.RoboGuice;
+
+public class AsymmetricIdenticon extends IdenticonView {
+
+    @Inject private CryptoComponent mCrypto;
+    private IdenticonBase mDelegate;
+
+    public AsymmetricIdenticon(Context context) {
+        super(context);
+        initDelegate();
+    }
+
+    public AsymmetricIdenticon(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initDelegate();
+    }
+
+    public AsymmetricIdenticon(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        initDelegate();
+    }
+
+    @Override
+    protected IdenticonBase getDelegate() {
+        return mDelegate;
+    }
+
+    private void initDelegate() {
+        RoboGuice.injectMembers(getContext(), this);
+        mDelegate = new IdenticonBase() {
+            @Override
+            protected CryptoComponent getCrypto() {
+                return mCrypto;
+            }
+
+            @Override
+            protected int getRowCount() {
+                return 4;
+            }
+
+            @Override
+            protected int getColumnCount() {
+                return 4;
+            }
+
+            @Override
+            protected boolean isCellVisible(int row, int column) {
+                return getByte(3 + row * getColumnCount() + column) >= 0;
+            }
+
+            @Override
+            protected int getIconColor() {
+                return Color.rgb(getByte(0) + 128, getByte(1) + 128, getByte(2) + 128);
+            }
+        };
+    }
+
+}
diff --git a/briar-android/src/im/delight/android/identicons/IdenticonBase.java b/briar-android/src/im/delight/android/identicons/IdenticonBase.java
new file mode 100644
index 0000000000000000000000000000000000000000..f5ccc90e4456f458ef07edbb2f4319efb8f16374
--- /dev/null
+++ b/briar-android/src/im/delight/android/identicons/IdenticonBase.java
@@ -0,0 +1,127 @@
+package im.delight.android.identicons;
+
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+
+import org.briarproject.api.crypto.CryptoComponent;
+
+/**
+ * Created by saiimons on 05/10/14.
+ */
+public abstract class IdenticonBase {
+    private final CryptoComponent mCrypto;
+    private final int mRowCount;
+    private final int mColumnCount;
+    private final Paint mPaint;
+    private volatile int mCellWidth;
+    private volatile int mCellHeight;
+    private volatile byte[] mHash;
+    private volatile int[][] mColors;
+    private volatile boolean mReady;
+
+    public IdenticonBase() {
+        mCrypto = getCrypto();
+        mRowCount = getRowCount();
+        mColumnCount = getColumnCount();
+        mPaint = new Paint();
+
+        mPaint.setStyle(Paint.Style.FILL);
+        mPaint.setAntiAlias(true);
+        mPaint.setDither(true);
+    }
+
+    public byte[] getHash(byte[] input) {
+        byte[] mHash;
+        // if the input was null
+        if (input == null) {
+            // we can't create a hash value and have nothing to show (draw to the view)
+            mHash = null;
+        } else {
+            // generate a hash from the input to get unique but deterministic byte values
+            try {
+                mHash = mCrypto.hash(input);
+            } catch (Exception e) {
+                mHash = null;
+            }
+        }
+        return mHash;
+    }
+
+    protected void setupColors() {
+        mColors = new int[mRowCount][mColumnCount];
+        int colorVisible = getIconColor();
+        int colorInvisible = getBackgroundColor();
+
+        for (int r = 0; r < mRowCount; r++) {
+            for (int c = 0; c < mColumnCount; c++) {
+                if (isCellVisible(r, c)) {
+                    mColors[r][c] = colorVisible;
+                } else {
+                    mColors[r][c] = colorInvisible;
+                }
+            }
+        }
+    }
+
+    public void show(byte[] input) {
+        if(input != null) {
+            mHash = getHash(input);
+        } else {
+            mHash = null;
+        }
+        // set up the cell colors according to the input that was provided via show(...)
+        setupColors();
+
+        // this view may now be drawn (and thus must be re-drawn)
+        mReady = true;
+    }
+
+    public byte getByte(int index) {
+        if (mHash == null) {
+            return -128;
+        } else {
+            return mHash[index % mHash.length];
+        }
+    }
+
+    abstract protected CryptoComponent getCrypto();
+
+    abstract protected int getRowCount();
+
+    abstract protected int getColumnCount();
+
+    abstract protected boolean isCellVisible(int row, int column);
+
+    abstract protected int getIconColor();
+
+    protected int getBackgroundColor() {
+        float[] hsv = new float[3];
+        Color.colorToHSV(getIconColor(), hsv);
+        if (hsv[2] < 0.5)
+            return Color.parseColor("#ffeeeeee"); // @color/background_material_light
+        else
+            return Color.parseColor("#ff303030"); // @color/background_material_dark
+    }
+
+    public void updateSize(int w, int h) {
+        mCellWidth = w / mColumnCount;
+        mCellHeight = h / mRowCount;
+    }
+
+    protected void draw(Canvas canvas) {
+        if (mReady) {
+            int x, y;
+            for (int r = 0; r < mRowCount; r++) {
+                for (int c = 0; c < mColumnCount; c++) {
+                    x = mCellWidth * c;
+                    y = mCellHeight * r;
+
+                    mPaint.setColor(mColors[r][c]);
+
+                    canvas.drawRect(x, y + mCellHeight, x + mCellWidth, y, mPaint);
+                }
+            }
+        }
+    }
+}
diff --git a/briar-android/src/im/delight/android/identicons/IdenticonDrawable.java b/briar-android/src/im/delight/android/identicons/IdenticonDrawable.java
new file mode 100644
index 0000000000000000000000000000000000000000..6c78367f9e8fa45f0bbe9f7b4d2fd6dc2b67fa21
--- /dev/null
+++ b/briar-android/src/im/delight/android/identicons/IdenticonDrawable.java
@@ -0,0 +1,102 @@
+package im.delight.android.identicons;
+
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+
+import org.briarproject.api.crypto.CryptoComponent;
+
+/**
+ * Created by saiimons on 05/10/14.
+ */
+public class IdenticonDrawable extends Drawable {
+    private IdenticonBase mDelegate;
+
+    private static final int CENTER_COLUMN_INDEX = 5;
+
+    public IdenticonDrawable(final CryptoComponent crypto, byte[] toShow) {
+        super();
+        mDelegate = new IdenticonBase() {
+            @Override
+            protected CryptoComponent getCrypto() {
+                return crypto;
+            }
+
+            @Override
+            protected int getRowCount() {
+                return 9;
+            }
+
+            @Override
+            protected int getColumnCount() {
+                return 9;
+            }
+
+            @Override
+            protected boolean isCellVisible(int row, int column) {
+                return getByte(3 + row * CENTER_COLUMN_INDEX + getSymmetricColumnIndex(column)) >= 0;
+            }
+
+            @Override
+            protected int getIconColor() {
+                return Color.rgb(getByte(0) + 128, getByte(1) + 128, getByte(2) + 128);
+            }
+        };
+        mDelegate.show(toShow);
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return 200;
+    }
+
+    @Override
+    public int getIntrinsicWidth() {
+        return 200;
+    }
+
+    @Override
+    public void setBounds(Rect bounds) {
+        super.setBounds(bounds);
+        Log.d("IDENTICON", "SIZE : " + (bounds.right - bounds.left) + " " + (bounds.bottom - bounds.top));
+        mDelegate.updateSize(bounds.right - bounds.left, bounds.bottom - bounds.top);
+    }
+
+    @Override
+    public void setBounds(int left, int top, int right, int bottom) {
+        super.setBounds(left, top, right, bottom);
+        mDelegate.updateSize(right - left, bottom - top);
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        Log.d("IDENTICON", "DRAW IN PROGRESS");
+        mDelegate.draw(canvas);
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter cf) {
+
+    }
+
+    @Override
+    public int getOpacity() {
+        return 0;
+    }
+
+    protected int getSymmetricColumnIndex(int row) {
+        if (row < CENTER_COLUMN_INDEX) {
+            return row;
+        } else {
+            return mDelegate.getColumnCount() - row - 1;
+        }
+    }
+}
diff --git a/briar-android/src/im/delight/android/identicons/IdenticonView.java b/briar-android/src/im/delight/android/identicons/IdenticonView.java
new file mode 100644
index 0000000000000000000000000000000000000000..32a4f863c05b4fabcfbea60041342aa9c6da9195
--- /dev/null
+++ b/briar-android/src/im/delight/android/identicons/IdenticonView.java
@@ -0,0 +1,122 @@
+package im.delight.android.identicons;
+
+/**
+ * Copyright 2014 www.delight.im <info@delight.im>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.View;
+
+abstract public class IdenticonView extends View {
+
+
+    public IdenticonView(Context context) {
+        super(context);
+        init();
+    }
+
+    public IdenticonView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    public IdenticonView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init();
+    }
+
+    @SuppressLint("NewApi")
+    protected void init() {
+        setWillNotDraw(false);
+        if (Build.VERSION.SDK_INT >= 11) {
+            setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+        }
+    }
+
+    public void show(byte[] input) {
+        getDelegate().show(input);
+        invalidate();
+    }
+
+    public void show(String input) {
+        show(input.getBytes());
+    }
+
+    public void show(int input) {
+        show(String.valueOf(input));
+    }
+
+    public void show(long input) {
+        show(String.valueOf(input));
+    }
+
+    public void show(float input) {
+        show(String.valueOf(input));
+    }
+
+    public void show(double input) {
+        show(String.valueOf(input));
+    }
+
+    public void show(byte input) {
+        show(new byte[] { input });
+    }
+
+    public void show(char input) {
+        show(String.valueOf(input));
+    }
+
+    public void show(boolean input) {
+        show(String.valueOf(input));
+    }
+
+    public void show(Object input) {
+        if (input == null) {
+            getDelegate().show(null);
+        } else {
+            show(String.valueOf(input));
+        }
+    }
+
+    protected byte getByte(int index) {
+        return getDelegate().getByte(index);
+    }
+
+    abstract protected IdenticonBase getDelegate();
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        getDelegate().updateSize(w, h);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        int size = Math.min(getMeasuredWidth(), getMeasuredHeight());
+        setMeasuredDimension(size, size);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+        getDelegate().draw(canvas);
+    }
+
+}
diff --git a/briar-android/src/im/delight/android/identicons/SymmetricIdenticon.java b/briar-android/src/im/delight/android/identicons/SymmetricIdenticon.java
new file mode 100644
index 0000000000000000000000000000000000000000..f102e45f4bdef4f385c5bb12b23c27d6415c1e06
--- /dev/null
+++ b/briar-android/src/im/delight/android/identicons/SymmetricIdenticon.java
@@ -0,0 +1,94 @@
+package im.delight.android.identicons;
+
+/**
+ * Copyright 2014 www.delight.im <info@delight.im>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import android.content.Context;
+import android.graphics.Color;
+import android.util.AttributeSet;
+
+import org.briarproject.api.crypto.CryptoComponent;
+
+import javax.inject.Inject;
+
+import roboguice.RoboGuice;
+
+public class SymmetricIdenticon extends IdenticonView {
+
+    private static final int CENTER_COLUMN_INDEX = 5;
+
+    @Inject private CryptoComponent mCrypto;
+    private IdenticonBase mDelegate;
+
+    public SymmetricIdenticon(Context context) {
+        super(context);
+        initDelegate();
+    }
+
+    public SymmetricIdenticon(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initDelegate();
+    }
+
+    public SymmetricIdenticon(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        initDelegate();
+    }
+
+    private void initDelegate() {
+        RoboGuice.injectMembers(getContext(), this);
+        mDelegate = new IdenticonBase() {
+            @Override
+            protected CryptoComponent getCrypto() {
+                return mCrypto;
+            }
+
+            @Override
+            protected int getRowCount() {
+                return 9;
+            }
+
+            @Override
+            protected int getColumnCount() {
+                return 9;
+            }
+
+            @Override
+            protected boolean isCellVisible(int row, int column) {
+                return getByte(3 + row * CENTER_COLUMN_INDEX + getSymmetricColumnIndex(column)) >= 0;
+            }
+
+            @Override
+            protected int getIconColor() {
+                return Color.rgb(getByte(0) + 128, getByte(1) + 128, getByte(2) + 128);
+            }
+        };
+    }
+
+    @Override
+    protected IdenticonBase getDelegate() {
+        return mDelegate;
+    }
+
+    protected int getSymmetricColumnIndex(int row) {
+        if (row < CENTER_COLUMN_INDEX) {
+            return row;
+        } else {
+            return getDelegate().getColumnCount() - row - 1;
+        }
+    }
+
+}
diff --git a/briar-android/src/org/briarproject/android/contact/ContactListAdapter.java b/briar-android/src/org/briarproject/android/contact/ContactListAdapter.java
index 9d040e87d55380621a75ef665676ca92a0809c57..739912d95343ed2c32afd572df539286a772bed6 100644
--- a/briar-android/src/org/briarproject/android/contact/ContactListAdapter.java
+++ b/briar-android/src/org/briarproject/android/contact/ContactListAdapter.java
@@ -14,10 +14,14 @@ import android.widget.TextView;
 
 import org.briarproject.R;
 import org.briarproject.api.contact.ContactId;
+import org.briarproject.api.crypto.CryptoComponent;
+import org.briarproject.api.identity.Author;
 import org.briarproject.api.sync.GroupId;
 
 import java.util.List;
 
+import im.delight.android.identicons.IdenticonDrawable;
+
 import static android.support.v7.util.SortedList.INVALID_POSITION;
 
 public class ContactListAdapter
@@ -83,9 +87,11 @@ public class ContactListAdapter
 						}
 					});
 	private Context ctx;
+	private CryptoComponent crypto;
 
-	public ContactListAdapter(Context context) {
+	public ContactListAdapter(Context context, CryptoComponent cryptoComponent) {
 		ctx = context;
+		crypto = cryptoComponent;
 	}
 
 	@Override
@@ -113,7 +119,10 @@ public class ContactListAdapter
 			ui.bulb.setImageResource(R.drawable.contact_disconnected);
 		}
 
-		String contactName = item.getContact().getAuthor().getName();
+		Author author = item.getContact().getAuthor();
+		ui.avatar.setImageDrawable(
+				new IdenticonDrawable(crypto, author.getId().getBytes()));
+		String contactName = author.getName();
 		if (unread > 0) {
 			ui.name.setText(contactName + " (" + unread + ")");
 		} else {
@@ -193,6 +202,7 @@ public class ContactListAdapter
 	public static class ContactHolder extends RecyclerView.ViewHolder {
 		public ViewGroup layout;
 		public ImageView bulb;
+		public ImageView avatar;
 		public TextView name;
 		public TextView date;
 
@@ -201,6 +211,7 @@ public class ContactListAdapter
 
 			layout = (ViewGroup) v;
 			bulb = (ImageView) v.findViewById(R.id.bulbView);
+			avatar = (ImageView) v.findViewById(R.id.avatarView);
 			name = (TextView) v.findViewById(R.id.nameView);
 			date = (TextView) v.findViewById(R.id.dateView);
 		}
diff --git a/briar-android/src/org/briarproject/android/contact/ContactListFragment.java b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
index d2bff01e582b45a40cb0c2bee5adbc1c897c9db8..bd52792c5b7d0b427b073d69d9ddf9b7dfd621fe 100644
--- a/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
+++ b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
@@ -16,6 +16,7 @@ import org.briarproject.android.util.BriarRecyclerView;
 import org.briarproject.api.contact.Contact;
 import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.contact.ContactManager;
+import org.briarproject.api.crypto.CryptoComponent;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.db.NoSuchContactException;
 import org.briarproject.api.event.ContactAddedEvent;
@@ -62,6 +63,8 @@ public class ContactListFragment extends BaseEventFragment {
 		return TAG;
 	}
 
+	@Inject
+	private CryptoComponent crypto;
 	@Inject
 	private ConnectionRegistry connectionRegistry;
 	private ContactListAdapter adapter = null;
@@ -83,7 +86,7 @@ public class ContactListFragment extends BaseEventFragment {
 				inflater.inflate(R.layout.activity_contact_list, container,
 						false);
 
-		adapter = new ContactListAdapter(getContext());
+		adapter = new ContactListAdapter(getContext(), crypto);
 		list = (BriarRecyclerView) contentView.findViewById(R.id.contactList);
 		list.setLayoutManager(new LinearLayoutManager(getContext()));
 		list.setAdapter(adapter);
diff --git a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
index 6b96389c8c8fbac2f62353b5c6c219fa5d618a4e..dc96bd47fb7e6bb51d1dfb480ff2b25a53df0e21 100644
--- a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
+++ b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
@@ -23,6 +23,7 @@ import org.briarproject.api.android.AndroidNotificationManager;
 import org.briarproject.api.contact.Contact;
 import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.contact.ContactManager;
+import org.briarproject.api.crypto.CryptoComponent;
 import org.briarproject.api.crypto.CryptoExecutor;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.db.NoSuchContactException;
@@ -71,6 +72,7 @@ public class ConversationActivity extends BriarActivity
 	private static final Logger LOG =
 			Logger.getLogger(ConversationActivity.class.getName());
 
+	@Inject private CryptoComponent crypto;
 	@Inject private AndroidNotificationManager notificationManager;
 	@Inject private ConnectionRegistry connectionRegistry;
 	@Inject @CryptoExecutor private Executor cryptoExecutor;
@@ -88,6 +90,7 @@ public class ConversationActivity extends BriarActivity
 	private volatile GroupId groupId = null;
 	private volatile ContactId contactId = null;
 	private volatile String contactName = null;
+	private volatile byte[] contactIdenticonKey = null;
 	private volatile boolean connected = false;
 
 	@Override
@@ -101,7 +104,7 @@ public class ConversationActivity extends BriarActivity
 
 		setContentView(R.layout.activity_conversation);
 
-		adapter = new ConversationAdapter(this);
+		adapter = new ConversationAdapter(this, crypto);
 		list = (BriarRecyclerView) findViewById(R.id.conversationView);
 		list.setLayoutManager(new LinearLayoutManager(this));
 		list.setAdapter(adapter);
@@ -165,6 +168,7 @@ public class ConversationActivity extends BriarActivity
 					contactId = messagingManager.getContactId(groupId);
 					Contact contact = contactManager.getContact(contactId);
 					contactName = contact.getAuthor().getName();
+					contactIdenticonKey = contact.getAuthor().getId().getBytes();
 					connected = connectionRegistry.isConnected(contactId);
 					long duration = System.currentTimeMillis() - now;
 					if (LOG.isLoggable(INFO))
@@ -192,6 +196,7 @@ public class ConversationActivity extends BriarActivity
 						actionBar.setSubtitle(getString(R.string.offline));
 					}
 				}
+				adapter.setIdenticonKey(contactIdenticonKey);
 			}
 		});
 	}
diff --git a/briar-android/src/org/briarproject/android/contact/ConversationAdapter.java b/briar-android/src/org/briarproject/android/contact/ConversationAdapter.java
index 09509c0ee4cfd411672bea00da246de2a3598d4c..a35db98bdd96863a16a752b4d87fdd7dd804c329 100644
--- a/briar-android/src/org/briarproject/android/contact/ConversationAdapter.java
+++ b/briar-android/src/org/briarproject/android/contact/ConversationAdapter.java
@@ -11,9 +11,12 @@ import android.widget.ImageView;
 import android.widget.TextView;
 
 import org.briarproject.R;
+import org.briarproject.api.crypto.CryptoComponent;
 import org.briarproject.api.messaging.PrivateMessageHeader;
 import org.briarproject.util.StringUtils;
 
+import im.delight.android.identicons.IdenticonDrawable;
+
 import static android.support.v7.util.SortedList.INVALID_POSITION;
 
 class ConversationAdapter extends
@@ -70,9 +73,17 @@ class ConversationAdapter extends
 						}
 					});
 	private Context ctx;
+	private CryptoComponent crypto;
+	private byte[] identiconKey;
 
-	public ConversationAdapter(Context context) {
+	public ConversationAdapter(Context context, CryptoComponent cryptoComponent) {
 		ctx = context;
+		crypto = cryptoComponent;
+	}
+
+	public void setIdenticonKey(byte[] key) {
+		this.identiconKey = key;
+		notifyDataSetChanged();
 	}
 
 	@Override
@@ -119,18 +130,23 @@ class ConversationAdapter extends
 			} else {
 				ui.status.setImageResource(R.drawable.message_stored);
 			}
-		} else if (!header.isRead()) {
-			int left = ui.layout.getPaddingLeft();
-			int top = ui.layout.getPaddingTop();
-			int right = ui.layout.getPaddingRight();
-			int bottom = ui.layout.getPaddingBottom();
-
-			// show unread messages in different color to not miss them
-			ui.layout.setBackgroundResource(R.drawable.msg_in_unread);
-
-			// re-apply the previous padding due to bug in some Android versions
-			// see: https://code.google.com/p/android/issues/detail?id=17885
-			ui.layout.setPadding(left, top, right, bottom);
+		} else {
+			if (identiconKey != null)
+				ui.avatar.setImageDrawable(
+						new IdenticonDrawable(crypto, identiconKey));
+			if (!header.isRead()) {
+				int left = ui.layout.getPaddingLeft();
+				int top = ui.layout.getPaddingTop();
+				int right = ui.layout.getPaddingRight();
+				int bottom = ui.layout.getPaddingBottom();
+
+				// show unread messages in different color to not miss them
+				ui.layout.setBackgroundResource(R.drawable.msg_in_unread);
+
+				// re-apply the previous padding due to bug in some Android versions
+				// see: https://code.google.com/p/android/issues/detail?id=17885
+				ui.layout.setPadding(left, top, right, bottom);
+			}
 		}
 
 		if (item.getBody() == null) {
@@ -186,6 +202,7 @@ class ConversationAdapter extends
 		public TextView body;
 		public TextView date;
 		public ImageView status;
+		public ImageView avatar;
 
 		public MessageHolder(View v, int type) {
 			super(v);
@@ -197,6 +214,8 @@ class ConversationAdapter extends
 			// outgoing message (local)
 			if (type == MSG_OUT) {
 				status = (ImageView) v.findViewById(R.id.msgStatus);
+			} else {
+				avatar = (ImageView) v.findViewById(R.id.msgAvatar);
 			}
 		}
 	}
diff --git a/briar-android/src/org/briarproject/android/forum/ForumActivity.java b/briar-android/src/org/briarproject/android/forum/ForumActivity.java
index 7f725d40d2db8a62bcd2653572a3348ce689bdc9..a67ebd12e702a697fcf8bde22b874ce0f0217136 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumActivity.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumActivity.java
@@ -18,6 +18,7 @@ import org.briarproject.android.util.ElasticHorizontalSpace;
 import org.briarproject.android.util.HorizontalBorder;
 import org.briarproject.android.util.ListLoadingProgressBar;
 import org.briarproject.api.android.AndroidNotificationManager;
+import org.briarproject.api.android.ReferenceManager;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.db.NoSuchMessageException;
 import org.briarproject.api.db.NoSuchSubscriptionException;
@@ -70,6 +71,8 @@ public class ForumActivity extends BriarActivity implements EventListener,
 	private ListLoadingProgressBar loading = null;
 	private ImageButton composeButton = null, shareButton = null;
 
+	@Inject private ReferenceManager referenceManager;
+
 	// Fields that are accessed from background threads must be volatile
 	@Inject private volatile ForumManager forumManager;
 	@Inject private volatile EventBus eventBus;
@@ -366,7 +369,8 @@ public class ForumActivity extends BriarActivity implements EventListener,
 		i.putExtra("briar.FORUM_NAME", forum.getName());
 		i.putExtra("briar.MESSAGE_ID", header.getId().getBytes());
 		Author author = header.getAuthor();
-		if (author != null) i.putExtra("briar.AUTHOR_NAME", author.getName());
+		if (author != null) i.putExtra("briar.AUTHOR_HANDLE",
+				referenceManager.putReference(author, Author.class));
 		i.putExtra("briar.AUTHOR_STATUS", header.getAuthorStatus().name());
 		i.putExtra("briar.CONTENT_TYPE", header.getContentType());
 		i.putExtra("briar.TIMESTAMP", header.getTimestamp());
diff --git a/briar-android/src/org/briarproject/android/forum/ForumAdapter.java b/briar-android/src/org/briarproject/android/forum/ForumAdapter.java
index ac121144740797b6f4a21c45f8b865e0f766ff54..30ff7d0bf8367158b88af5c8178dc9c13d7bdb15 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumAdapter.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumAdapter.java
@@ -55,8 +55,7 @@ class ForumAdapter extends ArrayAdapter<ForumItem> {
 		AuthorView authorView = new AuthorView(ctx);
 		authorView.setLayoutParams(WRAP_WRAP_1);
 		Author author = header.getAuthor();
-		if (author == null) authorView.init(null, header.getAuthorStatus());
-		else authorView.init(author.getName(), header.getAuthorStatus());
+		authorView.init(author, header.getAuthorStatus());
 		headerLayout.addView(authorView);
 
 		TextView date = new TextView(ctx);
diff --git a/briar-android/src/org/briarproject/android/forum/ReadForumPostActivity.java b/briar-android/src/org/briarproject/android/forum/ReadForumPostActivity.java
index 706cb31fe00a0414ffa3faac22ecb1c5ea8a78bf..5ffc8aaa89eccabf84427ff583f4112ef41c951e 100644
--- a/briar-android/src/org/briarproject/android/forum/ReadForumPostActivity.java
+++ b/briar-android/src/org/briarproject/android/forum/ReadForumPostActivity.java
@@ -17,6 +17,7 @@ import org.briarproject.android.util.AuthorView;
 import org.briarproject.android.util.ElasticHorizontalSpace;
 import org.briarproject.android.util.HorizontalBorder;
 import org.briarproject.android.util.LayoutUtils;
+import org.briarproject.api.android.ReferenceManager;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.db.NoSuchMessageException;
 import org.briarproject.api.forum.ForumManager;
@@ -56,6 +57,8 @@ implements OnClickListener {
 	private TextView content = null;
 	private int position = -1;
 
+	@Inject private ReferenceManager referenceManager;
+
 	// Fields that are accessed from background threads must be volatile
 	@Inject private volatile ForumManager forumManager;
 	private volatile MessageId messageId = null;
@@ -82,7 +85,9 @@ implements OnClickListener {
 		if (minTimestamp == -1) throw new IllegalStateException();
 		position = i.getIntExtra("briar.POSITION", -1);
 		if (position == -1) throw new IllegalStateException();
-		String authorName = i.getStringExtra("briar.AUTHOR_NAME");
+		long authorHandle = i.getLongExtra("briar.AUTHOR_HANDLE", -1);
+		if (authorHandle == -1) throw new IllegalStateException();
+		Author author = referenceManager.removeReference(authorHandle, Author.class);
 		String s = i.getStringExtra("briar.AUTHOR_STATUS");
 		if (s == null) throw new IllegalStateException();
 		Author.Status authorStatus = Author.Status.valueOf(s);
@@ -102,10 +107,10 @@ implements OnClickListener {
 		header.setOrientation(HORIZONTAL);
 		header.setGravity(CENTER_VERTICAL);
 
-		AuthorView author = new AuthorView(this);
-		author.setLayoutParams(WRAP_WRAP_1);
-		author.init(authorName, authorStatus);
-		header.addView(author);
+		AuthorView authorView = new AuthorView(this);
+		authorView.setLayoutParams(WRAP_WRAP_1);
+		authorView.init(author, authorStatus);
+		header.addView(authorView);
 
 		int pad = LayoutUtils.getPadding(this);
 
diff --git a/briar-android/src/org/briarproject/android/forum/WriteForumPostActivity.java b/briar-android/src/org/briarproject/android/forum/WriteForumPostActivity.java
index 40e72a14877abad5ce9c4f3d74b3dc372a3449f3..a38433f311e324066248a455671078095a0fec57 100644
--- a/briar-android/src/org/briarproject/android/forum/WriteForumPostActivity.java
+++ b/briar-android/src/org/briarproject/android/forum/WriteForumPostActivity.java
@@ -123,7 +123,7 @@ implements OnItemSelectedListener, OnClickListener {
 		left.addRule(CENTER_VERTICAL);
 		header.addView(from, left);
 
-		adapter = new LocalAuthorSpinnerAdapter(this, true);
+		adapter = new LocalAuthorSpinnerAdapter(this, crypto, true);
 		spinner = new Spinner(this);
 		spinner.setId(2);
 		spinner.setAdapter(adapter);
diff --git a/briar-android/src/org/briarproject/android/identity/LocalAuthorSpinnerAdapter.java b/briar-android/src/org/briarproject/android/identity/LocalAuthorSpinnerAdapter.java
index 2aa2358f45e1c62c248e7ba6109cd4ed0cb4aca3..a0e2d88de2ebfff7c8dd3e5d3bdcb01f4ad2ffce 100644
--- a/briar-android/src/org/briarproject/android/identity/LocalAuthorSpinnerAdapter.java
+++ b/briar-android/src/org/briarproject/android/identity/LocalAuthorSpinnerAdapter.java
@@ -1,21 +1,24 @@
 package org.briarproject.android.identity;
 
 import android.content.Context;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.BaseAdapter;
+import android.widget.ImageView;
 import android.widget.SpinnerAdapter;
 import android.widget.TextView;
 
 import org.briarproject.R;
-import org.briarproject.android.util.LayoutUtils;
+import org.briarproject.api.crypto.CryptoComponent;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 
-import static android.text.TextUtils.TruncateAt.END;
+import im.delight.android.identicons.IdenticonDrawable;
+
 import static org.briarproject.android.identity.LocalAuthorItem.ANONYMOUS;
 import static org.briarproject.android.identity.LocalAuthorItem.NEW;
 
@@ -23,11 +26,14 @@ public class LocalAuthorSpinnerAdapter extends BaseAdapter
 implements SpinnerAdapter {
 
 	private final Context ctx;
+	private final CryptoComponent crypto;
 	private final boolean includeAnonymous;
 	private final List<LocalAuthorItem> list = new ArrayList<LocalAuthorItem>();
 
-	public LocalAuthorSpinnerAdapter(Context ctx, boolean includeAnonymous) {
+	public LocalAuthorSpinnerAdapter(Context ctx,
+			CryptoComponent crypto, boolean includeAnonymous) {
 		this.ctx = ctx;
+		this.crypto = crypto;
 		this.includeAnonymous = includeAnonymous;
 	}
 
@@ -49,17 +55,33 @@ implements SpinnerAdapter {
 	@Override
 	public View getDropDownView(int position, View convertView,
 			ViewGroup parent) {
-		TextView name = new TextView(ctx);
-		name.setTextSize(18);
-		name.setSingleLine();
-		name.setEllipsize(END);
-		int pad = LayoutUtils.getPadding(ctx);
-		name.setPadding(pad, pad, pad, pad);
+		View view;
+		if (convertView == null) {
+			LayoutInflater inflater =
+					(LayoutInflater) ctx
+							.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+			view = inflater.inflate(R.layout.dropdown_author, parent, false);
+		} else
+			view = convertView;
+
+		TextView name = (TextView) view.findViewById(R.id.nameView);
+		ImageView avatar =
+				(ImageView) view.findViewById(R.id.avatarView);
+
 		LocalAuthorItem item = getItem(position);
-		if (item == ANONYMOUS) name.setText(R.string.anonymous);
-		else if (item == NEW) name.setText(R.string.new_identity_item);
-		else name.setText(item.getLocalAuthor().getName());
-		return name;
+		if (item == ANONYMOUS) {
+			name.setText(R.string.anonymous);
+			avatar.setVisibility(View.INVISIBLE);
+		} else if (item == NEW) {
+			name.setText(R.string.new_identity_item);
+			avatar.setVisibility(View.INVISIBLE);
+		} else {
+			name.setText(item.getLocalAuthor().getName());
+			avatar.setVisibility(View.VISIBLE);
+			avatar.setImageDrawable(new IdenticonDrawable(crypto,
+					item.getLocalAuthor().getId().getBytes()));
+		}
+		return view;
 	}
 
 	public LocalAuthorItem getItem(int position) {
@@ -78,15 +100,7 @@ implements SpinnerAdapter {
 	}
 
 	public View getView(int position, View convertView, ViewGroup parent) {
-		TextView name = new TextView(ctx);
-		name.setTextSize(18);
-		name.setSingleLine();
-		name.setEllipsize(END);
-		LocalAuthorItem item = getItem(position);
-		if (item == ANONYMOUS) name.setText(R.string.anonymous);
-		else if (item == NEW) name.setText(R.string.new_identity_item);
-		else name.setText(item.getLocalAuthor().getName());
-		return name;
+		return getDropDownView(position, convertView, parent);
 	}
 
 	@Override
diff --git a/briar-android/src/org/briarproject/android/invitation/ChooseIdentityView.java b/briar-android/src/org/briarproject/android/invitation/ChooseIdentityView.java
index 940de7e7594f83fff6a48fd6d1d0062ce8e8f048..912d28ccebaf78525a7115852776583b1e3a3000 100644
--- a/briar-android/src/org/briarproject/android/invitation/ChooseIdentityView.java
+++ b/briar-android/src/org/briarproject/android/invitation/ChooseIdentityView.java
@@ -16,11 +16,16 @@ import org.briarproject.android.identity.CreateIdentityActivity;
 import org.briarproject.android.identity.LocalAuthorItem;
 import org.briarproject.android.identity.LocalAuthorItemComparator;
 import org.briarproject.android.identity.LocalAuthorSpinnerAdapter;
+import org.briarproject.api.crypto.CryptoComponent;
 import org.briarproject.api.identity.AuthorId;
 import org.briarproject.api.identity.LocalAuthor;
 
 import java.util.Collection;
 
+import javax.inject.Inject;
+
+import roboguice.RoboGuice;
+
 import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE;
 import static android.bluetooth.BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION;
 import static org.briarproject.android.identity.LocalAuthorItem.NEW;
@@ -30,6 +35,7 @@ import static org.briarproject.android.invitation.AddContactActivity.REQUEST_CRE
 class ChooseIdentityView extends AddContactView
 implements OnItemSelectedListener, OnClickListener {
 
+	@Inject private CryptoComponent crypto;
 	private LocalAuthorSpinnerAdapter adapter = null;
 	private Spinner spinner = null;
 
@@ -40,6 +46,7 @@ implements OnItemSelectedListener, OnClickListener {
 	void populate() {
 		removeAllViews();
 		Context ctx = getContext();
+		RoboGuice.injectMembers(ctx, this);
 
 		LayoutInflater inflater = (LayoutInflater) ctx.getSystemService
 				(Context.LAYOUT_INFLATER_SERVICE);
@@ -50,7 +57,7 @@ implements OnItemSelectedListener, OnClickListener {
 		TextView step = (TextView) view.findViewById(R.id.stepView);
 		step.setText(String.format(ctx.getString(R.string.step), 1, 3));
 
-		adapter = new LocalAuthorSpinnerAdapter(ctx, false);
+		adapter = new LocalAuthorSpinnerAdapter(ctx, crypto, false);
 		spinner = (Spinner) view.findViewById(R.id.spinner);
 		spinner.setAdapter(adapter);
 		spinner.setOnItemSelectedListener(this);
diff --git a/briar-android/src/org/briarproject/android/util/AuthorView.java b/briar-android/src/org/briarproject/android/util/AuthorView.java
index 152a801b91495218d4b843b7bb4ff71daba326ab..dd20a28e7717cd0e54a2a9f745bf5aef0d414595 100644
--- a/briar-android/src/org/briarproject/android/util/AuthorView.java
+++ b/briar-android/src/org/briarproject/android/util/AuthorView.java
@@ -1,42 +1,69 @@
 package org.briarproject.android.util;
 
 import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
-import android.widget.RelativeLayout;
 import android.widget.TextView;
 
 import org.briarproject.R;
+import org.briarproject.api.crypto.CryptoComponent;
 import org.briarproject.api.identity.Author;
 
-import static android.text.TextUtils.TruncateAt.END;
+import javax.inject.Inject;
 
-public class AuthorView extends RelativeLayout {
+import im.delight.android.identicons.IdenticonDrawable;
+import roboguice.RoboGuice;
+
+public class AuthorView extends FrameLayout {
+
+	@Inject private CryptoComponent crypto;
+	private ImageView avatarView;
+	private TextView nameView;
+	private ImageView statusView;
 
 	public AuthorView(Context ctx) {
 		super(ctx);
+
+		initViews();
+	}
+
+	public AuthorView(Context context, AttributeSet attrs) {
+		super(context, attrs);
+
+		initViews();
 	}
 
-	public void init(String name, Author.Status status) {
-		Context ctx = getContext();
-		int pad = LayoutUtils.getPadding(ctx);
-
-		TextView nameView = new TextView(ctx);
-		nameView.setId(1);
-		nameView.setTextSize(18);
-		nameView.setSingleLine();
-		nameView.setEllipsize(END);
-		nameView.setPadding(pad, pad, pad, pad);
-		if (name == null) nameView.setText(R.string.anonymous);
-		else nameView.setText(name);
-		LayoutParams leftOf = CommonLayoutParams.relative();
-		leftOf.addRule(ALIGN_PARENT_LEFT);
-		leftOf.addRule(CENTER_VERTICAL);
-		leftOf.addRule(LEFT_OF, 2);
-		addView(nameView, leftOf);
-
-		ImageView statusView = new ImageView(ctx);
-		statusView.setId(2);
-		statusView.setPadding(0, pad, pad, pad);
+	public AuthorView(Context context, AttributeSet attrs,
+							 int defStyle) {
+		super(context, attrs, defStyle);
+
+		initViews();
+	}
+
+	private void initViews() {
+		RoboGuice.injectMembers(getContext(), this);
+		if (isInEditMode())
+			return;
+
+		View v = LayoutInflater.from(getContext()).inflate(
+				R.layout.author_view, this, true);
+
+		avatarView = (ImageView) v.findViewById(R.id.avatarView);
+		nameView = (TextView) v.findViewById(R.id.nameView);
+		statusView = (ImageView) v.findViewById(R.id.statusView);
+	}
+
+	public void init(Author author, Author.Status status) {
+		if (author == null) nameView.setText(R.string.anonymous);
+		else {
+			avatarView.setImageDrawable(
+					new IdenticonDrawable(crypto, author.getId().getBytes()));
+			nameView.setText(author.getName());
+		}
+
 		switch(status) {
 		case ANONYMOUS:
 			statusView.setImageResource(R.drawable.identity_anonymous);
@@ -51,9 +78,5 @@ public class AuthorView extends RelativeLayout {
 			statusView.setImageResource(R.drawable.identity_verified);
 			break;
 		}
-		LayoutParams right = CommonLayoutParams.relative();
-		right.addRule(ALIGN_PARENT_RIGHT);
-		right.addRule(CENTER_VERTICAL);
-		addView(statusView, right);
 	}
 }