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