contact badges

This commit is contained in:
Daniel Gultsch 2014-02-10 03:34:00 +01:00
parent c6440aab12
commit 2d0c0e6a40
10 changed files with 330 additions and 135 deletions

View file

@ -7,23 +7,24 @@
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="90dp"
android:layout_height="72dp"
android:padding="0dp">
<ImageView
android:id="@+id/details_contact_picture"
android:layout_width="96dp"
android:layout_height="96dp"
<QuickContactBadge
android:id="@+id/details_contact_badge"
android:layout_width="72dp"
android:layout_height="72dp"
android:layout_centerVertical="true"
android:paddingRight="8dp"/>
android:scaleType="fitXY"/>
<LinearLayout
android:id="@+id/details_jidbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/details_contact_picture"
android:layout_toRightOf="@+id/details_contact_badge"
android:orientation="vertical"
android:layout_alignParentTop="true">
android:layout_alignParentTop="true"
android:paddingLeft="8dp">
<TextView
android:layout_width="wrap_content"
@ -37,6 +38,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="something@jabber.example.com"
android:singleLine="true"
android:textSize="14sp"
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"/>
</RelativeLayout>
<TextView

View file

@ -24,7 +24,7 @@ public class Contact extends AbstractEntity implements Serializable {
protected String displayName;
protected String jid;
protected String subscription;
protected int systemAccount;
protected String systemAccount;
protected String photoUri;
protected String openPGPKey;
protected Presences presences = new Presences();
@ -44,7 +44,7 @@ public class Contact extends AbstractEntity implements Serializable {
}
public Contact(String uuid, String account, String displayName, String jid,
String subscription, String photoUri, int systemAccount,
String subscription, String photoUri, String systemAccount,
String pgpKey,String presences) {
this.uuid = uuid;
this.accountUuid = account;
@ -96,7 +96,7 @@ public class Contact extends AbstractEntity implements Serializable {
cursor.getString(cursor.getColumnIndex(JID)),
cursor.getString(cursor.getColumnIndex(SUBSCRIPTION)),
cursor.getString(cursor.getColumnIndex(PHOTOURI)),
cursor.getInt(cursor.getColumnIndex(SYSTEMACCOUNT)),
cursor.getString(cursor.getColumnIndex(SYSTEMACCOUNT)),
cursor.getString(cursor.getColumnIndex(OPENPGPKEY)),
cursor.getString(cursor.getColumnIndex(PRESENCES)));
}
@ -109,7 +109,7 @@ public class Contact extends AbstractEntity implements Serializable {
return this.subscription;
}
public void setSystemAccount(int account) {
public void setSystemAccount(String account) {
this.systemAccount = account;
}
@ -161,4 +161,16 @@ public class Contact extends AbstractEntity implements Serializable {
public void setPresences(Presences pres) {
this.presences = pres;
}
public void setPhotoUri(String uri) {
this.photoUri = uri;
}
public void setDisplayName(String name) {
this.displayName = name;
}
public String getSystemAccount() {
return systemAccount;
}
}

View file

@ -233,9 +233,15 @@ public class DatabaseBackend extends SQLiteOpenHelper {
public List<Contact> getContacts(Account account) {
List<Contact> list = new ArrayList<Contact>();
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));
}

View file

@ -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<String, Bundle> phoneContacts = new Hashtable<String, Bundle>();
final List<Contact> contacts = new ArrayList<Contact>();
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<String, Bundle> 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<Contact> contacts = new ArrayList<Contact>();
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<Cursor>() {
@Override
public void onLoadComplete(Loader<Cursor> 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<String, Bundle> phoneContacts) {
List<Contact> 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) {

View file

@ -38,6 +38,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;
private List<Conversation> conversationList = new ArrayList<Conversation>();
@ -83,6 +85,7 @@ public class ConversationActivity extends XmppActivity {
});
}
};
private boolean contactInserted = false;
public List<Conversation> 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;
}
}
}

View file

@ -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<Message> messageList = new ArrayList<Message>();
protected ArrayAdapter<Message> 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))

View file

@ -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;
@ -85,10 +93,34 @@ public class DialogContactDetails extends DialogFragment {
contactJid.setText(contact.getJid());
accountJid.setText(contact.getAccount().getJid());
if (contact.getProfilePhoto()!=null) {
contactPhoto.setImageURI(Uri.parse(contact.getProfilePhoto()));
} else {
contactPhoto.setImageBitmap(UIHelper.getUnknownContactPicture(contact.getDisplayName(), 300));
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.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);

View file

@ -0,0 +1,9 @@
package de.gultsch.chat.utils;
import java.util.Hashtable;
import android.os.Bundle;
public interface OnPhoneContactsLoadedListener {
public void onPhoneContactsLoaded(Hashtable<String, Bundle> phoneContacts);
}

View file

@ -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<String, Bundle> phoneContacts = new Hashtable<String, Bundle>();
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<Cursor>() {
@Override
public void onLoadComplete(Loader<Cursor> 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();
}
}

View file

@ -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) {
@ -71,43 +86,61 @@ public class UIHelper {
return bitmap;
}
public static Notification getUnreadMessageNotification(Context context, Conversation conversation) {
public static Notification getUnreadMessageNotification(Context context,
Conversation conversation) {
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
String ringtone = sharedPref.getString("notification_ringtone",null);
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);
Intent viewConversationIntent = new Intent(context,
ConversationActivity.class);
viewConversationIntent.setAction(Intent.ACTION_VIEW);
viewConversationIntent.putExtra(
ConversationActivity.CONVERSATION,
viewConversationIntent.putExtra(ConversationActivity.CONVERSATION,
conversation.getUuid());
viewConversationIntent
.setType(ConversationActivity.VIEW_CONVERSATION);
viewConversationIntent.setType(ConversationActivity.VIEW_CONVERSATION);
stackBuilder.addNextIntent(viewConversationIntent);
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
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));
}
}
}