adding and removing roster items now possible. basic error display on error messages

Daniel Gultsch created

Change summary

AndroidManifest.xml                                     |  3 
gen/de/gultsch/chat/R.java                              |  5 
res/layout/message_error.xml                            | 54 ++++++
src/de/gultsch/chat/entities/Contact.java               |  1 
src/de/gultsch/chat/entities/Message.java               |  1 
src/de/gultsch/chat/services/XmppConnectionService.java | 53 ++++-
src/de/gultsch/chat/ui/ConversationActivity.java        | 23 ++
src/de/gultsch/chat/ui/ConversationFragment.java        | 97 ++++++----
src/de/gultsch/chat/ui/DialogContactDetails.java        | 65 +++++--
src/de/gultsch/chat/utils/MessageParser.java            | 15 +
src/de/gultsch/chat/utils/UIHelper.java                 | 22 ++
src/de/gultsch/chat/xmpp/MessagePacket.java             |  3 
12 files changed, 263 insertions(+), 79 deletions(-)

Detailed changes

AndroidManifest.xml 🔗

@@ -22,7 +22,8 @@
         <activity
             android:name="de.gultsch.chat.ui.ConversationActivity"
             android:label="Secure Conversations"
-            android:windowSoftInputMode="stateHidden">
+            android:windowSoftInputMode="stateHidden"
+            android:configChanges="orientation|screenSize">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />

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

@@ -111,8 +111,9 @@ public final class R {
         public static final int fragment_conversation=0x7f030007;
         public static final int fragment_conversations_overview=0x7f030008;
         public static final int manage_accounts=0x7f030009;
-        public static final int message_recieved=0x7f03000a;
-        public static final int message_sent=0x7f03000b;
+        public static final int message_error=0x7f03000a;
+        public static final int message_recieved=0x7f03000b;
+        public static final int message_sent=0x7f03000c;
     }
     public static final class menu {
         public static final int conversations=0x7f090000;

res/layout/message_error.xml 🔗

@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:padding="8dp">
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:background="@drawable/message_border"
+        android:layout_toRightOf="@+id/message_photo"
+     	android:layout_alignParentBottom="true"
+     	android:minHeight="48dp"
+     	>
+<LinearLayout
+    android:layout_width="wrap_content"
+    android:layout_height="fill_parent"
+    android:orientation="vertical"
+    android:background="#ededed"
+    android:padding="5dp">   
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Username is already in use"
+        android:textSize="16sp"
+        android:id="@+id/message_body"
+        android:textColor="#e92727"
+        android:textStyle="bold"
+        android:typeface="monospace"/>
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingTop="1dp"
+        android:text="@string/sending"
+        android:textColor="#8e8e8e"
+        android:textSize="12sp"
+        android:id="@+id/message_time"/>
+
+	</LinearLayout>
+</LinearLayout>
+    <ImageView
+        android:id="@+id/message_photo"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentLeft="true"
+        android:layout_marginRight="-1.5dp"
+        android:padding="0dp"
+        android:src="@drawable/ic_profile"
+        android:scaleType="fitXY"/>
+	
+</RelativeLayout>

src/de/gultsch/chat/entities/Contact.java 🔗

@@ -44,6 +44,7 @@ public class Contact extends AbstractEntity implements Serializable {
 		} else {
 			this.accountUuid = account.getUuid();
 		}
+		this.account = account;
 		this.displayName = displayName;
 		this.jid = jid;
 		this.photoUri = photoUri;

src/de/gultsch/chat/entities/Message.java 🔗

@@ -12,6 +12,7 @@ public class Message extends AbstractEntity {
 	public static final int STATUS_RECIEVED = 0;
 	public static final int STATUS_UNSEND = 1;
 	public static final int STATUS_SEND = 2;
+	public static final int STATUS_ERROR = 3;
 
 	public static final int ENCRYPTION_NONE = 0;
 	public static final int ENCRYPTION_PGP = 1;

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

@@ -103,6 +103,8 @@ public class XmppConnectionService extends Service {
 				if (message != null) {
 					notify = (message.getStatus() == Message.STATUS_RECIEVED);
 				}
+			} else if (packet.getType() == MessagePacket.TYPE_ERROR) {
+				message = MessageParser.parseError(packet,account,service);
 			} else {
 				Log.d(LOGTAG, "unparsed message " + packet.toString());
 			}
@@ -126,7 +128,9 @@ public class XmppConnectionService extends Service {
 			}
 			Conversation conversation = message.getConversation();
 			conversation.getMessages().add(message);
-			databaseBackend.createMessage(message);
+			if (packet.getType() != MessagePacket.TYPE_ERROR) {
+				databaseBackend.createMessage(message);
+			}
 			if (convChangedListener != null) {
 				convChangedListener.onConversationListChanged();
 			} else {
@@ -171,7 +175,7 @@ public class XmppConnectionService extends Service {
 			Contact contact = findContact(account, fromParts[0]);
 			if (contact == null) {
 				// most likely muc, self or roster not synced
-				// Log.d(LOGTAG,"got presence for non contact "+packet.toString());
+				Log.d(LOGTAG,"got presence for non contact "+packet.toString());
 				return;
 			}
 			String type = packet.getAttribute("type");
@@ -197,7 +201,7 @@ public class XmppConnectionService extends Service {
 					databaseBackend.updateContact(contact);
 				}
 			}
-			replaceContactInConversation(contact);
+			replaceContactInConversation(contact.getJid(),contact);
 		}
 	};
 
@@ -233,21 +237,21 @@ public class XmppConnectionService extends Service {
 				} else {
 					if (subscription.equals("remove")) {
 						databaseBackend.deleteContact(contact);
+						replaceContactInConversation(contact.getJid(), null);
 					} else {
 						contact.setSubscription(subscription);
 						databaseBackend.updateContact(contact);
-						replaceContactInConversation(contact);
+						replaceContactInConversation(contact.getJid(),contact);
 					}
 				}
 			}
 		}
 	}
 
