StartConversationActivity.java

  1package eu.siacs.conversations.ui;
  2
  3import android.annotation.SuppressLint;
  4import android.app.ActionBar;
  5import android.app.ActionBar.Tab;
  6import android.app.ActionBar.TabListener;
  7import android.app.AlertDialog;
  8import android.app.Fragment;
  9import android.app.FragmentTransaction;
 10import android.app.ListFragment;
 11import android.content.Context;
 12import android.content.DialogInterface;
 13import android.content.DialogInterface.OnClickListener;
 14import android.content.Intent;
 15import android.net.Uri;
 16import android.nfc.NdefMessage;
 17import android.nfc.NdefRecord;
 18import android.nfc.NfcAdapter;
 19import android.os.Build;
 20import android.os.Bundle;
 21import android.os.Parcelable;
 22import android.support.v13.app.FragmentPagerAdapter;
 23import android.support.v4.view.ViewPager;
 24import android.text.Editable;
 25import android.text.TextWatcher;
 26import android.util.Log;
 27import android.view.ContextMenu;
 28import android.view.ContextMenu.ContextMenuInfo;
 29import android.view.KeyEvent;
 30import android.view.Menu;
 31import android.view.MenuItem;
 32import android.view.View;
 33import android.view.inputmethod.InputMethodManager;
 34import android.widget.AdapterView;
 35import android.widget.AdapterView.AdapterContextMenuInfo;
 36import android.widget.AdapterView.OnItemClickListener;
 37import android.widget.ArrayAdapter;
 38import android.widget.AutoCompleteTextView;
 39import android.widget.CheckBox;
 40import android.widget.EditText;
 41import android.widget.ListView;
 42import android.widget.Spinner;
 43
 44import com.google.zxing.integration.android.IntentIntegrator;
 45import com.google.zxing.integration.android.IntentResult;
 46
 47import java.io.UnsupportedEncodingException;
 48import java.net.URLDecoder;
 49import java.util.ArrayList;
 50import java.util.Arrays;
 51import java.util.Collections;
 52import java.util.List;
 53
 54import eu.siacs.conversations.Config;
 55import eu.siacs.conversations.R;
 56import eu.siacs.conversations.entities.Account;
 57import eu.siacs.conversations.entities.Bookmark;
 58import eu.siacs.conversations.entities.Contact;
 59import eu.siacs.conversations.entities.Conversation;
 60import eu.siacs.conversations.entities.ListItem;
 61import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
 62import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
 63import eu.siacs.conversations.ui.adapter.ListItemAdapter;
 64import eu.siacs.conversations.utils.Validator;
 65
 66public class StartConversationActivity extends XmppActivity {
 67
 68	private Tab mContactsTab;
 69	private Tab mConferencesTab;
 70	private ViewPager mViewPager;
 71
 72	private MyListFragment mContactsListFragment = new MyListFragment();
 73	private List<ListItem> contacts = new ArrayList<ListItem>();
 74	private ArrayAdapter<ListItem> mContactsAdapter;
 75
 76	private MyListFragment mConferenceListFragment = new MyListFragment();
 77	private List<ListItem> conferences = new ArrayList<ListItem>();
 78	private ArrayAdapter<ListItem> mConferenceAdapter;
 79
 80	private List<String> mActivatedAccounts = new ArrayList<String>();
 81	private List<String> mKnownHosts;
 82	private List<String> mKnownConferenceHosts;
 83
 84	private Invite mPendingInvite = null;
 85
 86	private Menu mOptionsMenu;
 87	private EditText mSearchEditText;
 88
 89	public int conference_context_id;
 90	public int contact_context_id;
 91
 92	private TabListener mTabListener = new TabListener() {
 93
 94		@Override
 95		public void onTabUnselected(Tab tab, FragmentTransaction ft) {
 96			return;
 97		}
 98
 99		@Override
100		public void onTabSelected(Tab tab, FragmentTransaction ft) {
101			mViewPager.setCurrentItem(tab.getPosition());
102			onTabChanged();
103		}
104
105		@Override
106		public void onTabReselected(Tab tab, FragmentTransaction ft) {
107			return;
108		}
109	};
110
111	private ViewPager.SimpleOnPageChangeListener mOnPageChangeListener = new ViewPager.SimpleOnPageChangeListener() {
112		@Override
113		public void onPageSelected(int position) {
114			getActionBar().setSelectedNavigationItem(position);
115			onTabChanged();
116		}
117	};
118
119	private MenuItem.OnActionExpandListener mOnActionExpandListener = new MenuItem.OnActionExpandListener() {
120
121		@Override
122		public boolean onMenuItemActionExpand(MenuItem item) {
123			mSearchEditText.post(new Runnable() {
124
125				@Override
126				public void run() {
127					mSearchEditText.requestFocus();
128					InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
129					imm.showSoftInput(mSearchEditText,
130							InputMethodManager.SHOW_IMPLICIT);
131				}
132			});
133
134			return true;
135		}
136
137		@Override
138		public boolean onMenuItemActionCollapse(MenuItem item) {
139			InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
140			imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(),
141					InputMethodManager.HIDE_IMPLICIT_ONLY);
142			mSearchEditText.setText("");
143			filter(null);
144			return true;
145		}
146	};
147	private TextWatcher mSearchTextWatcher = new TextWatcher() {
148
149		@Override
150		public void afterTextChanged(Editable editable) {
151			filter(editable.toString());
152		}
153
154		@Override
155		public void beforeTextChanged(CharSequence s, int start, int count,
156									  int after) {
157		}
158
159		@Override
160		public void onTextChanged(CharSequence s, int start, int before,
161								  int count) {
162		}
163	};
164	private OnRosterUpdate onRosterUpdate = new OnRosterUpdate() {
165
166		@Override
167		public void onRosterUpdate() {
168			runOnUiThread(new Runnable() {
169
170				@Override
171				public void run() {
172					if (mSearchEditText != null) {
173						filter(mSearchEditText.getText().toString());
174					}
175				}
176			});
177		}
178	};
179	private MenuItem mMenuSearchView;
180	private String mInitialJid;
181
182	@Override
183	public void onCreate(Bundle savedInstanceState) {
184		super.onCreate(savedInstanceState);
185		setContentView(R.layout.activity_start_conversation);
186		mViewPager = (ViewPager) findViewById(R.id.start_conversation_view_pager);
187		ActionBar actionBar = getActionBar();
188		actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
189
190		mContactsTab = actionBar.newTab().setText(R.string.contacts)
191				.setTabListener(mTabListener);
192		mConferencesTab = actionBar.newTab().setText(R.string.conferences)
193				.setTabListener(mTabListener);
194		actionBar.addTab(mContactsTab);
195		actionBar.addTab(mConferencesTab);
196
197		mViewPager.setOnPageChangeListener(mOnPageChangeListener);
198		mViewPager.setAdapter(new FragmentPagerAdapter(getFragmentManager()) {
199
200			@Override
201			public int getCount() {
202				return 2;
203			}
204
205			@Override
206			public Fragment getItem(int position) {
207				if (position == 0) {
208					return mContactsListFragment;
209				} else {
210					return mConferenceListFragment;
211				}
212			}
213		});
214
215		mConferenceAdapter = new ListItemAdapter(this, conferences);
216		mConferenceListFragment.setListAdapter(mConferenceAdapter);
217		mConferenceListFragment.setContextMenu(R.menu.conference_context);
218		mConferenceListFragment
219				.setOnListItemClickListener(new OnItemClickListener() {
220
221					@Override
222					public void onItemClick(AdapterView<?> arg0, View arg1,
223											int position, long arg3) {
224						openConversationForBookmark(position);
225					}
226				});
227
228		mContactsAdapter = new ListItemAdapter(this, contacts);
229		mContactsListFragment.setListAdapter(mContactsAdapter);
230		mContactsListFragment.setContextMenu(R.menu.contact_context);
231		mContactsListFragment
232				.setOnListItemClickListener(new OnItemClickListener() {
233
234					@Override
235					public void onItemClick(AdapterView<?> arg0, View arg1,
236											int position, long arg3) {
237						openConversationForContact(position);
238					}
239				});
240
241	}
242
243	@Override
244	public void onStop() {
245		super.onStop();
246		xmppConnectionService.removeOnRosterUpdateListener();
247	}
248
249	protected void openConversationForContact(int position) {
250		Contact contact = (Contact) contacts.get(position);
251		Conversation conversation = xmppConnectionService
252				.findOrCreateConversation(contact.getAccount(),
253						contact.getJid(), false);
254		switchToConversation(conversation);
255	}
256
257	protected void openConversationForContact() {
258		int position = contact_context_id;
259		openConversationForContact(position);
260	}
261
262	protected void openConversationForBookmark() {
263		openConversationForBookmark(conference_context_id);
264	}
265
266	protected void openConversationForBookmark(int position) {
267		Bookmark bookmark = (Bookmark) conferences.get(position);
268		Conversation conversation = xmppConnectionService
269				.findOrCreateConversation(bookmark.getAccount(),
270						bookmark.getJid(), true);
271		conversation.setBookmark(bookmark);
272		if (!conversation.getMucOptions().online()) {
273			xmppConnectionService.joinMuc(conversation);
274		}
275		if (!bookmark.autojoin()) {
276			bookmark.setAutojoin(true);
277			xmppConnectionService.pushBookmarks(bookmark.getAccount());
278		}
279		switchToConversation(conversation);
280	}
281
282	protected void openDetailsForContact() {
283		int position = contact_context_id;
284		Contact contact = (Contact) contacts.get(position);
285		switchToContactDetails(contact);
286	}
287
288	protected void deleteContact() {
289		int position = contact_context_id;
290		final Contact contact = (Contact) contacts.get(position);
291		AlertDialog.Builder builder = new AlertDialog.Builder(this);
292		builder.setNegativeButton(R.string.cancel, null);
293		builder.setTitle(R.string.action_delete_contact);
294		builder.setMessage(getString(R.string.remove_contact_text,
295				contact.getJid()));
296		builder.setPositiveButton(R.string.delete, new OnClickListener() {
297
298			@Override
299			public void onClick(DialogInterface dialog, int which) {
300				xmppConnectionService.deleteContactOnServer(contact);
301				filter(mSearchEditText.getText().toString());
302			}
303		});
304		builder.create().show();
305
306	}
307
308	protected void deleteConference() {
309		int position = conference_context_id;
310		final Bookmark bookmark = (Bookmark) conferences.get(position);
311
312		AlertDialog.Builder builder = new AlertDialog.Builder(this);
313		builder.setNegativeButton(R.string.cancel, null);
314		builder.setTitle(R.string.delete_bookmark);
315		builder.setMessage(getString(R.string.remove_bookmark_text,
316				bookmark.getJid()));
317		builder.setPositiveButton(R.string.delete, new OnClickListener() {
318
319			@Override
320			public void onClick(DialogInterface dialog, int which) {
321				bookmark.unregisterConversation();
322				Account account = bookmark.getAccount();
323				account.getBookmarks().remove(bookmark);
324				xmppConnectionService.pushBookmarks(account);
325				filter(mSearchEditText.getText().toString());
326			}
327		});
328		builder.create().show();
329
330	}
331
332	@SuppressLint("InflateParams")
333	protected void showCreateContactDialog(String prefilledJid) {
334		AlertDialog.Builder builder = new AlertDialog.Builder(this);
335		builder.setTitle(R.string.create_contact);
336		View dialogView = getLayoutInflater().inflate(
337				R.layout.create_contact_dialog, null);
338		final Spinner spinner = (Spinner) dialogView.findViewById(R.id.account);
339		final AutoCompleteTextView jid = (AutoCompleteTextView) dialogView
340				.findViewById(R.id.jid);
341		jid.setAdapter(new KnownHostsAdapter(this,
342				android.R.layout.simple_list_item_1, mKnownHosts));
343		if (prefilledJid != null) {
344			jid.append(prefilledJid);
345		}
346		populateAccountSpinner(spinner);
347		builder.setView(dialogView);
348		builder.setNegativeButton(R.string.cancel, null);
349		builder.setPositiveButton(R.string.create, null);
350		final AlertDialog dialog = builder.create();
351		dialog.show();
352		dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(
353				new View.OnClickListener() {
354
355					@Override
356					public void onClick(View v) {
357						if (!xmppConnectionServiceBound) {
358							return;
359						}
360						if (Validator.isValidJid(jid.getText().toString())) {
361							String accountJid = (String) spinner
362									.getSelectedItem();
363							String contactJid = jid.getText().toString();
364							Account account = xmppConnectionService
365									.findAccountByJid(accountJid);
366							if (account == null) {
367								dialog.dismiss();
368								return;
369							}
370							Contact contact = account.getRoster().getContact(
371									contactJid);
372							if (contact.showInRoster()) {
373								jid.setError(getString(R.string.contact_already_exists));
374							} else {
375								xmppConnectionService.createContact(contact);
376								dialog.dismiss();
377								switchToConversation(contact);
378							}
379						} else {
380							jid.setError(getString(R.string.invalid_jid));
381						}
382					}
383				});
384
385	}
386
387	@SuppressLint("InflateParams")
388	protected void showJoinConferenceDialog(String prefilledJid) {
389		AlertDialog.Builder builder = new AlertDialog.Builder(this);
390		builder.setTitle(R.string.join_conference);
391		View dialogView = getLayoutInflater().inflate(
392				R.layout.join_conference_dialog, null);
393		final Spinner spinner = (Spinner) dialogView.findViewById(R.id.account);
394		final AutoCompleteTextView jid = (AutoCompleteTextView) dialogView
395				.findViewById(R.id.jid);
396		jid.setAdapter(new KnownHostsAdapter(this,
397				android.R.layout.simple_list_item_1, mKnownConferenceHosts));
398		if (prefilledJid != null) {
399			jid.append(prefilledJid);
400		}
401		populateAccountSpinner(spinner);
402		final CheckBox bookmarkCheckBox = (CheckBox) dialogView
403				.findViewById(R.id.bookmark);
404		builder.setView(dialogView);
405		builder.setNegativeButton(R.string.cancel, null);
406		builder.setPositiveButton(R.string.join, null);
407		final AlertDialog dialog = builder.create();
408		dialog.show();
409		dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(
410				new View.OnClickListener() {
411
412					@Override
413					public void onClick(View v) {
414						if (!xmppConnectionServiceBound) {
415							return;
416						}
417						if (Validator.isValidJid(jid.getText().toString())) {
418							String accountJid = (String) spinner
419									.getSelectedItem();
420							String conferenceJid = jid.getText().toString();
421							Account account = xmppConnectionService
422									.findAccountByJid(accountJid);
423							if (account == null) {
424								dialog.dismiss();
425								return;
426							}
427							if (bookmarkCheckBox.isChecked()) {
428								if (account.hasBookmarkFor(conferenceJid)) {
429									jid.setError(getString(R.string.bookmark_already_exists));
430								} else {
431									Bookmark bookmark = new Bookmark(account,
432											conferenceJid);
433									bookmark.setAutojoin(true);
434									account.getBookmarks().add(bookmark);
435									xmppConnectionService
436											.pushBookmarks(account);
437									Conversation conversation = xmppConnectionService
438											.findOrCreateConversation(account,
439													conferenceJid, true);
440									conversation.setBookmark(bookmark);
441									if (!conversation.getMucOptions().online()) {
442										xmppConnectionService
443												.joinMuc(conversation);
444									}
445									dialog.dismiss();
446									switchToConversation(conversation);
447								}
448							} else {
449								Conversation conversation = xmppConnectionService
450										.findOrCreateConversation(account,
451												conferenceJid, true);
452								if (!conversation.getMucOptions().online()) {
453									xmppConnectionService.joinMuc(conversation);
454								}
455								dialog.dismiss();
456								switchToConversation(conversation);
457							}
458						} else {
459							jid.setError(getString(R.string.invalid_jid));
460						}
461					}
462				});
463	}
464
465	protected void switchToConversation(Contact contact) {
466		Conversation conversation = xmppConnectionService
467				.findOrCreateConversation(contact.getAccount(),
468						contact.getJid(), false);
469		switchToConversation(conversation);
470	}
471
472	private void populateAccountSpinner(Spinner spinner) {
473		ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
474				android.R.layout.simple_spinner_item, mActivatedAccounts);
475		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
476		spinner.setAdapter(adapter);
477	}
478
479	@Override
480	public boolean onCreateOptionsMenu(Menu menu) {
481		this.mOptionsMenu = menu;
482		getMenuInflater().inflate(R.menu.start_conversation, menu);
483		MenuItem menuCreateContact = menu
484				.findItem(R.id.action_create_contact);
485		MenuItem menuCreateConference = menu
486				.findItem(R.id.action_join_conference);
487		mMenuSearchView = menu.findItem(R.id.action_search);
488		mMenuSearchView.setOnActionExpandListener(mOnActionExpandListener);
489		View mSearchView = mMenuSearchView.getActionView();
490		mSearchEditText = (EditText) mSearchView
491				.findViewById(R.id.search_field);
492		mSearchEditText.addTextChangedListener(mSearchTextWatcher);
493		if (getActionBar().getSelectedNavigationIndex() == 0) {
494			menuCreateConference.setVisible(false);
495		} else {
496			menuCreateContact.setVisible(false);
497		}
498		if (mInitialJid != null) {
499			mMenuSearchView.expandActionView();
500			mSearchEditText.append(mInitialJid);
501			filter(mInitialJid);
502		}
503		return true;
504	}
505
506	@Override
507	public boolean onOptionsItemSelected(MenuItem item) {
508		switch (item.getItemId()) {
509			case R.id.action_create_contact:
510				showCreateContactDialog(null);
511				return true;
512			case R.id.action_join_conference:
513				showJoinConferenceDialog(null);
514				return true;
515			case R.id.action_scan_qr_code:
516				new IntentIntegrator(this).initiateScan();
517				return true;
518		}
519		return super.onOptionsItemSelected(item);
520	}
521
522	@Override
523	public boolean onKeyUp(int keyCode, KeyEvent event) {
524		if (keyCode == KeyEvent.KEYCODE_SEARCH && !event.isLongPress()) {
525			mOptionsMenu.findItem(R.id.action_search).expandActionView();
526			return true;
527		}
528		return super.onKeyUp(keyCode, event);
529	}
530
531	@Override
532	public void onActivityResult(int requestCode, int resultCode, Intent intent) {
533		if ((requestCode & 0xFFFF) == IntentIntegrator.REQUEST_CODE) {
534			IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
535			if (scanResult != null && scanResult.getFormatName() != null) {
536				String data = scanResult.getContents();
537				Invite invite = new Invite(data);
538				if (xmppConnectionServiceBound) {
539					invite.invite();
540				} else if (invite.jid != null) {
541					this.mPendingInvite = invite;
542				} else {
543					this.mPendingInvite = null;
544				}
545			}
546		}
547		super.onActivityResult(requestCode, requestCode, intent);
548	}
549
550	@Override
551	protected void onBackendConnected() {
552		xmppConnectionService.setOnRosterUpdateListener(this.onRosterUpdate);
553		this.mActivatedAccounts.clear();
554		for (Account account : xmppConnectionService.getAccounts()) {
555			if (account.getStatus() != Account.STATUS_DISABLED) {
556				this.mActivatedAccounts.add(account.getJid());
557			}
558		}
559		this.mKnownHosts = xmppConnectionService.getKnownHosts();
560		this.mKnownConferenceHosts = xmppConnectionService
561				.getKnownConferenceHosts();
562		if (this.mPendingInvite != null) {
563			mPendingInvite.invite();
564			this.mPendingInvite = null;
565		} else if (!handleIntent(getIntent())) {
566			if (mSearchEditText != null) {
567				filter(mSearchEditText.getText().toString());
568			} else {
569				filter(null);
570			}
571		}
572		setIntent(null);
573	}
574
575	protected boolean handleIntent(Intent intent) {
576		if (intent == null || intent.getAction() == null) {
577			return false;
578		}
579		Invite invite = null;
580		switch (intent.getAction()) {
581			case Intent.ACTION_SENDTO:
582				try {
583					// TODO use Intent.parse ?!?
584					// sample: imto://xmpp/jid@foo.com
585					String jid = URLDecoder.decode(
586							intent.getData().getEncodedPath(), "UTF-8").split(
587							"/")[1];
588					return handleJid(jid);
589				} catch (UnsupportedEncodingException e) {
590					return false;
591				}
592			case Intent.ACTION_VIEW:
593				invite = new Invite(intent.getData());
594				return invite.invite();
595			case NfcAdapter.ACTION_NDEF_DISCOVERED:
596				for (Parcelable message : getIntent().getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)) {
597					if (message instanceof NdefMessage) {
598						Log.d(Config.LOGTAG, "received message=" + message);
599						for (NdefRecord record : ((NdefMessage)message).getRecords()) {
600							switch (record.getTnf()) {
601							case NdefRecord.TNF_WELL_KNOWN:
602								if (Arrays.equals(record.getType(), NdefRecord.RTD_URI)) {
603									if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
604										invite = new Invite(record.toUri());
605									} else {
606										byte[] mPayload = record.getPayload();
607										if (mPayload[0] == 0) {
608											invite = new Invite(Uri.parse(new String(Arrays.copyOfRange(
609													mPayload, 1, mPayload.length))));
610										}
611									}
612									if (invite != null) {
613										return invite.invite();
614									}
615								}
616							}
617						}
618					}
619				}
620				return false;
621			default:
622				return false;
623		}
624	}
625
626	private boolean handleJid(String jid) {
627		List<Contact> contacts = xmppConnectionService.findContacts(jid);
628		if (contacts.size() == 0) {
629			showCreateContactDialog(jid);
630			return false;
631		} else if (contacts.size() == 1) {
632			switchToConversation(contacts.get(0));
633			return true;
634		} else {
635			if (mMenuSearchView != null) {
636				mMenuSearchView.expandActionView();
637				mSearchEditText.setText("");
638				mSearchEditText.append(jid);
639				filter(jid);
640			} else {
641				mInitialJid = jid;
642			}
643			return true;
644		}
645	}
646
647	protected void filter(String needle) {
648		if (xmppConnectionServiceBound) {
649			this.filterContacts(needle);
650			this.filterConferences(needle);
651		}
652	}
653
654	protected void filterContacts(String needle) {
655		this.contacts.clear();
656		for (Account account : xmppConnectionService.getAccounts()) {
657			if (account.getStatus() != Account.STATUS_DISABLED) {
658				for (Contact contact : account.getRoster().getContacts()) {
659					if (contact.showInRoster() && contact.match(needle)) {
660						this.contacts.add(contact);
661					}
662				}
663			}
664		}
665		Collections.sort(this.contacts);
666		mContactsAdapter.notifyDataSetChanged();
667	}
668
669	protected void filterConferences(String needle) {
670		this.conferences.clear();
671		for (Account account : xmppConnectionService.getAccounts()) {
672			if (account.getStatus() != Account.STATUS_DISABLED) {
673				for (Bookmark bookmark : account.getBookmarks()) {
674					if (bookmark.match(needle)) {
675						this.conferences.add(bookmark);
676					}
677				}
678			}
679		}
680		Collections.sort(this.conferences);
681		mConferenceAdapter.notifyDataSetChanged();
682	}
683
684	private void onTabChanged() {
685		invalidateOptionsMenu();
686	}
687
688	public static class MyListFragment extends ListFragment {
689		private AdapterView.OnItemClickListener mOnItemClickListener;
690		private int mResContextMenu;
691
692		public void setContextMenu(int res) {
693			this.mResContextMenu = res;
694		}
695
696		@Override
697		public void onListItemClick(ListView l, View v, int position, long id) {
698			if (mOnItemClickListener != null) {
699				mOnItemClickListener.onItemClick(l, v, position, id);
700			}
701		}
702
703		public void setOnListItemClickListener(AdapterView.OnItemClickListener l) {
704			this.mOnItemClickListener = l;
705		}
706
707		@Override
708		public void onViewCreated(View view, Bundle savedInstanceState) {
709			super.onViewCreated(view, savedInstanceState);
710			registerForContextMenu(getListView());
711			getListView().setFastScrollEnabled(true);
712		}
713
714		@Override
715		public void onCreateContextMenu(ContextMenu menu, View v,
716										ContextMenuInfo menuInfo) {
717			super.onCreateContextMenu(menu, v, menuInfo);
718			StartConversationActivity activity = (StartConversationActivity) getActivity();
719			activity.getMenuInflater().inflate(mResContextMenu, menu);
720			AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
721			if (mResContextMenu == R.menu.conference_context) {
722				activity.conference_context_id = acmi.position;
723			} else {
724				activity.contact_context_id = acmi.position;
725			}
726		}
727
728		@Override
729		public boolean onContextItemSelected(MenuItem item) {
730			StartConversationActivity activity = (StartConversationActivity) getActivity();
731			switch (item.getItemId()) {
732				case R.id.context_start_conversation:
733					activity.openConversationForContact();
734					break;
735				case R.id.context_contact_details:
736					activity.openDetailsForContact();
737					break;
738				case R.id.context_delete_contact:
739					activity.deleteContact();
740					break;
741				case R.id.context_join_conference:
742					activity.openConversationForBookmark();
743					break;
744				case R.id.context_delete_conference:
745					activity.deleteConference();
746			}
747			return true;
748		}
749	}
750
751	private class Invite {
752		private String jid;
753		private boolean muc;
754
755		Invite(Uri uri) {
756			parse(uri);
757		}
758
759		Invite(String uri) {
760			try {
761				parse(Uri.parse(uri));
762			} catch (IllegalArgumentException e) {
763				jid = null;
764			}
765		}
766
767		boolean invite() {
768			if (jid != null) {
769				if (muc) {
770					showJoinConferenceDialog(jid);
771				} else {
772					return handleJid(jid);
773				}
774			}
775			return false;
776		}
777
778		void parse(Uri uri) {
779			muc = uri.getQuery() != null && uri.getQuery().equalsIgnoreCase("join");
780			if (uri.getAuthority() != null) {
781				jid = uri.getAuthority();
782			} else {
783				jid = uri.getSchemeSpecificPart().split("\\?")[0];
784			}
785		}
786	}
787}