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