-	private void replaceContactInConversation(Contact contact) {
+	private void replaceContactInConversation(String jid, Contact contact) {
 		List<Conversation> conversations = getConversations();
 		for (int i = 0; i < conversations.size(); ++i) {
-			if ((conversations.get(i).getContact() != null)
-					&& (conversations.get(i).getContact().equals(contact))) {
+			if ((conversations.get(i).getContactJid().equals(jid))) {
 				conversations.get(i).setContact(contact);
 				break;
 			}
@@ -458,6 +462,7 @@ public class XmppConnectionService extends Service {
 								List<Contact> contactsToDelete = databaseBackend.getContats(mWhere.toString());
 								for(Contact contact : contactsToDelete) {
 									databaseBackend.deleteContact(contact);
+									replaceContactInConversation(contact.getJid(), null);
 								}
 							}
 							mergePhoneContactsWithRoster(new OnPhoneContactsMerged() {
@@ -517,10 +522,6 @@ public class XmppConnectionService extends Service {
 				});
 	}
 
-	public void addConversation(Conversation conversation) {
-		databaseBackend.createConversation(conversation);
-	}
-
 	public List<Conversation> getConversations() {
 		if (this.conversations == null) {
 			Hashtable<String, Account> accountLookupTable = new Hashtable<String, Account>();
@@ -629,6 +630,20 @@ public class XmppConnectionService extends Service {
 		if (accountChangedListener != null)
 			accountChangedListener.onAccountListChangedListener();
 	}
+	
+	public void deleteContact(Contact contact) {
+		IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
+		Element query = new Element("query");
+		query.setAttribute("xmlns", "jabber:iq:roster");
+		Element item = new Element("item");
+		item.setAttribute("jid", contact.getJid());
+		item.setAttribute("subscription", "remove");
+		query.addChild(item);
+		iq.addChild(query);
+		contact.getAccount().getXmppConnection().sendIqPacket(iq, null);
+		replaceContactInConversation(contact.getJid(), null);
+		databaseBackend.deleteContact(contact);
+	}
 
 	public void updateAccount(Account account) {
 		databaseBackend.updateAccount(account);
@@ -735,4 +750,20 @@ public class XmppConnectionService extends Service {
 	public void updateContact(Contact contact) {
 		databaseBackend.updateContact(contact);
 	}
+
+	public void createContact(Contact contact) {
+		IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
+		Element query = new Element("query");
+		query.setAttribute("xmlns", "jabber:iq:roster");
+		Element item = new Element("item");
+		item.setAttribute("jid", contact.getJid());
+		item.setAttribute("name", contact.getJid());
+		query.addChild(item);
+		iq.addChild(query);
+		Account account = contact.getAccount();
+		Log.d(LOGTAG,account.getJid()+": adding "+contact.getJid()+" to roster");
+		account.getXmppConnection().sendIqPacket(iq, null);
+		replaceContactInConversation(contact.getJid(), contact);
+		databaseBackend.createContact(contact);
+	}
 }

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

@@ -7,15 +7,18 @@ import java.util.List;
 
 import de.gultsch.chat.R;
 import de.gultsch.chat.R.id;
+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.utils.UIHelper;
 import android.net.Uri;
 import android.os.Bundle;
+import android.app.AlertDialog;
 import android.app.FragmentTransaction;
 import android.app.NotificationManager;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.content.Intent;
 import android.graphics.Typeface;
 import android.support.v4.widget.SlidingPaneLayout;
@@ -82,6 +85,18 @@ public class ConversationActivity extends XmppActivity {
 			});
 		}
 	};
+	
+	private DialogInterface.OnClickListener addToRoster = new DialogInterface.OnClickListener() {
+		
+		@Override
+		public void onClick(DialogInterface dialog, int which) {
+			String jid = getSelectedConversation().getContactJid();
+			Account account = getSelectedConversation().getAccount();
+			String name = jid.split("@")[0];
+			Contact contact = new Contact(account, name, jid, null);
+			xmppConnectionService.createContact(contact);
+		}
+	};
 	private boolean contactInserted = false;
 	
 	
@@ -288,7 +303,13 @@ public class ConversationActivity extends XmppActivity {
 				details.setContact(contact);
 				details.show(getFragmentManager(), "details");
 			} else {
-				Log.d("xmppService","contact was null - means not in roster");
+				String jid = getSelectedConversation().getContactJid();
+				AlertDialog.Builder builder = new AlertDialog.Builder(this);
+				builder.setTitle(jid);
+				builder.setMessage("The contact is not in your roster. Would you like to add it.");
+				builder.setNegativeButton("Cancel", null);
+				builder.setPositiveButton("Add",addToRoster);
+				builder.create().show();
 			}
 			break;
 		case R.id.action_security:

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

@@ -8,6 +8,8 @@ import java.util.Hashtable;
 import java.util.List;
 import java.util.Set;
 
+import javax.crypto.spec.PSource;
+
 import net.java.otr4j.OtrException;
 import net.java.otr4j.session.SessionStatus;
 
@@ -53,6 +55,8 @@ public class ConversationFragment extends Fragment {
 	protected BitmapCache mBitmapCache = new BitmapCache();
 
 	private EditText chatMsg;
+	
+	protected Bitmap selfBitmap;
 
 	private OnClickListener sendMsgListener = new OnClickListener() {
 
@@ -105,47 +109,26 @@ public class ConversationFragment extends Fragment {
 		sendButton.setOnClickListener(this.sendMsgListener);
 
 		messagesView = (ListView) view.findViewById(R.id.messages_view);
-
-		SharedPreferences sharedPref = PreferenceManager
-				.getDefaultSharedPreferences(getActivity()
-						.getApplicationContext());
-		boolean showPhoneSelfContactPicture = sharedPref.getBoolean(
-				"show_phone_selfcontact_picture", true);
-
-		Bitmap self;
-
-		if (showPhoneSelfContactPicture) {
-			Uri selfiUri = PhoneHelper.getSefliUri(getActivity());
-			try {
-				self = BitmapFactory.decodeStream(getActivity()
-						.getContentResolver().openInputStream(selfiUri));
-			} catch (FileNotFoundException e) {
-				self = UIHelper.getUnknownContactPicture(conversation
-						.getAccount().getJid(), 200);
-			}
-		} else {
-			self = UIHelper.getUnknownContactPicture(conversation.getAccount()
-					.getJid(), 200);
-		}
-
-		final Bitmap selfBitmap = self;
-
+		
 		messageListAdapter = new ArrayAdapter<Message>(this.getActivity()
 				.getApplicationContext(), R.layout.message_sent,
 				this.messageList) {
 
 			private static final int SENT = 0;
 			private static final int RECIEVED = 1;
+			private static final int ERROR = 2;
 
 			@Override
 			public int getViewTypeCount() {
-				return 2;
+				return 3;
 			}
 
 			@Override
 			public int getItemViewType(int position) {
 				if (getItem(position).getStatus() == Message.STATUS_RECIEVED) {
 					return RECIEVED;
+				} else if (getItem(position).getStatus() == Message.STATUS_ERROR) {
+					return ERROR;
 				} else {
 					return SENT;
 				}
@@ -167,7 +150,6 @@ public class ConversationFragment extends Fragment {
 						viewHolder.imageView.setImageBitmap(selfBitmap);
 						break;
 					case RECIEVED:
-						viewHolder = new ViewHolder();
 						view = (View) inflater.inflate(
 								R.layout.message_recieved, null);
 						viewHolder.imageView = (ImageView) view
@@ -185,6 +167,12 @@ public class ConversationFragment extends Fragment {
 							}
 						}
 						break;
+					case ERROR:
+						view = (View) inflater.inflate(R.layout.message_error, null);
+						viewHolder.imageView = (ImageView) view
+								.findViewById(R.id.message_photo);
+						viewHolder.imageView.setImageBitmap(mBitmapCache.getError());
+						break;
 					default:
 						viewHolder = null;
 						break;
@@ -193,7 +181,6 @@ public class ConversationFragment extends Fragment {
 							.findViewById(R.id.message_body);
 					viewHolder.time = (TextView) view
 							.findViewById(R.id.message_time);
-					
 					view.setTag(viewHolder);
 				} else {
 					viewHolder = (ViewHolder) view.getTag();
@@ -238,31 +225,47 @@ public class ConversationFragment extends Fragment {
 		return view;
 	}
 
+	protected Bitmap findSelfPicture() {
+		SharedPreferences sharedPref = PreferenceManager
+				.getDefaultSharedPreferences(getActivity()
+						.getApplicationContext());
+		boolean showPhoneSelfContactPicture = sharedPref.getBoolean(
+				"show_phone_selfcontact_picture", true);
+
+		Bitmap self;
+
+		if (showPhoneSelfContactPicture) {
+			Uri selfiUri = PhoneHelper.getSefliUri(getActivity());
+			try {
+				self = BitmapFactory.decodeStream(getActivity()
+						.getContentResolver().openInputStream(selfiUri));
+			} catch (FileNotFoundException e) {
+				self = UIHelper.getUnknownContactPicture(conversation
+						.getAccount().getJid(), 200);
+			}
+		} else {
+			self = UIHelper.getUnknownContactPicture(conversation.getAccount()
+					.getJid(), 200);
+		}
+
+		final Bitmap selfBitmap = self;
+		return selfBitmap;
+	}
+
 	@Override
 	public void onStart() {
 		super.onStart();
-		final ConversationActivity activity = (ConversationActivity) getActivity();
+		ConversationActivity activity = (ConversationActivity) getActivity();
 
 		if (activity.xmppConnectionServiceBound) {
-			this.conversation = activity.getSelectedConversation();
-			updateMessages();
-			// rendering complete. now go tell activity to close pane
-			if (!activity.shouldPaneBeOpen()) {
-				activity.getSlidingPaneLayout().closePane();
-				activity.getActionBar().setDisplayHomeAsUpEnabled(true);
-				activity.getActionBar().setTitle(conversation.getName());
-				activity.invalidateOptionsMenu();
-				if (!conversation.isRead()) {
-					conversation.markRead();
-					activity.updateConversationList();
-				}
-			}
+			this.onBackendConnected();
 		}
 	}
 
 	public void onBackendConnected() {
 		final ConversationActivity activity = (ConversationActivity) getActivity();
 		this.conversation = activity.getSelectedConversation();
+		this.selfBitmap = findSelfPicture();
 		updateMessages();
 		// rendering complete. now go tell activity to close pane
 		if (!activity.shouldPaneBeOpen()) {
@@ -353,7 +356,7 @@ public class ConversationFragment extends Fragment {
 			} else {
 				presences = null;
 			}
-			if ((presences != null) && (presences.size() == 0)) {
+			if ((presences == null) || (presences.size() == 0)) {
 				AlertDialog.Builder builder = new AlertDialog.Builder(
 						getActivity());
 				builder.setTitle("Contact is offline");
@@ -412,6 +415,7 @@ public class ConversationFragment extends Fragment {
 
 	private class BitmapCache {
 		private HashMap<String, Bitmap> bitmaps = new HashMap<String, Bitmap>();
+		private Bitmap error = null;
 
 		public Bitmap get(String name, Uri uri) {
 			if (bitmaps.containsKey(name)) {
@@ -432,5 +436,12 @@ public class ConversationFragment extends Fragment {
 				return bm;
 			}
 		}
+		
+		public Bitmap getError() {
+			if (error == null) {
+				error = UIHelper.getErrorPicture(200);
+			}
+			return error;
+		}
 	}
 }

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

@@ -9,7 +9,6 @@ import android.app.Dialog;
 import android.app.DialogFragment;
 import android.content.DialogInterface;
 import android.content.Intent;
-import android.net.Uri;
 import android.os.Bundle;
 import android.provider.ContactsContract.CommonDataKinds;
 import android.provider.ContactsContract.Contacts;
@@ -18,7 +17,6 @@ import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.CheckBox;
-import android.widget.ImageView;
 import android.widget.QuickContactBadge;
 import android.widget.TextView;
 
@@ -27,13 +25,53 @@ public class DialogContactDetails extends DialogFragment {
 	private Contact contact = null;
 	boolean displayingInRoster = false;
 	
+	private DialogContactDetails mDetailsDialog = this;
+	private XmppActivity activity;
+	
+	private DialogInterface.OnClickListener askRemoveFromRoster = new DialogInterface.OnClickListener() {
+		
+		@Override
+		public void onClick(DialogInterface dialog, int which) {
+			AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+			builder.setTitle("Delete from roster");
+			builder.setMessage("Do you want to delete "+contact.getJid()+" from your roster. The conversation assoziated with this account will not be removed.");
+			builder.setNegativeButton("Cancel", null);
+			builder.setPositiveButton("Delete",removeFromRoster);
+			builder.create().show();
+		}
+	};
+	
+	private DialogInterface.OnClickListener removeFromRoster = new DialogInterface.OnClickListener() {
+		
+		@Override
+		public void onClick(DialogInterface dialog, int which) {
+			activity.xmppConnectionService.deleteContact(contact);
+			mDetailsDialog.dismiss();
+		}
+	};
+	
+	private DialogInterface.OnClickListener addToPhonebook = new DialogInterface.OnClickListener() {
+		
+		@Override
+		public void onClick(DialogInterface dialog, int which) {
+			Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
+			intent.setType(Contacts.CONTENT_ITEM_TYPE);
+			intent.putExtra(Intents.Insert.IM_HANDLE,contact.getJid());
+			intent.putExtra(Intents.Insert.IM_PROTOCOL,CommonDataKinds.Im.PROTOCOL_JABBER);
+			intent.putExtra("finishActivityOnSaveCompleted", true);
+			getActivity().startActivityForResult(intent,ConversationActivity.INSERT_CONTACT);
+			mDetailsDialog.dismiss();
+		}
+	};
+	
 	public void setContact(Contact contact) {
 		this.contact = contact;
 	}
 	
 	@Override
 	public Dialog onCreateDialog(Bundle savedInstanceState) {
-		final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+		this.activity = (XmppActivity) getActivity();
+		AlertDialog.Builder builder = new AlertDialog.Builder(this.activity);
 		LayoutInflater inflater = getActivity().getLayoutInflater();
 		View view = inflater.inflate(R.layout.dialog_contact_details, null);
 		TextView contactJid = (TextView) view.findViewById(R.id.details_contactjid);
@@ -96,28 +134,15 @@ public class DialogContactDetails extends DialogFragment {
 		UIHelper.prepareContactBadge(getActivity(), badge, contact);
 		
 		if (contact.getSystemAccount()==null) {
-			final DialogContactDetails details = this;
 			badge.setOnClickListener(new OnClickListener() {
 				
 				@Override
 				public void onClick(View v) {
 					AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
-					builder.setTitle("Add to contacts");
-					builder.setMessage("Do you want to add "+contact.getJid()+" to your contact list?");
+					builder.setTitle("Add to phone book");
+					builder.setMessage("Do you want to add "+contact.getJid()+" to your phones contact list?");
 					builder.setNegativeButton("Cancel", null);
-					builder.setPositiveButton("Add", new DialogInterface.OnClickListener() {
-						
-						@Override
-						public void onClick(DialogInterface dialog, int which) {
-							Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
-							intent.setType(Contacts.CONTENT_ITEM_TYPE);
-							intent.putExtra(Intents.Insert.IM_HANDLE,contact.getJid());
-							intent.putExtra(Intents.Insert.IM_PROTOCOL,CommonDataKinds.Im.PROTOCOL_JABBER);
-							intent.putExtra("finishActivityOnSaveCompleted", true);
-							getActivity().startActivityForResult(intent,ConversationActivity.INSERT_CONTACT);
-							details.dismiss();
-						}
-					});
+					builder.setPositiveButton("Add",addToPhonebook);
 					builder.create().show();
 				}
 			});
@@ -127,7 +152,7 @@ public class DialogContactDetails extends DialogFragment {
 		builder.setTitle(contact.getDisplayName());
 		
 		builder.setNeutralButton("Done", null);
-		builder.setPositiveButton("Remove from roster", null);
+		builder.setPositiveButton("Remove from roster", this.askRemoveFromRoster);
 		return builder.create();
 	}
 }

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

@@ -116,4 +116,19 @@ public class MessageParser {
 		Conversation conversation = service.findOrCreateConversation(account, parts[0],false);
 		return new Message(conversation,fullJid, message.findChild("body").getContent(), Message.ENCRYPTION_NONE,status);
 	}
+
+	public static Message parseError(MessagePacket packet, Account account, XmppConnectionService service) {
+		
+			String[] fromParts = packet.getFrom().split("/");
+	Conversation conversation = service.findOrCreateConversation(account, fromParts[0],false);
+	Element error = packet.findChild("error");
+	String errorName = error.getChildren().get(0).getName();
+	String displayError;
+	if (errorName.equals("service-unavailable")) {
+		displayError = "Contact is offline and does not have offline storage";
+	} else {
+		displayError = errorName.replace("-", " ");
+	}
+	return new Message(conversation, packet.getFrom(), displayError, Message.ENCRYPTION_NONE, Message.STATUS_ERROR);
+	}
 }

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

@@ -54,7 +54,7 @@ public class UIHelper {
 			SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
 			return sdf.format(date);
 		} else {
-			SimpleDateFormat sdf = new SimpleDateFormat("M/D");
+			SimpleDateFormat sdf = new SimpleDateFormat("MM/dd");
 			return sdf.format(date);
 		}
 	}
@@ -85,6 +85,26 @@ public class UIHelper {
 
 		return bitmap;
 	}
+	
+	public static Bitmap getErrorPicture(int size) {
+		Bitmap bitmap = Bitmap
+				.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+		Canvas canvas = new Canvas(bitmap);
+
+		bitmap.eraseColor(0xFFe92727);
+
+		Paint paint = new Paint();
+		paint.setColor(0xffe5e5e5);
+		paint.setTextSize((float) (size * 0.9));
+		paint.setAntiAlias(true);
+		Rect rect = new Rect();
+		paint.getTextBounds("!", 0, 1, rect);
+		float width = paint.measureText("!");
+		canvas.drawText("!", (size / 2) - (width / 2), (size / 2)
+				+ (rect.height() / 2), paint);
+
+		return bitmap;
+	}
 
 	public static Notification getUnreadMessageNotification(Context context,
 			Conversation conversation) {

src/de/gultsch/chat/xmpp/MessagePacket.java 🔗

@@ -7,6 +7,7 @@ public class MessagePacket extends Element {
 	public static final int TYPE_UNKNOWN = 1;
 	public static final int TYPE_NO = 2;
 	public static final int TYPE_GROUPCHAT = 3;
+	public static final int TYPE_ERROR = 4;
 
 	private MessagePacket(String name) {
 		super(name);
@@ -71,6 +72,8 @@ public class MessagePacket extends Element {
 			return TYPE_CHAT;
 		} else if (type.equals("groupchat")) {
 			return TYPE_GROUPCHAT;
+		} else if (type.equals("error")) {
+			return TYPE_ERROR;
 		} else {
 			return TYPE_UNKNOWN;
 		}