NewConversationActivity.java

  1package eu.siacs.conversations.ui;
  2
  3import java.io.UnsupportedEncodingException;
  4import java.math.BigInteger;
  5import java.net.URLDecoder;
  6import java.security.SecureRandom;
  7import java.util.ArrayList;
  8import java.util.Collections;
  9import java.util.Comparator;
 10import java.util.List;
 11
 12import eu.siacs.conversations.R;
 13import eu.siacs.conversations.entities.Account;
 14import eu.siacs.conversations.entities.Contact;
 15import eu.siacs.conversations.entities.Conversation;
 16import eu.siacs.conversations.utils.UIHelper;
 17import eu.siacs.conversations.utils.Validator;
 18import android.os.Bundle;
 19import android.text.Editable;
 20import android.text.TextWatcher;
 21import android.util.Log;
 22import android.util.SparseBooleanArray;
 23import android.view.ActionMode;
 24import android.view.LayoutInflater;
 25import android.view.Menu;
 26import android.view.MenuInflater;
 27import android.view.MenuItem;
 28import android.view.View;
 29import android.view.ViewGroup;
 30import android.widget.AbsListView;
 31import android.widget.AdapterView;
 32import android.widget.AdapterView.OnItemClickListener;
 33import android.widget.AdapterView.OnItemLongClickListener;
 34import android.widget.ArrayAdapter;
 35import android.widget.EditText;
 36import android.widget.ListView;
 37import android.widget.ProgressBar;
 38import android.widget.TextView;
 39import android.widget.ImageView;
 40import android.annotation.SuppressLint;
 41import android.app.AlertDialog;
 42import android.content.Context;
 43import android.content.DialogInterface;
 44import android.content.DialogInterface.OnClickListener;
 45import android.content.Intent;
 46
 47public class NewConversationActivity extends XmppActivity {
 48
 49	protected List<Contact> rosterContacts = new ArrayList<Contact>();
 50	protected List<Contact> aggregatedContacts = new ArrayList<Contact>();
 51	protected ListView contactsView;
 52	protected ArrayAdapter<Contact> contactsAdapter;
 53
 54	protected EditText search;
 55	protected String searchString = "";
 56	private TextView contactsHeader;
 57	private List<Account> accounts;
 58	private List<Contact> selectedContacts = new ArrayList<Contact>();
 59
 60	private boolean isActionMode = false;
 61	private boolean inviteIntent = false;
 62	private ActionMode actionMode = null;
 63	private AbsListView.MultiChoiceModeListener actionModeCallback = new AbsListView.MultiChoiceModeListener() {
 64
 65		@Override
 66		public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
 67			menu.clear();
 68			MenuInflater inflater = mode.getMenuInflater();
 69			inflater.inflate(R.menu.newconversation_context, menu);
 70			SparseBooleanArray checkedItems = contactsView
 71					.getCheckedItemPositions();
 72			selectedContacts.clear();
 73			for (int i = 0; i < aggregatedContacts.size(); ++i) {
 74				if (checkedItems.get(i, false)) {
 75					selectedContacts.add(aggregatedContacts.get(i));
 76				}
 77			}
 78			if (selectedContacts.size() == 0) {
 79				menu.findItem(R.id.action_start_conversation).setVisible(false);
 80				menu.findItem(R.id.action_contact_details).setVisible(false);
 81				menu.findItem(R.id.action_invite).setVisible(false);
 82			} else if ((selectedContacts.size() == 1)&&(!inviteIntent)) {
 83				menu.findItem(R.id.action_start_conversation).setVisible(true);
 84				menu.findItem(R.id.action_contact_details).setVisible(true);
 85				menu.findItem(R.id.action_invite).setVisible(false);
 86			} else if (!inviteIntent){
 87				menu.findItem(R.id.action_start_conversation).setVisible(true);
 88				menu.findItem(R.id.action_contact_details).setVisible(false);
 89				menu.findItem(R.id.action_invite).setVisible(false);
 90			} else {
 91				menu.findItem(R.id.action_invite).setVisible(true);
 92				menu.findItem(R.id.action_start_conversation).setVisible(false);
 93				menu.findItem(R.id.action_contact_details).setVisible(false);
 94			}
 95			return true;
 96		}
 97
 98		@Override
 99		public void onDestroyActionMode(ActionMode mode) {
100			// TODO Auto-generated method stub
101
102		}
103
104		@Override
105		public boolean onCreateActionMode(ActionMode mode, Menu menu) {
106			return true;
107		}
108
109		@Override
110		public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
111			switch (item.getItemId()) {
112			case R.id.action_start_conversation:
113				if (selectedContacts.size() == 1) {
114					startConversation(selectedContacts.get(0));
115				} else {
116					startConference();
117				}
118				break;
119			case R.id.action_contact_details:
120				Intent intent = new Intent(getApplicationContext(),
121						ContactDetailsActivity.class);
122				intent.setAction(ContactDetailsActivity.ACTION_VIEW_CONTACT);
123				intent.putExtra("uuid", selectedContacts.get(0).getUuid());
124				startActivity(intent);
125				break;
126			case R.id.action_invite:
127				invite();
128		break;
129			default:
130				break;
131			}
132			// TODO Auto-generated method stub
133			return false;
134		}
135
136		@Override
137		public void onItemCheckedStateChanged(ActionMode mode, int position,
138				long id, boolean checked) {
139		}
140	};
141
142	private void invite() {
143		List<Conversation> conversations = xmppConnectionService
144				.getConversations();
145		Conversation conversation = null;
146		for (Conversation tmpConversation : conversations) {
147			if (tmpConversation.getUuid().equals(
148					getIntent().getStringExtra("uuid"))) {
149				conversation = tmpConversation;
150				break;
151			}
152		}
153		if (conversation != null) {
154			xmppConnectionService.inviteToConference(conversation, selectedContacts);
155		}
156		finish();
157	}
158	
159	private void startConference() {
160		if (accounts.size() > 1) {
161			getAccountChooser(new OnClickListener() {
162
163				@Override
164				public void onClick(DialogInterface dialog, int which) {
165					startConference(accounts.get(which), selectedContacts);
166				}
167			}).show();
168		} else {
169			startConference(accounts.get(0), selectedContacts);
170		}
171
172	}
173
174	private void startConference(Account account, List<Contact> contacts) {
175		SecureRandom random = new SecureRandom();
176		String mucName = new BigInteger(100, random).toString(32);
177		String serverName = account.getXmppConnection().getMucServer();
178		String jid = mucName + "@" + serverName;
179		Conversation conversation = xmppConnectionService
180				.findOrCreateConversation(account, jid, true);
181		StringBuilder subject = new StringBuilder();
182		for (int i = 0; i < selectedContacts.size(); ++i) {
183			if (i + 1 != selectedContacts.size()) {
184				subject.append(selectedContacts.get(i).getDisplayName() + ", ");
185			} else {
186				subject.append(selectedContacts.get(i).getDisplayName());
187			}
188		}
189		xmppConnectionService.sendConversationSubject(conversation,
190				subject.toString());
191		xmppConnectionService.inviteToConference(conversation, contacts);
192		switchToConversation(conversation, null);
193	}
194
195	protected void updateAggregatedContacts() {
196
197		aggregatedContacts.clear();
198		for (Contact contact : rosterContacts) {
199			if (contact.match(searchString))
200				aggregatedContacts.add(contact);
201		}
202
203		Collections.sort(aggregatedContacts, new Comparator<Contact>() {
204
205			@SuppressLint("DefaultLocale")
206			@Override
207			public int compare(Contact lhs, Contact rhs) {
208				return lhs.getDisplayName().toLowerCase()
209						.compareTo(rhs.getDisplayName().toLowerCase());
210			}
211		});
212
213		if (aggregatedContacts.size() == 0) {
214
215			if (Validator.isValidJid(searchString)) {
216				String name = searchString.split("@")[0];
217				Contact newContact = new Contact(null, name, searchString, null);
218				newContact.flagAsNotInRoster();
219				aggregatedContacts.add(newContact);
220				contactsHeader.setText("Create new contact");
221			} else {
222				contactsHeader.setText("Contacts");
223			}
224		} else {
225			contactsHeader.setText("Contacts");
226		}
227
228		contactsAdapter.notifyDataSetChanged();
229		contactsView.setScrollX(0);
230	}
231
232	private OnItemLongClickListener onLongClickListener = new OnItemLongClickListener() {
233
234		@Override
235		public boolean onItemLongClick(AdapterView<?> arg0, View view,
236				int position, long arg3) {
237			if (!isActionMode) {
238				contactsView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
239				contactsView.setItemChecked(position, true);
240				actionMode = contactsView.startActionMode(actionModeCallback);
241			}
242			return true;
243		}
244	};
245
246	@Override
247	protected void onStart() {
248		super.onStart();
249		inviteIntent = "invite".equals(getIntent().getAction());
250		if (inviteIntent) {
251			contactsHeader.setVisibility(View.GONE);
252			actionMode = contactsView.startActionMode(actionModeCallback);
253			search.setVisibility(View.GONE);
254		}
255	}
256
257	@Override
258	protected void onCreate(Bundle savedInstanceState) {
259
260		super.onCreate(savedInstanceState);
261
262		setContentView(R.layout.activity_new_conversation);
263
264		contactsHeader = (TextView) findViewById(R.id.contacts_header);
265
266		search = (EditText) findViewById(R.id.new_conversation_search);
267		search.addTextChangedListener(new TextWatcher() {
268
269			@Override
270			public void onTextChanged(CharSequence s, int start, int before,
271					int count) {
272				searchString = search.getText().toString();
273				updateAggregatedContacts();
274			}
275
276			@Override
277			public void afterTextChanged(Editable s) {
278				// TODO Auto-generated method stub
279
280			}
281
282			@Override
283			public void beforeTextChanged(CharSequence s, int start, int count,
284					int after) {
285				// TODO Auto-generated method stub
286
287			}
288		});
289
290		contactsView = (ListView) findViewById(R.id.contactList);
291		contactsAdapter = new ArrayAdapter<Contact>(getApplicationContext(),
292				R.layout.contact, aggregatedContacts) {
293			@Override
294			public View getView(int position, View view, ViewGroup parent) {
295				LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
296				Contact contact = getItem(position);
297				if (view == null) {
298					view = (View) inflater.inflate(R.layout.contact, null);
299				}
300
301				((TextView) view.findViewById(R.id.contact_display_name))
302						.setText(getItem(position).getDisplayName());
303				TextView contactJid = (TextView) view
304						.findViewById(R.id.contact_jid);
305				contactJid.setText(contact.getJid());
306				ImageView imageView = (ImageView) view
307						.findViewById(R.id.contact_photo);
308				imageView.setImageBitmap(UIHelper.getContactPicture(contact,
309						null, 90, this.getContext()));
310				return view;
311			}
312		};
313		contactsView.setAdapter(contactsAdapter);
314		contactsView.setMultiChoiceModeListener(actionModeCallback);
315		contactsView.setOnItemClickListener(new OnItemClickListener() {
316
317			@Override
318			public void onItemClick(AdapterView<?> arg0, final View view,
319					int pos, long arg3) {
320				if (!isActionMode) {
321					Contact clickedContact = aggregatedContacts.get(pos);
322					startConversation(clickedContact);
323
324				} else {
325					actionMode.invalidate();
326				}
327			}
328		});
329		contactsView.setOnItemLongClickListener(this.onLongClickListener);
330	}
331
332	public void startConversation(final Contact contact) {
333		if ((contact.getAccount() == null) && (accounts.size() > 1)) {
334			getAccountChooser(new OnClickListener() {
335
336				@Override
337				public void onClick(DialogInterface dialog, int which) {
338					contact.setAccount(accounts.get(which));
339					showIsMucDialogIfNeeded(contact);
340				}
341			}).show();
342		} else {
343			if (contact.getAccount() == null) {
344				contact.setAccount(accounts.get(0));
345			}
346			showIsMucDialogIfNeeded(contact);
347		}
348	}
349
350	protected AlertDialog getAccountChooser(OnClickListener listener) {
351		String[] accountList = new String[accounts.size()];
352		for (int i = 0; i < accounts.size(); ++i) {
353			accountList[i] = accounts.get(i).getJid();
354		}
355
356		AlertDialog.Builder accountChooser = new AlertDialog.Builder(this);
357		accountChooser.setTitle("Choose account");
358		accountChooser.setItems(accountList, listener);
359		return accountChooser.create();
360	}
361
362	public void showIsMucDialogIfNeeded(final Contact clickedContact) {
363		if (clickedContact.couldBeMuc()) {
364			AlertDialog.Builder dialog = new AlertDialog.Builder(this);
365			dialog.setTitle("Multi User Conference");
366			dialog.setMessage("Are you trying to join a conference?");
367			dialog.setPositiveButton("Yes", new OnClickListener() {
368
369				@Override
370				public void onClick(DialogInterface dialog, int which) {
371					startConversation(clickedContact,
372							clickedContact.getAccount(), true);
373				}
374			});
375			dialog.setNegativeButton("No", new OnClickListener() {
376
377				@Override
378				public void onClick(DialogInterface dialog, int which) {
379					startConversation(clickedContact,
380							clickedContact.getAccount(), false);
381				}
382			});
383			dialog.create().show();
384		} else {
385			startConversation(clickedContact, clickedContact.getAccount(),
386					false);
387		}
388	}
389
390	public void startConversation(Contact contact, Account account, boolean muc) {
391		if (!contact.isInRoster()) {
392			xmppConnectionService.createContact(contact);
393		}
394		Conversation conversation = xmppConnectionService
395				.findOrCreateConversation(account, contact.getJid(), muc);
396
397		switchToConversation(conversation, null);
398	}
399
400	@Override
401	void onBackendConnected() {
402		this.accounts = xmppConnectionService.getAccounts();
403		if (Intent.ACTION_SENDTO.equals(getIntent().getAction())) {
404			getActionBar().setDisplayHomeAsUpEnabled(false);
405			getActionBar().setHomeButtonEnabled(false);
406			String jid;
407			try {
408				jid = URLDecoder.decode(getIntent().getData().getEncodedPath(),
409						"UTF-8").split("/")[1];
410			} catch (UnsupportedEncodingException e) {
411				jid = null;
412			}
413			if (jid != null) {
414				final String finalJid = jid;
415				if (this.accounts.size() > 1) {
416					getAccountChooser(new OnClickListener() {
417
418						@Override
419						public void onClick(DialogInterface dialog, int which) {
420							Conversation conversation = xmppConnectionService
421									.findOrCreateConversation(
422											accounts.get(which), finalJid,
423											false);
424							switchToConversation(conversation, null);
425							finish();
426						}
427					}).show();
428				} else {
429					Conversation conversation = xmppConnectionService
430							.findOrCreateConversation(this.accounts.get(0),
431									jid, false);
432					switchToConversation(conversation, null);
433					finish();
434				}
435			}
436		}
437
438		if (xmppConnectionService.getConversationCount() == 0) {
439			getActionBar().setDisplayHomeAsUpEnabled(false);
440			getActionBar().setHomeButtonEnabled(false);
441		}
442		this.rosterContacts.clear();
443		for (int i = 0; i < accounts.size(); ++i) {
444			rosterContacts.addAll(xmppConnectionService.getRoster(accounts
445					.get(i)));
446		}
447		updateAggregatedContacts();
448	}
449
450	@Override
451	public boolean onCreateOptionsMenu(Menu menu) {
452		// Inflate the menu; this adds items to the action bar if it is present.
453		getMenuInflater().inflate(R.menu.newconversation, menu);
454		return true;
455	}
456
457	@Override
458	public boolean onOptionsItemSelected(MenuItem item) {
459		switch (item.getItemId()) {
460		case R.id.action_refresh_contacts:
461			refreshContacts();
462			break;
463		default:
464			break;
465		}
466		return super.onOptionsItemSelected(item);
467	}
468
469	private void refreshContacts() {
470		final ProgressBar progress = (ProgressBar) findViewById(R.id.progressBar1);
471		final EditText searchBar = (EditText) findViewById(R.id.new_conversation_search);
472		final TextView contactsHeader = (TextView) findViewById(R.id.contacts_header);
473		final ListView contactList = (ListView) findViewById(R.id.contactList);
474		searchBar.setVisibility(View.GONE);
475		contactsHeader.setVisibility(View.GONE);
476		contactList.setVisibility(View.GONE);
477		progress.setVisibility(View.VISIBLE);
478		this.accounts = xmppConnectionService.getAccounts();
479		this.rosterContacts.clear();
480		for (int i = 0; i < accounts.size(); ++i) {
481			if (accounts.get(i).getStatus() == Account.STATUS_ONLINE) {
482				xmppConnectionService.updateRoster(accounts.get(i),
483						new OnRosterFetchedListener() {
484
485							@Override
486							public void onRosterFetched(
487									final List<Contact> roster) {
488								runOnUiThread(new Runnable() {
489
490									@Override
491									public void run() {
492										rosterContacts.addAll(roster);
493										progress.setVisibility(View.GONE);
494										searchBar.setVisibility(View.VISIBLE);
495										contactList.setVisibility(View.VISIBLE);
496										contactList.setVisibility(View.VISIBLE);
497										updateAggregatedContacts();
498									}
499								});
500							}
501						});
502			}
503		}
504	}
505
506	@Override
507	public void onActionModeStarted(ActionMode mode) {
508		super.onActionModeStarted(mode);
509		this.isActionMode = true;
510		search.setEnabled(false);
511	}
512
513	@Override
514	public void onActionModeFinished(ActionMode mode) {
515		super.onActionModeFinished(mode);
516		if (inviteIntent) {
517			finish();
518		} else {
519			this.isActionMode = false;
520			contactsView.clearChoices();
521			contactsView.requestLayout();
522			contactsView.post(new Runnable() {
523				@Override
524				public void run() {
525					contactsView.setChoiceMode(ListView.CHOICE_MODE_NONE);
526				}
527			});
528			search.setEnabled(true);
529		}
530	}
531
532}