Java tutorial
/* 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 edu.sage.spark.ui; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; 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.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.preference.PreferenceManager; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.view.PagerTabStrip; import android.support.v4.view.ViewPager; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import edu.sage.spark.AppRater; import edu.sage.spark.R; import edu.sage.spark.service.aidl.IBeemRosterListener; import edu.sage.spark.service.aidl.IChatManager; import edu.sage.spark.service.aidl.IRoster; import edu.sage.spark.service.aidl.IXmppFacade; import edu.sage.spark.BeemApplication; import edu.sage.spark.service.Contact; import edu.sage.spark.service.PresenceAdapter; import edu.sage.spark.ui.dialogs.builders.Alias; import edu.sage.spark.ui.dialogs.builders.ChatList; import edu.sage.spark.ui.dialogs.builders.DeleteContact; import edu.sage.spark.ui.dialogs.builders.ResendSubscription; import edu.sage.spark.utils.BeemBroadcastReceiver; import org.jivesoftware.smack.util.StringUtils; import static com.google.android.apps.iosched.util.LogUtils.*; /** * The contact list activity displays the roster of the user. */ public class ContactList extends FragmentActivity { private static final Intent SERVICE_INTENT = new Intent(); static { SERVICE_INTENT.setComponent(new ComponentName("edu.sage.spark", "edu.sage.spark.BeemService")); } private static final String TAG = makeLogTag(ContactList.class); private static final float PAGER_TAB_SECONDARY_ALPHA = 0.5f; private final List<String> mListGroup = new ArrayList<String>(); /** * Map containing a list of the different contacts of a given group. Each * list is a @{link SortedList} so there is no need to sort it again. * */ private final Map<String, List<Contact>> mContactOnGroup = new HashMap<String, List<Contact>>(); private final ServiceConnection mServConn = new BeemServiceConnection(); private final BeemBroadcastReceiver mReceiver = new BeemBroadcastReceiver(); private final Map<String, ContactListAdapter> contactListAdapters = new HashMap<String, ContactListAdapter>(); private final BeemRosterListener mBeemRosterListener = new BeemRosterListener(); private IRoster mRoster; private IXmppFacade mXmppFacade; private IChatManager mChatManager; private SharedPreferences mSettings; private boolean mBinded; private ViewPager viewPager; private ListPagerAdapter groupsPagesAdapter; private PagerTabStrip pagerTabs; /** * 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: startActivity(new Intent(this, Settings.class)); return true; case R.id.contact_list_menu_add_contact: startActivity(new Intent(ContactList.this, AddContact.class)); return true; case R.id.menu_change_status: startActivity(new Intent(ContactList.this, ChangeStatus.class)); return true; case R.id.contact_list_menu_chatlist: List<Contact> openedChats; try { openedChats = mChatManager.getOpenedChatList(); LOGD(TAG, "opened chats = " + openedChats); Dialog chatList = new ChatList(ContactList.this, openedChats).create(); chatList.show(); } catch (RemoteException e) { e.printStackTrace(); } return true; case R.id.sageMail: sageMail(); return true;// almyz125 sageMail case opens sageMail method below. case R.id.sageCloud: openCloud(); return true;// almyz125 added to open sage cloud or find it on the market case R.id.menu_disconnect: stopService(SERVICE_INTENT); finish(); return true; default: return false; } } public void openCloud() { PackageManager pm = this.getPackageManager(); Intent appStartIntent = pm.getLaunchIntentForPackage("edu.sage.cloud"); if (null != appStartIntent) { this.startActivity(appStartIntent); this.finish(); } else { Intent i = new Intent(Intent.ACTION_VIEW); i.setData(Uri.parse("market://details?id=edu.sage.cloud")); startActivity(i); } }//almyz125 added to open sage cloud or find it on the market private void sageMail() { PackageManager pm = this.getPackageManager(); Intent appStartIntent = pm.getLaunchIntentForPackage("edu.sage.mail"); if (null != appStartIntent) { this.startActivity(appStartIntent); this.finish(); } else { Intent i = new Intent(Intent.ACTION_VIEW); i.setData(Uri.parse("market://details?id=edu.sage.mail")); startActivity(i); } } // almyz125 opens sage mail if it is on the phone, else opens the link in // the market. @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)); viewPager = (ViewPager) findViewById(R.id.pager); groupsPagesAdapter = new ListPagerAdapter(getSupportFragmentManager(), viewPager); pagerTabs = (PagerTabStrip) findViewById(R.id.tabstrip); pagerTabs.setTabIndicatorColorResource(R.color.vert_manu); pagerTabs.setNonPrimaryAlpha(PAGER_TAB_SECONDARY_ALPHA); mListGroup.add(getString(R.string.contact_list_all_contact)); mListGroup.add(getString(R.string.contact_list_no_group)); groupsPagesAdapter.notifyDataSetChanged(); AppRater.app_launched(this);// almyz125 added this to prompt the user to // rate the app. } @Override protected void onStart() { super.onStart(); if (!mSettings.getBoolean(BeemApplication.HIDE_GROUPS_KEY, false)) showGroups(); else hideGroups(); if (!mBinded) mBinded = bindService(SERVICE_INTENT, mServConn, BIND_AUTO_CREATE); } /** * {@inheritDoc} */ @Override protected void onPause() { super.onPause(); try { if (mRoster != null) { mRoster.removeRosterListener(mBeemRosterListener); mRoster = null; } } catch (RemoteException e) { LOGD("ContactList", "Remote exception", e); } if (mBinded) { unbindService(mServConn); mBinded = false; } mXmppFacade = null; } @Override protected void onDestroy() { super.onDestroy(); this.unregisterReceiver(mReceiver); LOGV(TAG, "onDestroy activity"); } /** * Get a {@link ContactListAdapter} for a group. The * {@link ContactListAdapter} will be created if it is not exist. * * @param group * the group * @return the adapter */ ContactListAdapter getContactListAdapter(String group) { synchronized (contactListAdapters) { ContactListAdapter contactListAdapter = contactListAdapters.get(group); if (contactListAdapter == null) { contactListAdapter = new ContactListAdapter(ContactList.this); contactListAdapters.put(group, contactListAdapter); List<String> realGroups = mListGroup.subList(1, mListGroup.size() - 1); if (!mListGroup.contains(group)) { boolean added = false; // insert group in sorted list for (ListIterator<String> iterator = realGroups.listIterator(); iterator.hasNext();) { String currentGroup = (String) iterator.next(); if (currentGroup.compareTo(group) > 0) { iterator.previous(); iterator.add(group); added = true; break; } } if (!added) realGroups.add(group); groupsPagesAdapter.notifyDataSetChanged(); } } boolean hideDisconnected = mSettings.getBoolean(BeemApplication.SHOW_OFFLINE_CONTACTS_KEY, false); contactListAdapter.setOnlineOnly(hideDisconnected); return contactListAdapter; } } /** * Exectute a context menu action on a specified contact. * * @param itemId * the id of the menu action * @param contact * the contact */ void doContextMenuAction(int itemId, Contact contact) { switch (itemId) { case R.id.contact_list_context_menu_call_item: try { mXmppFacade.call(contact.getJID() + "/psi"); } catch (RemoteException e) { e.printStackTrace(); } break; case R.id.contact_list_context_menu_userinfo_alias: Dialog alias = new Alias(ContactList.this, mRoster, contact).create(); alias.show(); break; case R.id.contact_list_context_menu_userinfo_subscription: Dialog subscription = new ResendSubscription(ContactList.this, mXmppFacade, contact).create(); subscription.show(); break; case R.id.contact_list_context_menu_userinfo_delete: Dialog delete = new DeleteContact(ContactList.this, mRoster, contact).create(); delete.show(); break; default: LOGW(TAG, "Context menu action not supported" + itemId); break; } } /** * Show the groups view. */ private void showGroups() { pagerTabs.setVisibility(View.VISIBLE); } /** * Hide the groups view. */ private void hideGroups() { pagerTabs.setVisibility(View.GONE); } /** * Remove old groups on the banner. * * @throws RemoteException * if an error occur when communicating with the service */ private void cleanBannerGroup() throws RemoteException { if (mListGroup.size() <= 2) return; List<String> rosterGroups = mRoster.getGroupsNames(); Collections.sort(rosterGroups); List<String> realGroups = mListGroup.subList(1, mListGroup.size() - 1); realGroups.clear(); realGroups.addAll(rosterGroups); groupsPagesAdapter.notifyDataSetChanged(); } /** * Add a contact to the special list No Group and All contacts. The contact * will be added if the list is not the current list otherwise the list must * be modified in a Handler. * * @param contact * the contact to add. */ private void addToSpecialList(Contact contact) { List<String> groups = contact.getGroups(); ContactListAdapter adapter = getContactListAdapter(getString(R.string.contact_list_all_contact)); adapter.put(contact); if (groups.isEmpty()) { adapter = getContactListAdapter(getString(R.string.contact_list_no_group)); adapter.put(contact); } } /** * Listener on service event. */ private class BeemRosterListener extends IBeemRosterListener.Stub { /** * Constructor. */ public BeemRosterListener() { } /** * {@inheritDoc} Simple stategy to handle the onEntriesAdded event. if * contact has to be shown : * <ul> * <li>add him to his groups</li> * <li>add him to the specials groups</> * </ul> */ @Override public void onEntriesAdded(final List<String> addresses) throws RemoteException { for (String newName : addresses) { final Contact contact = mRoster.getContact(StringUtils.parseBareAddress(newName)); putContactInList(contact); } } /** * {@inheritDoc} Simple stategy to handle the onEntriesDeleted event. * <ul> * <li>Remove the contact from all groups</li> * </ul> */ @Override public void onEntriesDeleted(final List<String> addresses) throws RemoteException { LOGD(TAG, "onEntries deleted " + addresses); for (String cToDelete : addresses) { final Contact contact = new Contact(cToDelete); for (final ContactListAdapter adapter : contactListAdapters.values()) { runOnUiThread(new Runnable() { @Override public void run() { adapter.remove(contact); } }); } } cleanBannerGroup(); } /** * {@inheritDoc} Simple stategy to handle the onEntriesUpdated event. * <ul> * <li>Remove the contact from all groups</li> * <li>if contact has to be shown add it to his groups</li> * <li>if contact has to be shown add it to the specials groups</li> * </ul> */ @Override public void onEntriesUpdated(final List<String> addresses) throws RemoteException { LOGD(TAG, "onEntries updated " + addresses); for (String cToDelete : addresses) { Contact contact = new Contact(cToDelete); for (ContactListAdapter adapter : contactListAdapters.values()) { adapter.remove(contact); } } for (String newName : addresses) { final Contact contact = mRoster.getContact(StringUtils.parseBareAddress(newName)); putContactInList(contact); } cleanBannerGroup(); } /** * {@inheritDoc} Simple stategy to handle the onPresenceChanged event. * <ul> * <li>Remove the contact from all groups</li> * <li>if contact has to be shown add it to his groups</li> * <li>if contact has to be shown add it to the specials groups</li> * </ul> */ @Override public void onPresenceChanged(PresenceAdapter presence) throws RemoteException { String from = presence.getFrom(); final Contact contact = mRoster.getContact(StringUtils.parseBareAddress(from)); putContactInList(contact); } /** * Put a contact in the different group list. * * @param contact * the contact */ private void putContactInList(final Contact contact) { List<String> groups = contact.getGroups(); for (final String group : groups) { runOnUiThread(new Runnable() { @Override public void run() { ContactListAdapter contactListAdapter = getContactListAdapter(group); contactListAdapter.put(contact); } }); } runOnUiThread(new Runnable() { @Override public void run() { addToSpecialList(contact); } }); } } /** * 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) { List<String> tmpGroupList = mRoster.getGroupsNames(); cleanBannerGroup(); synchronized (contactListAdapters) { for (ContactListAdapter ca : contactListAdapters.values()) { ca.clear(); } } assignContactToGroups(mRoster.getContactList(), tmpGroupList); mRoster.addRosterListener(mBeemRosterListener); LOGD(TAG, "add roster listener"); mChatManager = mXmppFacade.getChatManager(); } } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { try { mRoster.removeRosterListener(mBeemRosterListener); } catch (RemoteException e) { e.printStackTrace(); } mXmppFacade = null; mChatManager = null; mRoster = null; mListGroup.clear(); mContactOnGroup.clear(); mBinded = false; } /** * Assign the differents contact to their groups. This methods will fill * the mContactOnGroup map. * * @param contacts * list of contacts * @param groupNames * list of existing groups */ private void assignContactToGroups(List<Contact> contacts, List<String> groupNames) { for (Contact c : contacts) { addToSpecialList(c); List<String> groups = c.getGroups(); for (String currentGroup : groups) { ContactListAdapter cl = getContactListAdapter(currentGroup); cl.put(c); } } } } /** * PagerAdapter for the contact list. */ private class ListPagerAdapter extends FragmentPagerAdapter { /** * Create a {@link ListPagerAdapter}. * * @param fm * the {@link FragmentManager} * @param viewPager * the {@link ViewPager} associate with this adapter */ public ListPagerAdapter(final FragmentManager fm, final ViewPager viewPager) { super(fm); viewPager.setAdapter(this); } @Override public Fragment getItem(int position) { String group = mListGroup.get(position); ContactListFragment f = ContactListFragment.newInstance(group); f.setListAdapter(getContactListAdapter(group)); return f; } @Override public int getCount() { return mListGroup.size(); } @Override public String getPageTitle(int position) { return mListGroup.get(position); } } }