From c82179c0b8728a9c2cd567d4227c60c758a1e682 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 20 Feb 2014 17:00:50 +0100 Subject: [PATCH] adding and removing roster items now possible. basic error display on error messages --- 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 + .../chat/services/XmppConnectionService.java | 53 +++++++--- .../gultsch/chat/ui/ConversationActivity.java | 23 ++++- .../gultsch/chat/ui/ConversationFragment.java | 97 +++++++++++-------- .../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(-) create mode 100644 res/layout/message_error.xml diff --git a/AndroidManifest.xml b/AndroidManifest.xml index f60af6324..28640fc70 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -22,7 +22,8 @@ + android:windowSoftInputMode="stateHidden" + android:configChanges="orientation|screenSize"> diff --git a/gen/de/gultsch/chat/R.java b/gen/de/gultsch/chat/R.java index 857311176..a1e79626e 100644 --- a/gen/de/gultsch/chat/R.java +++ b/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; diff --git a/res/layout/message_error.xml b/res/layout/message_error.xml new file mode 100644 index 000000000..410e1b050 --- /dev/null +++ b/res/layout/message_error.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/de/gultsch/chat/entities/Contact.java b/src/de/gultsch/chat/entities/Contact.java index c899603fc..eff46e53b 100644 --- a/src/de/gultsch/chat/entities/Contact.java +++ b/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; diff --git a/src/de/gultsch/chat/entities/Message.java b/src/de/gultsch/chat/entities/Message.java index 73a6e46cc..9b308c754 100644 --- a/src/de/gultsch/chat/entities/Message.java +++ b/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; diff --git a/src/de/gultsch/chat/services/XmppConnectionService.java b/src/de/gultsch/chat/services/XmppConnectionService.java index 7111d94a2..2bff2af42 100644 --- a/src/de/gultsch/chat/services/XmppConnectionService.java +++ b/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 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 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 getConversations() { if (this.conversations == null) { Hashtable accountLookupTable = new Hashtable(); @@ -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); + } } \ No newline at end of file diff --git a/src/de/gultsch/chat/ui/ConversationActivity.java b/src/de/gultsch/chat/ui/ConversationActivity.java index 464054ad9..f9a924ecf 100644 --- a/src/de/gultsch/chat/ui/ConversationActivity.java +++ b/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: diff --git a/src/de/gultsch/chat/ui/ConversationFragment.java b/src/de/gultsch/chat/ui/ConversationFragment.java index 0d2116e62..1770e7bd7 100644 --- a/src/de/gultsch/chat/ui/ConversationFragment.java +++ b/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(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 bitmaps = new HashMap(); + 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; + } } } diff --git a/src/de/gultsch/chat/ui/DialogContactDetails.java b/src/de/gultsch/chat/ui/DialogContactDetails.java index 88bded870..20be4b397 100644 --- a/src/de/gultsch/chat/ui/DialogContactDetails.java +++ b/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(); } } diff --git a/src/de/gultsch/chat/utils/MessageParser.java b/src/de/gultsch/chat/utils/MessageParser.java index aec492c84..1dca73432 100644 --- a/src/de/gultsch/chat/utils/MessageParser.java +++ b/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); + } } diff --git a/src/de/gultsch/chat/utils/UIHelper.java b/src/de/gultsch/chat/utils/UIHelper.java index 52292a0f4..5e81f0990 100644 --- a/src/de/gultsch/chat/utils/UIHelper.java +++ b/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) { diff --git a/src/de/gultsch/chat/xmpp/MessagePacket.java b/src/de/gultsch/chat/xmpp/MessagePacket.java index 0d4d07d7d..160a8c0ad 100644 --- a/src/de/gultsch/chat/xmpp/MessagePacket.java +++ b/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; }