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