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 Contact contact = account.getRoster().getContact(
344 contactJid);
345 if (contact.showInRoster()) {
346 jid.setError(getString(R.string.contact_already_exists));
347 } else {
348 xmppConnectionService.createContact(contact);
349 switchToConversation(contact);
350 dialog.dismiss();
351 }
352 } else {
353 jid.setError(getString(R.string.invalid_jid));
354 }
355 }
356 });
357
358 }
359
360 protected void showJoinConferenceDialog() {
361 AlertDialog.Builder builder = new AlertDialog.Builder(this);
362 builder.setTitle(R.string.join_conference);
363 View dialogView = getLayoutInflater().inflate(
364 R.layout.join_conference_dialog, null);
365 final Spinner spinner = (Spinner) dialogView.findViewById(R.id.account);
366 final AutoCompleteTextView jid = (AutoCompleteTextView) dialogView
367 .findViewById(R.id.jid);
368 jid.setAdapter(new KnownHostsAdapter(this,
369 android.R.layout.simple_list_item_1, mKnownConferenceHosts));
370 populateAccountSpinner(spinner);
371 final CheckBox bookmarkCheckBox = (CheckBox) dialogView
372 .findViewById(R.id.bookmark);
373 builder.setView(dialogView);
374 builder.setNegativeButton(R.string.cancel, null);
375 builder.setPositiveButton(R.string.join, null);
376 final AlertDialog dialog = builder.create();
377 dialog.show();
378 dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(
379 new View.OnClickListener() {
380
381 @Override
382 public void onClick(View v) {
383 if (!xmppConnectionServiceBound) {
384 return;
385 }
386 if (Validator.isValidJid(jid.getText().toString())) {
387 String accountJid = (String) spinner
388 .getSelectedItem();
389 String conferenceJid = jid.getText().toString();
390 Account account = xmppConnectionService
391 .findAccountByJid(accountJid);
392 if (bookmarkCheckBox.isChecked()) {
393 if (account.hasBookmarkFor(conferenceJid)) {
394 jid.setError(getString(R.string.bookmark_already_exists));
395 } else {
396 Bookmark bookmark = new Bookmark(account,
397 conferenceJid);
398 bookmark.setAutojoin(true);
399 account.getBookmarks().add(bookmark);
400 xmppConnectionService
401 .pushBookmarks(account);
402 Conversation conversation = xmppConnectionService
403 .findOrCreateConversation(account,
404 conferenceJid, true);
405 conversation.setBookmark(bookmark);
406 if (!conversation.getMucOptions().online()) {
407 xmppConnectionService
408 .joinMuc(conversation);
409 }
410 switchToConversation(conversation);
411 }
412 } else {
413 Conversation conversation = xmppConnectionService
414 .findOrCreateConversation(account,
415 conferenceJid, true);
416 if (!conversation.getMucOptions().online()) {
417 xmppConnectionService.joinMuc(conversation);
418 }
419 switchToConversation(conversation);
420 }
421 } else {
422 jid.setError(getString(R.string.invalid_jid));
423 }
424 }
425 });
426 }
427
428 protected void switchToConversation(Contact contact) {
429 Conversation conversation = xmppConnectionService
430 .findOrCreateConversation(contact.getAccount(),
431 contact.getJid(), false);
432 switchToConversation(conversation);
433 }
434
435 private void populateAccountSpinner(Spinner spinner) {
436 ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
437 android.R.layout.simple_spinner_item, mActivatedAccounts);
438 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
439 spinner.setAdapter(adapter);
440 }
441
442 @Override
443 public boolean onCreateOptionsMenu(Menu menu) {
444 this.mOptionsMenu = menu;
445 getMenuInflater().inflate(R.menu.start_conversation, menu);
446 MenuItem menuCreateContact = (MenuItem) menu
447 .findItem(R.id.action_create_contact);
448 MenuItem menuCreateConference = (MenuItem) menu
449 .findItem(R.id.action_join_conference);
450 MenuItem menuSearchView = (MenuItem) menu.findItem(R.id.action_search);
451 menuSearchView.setOnActionExpandListener(mOnActionExpandListener);
452 View mSearchView = menuSearchView.getActionView();
453 mSearchEditText = (EditText) mSearchView
454 .findViewById(R.id.search_field);
455 mSearchEditText.addTextChangedListener(mSearchTextWatcher);
456 if (getActionBar().getSelectedNavigationIndex() == 0) {
457 menuCreateConference.setVisible(false);
458 } else {
459 menuCreateContact.setVisible(false);
460 }
461 return true;
462 }
463
464 @Override
465 public boolean onOptionsItemSelected(MenuItem item) {
466 switch (item.getItemId()) {
467 case R.id.action_create_contact:
468 showCreateContactDialog();
469 break;
470 case R.id.action_join_conference:
471 showJoinConferenceDialog();
472 break;
473 }
474 return super.onOptionsItemSelected(item);
475 }
476
477 @Override
478 public boolean onKeyUp(int keyCode, KeyEvent event) {
479 if (keyCode == KeyEvent.KEYCODE_SEARCH && !event.isLongPress()) {
480 mOptionsMenu.findItem(R.id.action_search).expandActionView();
481 return true;
482 }
483 return super.onKeyUp(keyCode, event);
484 }
485
486 @Override
487 void onBackendConnected() {
488 xmppConnectionService.setOnRosterUpdateListener(this.onRosterUpdate);
489 if (mSearchEditText != null) {
490 filter(mSearchEditText.getText().toString());
491 } else {
492 filter(null);
493 }
494 this.mActivatedAccounts.clear();
495 for (Account account : xmppConnectionService.getAccounts()) {
496 if (account.getStatus() != Account.STATUS_DISABLED) {
497 this.mActivatedAccounts.add(account.getJid());
498 }
499 }
500 this.mKnownHosts = xmppConnectionService.getKnownHosts();
501 this.mKnownConferenceHosts = xmppConnectionService
502 .getKnownConferenceHosts();
503 }
504
505 protected void filter(String needle) {
506 if (xmppConnectionServiceBound) {
507 this.filterContacts(needle);
508 this.filterConferences(needle);
509 }
510 }
511
512 protected void filterContacts(String needle) {
513 this.contacts.clear();
514 for (Account account : xmppConnectionService.getAccounts()) {
515 if (account.getStatus() != Account.STATUS_DISABLED) {
516 for (Contact contact : account.getRoster().getContacts()) {
517 if (contact.showInRoster() && contact.match(needle)) {
518 this.contacts.add(contact);
519 }
520 }
521 }
522 }
523 Collections.sort(this.contacts);
524 mContactsAdapter.notifyDataSetChanged();
525 }
526
527 protected void filterConferences(String needle) {
528 this.conferences.clear();
529 for (Account account : xmppConnectionService.getAccounts()) {
530 if (account.getStatus() != Account.STATUS_DISABLED) {
531 for (Bookmark bookmark : account.getBookmarks()) {
532 if (bookmark.match(needle)) {
533 this.conferences.add(bookmark);
534 }
535 }
536 }
537 }
538 Collections.sort(this.conferences);
539 mConferenceAdapter.notifyDataSetChanged();
540 }
541
542 private void onTabChanged() {
543 invalidateOptionsMenu();
544 }
545
546 public static class MyListFragment extends ListFragment {
547 private AdapterView.OnItemClickListener mOnItemClickListener;
548 private int mResContextMenu;
549
550 public void setContextMenu(int res) {
551 this.mResContextMenu = res;
552 }
553
554 @Override
555 public void onListItemClick(ListView l, View v, int position, long id) {
556 if (mOnItemClickListener != null) {
557 mOnItemClickListener.onItemClick(l, v, position, id);
558 }
559 }
560
561 public void setOnListItemClickListener(AdapterView.OnItemClickListener l) {
562 this.mOnItemClickListener = l;
563 }
564
565 @Override
566 public void onViewCreated(View view, Bundle savedInstanceState) {
567 super.onViewCreated(view, savedInstanceState);
568 registerForContextMenu(getListView());
569 }
570
571 @Override
572 public void onCreateContextMenu(ContextMenu menu, View v,
573 ContextMenuInfo menuInfo) {
574 super.onCreateContextMenu(menu, v, menuInfo);
575 StartConversationActivity activity = (StartConversationActivity) getActivity();
576 activity.getMenuInflater().inflate(mResContextMenu, menu);
577 AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
578 if (mResContextMenu == R.menu.conference_context) {
579 activity.conference_context_id = acmi.position;
580 } else {
581 activity.contact_context_id = acmi.position;
582 }
583 }
584
585 @Override
586 public boolean onContextItemSelected(MenuItem item) {
587 StartConversationActivity activity = (StartConversationActivity) getActivity();
588 switch (item.getItemId()) {
589 case R.id.context_start_conversation:
590 activity.openConversationForContact();
591 break;
592 case R.id.context_contact_details:
593 activity.openDetailsForContact();
594 break;
595 case R.id.context_delete_contact:
596 activity.deleteContact();
597 break;
598 case R.id.context_join_conference:
599 activity.openConversationForBookmark();
600 break;
601 case R.id.context_delete_conference:
602 activity.deleteConference();
603 }
604 return true;
605 }
606 }
607}