/*
BEEM is a videoconference application on the Android Platform.
Copyright (C) 2009 by Frederic-Charles Barthelery,
Jean-Manuel Da Silva,
Nikita Kozlov,
Philippe Lago,
Jean Baptiste Vergely,
Vincent Veronis.
This file is part of BEEM.
BEEM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
BEEM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with BEEM. If not, see <http://www.gnu.org/licenses/>.
Please send bug reports with examples or suggestions to
contact@beem-project.com or http://dev.beem-project.com/
Epitech, hereby disclaims all copyright interest in the program "Beem"
written by Frederic-Charles Barthelery,
Jean-Manuel Da Silva,
Nikita Kozlov,
Philippe Lago,
Jean Baptiste Vergely,
Vincent Veronis.
Nicolas Sadirac, November 26, 2009
President of Epitech.
Flavien Astraud, November 26, 2009
*/
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.Activity;
import android.app.Dialog;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.graphics.drawable.LevelListDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.ContextMenu;
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.ViewStub;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener;
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.ui.dialogs.builders.Alias;
import com.beem.project.beem.ui.dialogs.builders.DeleteContact;
import com.beem.project.beem.ui.dialogs.builders.ResendSubscription;
import com.beem.project.beem.utils.BeemBroadcastReceiver;
import com.beem.project.beem.utils.Status;
/**
* The contact list activity displays the roster of the user.
*/
public class ContactList extends Activity {
// private static final String TAG = "CONTACTLIST_ACT";
private static final Intent SERVICE_INTENT = new Intent();
static {
SERVICE_INTENT.setComponent(new ComponentName("com.beem.project.beem", "com.beem.project.beem.BeemService"));
}
private static final int REQUEST_CODE = 1;
private static final String SETTINGS_HIDDEN_CONTACT = "settings_key_hidden_contact";
private static final String TAG = "ContactList";
private final BeemContactList mAdapterContactList = new BeemContactList();
private final BeemBanner mAdapterBanner = new BeemBanner();
private final List<String> mListGroup = new ArrayList<String>();
private final Map<String, List<Contact>> mContactOnGroup = new HashMap<String, List<Contact>>();
private final Handler mHandler = new Handler();
private final ServiceConnection mServConn = new BeemServiceConnection();
private final BeemBroadcastReceiver mReceiver = new BeemBroadcastReceiver();
private final ComparatorContactListByStatusAndName<Contact> mComparator =
new ComparatorContactListByStatusAndName<Contact>();
private final BeemRosterListener mBeemRosterListener = new BeemRosterListener();
private List<Contact> mListContact;
private IRoster mRoster;
private String mCurGroup;
private Contact mContact;
private IXmppFacade mXmppFacade;
private SharedPreferences mSettings;
private LayoutInflater mInflater;
/**
* Constructor.
*/
public ContactList() {
}
/**
* 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;
}
@Override
public final boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.contact_list_menu_settings:
startActivityForResult(new Intent(this, Settings.class), REQUEST_CODE);
return true;
case R.id.contact_list_menu_add_contact:
startActivity(new Intent(ContactList.this, AddContact.class));
return true;
case R.id.menu_disconnect:
stopService(SERVICE_INTENT);
finish();
return true;
default:
return false;
}
}
/**
* {@inheritDoc}
*/
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.contactlist_context, menu);
AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
Contact c = mListContact.get(info.position);
try {
mContact = mRoster.getContact(c.getJID());
} catch (RemoteException e) {
e.printStackTrace();
}
menu.setHeaderTitle(mContact.getJID());
}
/**
* {@inheritDoc}
*/
@Override
public boolean onContextItemSelected(MenuItem item) {
Intent in;
boolean result;
if (mContact != null) {
switch (item.getItemId()) {
case R.id.contact_list_context_menu_chat_item:
List<String> res = mContact.getMRes();
if (res.isEmpty()) {
result = false;
break;
}
for (String resv : res) {
in = new Intent(this, Chat.class);
in.setData(mContact.toUri(resv));
item.getSubMenu().add(resv).setIntent(in);
}
result = true;
break;
case R.id.contact_list_context_menu_call_item:
try {
mXmppFacade.call(mContact.getJID() + "/psi");
result = true;
} catch (RemoteException e) {
e.printStackTrace();
}
result = true;
break;
case R.id.contact_list_context_menu_user_info:
item.getSubMenu().setHeaderTitle(mContact.getJID());
result = true;
break;
case R.id.contact_list_context_menu_userinfo_alias:
Dialog alias = new Alias(ContactList.this, mRoster, mContact).create();
alias.show();
result = true;
break;
case R.id.contact_list_context_menu_userinfo_group:
in = new Intent(this, GroupList.class);
in.putExtra("contact", mContact);
startActivity(in);
result = true;
break;
case R.id.contact_list_context_menu_userinfo_subscription:
Dialog subscription = new ResendSubscription(ContactList.this, mXmppFacade, mContact).create();
subscription.show();
result = true;
break;
case R.id.contact_list_context_menu_userinfo_block:
result = true;
break;
case R.id.contact_list_context_menu_userinfo_delete:
Dialog delete = new DeleteContact(ContactList.this, mRoster, mContact).create();
delete.show();
result = true;
break;
default:
result = super.onContextItemSelected(item);
break;
}
return result;
}
return super.onContextItemSelected(item);
}
/*
* (non-Javadoc)
* @see android.app.Activity#onActivityResult(int, int, android.content.Intent)
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE) {
if (resultCode == RESULT_OK) {
mListContact.clear();
stopService(SERVICE_INTENT);
finish();
}
}
}
/**
* {@inheritDoc}
*/
@Override
protected void onCreate(Bundle saveBundle) {
super.onCreate(saveBundle);
mSettings = PreferenceManager.getDefaultSharedPreferences(this);
setContentView(R.layout.contactlist);
this.registerReceiver(mReceiver, new IntentFilter(BeemBroadcastReceiver.BEEM_CONNECTION_CLOSED));
mInflater = getLayoutInflater();
}
/**
* {@inheritDoc}
*/
@Override
protected void onResume() {
super.onResume();
if (mXmppFacade == null)
bindService(SERVICE_INTENT, mServConn, BIND_AUTO_CREATE);
else {
if (!mSettings.getBoolean("settings_key_hide_groups", false))
buildBanner();
else
hideGroups();
buildContactList();
}
}
/**
* {@inheritDoc}
*/
@Override
protected void onPause() {
super.onPause();
try {
if (mRoster != null) {
mRoster.removeRosterListener(mBeemRosterListener);
mRoster = null;
}
} catch (RemoteException e) {
Log.d("ContactList", "Remote exception", e);
}
unbindService(mServConn);
mXmppFacade = null;
}
/*
* (non-Javadoc)
* @see android.app.Activity#onDestroy()
*/
@Override
protected void onDestroy() {
super.onDestroy();
this.unregisterReceiver(mReceiver);
Log.e(TAG, "onDestroy activity");
}
/**
* Comparator Contact by Name.
*/
@SuppressWarnings("unused")
private class ComparatorContactListByName<T> implements Comparator<T> {
/**
* Constructor.
*/
public ComparatorContactListByName() {
}
/**
* {@inheritDoc}
*/
@Override
public int compare(T c1, T c2) {
return ((Contact) c1).getName().compareToIgnoreCase(((Contact) c2).getName());
}
}
/**
* Comparator Contact by status and name.
*/
private class ComparatorContactListByStatusAndName<T> implements Comparator<T> {
/**
* Constructor.
*/
public ComparatorContactListByStatusAndName() {
}
/**
* {@inheritDoc}
*/
@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());
}
}
/**
* Contact List construction.
*/
private void buildContactList() {
if (mCurGroup != null) {
mListContact = mContactOnGroup.get(mCurGroup);
}
sortBeemContactList();
ListView listView = (ListView) findViewById(R.id.contactlist);
listView.setOnItemClickListener(new BeemContactListOnClick());
registerForContextMenu(listView);
listView.setAdapter(mAdapterContactList);
}
/**
* buildBanner.
*/
private void buildBanner() {
ViewStub stub = (ViewStub) findViewById(R.id.contactlist_stub);
if (stub != null)
stub.inflate();
else
((LinearLayout) findViewById(R.id.contactlist_groupstub)).setVisibility(View.VISIBLE);
Gallery g = (Gallery) findViewById(R.id.contactlist_banner);
g.setOnItemClickListener(new OnItemClickGroupName());
g.setAdapter(mAdapterBanner);
}
/**
* Event simple click on item of the contact list.
*/
private class BeemContactListOnClick implements OnItemClickListener {
/**
* Constructor.
*/
public BeemContactListOnClick() {
}
/**
* {@inheritDoc}
*/
@Override
public void onItemClick(AdapterView<?> arg0, View v, int pos, long lpos) {
Contact c = mListContact.get(pos);
Intent i = new Intent(ContactList.this, Chat.class);
i.setData(c.toUri());
startActivity(i);
}
}
/**
* Event simple click on middle groupe name.
*/
private class OnItemClickGroupName implements OnItemClickListener {
/**
* Constructor.
*/
public OnItemClickGroupName() {
}
@Override
public void onItemClick(AdapterView<?> arg0, View v, int i, long l) {
mCurGroup = mListGroup.get(i);
buildContactList();
}
}
/**
* Sort the contact list.
*/
private void sortBeemContactList() {
Collections.sort(mListContact, mComparator);
}
/**
* Listener on service event.
*/
private class BeemRosterListener extends IBeemRosterListener.Stub {
/**
* Constructor.
*/
public BeemRosterListener() {
}
/**
* Refresh the contact list.
*/
private class RunnableChange implements Runnable {
/**
* Constructor.
*/
public RunnableChange() {
}
/**
* {@inheritDoc}
*/
@Override
public void run() {
sortBeemContactList();
mAdapterContactList.notifyDataSetChanged();
mAdapterBanner.notifyDataSetChanged();
}
}
/**
* {@inheritDoc}
*/
@Override
public void onEntriesAdded(List<String> addresses) throws RemoteException {
for (String newName : addresses) {
Contact c = mRoster.getContact(newName);
mContactOnGroup.get(getString(R.string.contact_list_all_contact)).add(c);
if (c.getGroups().size() == 0)
mContactOnGroup.get(getString(R.string.contact_list_no_group)).add(c);
else {
for (String group : c.getGroups()) {
if (!mListGroup.contains(group)) {
mListGroup.add(mListGroup.size() - 1, group);
List<Contact> tmplist = new ArrayList<Contact>();
mContactOnGroup.put(group, tmplist);
}
mContactOnGroup.get(group).add(c);
if (group.equals(mCurGroup) && !mListContact.contains(c))
mListContact.add(c);
}
}
}
mHandler.post(new RunnableChange());
}
/**
* {@inheritDoc}
*/
@Override
public void onEntriesDeleted(List<String> addresses) throws RemoteException {
for (String cToDelete : addresses) {
for (Contact c : mListContact) {
if (c.getJID().equals(cToDelete)) {
for (String group : mListGroup) {
mContactOnGroup.get(group).remove(c);
if (mContactOnGroup.get(group).size() == 0) {
//mContactOnGroup.remove(group);
//mListGroup.remove(group);
}
}
mListContact.remove(c);
break;
}
}
}
mListContact = mContactOnGroup.get(getString(R.string.contact_list_all_contact));
mHandler.post(new RunnableChange());
}
/**
* {@inheritDoc}
*/
@Override
public void onEntriesUpdated(List<String> addresses) throws RemoteException {
for (String adr : addresses) {
Contact c = mRoster.getContact(adr);
if (c.getGroups() != null) {
if (mContactOnGroup.get(getString(R.string.contact_list_no_group)).contains(c)) {
mContactOnGroup.get(getString(R.string.contact_list_no_group)).remove(c);
}
}
}
mHandler.post(new RunnableChange());
}
/**
* {@inheritDoc}
*/
@Override
public void onPresenceChanged(PresenceAdapter presence) throws RemoteException {
String from = presence.getFrom();
boolean resfound = false;
int foundIdx = 0;
boolean found = false;
Contact contact = mRoster.getContact(StringUtils.parseBareAddress(from));
for (int i = 0; i < mListContact.size(); i++) {
Contact curContact = mListContact.get(i);
if (curContact.getJID().equals(contact.getJID())) {
found = true;
foundIdx = i;
break;
}
}
if (found){
mListContact.set(foundIdx, contact);
} else {
mListContact.add(contact);
}
mHandler.post(new RunnableChange());
}
}
/**
* Adapter contact list.
*/
private class BeemContactList extends BaseAdapter {
/**
* Constructor.
*/
public BeemContactList() {
}
/**
* {@inheritDoc}
*/
@Override
public int getCount() {
if (mSettings.getBoolean(SETTINGS_HIDDEN_CONTACT, false)) {
int res = 0;
for (Contact c : mListContact) {
if (Status.statusOnline(c.getStatus()))
res++;
}
return res;
} else {
return mListContact.size();
}
}
/**
* {@inheritDoc}
*/
@Override
public Object getItem(int position) {
Contact c = null;
if (mSettings.getBoolean(SETTINGS_HIDDEN_CONTACT, false)) {
int res = 0;
for (Contact cur : mListContact) {
if (res == position && Status.statusOnline(cur.getStatus())) {
c = cur;
break;
}
if (Status.statusOnline(cur.getStatus()))
res++;
}
} else
c = mListContact.get(position);
return c;
}
/**
* {@inheritDoc}
*/
@Override
public long getItemId(int position) {
return position;
}
/**
* {@inheritDoc}
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (convertView == null) {
v = mInflater.inflate(R.layout.contactlistcontact, null);
}
Contact c = null;
if (mSettings.getBoolean(SETTINGS_HIDDEN_CONTACT, false)) {
int res = 0;
for (Contact cur : mListContact) {
if (res == position && Status.statusOnline(cur.getStatus())) {
c = cur;
break;
}
if (Status.statusOnline(cur.getStatus()))
res++;
}
} else
c = mListContact.get(position);
if (mRoster != null) {
try {
c = mRoster.getContact(c.getJID());
} catch (RemoteException e) {
e.printStackTrace();
}
}
if (mSettings.getBoolean(SETTINGS_HIDDEN_CONTACT, false) && !Status.statusOnline(c.getStatus())) {
v.setVisibility(View.GONE);
} else {
bindView(v, c);
}
return v;
}
/**
* Adapte curContact to the view.
* @param view the row view.
* @param curContact the current contact.
*/
private void bindView(View view, Contact curContact) {
if (curContact != null) {
TextView v = (TextView) view.findViewById(R.id.contactlistpseudo);
LevelListDrawable mStatusDrawable = (LevelListDrawable) getResources().getDrawable(R.drawable.status_icon);
mStatusDrawable.setLevel(curContact.getStatus());
v.setCompoundDrawablesWithIntrinsicBounds(mStatusDrawable, null, null, null);
v.setText(curContact.getName());
v = (TextView) view.findViewById(R.id.contactlistmsgperso);
v.setText(curContact.getMsgState());
}
}
}
/**
* Adapter banner list.
*/
private class BeemBanner extends BaseAdapter {
/**
* Constructor.
*/
public BeemBanner() {
}
/**
* {@inheritDoc}
*/
@Override
public int getCount() {
return mListGroup.size();
}
/**
* {@inheritDoc}
*/
@Override
public Object getItem(int position) {
return mListGroup.get(position);
}
/**
* {@inheritDoc}
*/
@Override
public long getItemId(int position) {
return position;
}
/**
* {@inheritDoc}
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (convertView == null) {
v = mInflater.inflate(R.layout.contactlist_group, null);
}
((TextView) v).setText(mListGroup.get(position));
return v;
}
}
/**
* Create dialog resend subscription.
*/
/**
* The service connection used to connect to the Beem service.
*/
private class BeemServiceConnection implements ServiceConnection {
/**
* Constructor.
*/
public BeemServiceConnection() {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mXmppFacade = IXmppFacade.Stub.asInterface(service);
try {
mRoster = mXmppFacade.getRoster();
if (mRoster != null) {
mRoster.addRosterListener(mBeemRosterListener);
List<Contact> tmpContactList = mRoster.getContactList();
List<String> tmpGroupList = mRoster.getGroupsNames();
Collections.sort(tmpGroupList);
if (mListGroup.size() > 0)
mListGroup.clear();
mListGroup.add(getString(R.string.contact_list_all_contact));
mListGroup.addAll(tmpGroupList);
mListGroup.add(getString(R.string.contact_list_no_group));
mContactOnGroup.clear();
if (tmpGroupList.size() > 0) {
List<Contact> tmpNoGroup = new ArrayList<Contact>();
for (String s : tmpGroupList) {
List<Contact> tmpList = new ArrayList<Contact>();
for (Contact c : tmpContactList) {
if (c.getGroups().size() == 0 && !tmpNoGroup.contains(c))
tmpNoGroup.add(c);
else if (c.getGroups().contains(s))
tmpList.add(c);
}
mContactOnGroup.put(s, tmpList);
}
mContactOnGroup.put(getString(R.string.contact_list_no_group), tmpNoGroup);
} else {
mContactOnGroup.put(getString(R.string.contact_list_no_group), tmpContactList);
}
mContactOnGroup.put(getString(R.string.contact_list_all_contact), tmpContactList);
mCurGroup = getString(R.string.contact_list_all_contact);
if (!mSettings.getBoolean("settings_key_hide_groups", false))
buildBanner();
else
hideGroups();
buildContactList();
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
try {
mRoster.removeRosterListener(mBeemRosterListener);
} catch (RemoteException e) {
e.printStackTrace();
}
mXmppFacade = null;
mRoster = null;
mListContact.clear();
mListGroup.clear();
mContactOnGroup.clear();
}
}
@Override
protected void finalize() {
Log.e(TAG, "FINALIZE");
}
/**
* Hide the groups view.
*/
private void hideGroups() {
ViewStub stub = (ViewStub) findViewById(R.id.contactlist_stub);
if (stub == null)
((LinearLayout) findViewById(R.id.contactlist_groupstub)).setVisibility(View.GONE);
}
}