basic notifications

Daniel Gultsch created

Change summary

gen/de/gultsch/chat/R.java                              |   3 
res/drawable/notification.png                           |   0 
src/de/gultsch/chat/services/XmppConnectionService.java | 365 +++++-----
src/de/gultsch/chat/ui/ConversationActivity.java        |   8 
src/de/gultsch/chat/ui/ConversationFragment.java        |   6 
src/de/gultsch/chat/ui/NewConversationActivity.java     |   4 
src/de/gultsch/chat/utils/Beautifier.java               |  60 -
src/de/gultsch/chat/utils/UIHelper.java                 | 114 +++
8 files changed, 323 insertions(+), 237 deletions(-)

Detailed changes

gen/de/gultsch/chat/R.java 🔗

@@ -34,7 +34,8 @@ public final class R {
         public static final int ic_launcher=0x7f020006;
         public static final int ic_profile=0x7f020007;
         public static final int message_border=0x7f020008;
-        public static final int section_header=0x7f020009;
+        public static final int notification=0x7f020009;
+        public static final int section_header=0x7f02000a;
     }
     public static final class id {
         public static final int account_confirm_password_desc=0x7f0a0011;

src/de/gultsch/chat/services/XmppConnectionService.java 🔗

@@ -5,222 +5,252 @@ import java.util.ArrayList;
 import java.util.Hashtable;
 import java.util.List;
 
-
 import de.gultsch.chat.entities.Account;
 import de.gultsch.chat.entities.Contact;
 import de.gultsch.chat.entities.Conversation;
 import de.gultsch.chat.entities.Message;
 import de.gultsch.chat.persistance.DatabaseBackend;
+import de.gultsch.chat.ui.ConversationActivity;
 import de.gultsch.chat.ui.OnConversationListChangedListener;
 import de.gultsch.chat.ui.OnRosterFetchedListener;
+import de.gultsch.chat.utils.UIHelper;
 import de.gultsch.chat.xml.Element;
 import de.gultsch.chat.xmpp.IqPacket;
 import de.gultsch.chat.xmpp.MessagePacket;
 import de.gultsch.chat.xmpp.OnIqPacketReceived;
 import de.gultsch.chat.xmpp.OnMessagePacketReceived;
 import de.gultsch.chat.xmpp.XmppConnection;
+import android.R;
+import android.R.dimen;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Resources;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.PowerManager;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.TaskStackBuilder;
 import android.util.Log;
 
 public class XmppConnectionService extends Service {
-	
+
 	protected static final String LOGTAG = "xmppService";
 	protected DatabaseBackend databaseBackend;
-	
+
 	public long startDate;
-	
+
 	private List<Account> accounts;
 	private List<Conversation> conversations = null;
-	
-	private Hashtable<Account,XmppConnection> connections = new Hashtable<Account, XmppConnection>();
+
+	private Hashtable<Account, XmppConnection> connections = new Hashtable<Account, XmppConnection>();
 
 	private OnConversationListChangedListener convChangedListener = null;
-	
-    private final IBinder mBinder = new XmppConnectionBinder();
+
+	private final IBinder mBinder = new XmppConnectionBinder();
 	private OnMessagePacketReceived messageListener = new OnMessagePacketReceived() {
-		
+
 		@Override
-		public void onMessagePacketReceived(Account account, MessagePacket packet) {
-			if (packet.getType()==MessagePacket.TYPE_CHAT) {
+		public void onMessagePacketReceived(Account account,
+				MessagePacket packet) {
+			if (packet.getType() == MessagePacket.TYPE_CHAT) {
 				String fullJid = packet.getFrom();
 				String jid = fullJid.split("/")[0];
 				String name = jid.split("@")[0];
-				Contact contact = new Contact(account,name,jid,null); //dummy contact
-				Conversation conversation = findOrCreateConversation(account, contact);
-				Message message = new Message(conversation, fullJid, packet.getBody(), Message.ENCRYPTION_NONE, Message.STATUS_RECIEVED);
+				Contact contact = new Contact(account, name, jid, null); // dummy
+																			// contact
+				Conversation conversation = findOrCreateConversation(account,
+						contact);
+				Message message = new Message(conversation, fullJid,
+						packet.getBody(), Message.ENCRYPTION_NONE,
+						Message.STATUS_RECIEVED);
 				conversation.getMessages().add(message);
 				databaseBackend.createMessage(message);
 				if (convChangedListener != null) {
 					convChangedListener.onConversationListChanged();
+				} else {
+					NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+					mNotificationManager.notify(2342, UIHelper
+							.getUnreadMessageNotification(
+									getApplicationContext(), conversation));
 				}
 			}
 		}
 	};
 
-    public class XmppConnectionBinder extends Binder {
-        public XmppConnectionService getService() {
-        	return XmppConnectionService.this;
-        }
-    }
-    
-    @Override 
-    public int onStartCommand(Intent intent, int flags, int startId) {
-        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
-        for(Account account : accounts) {
-        	if (!connections.containsKey(account)) {
-        		XmppConnection connection = new XmppConnection(account, pm);
-        		connection.setOnMessagePacketReceivedListener(this.messageListener );
-        		Thread thread = new Thread(connection);
-        		thread.start();
-        		this.connections.put(account, connection);
-        	}
-        }
-        return START_STICKY;
-    }
-    
-    @Override
-    public void onCreate() {
-    	databaseBackend = DatabaseBackend.getInstance(getApplicationContext());
-    	this.accounts = databaseBackend.getAccounts();
-    }
-    
-    @Override
-    public IBinder onBind(Intent intent) {
-        return mBinder;
-    }
-    
-    public void sendMessage(final Account account, final Message message) {
-    	new Thread() {
-    		@Override
-    		public void run() {
-    			Log.d(LOGTAG,"sending message for "+account.getJid()+" to: "+message.getCounterpart());
-    			databaseBackend.createMessage(message);
-    			MessagePacket packet = new MessagePacket();
-    			packet.setType(MessagePacket.TYPE_CHAT);
-    			packet.setTo(message.getCounterpart());
-    			packet.setFrom(account.getJid());
-    			packet.setBody(message.getBody());
-    			try {
+	public class XmppConnectionBinder extends Binder {
+		public XmppConnectionService getService() {
+			return XmppConnectionService.this;
+		}
+	}
+
+	@Override
+	public int onStartCommand(Intent intent, int flags, int startId) {
+		PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+		for (Account account : accounts) {
+			if (!connections.containsKey(account)) {
+				XmppConnection connection = new XmppConnection(account, pm);
+				connection
+						.setOnMessagePacketReceivedListener(this.messageListener);
+				Thread thread = new Thread(connection);
+				thread.start();
+				this.connections.put(account, connection);
+			}
+		}
+		return START_STICKY;
+	}
+
+	@Override
+	public void onCreate() {
+		databaseBackend = DatabaseBackend.getInstance(getApplicationContext());
+		this.accounts = databaseBackend.getAccounts();
+	}
+
+	@Override
+	public IBinder onBind(Intent intent) {
+		return mBinder;
+	}
+
+	public void sendMessage(final Account account, final Message message) {
+		new Thread() {
+			@Override
+			public void run() {
+				Log.d(LOGTAG, "sending message for " + account.getJid()
+						+ " to: " + message.getCounterpart());
+				databaseBackend.createMessage(message);
+				MessagePacket packet = new MessagePacket();
+				packet.setType(MessagePacket.TYPE_CHAT);
+				packet.setTo(message.getCounterpart());
+				packet.setFrom(account.getJid());
+				packet.setBody(message.getBody());
+				try {
 					connections.get(account).sendMessagePacket(packet);
 					message.setStatus(Message.STATUS_SEND);
 					databaseBackend.updateMessage(message);
 				} catch (IOException e) {
-					Log.d(LOGTAG,"io exception during send. message is in database. will try again later");
+					Log.d(LOGTAG,
+							"io exception during send. message is in database. will try again later");
 				}
-    		}
-    	}.start();
-    }
-    
-    public void getRoster(final Account account, final OnRosterFetchedListener listener) {
-    	new Thread() {
-    		@Override
-    		public void run() {
-    			IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET);
-    	    	Element query = new Element("query");
-    	    	query.setAttribute("xmlns", "jabber:iq:roster");
-    	    	query.setAttribute("ver", "");
-    	    	iqPacket.addChild(query);
-    	    	try {
-    	    		connections.get(account).sendIqPacket(iqPacket, new OnIqPacketReceived() {
-    					
-    					@Override
-    					public void onIqPacketReceived(Account account, IqPacket packet) {
-    						Element roster = packet.findChild("query");
-    						List<Contact> contacts = new ArrayList<Contact>();
-    						for(Element item : roster.getChildren()) {
-    							String name = item.getAttribute("name");
-    							String jid = item.getAttribute("jid");
-    							if (name==null) {
-    								name = jid.split("@")[0];
-    							}
-    							Contact contact = new Contact(account, name, jid, null);
-    							contacts.add(contact);
-    						}
-    						if (listener != null) {
-    							listener.onRosterFetched(contacts);
-    						}
-    					}
-    				});
-    			} catch (IOException e) {
-    				Log.d(LOGTAG,"io error during roster fetch");
-    			}
-    		}
-    	}.start();
-    }
-    
-    public void addConversation(Conversation conversation) {
-    	databaseBackend.createConversation(conversation);
-    }
-    
-    public List<Conversation> getConversations() {
-    	if (this.conversations == null) {
-	    	Hashtable<String, Account> accountLookupTable = new Hashtable<String, Account>();
-	    	for(Account account : this.accounts) {
-	    		accountLookupTable.put(account.getUuid(), account);
-	    	}
-	    	this.conversations = databaseBackend.getConversations(Conversation.STATUS_AVAILABLE);
-	    	for(Conversation conv : this.conversations) {
-	    		conv.setAccount(accountLookupTable.get(conv.getAccountUuid()));
-	    	}
-    	}
-    	return this.conversations;
-    }
-    
-    public List<Account> getAccounts() {
-    	return this.accounts;
-    }
-    
-    public List<Message> getMessages(Conversation conversation) {
-    	return databaseBackend.getMessages(conversation, 100);
-    }
-
-    public Conversation findOrCreateConversation(Account account, Contact contact) {
-    	//Log.d(LOGTAG,"was asked to find conversation for "+contact.getJid());
-    	for(Conversation conv : this.getConversations()) {
-    		if ((conv.getAccount().equals(account))&&(conv.getContactJid().equals(contact.getJid()))) {
-    			//Log.d(LOGTAG,"found one in memory");
-    			return conv;
-    		}
-    	}
-    	Conversation conversation = databaseBackend.findConversation(account, contact.getJid());
-    	if (conversation!=null) {
-    		Log.d("gultsch","found one. unarchive it");
-    		conversation.setStatus(Conversation.STATUS_AVAILABLE);
-    		conversation.setAccount(account);
-    		this.databaseBackend.updateConversation(conversation);
-    	} else {
-    		Log.d(LOGTAG,"didnt find one in archive. create new one");
-    		conversation = new Conversation(contact.getDisplayName(), contact.getProfilePhoto(), account, contact.getJid());
-    		this.databaseBackend.createConversation(conversation);
-    	}
-    	this.conversations.add(conversation);
-    	if (this.convChangedListener != null) {
-    		this.convChangedListener.onConversationListChanged();
-    	}
-    	return conversation;
-    }
-    
-    public void archiveConversation(Conversation conversation) {
-    	this.databaseBackend.updateConversation(conversation);
-    	this.conversations.remove(conversation);
-    	if (this.convChangedListener != null) {
-    		this.convChangedListener.onConversationListChanged();
-    	}
-    }
-    
-    public int getConversationCount() {
-    	return this.databaseBackend.getConversationCount();
-    }
+			}
+		}.start();
+	}
+
+	public void getRoster(final Account account,
+			final OnRosterFetchedListener listener) {
+		new Thread() {
+			@Override
+			public void run() {
+				IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET);
+				Element query = new Element("query");
+				query.setAttribute("xmlns", "jabber:iq:roster");
+				query.setAttribute("ver", "");
+				iqPacket.addChild(query);
+				try {
+					connections.get(account).sendIqPacket(iqPacket,
+							new OnIqPacketReceived() {
+
+								@Override
+								public void onIqPacketReceived(Account account,
+										IqPacket packet) {
+									Element roster = packet.findChild("query");
+									List<Contact> contacts = new ArrayList<Contact>();
+									for (Element item : roster.getChildren()) {
+										String name = item.getAttribute("name");
+										String jid = item.getAttribute("jid");
+										if (name == null) {
+											name = jid.split("@")[0];
+										}
+										Contact contact = new Contact(account,
+												name, jid, null);
+										contacts.add(contact);
+									}
+									if (listener != null) {
+										listener.onRosterFetched(contacts);
+									}
+								}
+							});
+				} catch (IOException e) {
+					Log.d(LOGTAG, "io error during roster fetch");
+				}
+			}
+		}.start();
+	}
+
+	public void addConversation(Conversation conversation) {
+		databaseBackend.createConversation(conversation);
+	}
+
+	public List<Conversation> getConversations() {
+		if (this.conversations == null) {
+			Hashtable<String, Account> accountLookupTable = new Hashtable<String, Account>();
+			for (Account account : this.accounts) {
+				accountLookupTable.put(account.getUuid(), account);
+			}
+			this.conversations = databaseBackend
+					.getConversations(Conversation.STATUS_AVAILABLE);
+			for (Conversation conv : this.conversations) {
+				conv.setAccount(accountLookupTable.get(conv.getAccountUuid()));
+			}
+		}
+		return this.conversations;
+	}
+
+	public List<Account> getAccounts() {
+		return this.accounts;
+	}
+
+	public List<Message> getMessages(Conversation conversation) {
+		return databaseBackend.getMessages(conversation, 100);
+	}
+
+	public Conversation findOrCreateConversation(Account account,
+			Contact contact) {
+		// Log.d(LOGTAG,"was asked to find conversation for "+contact.getJid());
+		for (Conversation conv : this.getConversations()) {
+			if ((conv.getAccount().equals(account))
+					&& (conv.getContactJid().equals(contact.getJid()))) {
+				// Log.d(LOGTAG,"found one in memory");
+				return conv;
+			}
+		}
+		Conversation conversation = databaseBackend.findConversation(account,
+				contact.getJid());
+		if (conversation != null) {
+			Log.d("gultsch", "found one. unarchive it");
+			conversation.setStatus(Conversation.STATUS_AVAILABLE);
+			conversation.setAccount(account);
+			this.databaseBackend.updateConversation(conversation);
+		} else {
+			Log.d(LOGTAG, "didnt find one in archive. create new one");
+			conversation = new Conversation(contact.getDisplayName(),
+					contact.getProfilePhoto(), account, contact.getJid());
+			this.databaseBackend.createConversation(conversation);
+		}
+		this.conversations.add(conversation);
+		if (this.convChangedListener != null) {
+			this.convChangedListener.onConversationListChanged();
+		}
+		return conversation;
+	}
+
+	public void archiveConversation(Conversation conversation) {
+		this.databaseBackend.updateConversation(conversation);
+		this.conversations.remove(conversation);
+		if (this.convChangedListener != null) {
+			this.convChangedListener.onConversationListChanged();
+		}
+	}
+
+	public int getConversationCount() {
+		return this.databaseBackend.getConversationCount();
+	}
 
 	public void createAccount(Account account) {
 		databaseBackend.createAccount(account);
 	}
-	
+
 	public void updateAccount(Account account) {
 		databaseBackend.updateAccount(account);
 	}
@@ -228,11 +258,12 @@ public class XmppConnectionService extends Service {
 	public void deleteAccount(Account account) {
 		databaseBackend.deleteAccount(account);
 	}
-	
-	public void setOnConversationListChangedListener(OnConversationListChangedListener listener) {
+
+	public void setOnConversationListChangedListener(
+			OnConversationListChangedListener listener) {
 		this.convChangedListener = listener;
 	}
-	
+
 	public void removeOnConversationListChangedListener() {
 		this.convChangedListener = null;
 	}

src/de/gultsch/chat/ui/ConversationActivity.java 🔗

@@ -8,7 +8,7 @@ import java.util.List;
 import de.gultsch.chat.R;
 import de.gultsch.chat.R.id;
 import de.gultsch.chat.entities.Conversation;
-import de.gultsch.chat.utils.Beautifier;
+import de.gultsch.chat.utils.UIHelper;
 import android.net.Uri;
 import android.os.Bundle;
 import android.app.FragmentTransaction;
@@ -34,7 +34,7 @@ import android.widget.ImageView;
 public class ConversationActivity extends XmppActivity {
 
 	public static final String VIEW_CONVERSATION = "viewConversation";
-	protected static final String CONVERSATION = "conversationUuid";
+	public static final String CONVERSATION = "conversationUuid";
 
 	protected SlidingPaneLayout spl;
 
@@ -137,14 +137,14 @@ public class ConversationActivity extends XmppActivity {
 						.setText(getItem(position).getName());
 				((TextView) view.findViewById(R.id.conversation_lastmsg)).setText(getItem(position).getLatestMessage());
 				((TextView) view.findViewById(R.id.conversation_lastupdate))
-				.setText(Beautifier.readableTimeDifference(getItem(position).getLatestMessageDate()));
+				.setText(UIHelper.readableTimeDifference(getItem(position).getLatestMessageDate()));
 				
 				Uri profilePhoto = getItem(position).getProfilePhotoUri();
 				ImageView imageView = (ImageView) view.findViewById(R.id.conversation_image);
 				if (profilePhoto!=null) {
 					imageView.setImageURI(profilePhoto);
 				} else {
-					imageView.setImageBitmap(Beautifier.getUnknownContactPicture(getItem(position).getName(),200));
+					imageView.setImageBitmap(UIHelper.getUnknownContactPicture(getItem(position).getName(),200));
 				}
 				
 				

src/de/gultsch/chat/ui/ConversationFragment.java 🔗

@@ -6,7 +6,7 @@ import java.util.List;
 import de.gultsch.chat.R;
 import de.gultsch.chat.entities.Conversation;
 import de.gultsch.chat.entities.Message;
-import de.gultsch.chat.utils.Beautifier;
+import de.gultsch.chat.utils.UIHelper;
 import android.app.Fragment;
 import android.database.Cursor;
 import android.graphics.Typeface;
@@ -119,7 +119,7 @@ public class ConversationFragment extends Fragment {
 					if (uri!=null) {
 						imageView.setImageURI(uri);
 					} else {
-						imageView.setImageBitmap(Beautifier.getUnknownContactPicture(item.getConversation().getName(), 200));
+						imageView.setImageBitmap(UIHelper.getUnknownContactPicture(item.getConversation().getName(), 200));
 					}
 				} else {
 					imageView.setImageURI(profilePicture);
@@ -133,7 +133,7 @@ public class ConversationFragment extends Fragment {
 				if (item.getStatus() == Message.STATUS_UNSEND) {
 					time.setTypeface(null, Typeface.ITALIC);
 				} else {
-					time.setText(Beautifier.readableTimeDifference(item
+					time.setText(UIHelper.readableTimeDifference(item
 							.getTimeSent()));
 				}
 				return view;

src/de/gultsch/chat/ui/NewConversationActivity.java 🔗

@@ -9,7 +9,7 @@ import de.gultsch.chat.R;
 import de.gultsch.chat.entities.Account;
 import de.gultsch.chat.entities.Contact;
 import de.gultsch.chat.entities.Conversation;
-import de.gultsch.chat.utils.Beautifier;
+import de.gultsch.chat.utils.UIHelper;
 import de.gultsch.chat.utils.Validator;
 import android.net.Uri;
 import android.os.Bundle;
@@ -158,7 +158,7 @@ public class NewConversationActivity extends XmppActivity {
 				if (profilePhoto!=null) {
 					imageView.setImageURI(Uri.parse(profilePhoto));
 				} else {
-					imageView.setImageBitmap(Beautifier.getUnknownContactPicture(getItem(position).getDisplayName(),90));
+					imageView.setImageBitmap(UIHelper.getUnknownContactPicture(getItem(position).getDisplayName(),90));
 				}
 				return view;
 			}

src/de/gultsch/chat/utils/Beautifier.java 🔗

@@ -1,60 +0,0 @@
-package de.gultsch.chat.utils;
-
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.util.DisplayMetrics;
-
-public class Beautifier {
-	public static String readableTimeDifference(long time) {
-		if (time == 0) {
-			return "just now";
-		}
-		Date date = new Date(time);
-		long difference = (System.currentTimeMillis() - time) / 1000;
-		if (difference < 60) {
-			return "just now";
-		} else if (difference < 60 * 10) {
-			return difference / 60 + " min ago";
-		} else if (difference < 60 * 60 * 24) {
-			SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
-			return sdf.format(date);
-		} else {
-			SimpleDateFormat sdf = new SimpleDateFormat("M/D");
-			return sdf.format(date);
-		}
-	}
-
-	public static Bitmap getUnknownContactPicture(String name, int size) {
-		String firstLetter = name.substring(0, 1).toUpperCase();
-		String centerLetter = name.substring(name.length() / 2,
-				(name.length() / 2) + 1);
-
-		int holoColors[] = { 0xFF1da9da, 0xFFb368d9, 0xFF83b600, 0xFFffa713,
-				0xFFe92727 };
-
-		int color = holoColors[centerLetter.charAt(0) % holoColors.length];
-
-		Bitmap bitmap = Bitmap
-				.createBitmap(size, size, Bitmap.Config.ARGB_8888);
-		Canvas canvas = new Canvas(bitmap);
-
-		bitmap.eraseColor(color);
-
-		Paint paint = new Paint();
-		paint.setColor(0xffe5e5e5);
-		paint.setTextSize((float) (size * 0.9));
-		paint.setAntiAlias(true);
-		Rect rect = new Rect();
-		paint.getTextBounds(firstLetter, 0, 1, rect);
-		float width = paint.measureText(firstLetter);
-		canvas.drawText(firstLetter, (size / 2) - (width / 2), (size / 2)
-				+ (rect.height() / 2), paint);
-
-		return bitmap;
-	}
-}

src/de/gultsch/chat/utils/UIHelper.java 🔗

@@ -0,0 +1,114 @@
+package de.gultsch.chat.utils;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import de.gultsch.chat.R;
+import de.gultsch.chat.entities.Conversation;
+import de.gultsch.chat.ui.ConversationActivity;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.preference.PreferenceManager;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.TaskStackBuilder;
+import android.util.DisplayMetrics;
+import android.util.Log;
+
+public class UIHelper {
+	public static String readableTimeDifference(long time) {
+		if (time == 0) {
+			return "just now";
+		}
+		Date date = new Date(time);
+		long difference = (System.currentTimeMillis() - time) / 1000;
+		if (difference < 60) {
+			return "just now";
+		} else if (difference < 60 * 10) {
+			return difference / 60 + " min ago";
+		} else if (difference < 60 * 60 * 24) {
+			SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
+			return sdf.format(date);
+		} else {
+			SimpleDateFormat sdf = new SimpleDateFormat("M/D");
+			return sdf.format(date);
+		}
+	}
+
+	public static Bitmap getUnknownContactPicture(String name, int size) {
+		String firstLetter = name.substring(0, 1).toUpperCase();
+		String centerLetter = name.substring(name.length() / 2,
+				(name.length() / 2) + 1);
+
+		int holoColors[] = { 0xFF1da9da, 0xFFb368d9, 0xFF83b600, 0xFFffa713,
+				0xFFe92727 };
+
+		int color = holoColors[centerLetter.charAt(0) % holoColors.length];
+
+		Bitmap bitmap = Bitmap
+				.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+		Canvas canvas = new Canvas(bitmap);
+
+		bitmap.eraseColor(color);
+
+		Paint paint = new Paint();
+		paint.setColor(0xffe5e5e5);
+		paint.setTextSize((float) (size * 0.9));
+		paint.setAntiAlias(true);
+		Rect rect = new Rect();
+		paint.getTextBounds(firstLetter, 0, 1, rect);
+		float width = paint.measureText(firstLetter);
+		canvas.drawText(firstLetter, (size / 2) - (width / 2), (size / 2)
+				+ (rect.height() / 2), paint);
+
+		return bitmap;
+	}
+	
+	public static Notification getUnreadMessageNotification(Context context, Conversation conversation) {
+		
+		SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
+		String ringtone = sharedPref.getString("notification_ringtone",null);
+		
+		Resources res = context.getResources();
+		NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context);
+		mBuilder.setLargeIcon(UIHelper.getUnknownContactPicture(conversation.getName(),(int) res.getDimension(android.R.dimen.notification_large_icon_width)));
+		mBuilder.setContentTitle(conversation.getName());
+		mBuilder.setContentText(conversation.getLatestMessage());
+		mBuilder.setSmallIcon(R.drawable.notification);
+		if (ringtone!=null) {
+			mBuilder.setSound(Uri.parse(ringtone));
+		}
+		
+		TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
+		stackBuilder.addParentStack(ConversationActivity.class);
+		
+		Intent viewConversationIntent = new Intent(context,ConversationActivity.class);
+		viewConversationIntent.setAction(Intent.ACTION_VIEW);
+		viewConversationIntent.putExtra(
+				ConversationActivity.CONVERSATION,
+				conversation.getUuid());
+		viewConversationIntent
+				.setType(ConversationActivity.VIEW_CONVERSATION);
+		
+		stackBuilder.addNextIntent(viewConversationIntent);
+		
+		PendingIntent resultPendingIntent =
+		        stackBuilder.getPendingIntent(
+		            0,
+		            PendingIntent.FLAG_UPDATE_CURRENT
+		        );
+
+		
+		mBuilder.setContentIntent(resultPendingIntent);
+		return mBuilder.build();
+	}
+}