From 2d0c0e6a4081955b68669c82071a5099e2e1a96e Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 10 Feb 2014 03:34:00 +0100 Subject: [PATCH] contact badges --- res/layout/dialog_contact_details.xml | 22 ++- src/de/gultsch/chat/entities/Contact.java | 20 +- .../chat/persistance/DatabaseBackend.java | 12 +- .../chat/services/XmppConnectionService.java | 186 ++++++++++-------- .../gultsch/chat/ui/ConversationActivity.java | 19 ++ .../gultsch/chat/ui/ConversationFragment.java | 3 +- .../gultsch/chat/ui/DialogContactDetails.java | 42 +++- .../utils/OnPhoneContactsLoadedListener.java | 9 + src/de/gultsch/chat/utils/PhoneHelper.java | 65 ++++++ src/de/gultsch/chat/utils/UIHelper.java | 87 +++++--- 10 files changed, 330 insertions(+), 135 deletions(-) create mode 100644 src/de/gultsch/chat/utils/OnPhoneContactsLoadedListener.java create mode 100644 src/de/gultsch/chat/utils/PhoneHelper.java diff --git a/res/layout/dialog_contact_details.xml b/res/layout/dialog_contact_details.xml index f52f7ba2d..00da4396f 100644 --- a/res/layout/dialog_contact_details.xml +++ b/res/layout/dialog_contact_details.xml @@ -7,23 +7,24 @@ - + android:scaleType="fitXY"/> + android:layout_alignParentTop="true" + android:paddingLeft="8dp"> @@ -51,8 +53,8 @@ android:textStyle="bold" android:gravity="center_vertical" android:layout_below="@+id/details_jidbox" - android:layout_toRightOf="@id/details_contact_picture" - android:paddingLeft="8dp"/> + android:layout_toRightOf="@id/details_contact_badge" + android:paddingLeft="16dp"/> getContacts(Account account) { List list = new ArrayList(); SQLiteDatabase db = this.getReadableDatabase(); - String args[] = {account.getUuid()}; - Cursor cursor = db.query(Contact.TABLENAME, null, Contact.ACCOUNT+"=?", args, null, - null, null); + Cursor cursor; + if (account==null) { + cursor = db.query(Contact.TABLENAME, null, null, null, null, + null, null); + } else { + String args[] = {account.getUuid()}; + cursor = db.query(Contact.TABLENAME, null, Contact.ACCOUNT+"=?", args, null, + null, null); + } while (cursor.moveToNext()) { list.add(Contact.fromCursor(cursor)); } diff --git a/src/de/gultsch/chat/services/XmppConnectionService.java b/src/de/gultsch/chat/services/XmppConnectionService.java index a1db28dde..55380c12c 100644 --- a/src/de/gultsch/chat/services/XmppConnectionService.java +++ b/src/de/gultsch/chat/services/XmppConnectionService.java @@ -13,6 +13,8 @@ import de.gultsch.chat.persistance.DatabaseBackend; import de.gultsch.chat.ui.OnAccountListChangedListener; import de.gultsch.chat.ui.OnConversationListChangedListener; import de.gultsch.chat.ui.OnRosterFetchedListener; +import de.gultsch.chat.utils.OnPhoneContactsLoadedListener; +import de.gultsch.chat.utils.PhoneHelper; import de.gultsch.chat.utils.UIHelper; import de.gultsch.chat.xml.Element; import de.gultsch.chat.xmpp.IqPacket; @@ -76,7 +78,7 @@ public class XmppConnectionService extends Service { status = Message.STATUS_SEND; } else { return; // massage has no body and is not carbon. just - // skip + // skip } if (forwarded != null) { Element message = forwarded.findChild("message"); @@ -258,98 +260,112 @@ public class XmppConnectionService extends Service { public void updateRoster(final Account account, final OnRosterFetchedListener listener) { - final Hashtable phoneContacts = new Hashtable(); - final List contacts = new ArrayList(); + PhoneHelper.loadPhoneContacts(this, + new OnPhoneContactsLoadedListener() { - final String[] PROJECTION = new String[] { - ContactsContract.Data.CONTACT_ID, - ContactsContract.Data.DISPLAY_NAME, - ContactsContract.Data.PHOTO_THUMBNAIL_URI, - ContactsContract.CommonDataKinds.Im.DATA }; + @Override + public void onPhoneContactsLoaded( + final Hashtable phoneContacts) { + IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET); + Element query = new Element("query"); + query.setAttribute("xmlns", "jabber:iq:roster"); + query.setAttribute("ver", ""); + iqPacket.addChild(query); + connections.get(account).sendIqPacket(iqPacket, + new OnIqPacketReceived() { - final String SELECTION = "(" + ContactsContract.Data.MIMETYPE + "=\"" - + ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE - + "\") AND (" + ContactsContract.CommonDataKinds.Im.PROTOCOL - + "=\"" + ContactsContract.CommonDataKinds.Im.PROTOCOL_JABBER - + "\")"; + @Override + public void onIqPacketReceived( + Account account, IqPacket packet) { + List contacts = new ArrayList(); + Element roster = packet + .findChild("query"); + if (roster != null) { + for (Element item : roster + .getChildren()) { + Contact contact; + String name = item + .getAttribute("name"); + String jid = item + .getAttribute("jid"); + if (phoneContacts + .containsKey(jid)) { + Bundle phoneContact = phoneContacts + .get(jid); + String systemAccount = phoneContact + .getInt("phoneid") + + "#" + + phoneContact + .getString("lookup"); + contact = new Contact( + account, + phoneContact + .getString("displayname"), + jid, + phoneContact + .getString("photouri")); + contact.setSystemAccount(systemAccount); + } else { + if (name == null) { + name = jid.split("@")[0]; + } + contact = new Contact( + account, name, jid, + null); - CursorLoader mCursorLoader = new CursorLoader(this, - ContactsContract.Data.CONTENT_URI, PROJECTION, SELECTION, null, - null); - mCursorLoader.registerListener(0, new OnLoadCompleteListener() { - - @Override - public void onLoadComplete(Loader arg0, Cursor cursor) { - while (cursor.moveToNext()) { - Bundle contact = new Bundle(); - contact.putInt("phoneid", cursor.getInt(cursor - .getColumnIndex(ContactsContract.Data.CONTACT_ID))); - contact.putString( - "displayname", - cursor.getString(cursor - .getColumnIndex(ContactsContract.Data.DISPLAY_NAME))); - contact.putString( - "photouri", - cursor.getString(cursor - .getColumnIndex(ContactsContract.Data.PHOTO_THUMBNAIL_URI))); - phoneContacts.put( - cursor.getString(cursor - .getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA)), - contact); - } - IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET); - Element query = new Element("query"); - query.setAttribute("xmlns", "jabber:iq:roster"); - query.setAttribute("ver", ""); - iqPacket.addChild(query); - connections.get(account).sendIqPacket(iqPacket, - new OnIqPacketReceived() { - - @Override - public void onIqPacketReceived(Account account, - IqPacket packet) { - Element roster = packet.findChild("query"); - if (roster != null) { - for (Element item : roster.getChildren()) { - Contact contact; - String name = item.getAttribute("name"); - String jid = item.getAttribute("jid"); - if (phoneContacts.containsKey(jid)) { - Bundle phoneContact = phoneContacts - .get(jid); - contact = new Contact( - account, - phoneContact - .getString("displayname"), - jid, - phoneContact - .getString("photouri")); - contact.setSystemAccount(phoneContact - .getInt("phoneid")); - } else { - if (name == null) { - name = jid.split("@")[0]; + } + contact.setAccount(account); + contact.setSubscription(item + .getAttribute("subscription")); + contacts.add(contact); + } + databaseBackend + .mergeContacts(contacts); + if (listener != null) { + listener.onRosterFetched(contacts); } - contact = new Contact(account, - name, jid, null); - } - contact.setAccount(account); - contact.setSubscription(item - .getAttribute("subscription")); - contacts.add(contact); - } - databaseBackend.mergeContacts(contacts); - if (listener != null) { - listener.onRosterFetched(contacts); } + }); + + } + }); + } + + public void mergePhoneContactsWithRoster() { + PhoneHelper.loadPhoneContacts(this, + new OnPhoneContactsLoadedListener() { + @Override + public void onPhoneContactsLoaded( + Hashtable phoneContacts) { + List contacts = databaseBackend + .getContacts(null); + for (int i = 0; i < contacts.size(); ++i) { + Contact contact = contacts.get(i); + if (phoneContacts.containsKey(contact.getJid())) { + Bundle phoneContact = phoneContacts.get(contact + .getJid()); + String systemAccount = phoneContact + .getInt("phoneid") + + "#" + + phoneContact.getString("lookup"); + contact.setSystemAccount(systemAccount); + contact.setPhotoUri(phoneContact + .getString("photouri")); + contact.setDisplayName(phoneContact + .getString("displayname")); + databaseBackend.updateContact(contact); + } else { + if ((contact.getSystemAccount() != null) + || (contact.getProfilePhoto() != null)) { + contact.setSystemAccount(null); + contact.setPhotoUri(null); + databaseBackend.updateContact(contact); } } - }); - - } - }); - mCursorLoader.startLoading(); + } + } + }); } public void addConversation(Conversation conversation) { diff --git a/src/de/gultsch/chat/ui/ConversationActivity.java b/src/de/gultsch/chat/ui/ConversationActivity.java index 37a988fba..c5c03b12c 100644 --- a/src/de/gultsch/chat/ui/ConversationActivity.java +++ b/src/de/gultsch/chat/ui/ConversationActivity.java @@ -37,6 +37,8 @@ public class ConversationActivity extends XmppActivity { public static final String VIEW_CONVERSATION = "viewConversation"; public static final String CONVERSATION = "conversationUuid"; + + public static final int INSERT_CONTACT = 0x9889; protected SlidingPaneLayout spl; @@ -83,6 +85,7 @@ public class ConversationActivity extends XmppActivity { }); } }; + private boolean contactInserted = false; public List getConversationList() { @@ -229,6 +232,8 @@ public class ConversationActivity extends XmppActivity { if (this.getSelectedConversation()!=null) { if (this.getSelectedConversation().getMode() == Conversation.MODE_MULTI) { ((MenuItem) menu.findItem(R.id.action_security)).setVisible(false); + ((MenuItem) menu.findItem(R.id.action_details)).setVisible(false); + ((MenuItem) menu.findItem(R.id.action_archive)).setTitle("Leave conference"); } } } @@ -322,6 +327,13 @@ public class ConversationActivity extends XmppActivity { @Override void onBackendConnected() { + + if (contactInserted) { + Log.d("xmppService","merge phone contacts with roster"); + contactInserted = false; + xmppConnectionService.mergePhoneContactsWithRoster(); + } + xmppConnectionService.setOnConversationListChangedListener(this.onConvChanged); if (conversationList.size()==0) { @@ -375,4 +387,11 @@ public class ConversationActivity extends XmppActivity { } } } + + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode==INSERT_CONTACT) { + Log.d("xmppService","contact inserted"); + this.contactInserted = true; + } + } } diff --git a/src/de/gultsch/chat/ui/ConversationFragment.java b/src/de/gultsch/chat/ui/ConversationFragment.java index 13e617fd7..204518696 100644 --- a/src/de/gultsch/chat/ui/ConversationFragment.java +++ b/src/de/gultsch/chat/ui/ConversationFragment.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; import de.gultsch.chat.R; +import de.gultsch.chat.entities.Contact; import de.gultsch.chat.entities.Conversation; import de.gultsch.chat.entities.Message; import de.gultsch.chat.utils.UIHelper; @@ -32,6 +33,7 @@ public class ConversationFragment extends Fragment { protected LayoutInflater inflater; protected List messageList = new ArrayList(); protected ArrayAdapter messageListAdapter; + protected Contact contact; @Override public View onCreateView(final LayoutInflater inflater, @@ -40,7 +42,6 @@ public class ConversationFragment extends Fragment { this.inflater = inflater; - final View view = inflater.inflate(R.layout.fragment_conversation, container, false); ((ImageButton) view.findViewById(R.id.textSendButton)) diff --git a/src/de/gultsch/chat/ui/DialogContactDetails.java b/src/de/gultsch/chat/ui/DialogContactDetails.java index 58e5f67fc..88bded870 100644 --- a/src/de/gultsch/chat/ui/DialogContactDetails.java +++ b/src/de/gultsch/chat/ui/DialogContactDetails.java @@ -7,12 +7,19 @@ import de.gultsch.chat.utils.UIHelper; import android.app.AlertDialog; 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; +import android.provider.ContactsContract.Intents; 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; public class DialogContactDetails extends DialogFragment { @@ -34,7 +41,8 @@ public class DialogContactDetails extends DialogFragment { TextView status = (TextView) view.findViewById(R.id.details_contactstatus); CheckBox send = (CheckBox) view.findViewById(R.id.details_send_presence); CheckBox receive = (CheckBox) view.findViewById(R.id.details_receive_presence); - ImageView contactPhoto = (ImageView) view.findViewById(R.id.details_contact_picture); + //ImageView contactPhoto = (ImageView) view.findViewById(R.id.details_contact_picture); + QuickContactBadge badge = (QuickContactBadge) view.findViewById(R.id.details_contact_badge); boolean subscriptionSend = false; boolean subscriptionReceive = false; @@ -84,11 +92,35 @@ public class DialogContactDetails extends DialogFragment { receive.setChecked(subscriptionReceive); contactJid.setText(contact.getJid()); accountJid.setText(contact.getAccount().getJid()); + + UIHelper.prepareContactBadge(getActivity(), badge, contact); - if (contact.getProfilePhoto()!=null) { - contactPhoto.setImageURI(Uri.parse(contact.getProfilePhoto())); - } else { - contactPhoto.setImageBitmap(UIHelper.getUnknownContactPicture(contact.getDisplayName(), 300)); + 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.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.create().show(); + } + }); } builder.setView(view); diff --git a/src/de/gultsch/chat/utils/OnPhoneContactsLoadedListener.java b/src/de/gultsch/chat/utils/OnPhoneContactsLoadedListener.java new file mode 100644 index 000000000..c4df4ac30 --- /dev/null +++ b/src/de/gultsch/chat/utils/OnPhoneContactsLoadedListener.java @@ -0,0 +1,9 @@ +package de.gultsch.chat.utils; + +import java.util.Hashtable; + +import android.os.Bundle; + +public interface OnPhoneContactsLoadedListener { + public void onPhoneContactsLoaded(Hashtable phoneContacts); +} diff --git a/src/de/gultsch/chat/utils/PhoneHelper.java b/src/de/gultsch/chat/utils/PhoneHelper.java new file mode 100644 index 000000000..f1d491d6f --- /dev/null +++ b/src/de/gultsch/chat/utils/PhoneHelper.java @@ -0,0 +1,65 @@ +package de.gultsch.chat.utils; + +import java.util.ArrayList; +import java.util.Hashtable; + +import android.content.Context; +import android.content.CursorLoader; +import android.content.Loader; +import android.content.Loader.OnLoadCompleteListener; +import android.database.Cursor; +import android.os.Bundle; +import android.provider.ContactsContract; + +public class PhoneHelper { + + public static void loadPhoneContacts(Context context, final OnPhoneContactsLoadedListener listener) { + final Hashtable phoneContacts = new Hashtable(); + + final String[] PROJECTION = new String[] { + ContactsContract.Data._ID, + ContactsContract.Data.DISPLAY_NAME, + ContactsContract.Data.PHOTO_THUMBNAIL_URI, + ContactsContract.Data.LOOKUP_KEY, + ContactsContract.CommonDataKinds.Im.DATA }; + + final String SELECTION = "(" + ContactsContract.Data.MIMETYPE + "=\"" + + ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE + + "\") AND (" + ContactsContract.CommonDataKinds.Im.PROTOCOL + + "=\"" + ContactsContract.CommonDataKinds.Im.PROTOCOL_JABBER + + "\")"; + + CursorLoader mCursorLoader = new CursorLoader(context, + ContactsContract.Data.CONTENT_URI, PROJECTION, SELECTION, null, + null); + mCursorLoader.registerListener(0, new OnLoadCompleteListener() { + + @Override + public void onLoadComplete(Loader arg0, Cursor cursor) { + while (cursor.moveToNext()) { + Bundle contact = new Bundle(); + contact.putInt("phoneid", cursor.getInt(cursor + .getColumnIndex(ContactsContract.Data._ID))); + contact.putString( + "displayname", + cursor.getString(cursor + .getColumnIndex(ContactsContract.Data.DISPLAY_NAME))); + contact.putString( + "photouri", + cursor.getString(cursor + .getColumnIndex(ContactsContract.Data.PHOTO_THUMBNAIL_URI))); + contact.putString("lookup",cursor.getString(cursor + .getColumnIndex(ContactsContract.Data.LOOKUP_KEY))); + phoneContacts.put( + cursor.getString(cursor + .getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA)), + contact); + } + if (listener!=null) { + listener.onPhoneContactsLoaded(phoneContacts); + } + } + }); + mCursorLoader.startLoading(); + } +} diff --git a/src/de/gultsch/chat/utils/UIHelper.java b/src/de/gultsch/chat/utils/UIHelper.java index b7ea5289e..75c916825 100644 --- a/src/de/gultsch/chat/utils/UIHelper.java +++ b/src/de/gultsch/chat/utils/UIHelper.java @@ -1,28 +1,43 @@ package de.gultsch.chat.utils; +import java.io.FileDescriptor; +import java.io.FileNotFoundException; import java.text.SimpleDateFormat; import java.util.Date; import de.gultsch.chat.R; +import de.gultsch.chat.entities.Contact; import de.gultsch.chat.entities.Conversation; import de.gultsch.chat.ui.ConversationActivity; +import android.app.Activity; +import android.app.AlertDialog; import android.app.Notification; import android.app.PendingIntent; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; +import android.content.res.AssetFileDescriptor; import android.content.res.Resources; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.net.Uri; import android.preference.PreferenceManager; +import android.provider.ContactsContract; +import android.provider.ContactsContract.CommonDataKinds; +import android.provider.ContactsContract.Contacts; +import android.provider.ContactsContract.Intents; import android.support.v4.app.NotificationCompat; import android.support.v4.app.TaskStackBuilder; import android.util.DisplayMetrics; import android.util.Log; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.QuickContactBadge; public class UIHelper { public static String readableTimeDifference(long time) { @@ -70,44 +85,62 @@ public class UIHelper { return bitmap; } - - public static Notification getUnreadMessageNotification(Context context, Conversation conversation) { - - SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context); - String ringtone = sharedPref.getString("notification_ringtone",null); - + + 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))); + 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); mBuilder.setLights(0xffffffff, 2000, 4000); - if (ringtone!=null) { + 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 - ); - + 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(); } + + public static void prepareContactBadge(final Activity activity, + QuickContactBadge badge, final Contact contact) { + if (contact.getSystemAccount()!=null) { + String[] systemAccount = contact.getSystemAccount().split("#"); + long id = Long.parseLong(systemAccount[0]); + badge.assignContactUri(Contacts.getLookupUri(id, systemAccount[1])); + + if (contact.getProfilePhoto() != null) { + badge.setImageURI(Uri.parse(contact.getProfilePhoto())); + } else { + badge.setImageBitmap(UIHelper.getUnknownContactPicture(contact.getDisplayName(), 400)); + } + } else { + badge.setImageBitmap(UIHelper.getUnknownContactPicture(contact.getDisplayName(), 400)); + } + + } }