Feature #181
Voila, c'est fait, grace à la classe Linkify de Android.
package com.beem.project.beem.ui;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jivesoftware.smack.util.StringUtils;
import android.app.ExpandableListActivity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.database.DataSetObserver;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.text.util.Linkify;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.widget.ExpandableListAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.beem.project.beem.BeemService;
import com.beem.project.beem.R;
import com.beem.project.beem.service.Contact;
import com.beem.project.beem.service.PresenceAdapter;
import com.beem.project.beem.service.aidl.IBeemRosterListener;
import com.beem.project.beem.service.aidl.IRoster;
import com.beem.project.beem.service.aidl.IXmppFacade;
import com.beem.project.beem.utils.Status;
public class ContactList extends ExpandableListActivity {
private static final String TAG = "CONTACTLIST_ACT";
private static final String DEFAULT_GROUP = "Default";
private MyExpandableListAdapter mAdapter;
private IRoster mRoster;
private Map<String, List<Contact>> groupMap;
private List<String> groupName;
private List<Contact> mListContact;
private Handler mHandler;
private IXmppFacade xmppFacade = null;
private final ServiceConnection mServConn = new BeemServiceConnection();
/**
* Callback for menu creation.
* @param menu the menu created
* @return true on success, false otherwise
*/
@Override
public final boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.contact_list, menu);
return true;
}
/**
* Callback for menu item selected.
* @param item the item selected
* @return true on success, false otherwise
*/
@Override
public final boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.contact_list_menu_settings:
startActivity(new Intent(this, EditSettings.class));
return true;
case R.id.contact_list_menu_add_contact:
startActivity(new Intent(ContactList.this, AddContact.class));
return true;
default:
return false;
}
}
@Override
protected void onCreate(Bundle saveBundle) {
super.onCreate(saveBundle);
bindService(new Intent(this, BeemService.class), mServConn, BIND_AUTO_CREATE);
mHandler = new Handler();
groupMap = new HashMap<String, List<Contact>>();
groupName = new ArrayList<String>();
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mServConn);
}
class ComparatorContactListByName<T> implements Comparator<T> {
@Override
public int compare(T c1, T c2) {
return ((Contact) c1).getName().compareToIgnoreCase(((Contact) c2).getName());
}
}
class ComparatorContactListByStatusAndName<T> implements Comparator<T> {
@Override
public int compare(T c1, T c2) {
if (((Contact) c1).getStatus() < ((Contact) c2).getStatus()) {
return 1;
} else if (((Contact) c1).getStatus() > ((Contact) c2).getStatus()) {
return -1;
} else
return ((Contact) c1).getName().compareToIgnoreCase(((Contact) c2).getName());
}
}
private void buildContactList(List<Contact> listContact) {
mListContact = listContact;
Collections.sort(mListContact, new ComparatorContactListByStatusAndName<Contact>());
for (Contact contact : mListContact) {
for (String group : contact.getGroups()) {
if (!groupMap.containsKey(group)) {
groupMap.put(group, new ArrayList<Contact>());
Collections.sort(groupMap.get(group), new ComparatorContactListByStatusAndName<Contact>());
groupName.add(group);
}
try {
if (!groupMap.get(group).contains(contact))
groupMap.get(group).add(contact);
} catch (NullPointerException e) {
Log.e(TAG, "Failed to find group in groupMap", e);
}
}
if (contact.getGroups().isEmpty()) {
if (!groupMap.containsKey(DEFAULT_GROUP)) {
groupMap.put(DEFAULT_GROUP, new ArrayList<Contact>());
groupName.add(DEFAULT_GROUP);
}
groupMap.get(DEFAULT_GROUP).add(contact);
}
}
mAdapter = new MyExpandableListAdapter();
setListAdapter(mAdapter);
}
private class BeemRosterListener extends IBeemRosterListener.Stub {
@Override
public void onEntriesAdded(List<String> addresses) throws RemoteException {
for (String str : addresses) {
Contact curContact = mRoster.getContact(str);
for (String group : curContact.getGroups()) {
if (!groupMap.containsKey(group)) {
groupMap.put(group, new ArrayList<Contact>());
groupName.add(group);
}
try {
groupMap.get(group).add(curContact);
} catch (NullPointerException e) {
Log.e(TAG, "Failed to find group in groupMap", e);
}
}
}
mHandler.post(new RunnableChange());
}
@Override
public void onEntriesDeleted(List<String> addresses) throws RemoteException {
for (List<Contact> cList : groupMap.values()) {
for (Contact curContact : cList) {
for (String addr : addresses) {
if (addr.equals(curContact.getJID())) {
cList.remove(curContact);
if (cList.isEmpty()) {
groupMap.values().remove(cList);
}
}
}
}
}
mHandler.post(new RunnableChange());
}
@Override
public void onEntriesUpdated(List<String> addresses) throws RemoteException {
for (String str : addresses) {
Contact curContact = mRoster.getContact(str);
for (String group : curContact.getGroups()) {
if (!groupMap.containsKey(group)) {
groupMap.put(group, new ArrayList<Contact>());
groupName.add(group);
groupMap.get(group).add(curContact);
} else {
boolean found = false;
for (Contact tempContact : groupMap.get(group)) {
if (tempContact.getJID() == str) {
curContact = tempContact;
found = true;
break;
}
}
if (!found) {
groupMap.get(group).add(curContact);
}
}
}
}
mHandler.post(new RunnableChange());
}
@Override
public void onPresenceChanged(PresenceAdapter presence) throws RemoteException {
for (Contact curContact : mListContact) {
if (curContact.getJID().equals(StringUtils.parseBareAddress(presence.getFrom()))) {
curContact.setStatus(mRoster.getPresence(StringUtils.parseBareAddress(presence.getFrom())));
mHandler.post(new RunnableChange());
return;
}
}
}
private class RunnableChange implements Runnable {
@Override
public void run() {
mAdapter.changed();
}
}
}
private class MyExpandableListAdapter implements ExpandableListAdapter {
class MyOnClickListener implements OnClickListener {
private final Contact mContact;
public MyOnClickListener(Contact contact) {
mContact = contact;
}
@Override
public void onClick(View v) {
Intent i = new Intent(ContactList.this, SendIM.class);
i.setData(mContact.toUri());
startActivity(i);
}
}
class MyOnLongClickListener implements OnLongClickListener {
private final Contact mContact;
public MyOnLongClickListener(Contact contact) {
mContact = contact;
}
@Override
public boolean onLongClick(View v) {
createDialog(mContact);
return true;
}
}
private final List<DataSetObserver> observers;
public MyExpandableListAdapter() {
observers = new ArrayList<DataSetObserver>();
}
@Override
public boolean areAllItemsEnabled() {
return true;
}
private void bindView(View view, Contact curContact) {
if (curContact != null) {
ImageView imgV = (ImageView) view.findViewById(R.id.contactliststatus);
Drawable imageDrawable = null;
switch (curContact.getStatus()) {
case Status.CONTACT_STATUS_AVAILABLE:
imageDrawable = getResources().getDrawable(R.drawable.online);
break;
case Status.CONTACT_STATUS_AVAILABLE_FOR_CHAT:
imageDrawable = getResources().getDrawable(R.drawable.chat);
break;
case Status.CONTACT_STATUS_AWAY:
imageDrawable = getResources().getDrawable(R.drawable.away);
break;
case Status.CONTACT_STATUS_BUSY:
imageDrawable = getResources().getDrawable(R.drawable.dnd);
break;
case Status.CONTACT_STATUS_DISCONNECT:
imageDrawable = getResources().getDrawable(R.drawable.offline);
break;
case Status.CONTACT_STATUS_UNAVAILABLE:
imageDrawable = getResources().getDrawable(R.drawable.requested);
break;
default:
imageDrawable = getResources().getDrawable(R.drawable.error);
break;
}
imgV.setImageDrawable(imageDrawable);
TextView v = (TextView) view.findViewById(R.id.contactlistpseudo);
if (v != null) {
v.setText(curContact.getName());
}
v = (TextView) view.findViewById(R.id.contactlistmsgperso);
if (v != null) {
v.setText(curContact.getMsgState());
Linkify.addLinks(v, Linkify.WEB_URLS);
}
// TODO: Rajouter l'avatar du contact getAvatar() dans la classe
imgV = (ImageView) view.findViewById(R.id.contactlistavatar);
if (imgV != null) {
imageDrawable = getResources().getDrawable(R.drawable.avatar);
imgV.setImageDrawable(imageDrawable);
}
}
}
public void changed() {
for (String name : groupName) {
Collections.sort(groupMap.get(name), new ComparatorContactListByStatusAndName<Contact>());
}
for (DataSetObserver obs : observers) {
obs.onChanged();
}
}
void createDialog(Contact contact) {
ContactDialog dialogContact = new ContactDialog(ContactList.this, contact);
dialogContact.setOwnerActivity(ContactList.this);
dialogContact.show();
}
@Override
public Object getChild(int groupPosition, int childPosition) {
try {
return groupMap.get(groupName.get(groupPosition)).get(childPosition);
} catch (NullPointerException e) {
Log.e(TAG, "Child not found", e);
return null;
}
}
@Override
public long getChildId(int groupPosition, int childPosition) {
try {
groupMap.get(groupName.get(groupPosition)).get(childPosition);
} catch (NullPointerException e) {
Log.e(TAG, "Child not found", e);
return 0;
}
return childPosition;
}
@Override
public int getChildrenCount(int groupPosition) {
try {
return groupMap.get(groupName.get(groupPosition)).size();
} catch (NullPointerException e) {
Log.e(TAG, "Child not found", e);
return 0;
}
}
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView,
ViewGroup parent) {
View v;
if (convertView == null) {
v = LayoutInflater.from(ContactList.this).inflate(R.layout.contactlistcontact, null);
} else {
v = convertView;
}
Contact contact = groupMap.get(groupName.get(groupPosition)).get(childPosition);
bindView(v, contact);
v.setOnLongClickListener(new MyOnLongClickListener(contact));
v.setOnClickListener(new MyOnClickListener(contact));
return v;
}
@Override
public long getCombinedChildId(long groupId, long childId) {
return 1000 * groupId + childId;
}
@Override
public long getCombinedGroupId(long groupId) {
return 1000 * groupId;
}
@Override
public Object getGroup(int groupPosition) {
try {
return groupMap.get(groupName.get(groupPosition));
} catch (NullPointerException e) {
Log.e(TAG, "Group not found", e);
return null;
}
}
@Override
public int getGroupCount() {
return groupMap.size();
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(ContactList.this).inflate(R.layout.contactlistgroup, null);
}
TextView groupTextView = (TextView) convertView.findViewById(R.id.textgroup);
groupTextView.setText(groupName.get(groupPosition));
return convertView;
}
@Override
public boolean hasStableIds() {
return false;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
@Override
public boolean isEmpty() {
return groupMap.isEmpty();
}
@Override
public void onGroupCollapsed(int groupPosition) {
}
@Override
public void onGroupExpanded(int groupPosition) {
}
@Override
public void registerDataSetObserver(DataSetObserver observer) {
observers.add(observer);
}
@Override
public void unregisterDataSetObserver(DataSetObserver observer) {
observers.remove(observer);
}
}
private class BeemServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
xmppFacade = IXmppFacade.Stub.asInterface(service);
try {
mRoster = xmppFacade.getRoster();
if (mRoster != null) {
mRoster.addRosterListener(new BeemRosterListener());
buildContactList(mRoster.getContactList());
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
xmppFacade = null;
mRoster = null;
}
}
}