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