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(getApplicationContext(),
195 conferences);
196 mConferenceListFragment.setListAdapter(mConferenceAdapter);
197 mConferenceListFragment.setContextMenu(R.menu.conference_context);
198 mConferenceListFragment
199 .setOnListItemClickListener(new OnItemClickListener() {
200
201 @Override
202 public void onItemClick(AdapterView<?> arg0, View arg1,
203 int position, long arg3) {
204 openConversationForBookmark(position);
205 }
206 });
207
208 mContactsAdapter = new ListItemAdapter(getApplicationContext(),
209 contacts);
210 mContactsListFragment.setListAdapter(mContactsAdapter);
211 mContactsListFragment.setContextMenu(R.menu.contact_context);
212 mContactsListFragment
213 .setOnListItemClickListener(new OnItemClickListener() {
214
215 @Override
216 public void onItemClick(AdapterView<?> arg0, View arg1,
217 int position, long arg3) {
218 openConversationForContact(position);
219 }
220 });
221
222 }
223
224 @Override
225 public void onStop() {
226 super.onStop();
227 xmppConnectionService.removeOnRosterUpdateListener();
228 }
229
230 protected void openConversationForContact(int position) {
231 Contact contact = (Contact) contacts.get(position);
232 Conversation conversation = xmppConnectionService
233 .findOrCreateConversation(contact.getAccount(),
234 contact.getJid(), false);
235 switchToConversation(conversation);
236 }
237
238 protected void openConversationForContact() {
239 int position = contact_context_id;
240 openConversationForContact(position);
241 }
242
243 protected void openConversationForBookmark() {
244 openConversationForBookmark(conference_context_id);
245 }
246
247 protected void openConversationForBookmark(int position) {
248 Bookmark bookmark = (Bookmark) conferences.get(position);
249 Conversation conversation = xmppConnectionService
250 .findOrCreateConversation(bookmark.getAccount(),
251 bookmark.getJid(), true);
252 conversation.setBookmark(bookmark);
253 if (!conversation.getMucOptions().online()) {
254 xmppConnectionService.joinMuc(conversation);
255 }
256 if (!bookmark.autojoin()) {
257 bookmark.setAutojoin(true);
258 xmppConnectionService.pushBookmarks(bookmark.getAccount());
259 }
260 switchToConversation(conversation);
261 }
262
263 protected void openDetailsForContact() {
264 int position = contact_context_id;
265 Contact contact = (Contact) contacts.get(position);
266 switchToContactDetails(contact);
267 }
268
269 protected void deleteContact() {
270 int position = contact_context_id;
271 final Contact contact = (Contact) contacts.get(position);
272 AlertDialog.Builder builder = new AlertDialog.Builder(this);
273 builder.setNegativeButton(R.string.cancel, null);
274 builder.setTitle(R.string.action_delete_contact);
275 builder.setMessage(getString(R.string.remove_contact_text,
276 contact.getJid()));
277 builder.setPositiveButton(R.string.delete, new OnClickListener() {
278
279 @Override
280 public void onClick(DialogInterface dialog, int which) {
281 xmppConnectionService.deleteContactOnServer(contact);
282 filter(mSearchEditText.getText().toString());
283 }
284 });
285 builder.create().show();
286
287 }
288
289 protected void deleteConference() {
290 int position = conference_context_id;
291 final Bookmark bookmark = (Bookmark) conferences.get(position);
292
293 AlertDialog.Builder builder = new AlertDialog.Builder(this);
294 builder.setNegativeButton(R.string.cancel, null);
295 builder.setTitle(R.string.delete_bookmark);
296 builder.setMessage(getString(R.string.remove_bookmark_text,
297 bookmark.getJid()));
298 builder.setPositiveButton(R.string.delete, new OnClickListener() {
299
300 @Override
301 public void onClick(DialogInterface dialog, int which) {
302 bookmark.unregisterConversation();
303 Account account = bookmark.getAccount();
304 account.getBookmarks().remove(bookmark);
305 xmppConnectionService.pushBookmarks(account);
306 filter(mSearchEditText.getText().toString());
307 }
308 });
309 builder.create().show();
310
311 }
312
313 protected void showCreateContactDialog() {
314 AlertDialog.Builder builder = new AlertDialog.Builder(this);
315 builder.setTitle(R.string.create_contact);
316 View dialogView = getLayoutInflater().inflate(
317 R.layout.create_contact_dialog, null);
318 final Spinner spinner = (Spinner) dialogView.findViewById(R.id.account);
319 final AutoCompleteTextView jid = (AutoCompleteTextView) dialogView
320 .findViewById(R.id.jid);
321 jid.setAdapter(new KnownHostsAdapter(this,
322 android.R.layout.simple_list_item_1, mKnownHosts));
323 populateAccountSpinner(spinner);
324 builder.setView(dialogView);
325 builder.setNegativeButton(R.string.cancel, null);
326 builder.setPositiveButton(R.string.create, null);
327 final AlertDialog dialog = builder.create();
328 dialog.show();
329 dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(
330 new View.OnClickListener() {
331
332 @Override
333 public void onClick(View v) {
334 if (!xmppConnectionServiceBound) {
335 return;
336 }
337 if (Validator.isValidJid(jid.getText().toString())) {
338 String accountJid = (String) spinner
339 .getSelectedItem();
340 String contactJid = jid.getText().toString();
341 Account account = xmppConnectionService
342 .findAccountByJid(accountJid);
343 if (account == null) {
344 dialog.dismiss();
345 return;
346 }
347 Contact contact = account.getRoster().getContact(
348 contactJid);
349 if (contact.showInRoster()) {
350 jid.setError(getString(R.string.contact_already_exists));
351 } else {
352 xmppConnectionService.createContact(contact);
353 switchToConversation(contact);
354 dialog.dismiss();
355 }
356 } else {
357 jid.setError(getString(R.string.invalid_jid));
358 }
359 }
360 });
361
362 }
363
364 protected void showJoinConferenceDialog() {
365 AlertDialog.Builder builder = new AlertDialog.Builder(this);
366 builder.setTitle(R.string.join_conference);
367 View dialogView = getLayoutInflater().inflate(
368 R.layout.join_conference_dialog, null);
369 final Spinner spinner = (Spinner) dialogView.findViewById(R.id.account);
370 final AutoCompleteTextView jid = (AutoCompleteTextView) dialogView
371 .findViewById(R.id.jid);
372 jid.setAdapter(new KnownHostsAdapter(this,
373 android.R.layout.simple_list_item_1, mKnownConferenceHosts));
374 populateAccountSpinner(spinner);
375 final CheckBox bookmarkCheckBox = (CheckBox) dialogView
376 .findViewById(R.id.bookmark);
377 builder.setView(dialogView);
378 builder.setNegativeButton(R.string.cancel, null);
379 builder.setPositiveButton(R.string.join, null);
380 final AlertDialog dialog = builder.create();
381 dialog.show();
382 dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(
383 new View.OnClickListener() {
384
385 @Override
386 public void onClick(View v) {
387 if (!xmppConnectionServiceBound) {
388 return;
389 }
390 if (Validator.isValidJid(jid.getText().toString())) {
391 String accountJid = (String) spinner
392 .getSelectedItem();
393 String conferenceJid = jid.getText().toString();
394 Account account = xmppConnectionService
395 .findAccountByJid(accountJid);
396 if (bookmarkCheckBox.isChecked()) {
397 if (account.hasBookmarkFor(conferenceJid)) {
398 jid.setError(getString(R.string.bookmark_already_exists));
399 } else {
400 Bookmark bookmark = new Bookmark(account,
401 conferenceJid);
402 bookmark.setAutojoin(true);
403 account.getBookmarks().add(bookmark);
404 xmppConnectionService
405 .pushBookmarks(account);
406 Conversation conversation = xmppConnectionService
407 .findOrCreateConversation(account,
408 conferenceJid, true);
409 conversation.setBookmark(bookmark);
410 if (!conversation.getMucOptions().online()) {
411 xmppConnectionService
412 .joinMuc(conversation);
413 }
414 switchToConversation(conversation);
415 }
416 } else {
417 Conversation conversation = xmppConnectionService
418 .findOrCreateConversation(account,
419 conferenceJid, true);
420 if (!conversation.getMucOptions().online()) {
421 xmppConnectionService.joinMuc(conversation);
422 }
423 switchToConversation(conversation);
424 }
425 } else {
426 jid.setError(getString(R.string.invalid_jid));
427 }
428 }
429 });
430 }
431
432 protected void switchToConversation(Contact contact) {
433 Conversation conversation = xmppConnectionService
434 .findOrCreateConversation(contact.getAccount(),
435 contact.getJid(), false);
436 switchToConversation(conversation);
437 }
438
439 private void populateAccountSpinner(Spinner spinner) {
440 ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
441 android.R.layout.simple_spinner_item, mActivatedAccounts);
442 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
443 spinner.setAdapter(adapter);
444 }
445
446 @Override
447 public boolean onCreateOptionsMenu(Menu menu) {
448 this.mOptionsMenu = menu;
449 getMenuInflater().inflate(R.menu.start_conversation, menu);
450 MenuItem menuCreateContact = (MenuItem) menu
451 .findItem(R.id.action_create_contact);
452 MenuItem menuCreateConference = (MenuItem) menu
453 .findItem(R.id.action_join_conference);
454 MenuItem menuSearchView = (MenuItem) menu.findItem(R.id.action_search);
455 menuSearchView.setOnActionExpandListener(mOnActionExpandListener);
456 View mSearchView = menuSearchView.getActionView();
457 mSearchEditText = (EditText) mSearchView
458 .findViewById(R.id.search_field);
459 mSearchEditText.addTextChangedListener(mSearchTextWatcher);
460 if (getActionBar().getSelectedNavigationIndex() == 0) {
461 menuCreateConference.setVisible(false);
462 } else {
463 menuCreateContact.setVisible(false);
464 }
465 return true;
466 }
467
468 @Override
469 public boolean onOptionsItemSelected(MenuItem item) {
470 switch (item.getItemId()) {
471 case R.id.action_create_contact:
472 showCreateContactDialog();
473 break;
474 case R.id.action_join_conference:
475 showJoinConferenceDialog();
476 break;
477 }
478 return super.onOptionsItemSelected(item);
479 }
480
481 @Override
482 public boolean onKeyUp(int keyCode, KeyEvent event) {
483 if (keyCode == KeyEvent.KEYCODE_SEARCH && !event.isLongPress()) {
484 mOptionsMenu.findItem(R.id.action_search).expandActionView();
485 return true;
486 }
487 return super.onKeyUp(keyCode, event);
488 }
489
490 @Override
491 void onBackendConnected() {
492 xmppConnectionService.setOnRosterUpdateListener(this.onRosterUpdate);
493 if (mSearchEditText != null) {
494 filter(mSearchEditText.getText().toString());
495 } else {
496 filter(null);
497 }
498 this.mActivatedAccounts.clear();
499 for (Account account : xmppConnectionService.getAccounts()) {
500 if (account.getStatus() != Account.STATUS_DISABLED) {
501 this.mActivatedAccounts.add(account.getJid());
502 }
503 }
504 this.mKnownHosts = xmppConnectionService.getKnownHosts();
505 this.mKnownConferenceHosts = xmppConnectionService
506 .getKnownConferenceHosts();
507 }
508
509 protected void filter(String needle) {
510 if (xmppConnectionServiceBound) {
511 this.filterContacts(needle);
512 this.filterConferences(needle);
513 }
514 }
515
516 protected void filterContacts(String needle) {
517 this.contacts.clear();
518 for (Account account : xmppConnectionService.getAccounts()) {
519 if (account.getStatus() != Account.STATUS_DISABLED) {
520 for (Contact contact : account.getRoster().getContacts()) {
521 if (contact.showInRoster() && contact.match(needle)) {
522 this.contacts.add(contact);
523 }
524 }
525 }
526 }
527 Collections.sort(this.contacts);
528 mContactsAdapter.notifyDataSetChanged();
529 }
530
531 protected void filterConferences(String needle) {
532 this.conferences.clear();
533 for (Account account : xmppConnectionService.getAccounts()) {
534 if (account.getStatus() != Account.STATUS_DISABLED) {
535 for (Bookmark bookmark : account.getBookmarks()) {
536 if (bookmark.match(needle)) {
537 this.conferences.add(bookmark);
538 }
539 }
540 }
541 }
542 Collections.sort(this.conferences);
543 mConferenceAdapter.notifyDataSetChanged();
544 }
545
546 private void onTabChanged() {
547 invalidateOptionsMenu();
548 }
549
550 public static class MyListFragment extends ListFragment {
551 private AdapterView.OnItemClickListener mOnItemClickListener;
552 private int mResContextMenu;
553
554 public void setContextMenu(int res) {
555 this.mResContextMenu = res;
556 }
557
558 @Override
559 public void onListItemClick(ListView l, View v, int position, long id) {
560 if (mOnItemClickListener != null) {
561 mOnItemClickListener.onItemClick(l, v, position, id);
562 }
563 }
564
565 public void setOnListItemClickListener(AdapterView.OnItemClickListener l) {
566 this.mOnItemClickListener = l;
567 }
568
569 @Override
570 public void onViewCreated(View view, Bundle savedInstanceState) {
571 super.onViewCreated(view, savedInstanceState);
572 registerForContextMenu(getListView());
573 }
574
575 @Override
576 public void onCreateContextMenu(ContextMenu menu, View v,
577 ContextMenuInfo menuInfo) {
578 super.onCreateContextMenu(menu, v, menuInfo);
579 StartConversationActivity activity = (StartConversationActivity) getActivity();
580 activity.getMenuInflater().inflate(mResContextMenu, menu);
581 AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
582 if (mResContextMenu == R.menu.conference_context) {
583 activity.conference_context_id = acmi.position;
584 } else {
585 activity.contact_context_id = acmi.position;
586 }
587 }
588
589 @Override
590 public boolean onContextItemSelected(MenuItem item) {
591 StartConversationActivity activity = (StartConversationActivity) getActivity();
592 switch (item.getItemId()) {
593 case R.id.context_start_conversation:
594 activity.openConversationForContact();
595 break;
596 case R.id.context_contact_details:
597 activity.openDetailsForContact();
598 break;
599 case R.id.context_delete_contact:
600 activity.deleteContact();
601 break;
602 case R.id.context_join_conference:
603 activity.openConversationForBookmark();
604 break;
605 case R.id.context_delete_conference:
606 activity.deleteConference();
607 }
608 return true;
609 }
610 }
611}