StartConversationActivity.java

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