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