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.LayoutInflater;
25import android.view.Menu;
26import android.view.MenuItem;
27import android.view.View;
28import android.view.ViewGroup;
29import android.view.inputmethod.InputMethodManager;
30import android.widget.AdapterView;
31import android.widget.AdapterView.AdapterContextMenuInfo;
32import android.widget.AdapterView.OnItemClickListener;
33import android.widget.ArrayAdapter;
34import android.widget.AutoCompleteTextView;
35import android.widget.CheckBox;
36import android.widget.EditText;
37import android.widget.ImageView;
38import android.widget.ListView;
39import android.widget.Spinner;
40import android.widget.TextView;
41import eu.siacs.conversations.R;
42import eu.siacs.conversations.entities.Account;
43import eu.siacs.conversations.entities.Bookmark;
44import eu.siacs.conversations.entities.Contact;
45import eu.siacs.conversations.entities.Conversation;
46import eu.siacs.conversations.entities.ListItem;
47import eu.siacs.conversations.utils.KnownHostsAdapter;
48import eu.siacs.conversations.utils.Validator;
49
50public class StartConversation extends XmppActivity {
51
52 private Tab mContactsTab;
53 private Tab mConferencesTab;
54 private ViewPager mViewPager;
55
56 private MyListFragment mContactsListFragment = new MyListFragment();
57 private List<ListItem> contacts = new ArrayList<ListItem>();
58 private ArrayAdapter<ListItem> mContactsAdapter;
59
60 private MyListFragment mConferenceListFragment = new MyListFragment();
61 private List<ListItem> conferences = new ArrayList<ListItem>();
62 private ArrayAdapter<ListItem> mConferenceAdapter;
63
64 private List<String> mActivatedAccounts = new ArrayList<String>();
65 private List<String> mKnownHosts;
66 private List<String> mKnownConferenceHosts;
67
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
146 @Override
147 public void onCreate(Bundle savedInstanceState) {
148 super.onCreate(savedInstanceState);
149 setContentView(R.layout.activity_start_conversation);
150 mViewPager = (ViewPager) findViewById(R.id.start_conversation_view_pager);
151 ActionBar actionBar = getActionBar();
152 actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
153
154 mContactsTab = actionBar.newTab().setText(R.string.contacts)
155 .setTabListener(mTabListener);
156 mConferencesTab = actionBar.newTab().setText(R.string.conferences)
157 .setTabListener(mTabListener);
158 actionBar.addTab(mContactsTab);
159 actionBar.addTab(mConferencesTab);
160
161 mViewPager.setOnPageChangeListener(mOnPageChangeListener);
162 mViewPager.setAdapter(new FragmentPagerAdapter(getFragmentManager()) {
163
164 @Override
165 public int getCount() {
166 return 2;
167 }
168
169 @Override
170 public Fragment getItem(int position) {
171 if (position == 0) {
172 return mContactsListFragment;
173 } else {
174 return mConferenceListFragment;
175 }
176 }
177 });
178
179 mConferenceAdapter = new ListItemAdapter(conferences);
180 mConferenceListFragment.setListAdapter(mConferenceAdapter);
181 mConferenceListFragment.setContextMenu(R.menu.conference_context);
182 mConferenceListFragment
183 .setOnListItemClickListener(new OnItemClickListener() {
184
185 @Override
186 public void onItemClick(AdapterView<?> arg0, View arg1,
187 int position, long arg3) {
188 openConversationForBookmark(position);
189 }
190 });
191
192 mContactsAdapter = new ListItemAdapter(contacts);
193 mContactsListFragment.setListAdapter(mContactsAdapter);
194 mContactsListFragment.setContextMenu(R.menu.contact_context);
195 mContactsListFragment
196 .setOnListItemClickListener(new OnItemClickListener() {
197
198 @Override
199 public void onItemClick(AdapterView<?> arg0, View arg1,
200 int position, long arg3) {
201 openConversationForContact(position);
202 }
203 });
204
205 }
206
207 protected void openConversationForContact(int position) {
208 Contact contact = (Contact) contacts.get(position);
209 Conversation conversation = xmppConnectionService
210 .findOrCreateConversation(contact.getAccount(),
211 contact.getJid(), false);
212 switchToConversation(conversation);
213 }
214
215 protected void openConversationForContact() {
216 int position = contact_context_id;
217 openConversationForContact(position);
218 }
219
220 protected void openConversationForBookmark() {
221 openConversationForBookmark(conference_context_id);
222 }
223
224 protected void openConversationForBookmark(int position) {
225 Bookmark bookmark = (Bookmark) conferences.get(position);
226 Conversation conversation = xmppConnectionService
227 .findOrCreateConversation(bookmark.getAccount(),
228 bookmark.getJid(), true);
229 conversation.setBookmark(bookmark);
230 if (!conversation.getMucOptions().online()) {
231 xmppConnectionService.joinMuc(conversation);
232 }
233 if (!bookmark.autojoin()) {
234 bookmark.setAutojoin(true);
235 xmppConnectionService.pushBookmarks(bookmark.getAccount());
236 }
237 switchToConversation(conversation);
238 }
239
240 protected void openDetailsForContact() {
241 int position = contact_context_id;
242 Contact contact = (Contact) contacts.get(position);
243 switchToContactDetails(contact);
244 }
245
246 protected void deleteContact() {
247 int position = contact_context_id;
248 final Contact contact = (Contact) contacts.get(position);
249 AlertDialog.Builder builder = new AlertDialog.Builder(this);
250 builder.setNegativeButton(R.string.cancel, null);
251 builder.setTitle(R.string.action_delete_contact);
252 builder.setMessage(
253 getString(R.string.remove_contact_text,
254 contact.getJid()));
255 builder.setPositiveButton(R.string.delete,new OnClickListener() {
256
257 @Override
258 public void onClick(DialogInterface dialog, int which) {
259 xmppConnectionService.deleteContactOnServer(contact);
260 filter(mSearchEditText.getText().toString());
261 }
262 });
263 builder.create().show();
264
265 }
266
267 protected void deleteConference() {
268 int position = conference_context_id;
269 final Bookmark bookmark = (Bookmark) conferences.get(position);
270
271 AlertDialog.Builder builder = new AlertDialog.Builder(this);
272 builder.setNegativeButton(R.string.cancel, null);
273 builder.setTitle(R.string.delete_bookmark);
274 builder.setMessage(
275 getString(R.string.remove_bookmark_text,
276 bookmark.getJid()));
277 builder.setPositiveButton(R.string.delete,new OnClickListener() {
278
279 @Override
280 public void onClick(DialogInterface dialog, int which) {
281 bookmark.unregisterConversation();
282 Account account = bookmark.getAccount();
283 account.getBookmarks().remove(bookmark);
284 xmppConnectionService.pushBookmarks(account);
285 filter(mSearchEditText.getText().toString());
286 }
287 });
288 builder.create().show();
289
290 }
291
292 protected void showCreateContactDialog() {
293 AlertDialog.Builder builder = new AlertDialog.Builder(this);
294 builder.setTitle(R.string.create_contact);
295 View dialogView = getLayoutInflater().inflate(
296 R.layout.create_contact_dialog, null);
297 final Spinner spinner = (Spinner) dialogView.findViewById(R.id.account);
298 final AutoCompleteTextView jid = (AutoCompleteTextView) dialogView
299 .findViewById(R.id.jid);
300 jid.setAdapter(new KnownHostsAdapter(this,
301 android.R.layout.simple_list_item_1, mKnownHosts));
302 populateAccountSpinner(spinner);
303 builder.setView(dialogView);
304 builder.setNegativeButton(R.string.cancel, null);
305 builder.setPositiveButton(R.string.create, null);
306 final AlertDialog dialog = builder.create();
307 dialog.show();
308 dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(
309 new View.OnClickListener() {
310
311 @Override
312 public void onClick(View v) {
313 if (Validator.isValidJid(jid.getText().toString())) {
314 String accountJid = (String) spinner
315 .getSelectedItem();
316 String contactJid = jid.getText().toString();
317 Account account = xmppConnectionService
318 .findAccountByJid(accountJid);
319 Contact contact = account.getRoster().getContact(
320 contactJid);
321 if (contact.showInRoster()) {
322 jid.setError(getString(R.string.contact_already_exists));
323 } else {
324 xmppConnectionService.createContact(contact);
325 switchToConversation(contact);
326 dialog.dismiss();
327 }
328 } else {
329 jid.setError(getString(R.string.invalid_jid));
330 }
331 }
332 });
333
334 }
335
336 protected void showJoinConferenceDialog() {
337 AlertDialog.Builder builder = new AlertDialog.Builder(this);
338 builder.setTitle(R.string.join_conference);
339 View dialogView = getLayoutInflater().inflate(
340 R.layout.join_conference_dialog, null);
341 final Spinner spinner = (Spinner) dialogView.findViewById(R.id.account);
342 final AutoCompleteTextView jid = (AutoCompleteTextView) dialogView
343 .findViewById(R.id.jid);
344 jid.setAdapter(new KnownHostsAdapter(this,
345 android.R.layout.simple_list_item_1, mKnownConferenceHosts));
346 populateAccountSpinner(spinner);
347 final CheckBox bookmarkCheckBox = (CheckBox) dialogView
348 .findViewById(R.id.bookmark);
349 builder.setView(dialogView);
350 builder.setNegativeButton(R.string.cancel, null);
351 builder.setPositiveButton(R.string.join, null);
352 final AlertDialog dialog = builder.create();
353 dialog.show();
354 dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(
355 new View.OnClickListener() {
356
357 @Override
358 public void onClick(View v) {
359 if (Validator.isValidJid(jid.getText().toString())) {
360 String accountJid = (String) spinner
361 .getSelectedItem();
362 String conferenceJid = jid.getText().toString();
363 Account account = xmppConnectionService
364 .findAccountByJid(accountJid);
365 if (bookmarkCheckBox.isChecked()) {
366 if (account.hasBookmarkFor(conferenceJid)) {
367 jid.setError(getString(R.string.bookmark_already_exists));
368 } else {
369 Bookmark bookmark = new Bookmark(account,
370 conferenceJid);
371 bookmark.setAutojoin(true);
372 account.getBookmarks().add(bookmark);
373 xmppConnectionService
374 .pushBookmarks(account);
375 Conversation conversation = xmppConnectionService
376 .findOrCreateConversation(account,
377 conferenceJid, true);
378 conversation.setBookmark(bookmark);
379 if (!conversation.getMucOptions().online()) {
380 xmppConnectionService.joinMuc(conversation);
381 }
382 switchToConversation(conversation);
383 }
384 } else {
385 Conversation conversation = xmppConnectionService
386 .findOrCreateConversation(account,
387 conferenceJid, true);
388 if (!conversation.getMucOptions().online()) {
389 xmppConnectionService.joinMuc(conversation);
390 }
391 switchToConversation(conversation);
392 }
393 } else {
394 jid.setError(getString(R.string.invalid_jid));
395 }
396 }
397 });
398 }
399
400 protected void switchToConversation(Contact contact) {
401 Conversation conversation = xmppConnectionService
402 .findOrCreateConversation(contact.getAccount(),
403 contact.getJid(), false);
404 switchToConversation(conversation);
405 }
406
407 private void populateAccountSpinner(Spinner spinner) {
408 ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
409 android.R.layout.simple_spinner_item, mActivatedAccounts);
410 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
411 spinner.setAdapter(adapter);
412 }
413
414 @Override
415 public boolean onCreateOptionsMenu(Menu menu) {
416 getMenuInflater().inflate(R.menu.start_conversation, menu);
417 MenuItem menuCreateContact = (MenuItem) menu
418 .findItem(R.id.action_create_contact);
419 MenuItem menuCreateConference = (MenuItem) menu
420 .findItem(R.id.action_join_conference);
421 MenuItem menuSearchView = (MenuItem) menu.findItem(R.id.action_search);
422 menuSearchView.setOnActionExpandListener(mOnActionExpandListener);
423 View mSearchView = menuSearchView.getActionView();
424 mSearchEditText = (EditText) mSearchView
425 .findViewById(R.id.search_field);
426 mSearchEditText.addTextChangedListener(mSearchTextWatcher);
427 if (getActionBar().getSelectedNavigationIndex() == 0) {
428 menuCreateConference.setVisible(false);
429 } else {
430 menuCreateContact.setVisible(false);
431 }
432 return true;
433 }
434
435 @Override
436 public boolean onOptionsItemSelected(MenuItem item) {
437 switch (item.getItemId()) {
438 case R.id.action_create_contact:
439 showCreateContactDialog();
440 break;
441 case R.id.action_join_conference:
442 showJoinConferenceDialog();
443 break;
444 }
445 return super.onOptionsItemSelected(item);
446 }
447
448 @Override
449 void onBackendConnected() {
450 if (mSearchEditText != null) {
451 filter(mSearchEditText.getText().toString());
452 } else {
453 filter(null);
454 }
455 this.mActivatedAccounts.clear();
456 for (Account account : xmppConnectionService.getAccounts()) {
457 if (account.getStatus() != Account.STATUS_DISABLED) {
458 this.mActivatedAccounts.add(account.getJid());
459 }
460 }
461 this.mKnownHosts = xmppConnectionService.getKnownHosts();
462 this.mKnownConferenceHosts = xmppConnectionService
463 .getKnownConferenceHosts();
464 }
465
466 protected void filter(String needle) {
467 this.filterContacts(needle);
468 this.filterConferences(needle);
469 }
470
471 protected void filterContacts(String needle) {
472 this.contacts.clear();
473 for (Account account : xmppConnectionService.getAccounts()) {
474 if (account.getStatus() != Account.STATUS_DISABLED) {
475 for (Contact contact : account.getRoster().getContacts()) {
476 if (contact.showInRoster() && contact.match(needle)) {
477 this.contacts.add(contact);
478 }
479 }
480 }
481 }
482 Collections.sort(this.contacts);
483 mContactsAdapter.notifyDataSetChanged();
484 }
485
486 protected void filterConferences(String needle) {
487 this.conferences.clear();
488 for (Account account : xmppConnectionService.getAccounts()) {
489 if (account.getStatus() != Account.STATUS_DISABLED) {
490 for (Bookmark bookmark : account.getBookmarks()) {
491 if (bookmark.match(needle)) {
492 this.conferences.add(bookmark);
493 }
494 }
495 }
496 }
497 Collections.sort(this.conferences);
498 mConferenceAdapter.notifyDataSetChanged();
499 }
500
501 private void onTabChanged() {
502 invalidateOptionsMenu();
503 }
504
505 private class ListItemAdapter extends ArrayAdapter<ListItem> {
506
507 public ListItemAdapter(List<ListItem> objects) {
508 super(getApplicationContext(), 0, objects);
509 }
510
511 @Override
512 public View getView(int position, View view, ViewGroup parent) {
513 LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
514 ListItem item = getItem(position);
515 if (view == null) {
516 view = (View) inflater.inflate(R.layout.contact, null);
517 }
518 TextView name = (TextView) view
519 .findViewById(R.id.contact_display_name);
520 TextView jid = (TextView) view.findViewById(R.id.contact_jid);
521 ImageView picture = (ImageView) view
522 .findViewById(R.id.contact_photo);
523
524 jid.setText(item.getJid());
525 name.setText(item.getDisplayName());
526 picture.setImageBitmap(item.getImage(48, getApplicationContext()));
527 return view;
528 }
529
530 }
531
532 public static class MyListFragment extends ListFragment {
533 private AdapterView.OnItemClickListener mOnItemClickListener;
534 private int mResContextMenu;
535
536 public void setContextMenu(int res) {
537 this.mResContextMenu = res;
538 }
539
540 @Override
541 public void onListItemClick(ListView l, View v, int position, long id) {
542 if (mOnItemClickListener != null) {
543 mOnItemClickListener.onItemClick(l, v, position, id);
544 }
545 }
546
547 public void setOnListItemClickListener(AdapterView.OnItemClickListener l) {
548 this.mOnItemClickListener = l;
549 }
550
551 @Override
552 public void onViewCreated(View view, Bundle savedInstanceState) {
553 super.onViewCreated(view, savedInstanceState);
554 registerForContextMenu(getListView());
555 }
556
557 @Override
558 public void onCreateContextMenu(ContextMenu menu, View v,
559 ContextMenuInfo menuInfo) {
560 super.onCreateContextMenu(menu, v, menuInfo);
561 StartConversation activity = (StartConversation) getActivity();
562 activity.getMenuInflater().inflate(mResContextMenu, menu);
563 AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
564 if (mResContextMenu == R.menu.conference_context) {
565 activity.conference_context_id = acmi.position;
566 } else {
567 activity.contact_context_id = acmi.position;
568 }
569 }
570
571 @Override
572 public boolean onContextItemSelected(MenuItem item) {
573 StartConversation activity = (StartConversation) getActivity();
574 switch (item.getItemId()) {
575 case R.id.context_start_conversation:
576 activity.openConversationForContact();
577 break;
578 case R.id.context_contact_details:
579 activity.openDetailsForContact();
580 break;
581 case R.id.context_delete_contact:
582 activity.deleteContact();
583 break;
584 case R.id.context_join_conference:
585 activity.openConversationForBookmark();
586 break;
587 case R.id.context_delete_conference:
588 activity.deleteConference();
589 }
590 return true;
591 }
592 }
593}