StartConversationActivity.java

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