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