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