ConversationActivity.java

  1package de.gultsch.chat.ui;
  2
  3import java.util.ArrayList;
  4import java.util.Collections;
  5import java.util.Comparator;
  6import java.util.List;
  7
  8import org.openintents.openpgp.OpenPgpSignatureResult;
  9import org.openintents.openpgp.util.OpenPgpConstants;
 10
 11import de.gultsch.chat.R;
 12import de.gultsch.chat.R.id;
 13import de.gultsch.chat.crypto.PgpEngine;
 14import de.gultsch.chat.crypto.PgpEngine.UserInputRequiredException;
 15import de.gultsch.chat.entities.Account;
 16import de.gultsch.chat.entities.Contact;
 17import de.gultsch.chat.entities.Conversation;
 18import de.gultsch.chat.entities.Message;
 19import de.gultsch.chat.utils.UIHelper;
 20import android.net.Uri;
 21import android.os.Bundle;
 22import android.app.AlertDialog;
 23import android.app.FragmentTransaction;
 24import android.app.NotificationManager;
 25import android.content.Context;
 26import android.content.DialogInterface;
 27import android.content.Intent;
 28import android.content.IntentSender.SendIntentException;
 29import android.graphics.Typeface;
 30import android.support.v4.widget.SlidingPaneLayout;
 31import android.support.v4.widget.SlidingPaneLayout.PanelSlideListener;
 32import android.util.Log;
 33import android.view.KeyEvent;
 34import android.view.LayoutInflater;
 35import android.view.Menu;
 36import android.view.MenuItem;
 37import android.view.View;
 38import android.view.ViewGroup;
 39import android.view.inputmethod.InputMethodManager;
 40import android.widget.AdapterView;
 41import android.widget.AdapterView.OnItemClickListener;
 42import android.widget.ArrayAdapter;
 43import android.widget.ListView;
 44import android.widget.PopupMenu;
 45import android.widget.PopupMenu.OnMenuItemClickListener;
 46import android.widget.TextView;
 47import android.widget.ImageView;
 48
 49public class ConversationActivity extends XmppActivity {
 50
 51	public static final String VIEW_CONVERSATION = "viewConversation";
 52	public static final String CONVERSATION = "conversationUuid";
 53	
 54	public static final int REQUEST_SEND_MESSAGE = 0x75441;
 55	public static final int REQUEST_DECRYPT_PGP = 0x76783;
 56
 57	protected SlidingPaneLayout spl;
 58
 59	private List<Conversation> conversationList = new ArrayList<Conversation>();
 60	private Conversation selectedConversation = null;
 61	private ListView listView;
 62	
 63	private boolean paneShouldBeOpen = true;
 64	private ArrayAdapter<Conversation> listAdapter;
 65	
 66	private OnConversationListChangedListener onConvChanged = new OnConversationListChangedListener() {
 67		
 68		@Override
 69		public void onConversationListChanged() {
 70			final Conversation currentConv = getSelectedConversation();
 71			conversationList.clear();
 72			conversationList.addAll(xmppConnectionService
 73					.getConversations());
 74			runOnUiThread(new Runnable() {
 75				
 76				@Override
 77				public void run() {	
 78					updateConversationList();
 79					if(paneShouldBeOpen) {
 80						if (conversationList.size() >= 1) {
 81							swapConversationFragment();
 82						} else {
 83							startActivity(new Intent(getApplicationContext(), NewConversationActivity.class));
 84							finish();
 85						}
 86					}
 87					ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager().findFragmentByTag("conversation");
 88					if (selectedFragment!=null) {
 89						selectedFragment.updateMessages();
 90					}
 91				}
 92			});
 93		}
 94	};
 95	
 96	private DialogInterface.OnClickListener addToRoster = new DialogInterface.OnClickListener() {
 97		
 98		@Override
 99		public void onClick(DialogInterface dialog, int which) {
100			String jid = getSelectedConversation().getContactJid();
101			Account account = getSelectedConversation().getAccount();
102			String name = jid.split("@")[0];
103			Contact contact = new Contact(account, name, jid, null);
104			xmppConnectionService.createContact(contact);
105		}
106	};
107	private boolean contactInserted = false;
108	
109	
110	public List<Conversation> getConversationList() {
111		return this.conversationList;
112	}
113
114	public Conversation getSelectedConversation() {
115		return this.selectedConversation;
116	}
117	
118	public ListView getConversationListView() {
119		return this.listView;
120	}
121	
122	public SlidingPaneLayout getSlidingPaneLayout() {
123		return this.spl;
124	}
125	
126	public boolean shouldPaneBeOpen() {
127		return paneShouldBeOpen;
128	}
129	
130	public void updateConversationList() {
131		if (conversationList.size() >= 1) {
132			Collections.sort(this.conversationList, new Comparator<Conversation>() {
133				@Override
134				public int compare(Conversation lhs, Conversation rhs) {
135					return (int) (rhs.getLatestMessage().getTimeSent() - lhs.getLatestMessage().getTimeSent());
136				}
137			});
138		}
139		this.listView.invalidateViews();
140	}
141	
142	@Override
143	protected void onCreate(Bundle savedInstanceState) {
144
145		super.onCreate(savedInstanceState);
146
147		setContentView(R.layout.fragment_conversations_overview);
148
149		listView = (ListView) findViewById(R.id.list);
150
151		this.listAdapter = new ArrayAdapter<Conversation>(this,
152				R.layout.conversation_list_row, conversationList) {
153			@Override
154			public View getView(int position, View view, ViewGroup parent) {
155				if (view == null) {
156					LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
157					view = (View) inflater.inflate(
158							R.layout.conversation_list_row, null);
159				}
160				Conversation conv = getItem(position);
161				TextView convName = (TextView) view.findViewById(R.id.conversation_name);
162				convName.setText(conv.getName());
163				TextView convLastMsg = (TextView) view.findViewById(R.id.conversation_lastmsg);
164				convLastMsg.setText(conv.getLatestMessage().getBody());
165				
166				if(!conv.isRead()) {
167					convName.setTypeface(null,Typeface.BOLD);
168					convLastMsg.setTypeface(null,Typeface.BOLD);
169				} else {
170					convName.setTypeface(null,Typeface.NORMAL);
171					convLastMsg.setTypeface(null,Typeface.NORMAL);
172				}
173				
174				((TextView) view.findViewById(R.id.conversation_lastupdate))
175				.setText(UIHelper.readableTimeDifference(getItem(position).getLatestMessage().getTimeSent()));
176				
177				Uri profilePhoto = getItem(position).getProfilePhotoUri();
178				ImageView imageView = (ImageView) view.findViewById(R.id.conversation_image);
179				if (profilePhoto!=null) {
180					imageView.setImageURI(profilePhoto);
181				} else {
182					imageView.setImageBitmap(UIHelper.getUnknownContactPicture(getItem(position).getName(),200));
183				}
184				
185				
186				((ImageView) view.findViewById(R.id.conversation_image))
187						.setImageURI(getItem(position).getProfilePhotoUri());
188				return view;
189			}
190
191		};
192		
193		listView.setAdapter(this.listAdapter);
194
195		listView.setOnItemClickListener(new OnItemClickListener() {
196
197			@Override
198			public void onItemClick(AdapterView<?> arg0, View clickedView,
199					int position, long arg3) {
200				paneShouldBeOpen = false;
201				if (selectedConversation != conversationList.get(position)) {
202					selectedConversation = conversationList.get(position);
203					swapConversationFragment(); //.onBackendConnected(conversationList.get(position));
204				} else {
205					spl.closePane();
206				}
207			}
208		});
209		spl = (SlidingPaneLayout) findViewById(id.slidingpanelayout);
210		spl.setParallaxDistance(150);
211		spl.setShadowResource(R.drawable.es_slidingpane_shadow);
212		spl.setSliderFadeColor(0);
213		spl.setPanelSlideListener(new PanelSlideListener() {
214
215			@Override
216			public void onPanelOpened(View arg0) {
217				paneShouldBeOpen = true;
218				getActionBar().setDisplayHomeAsUpEnabled(false);
219				getActionBar().setTitle(R.string.app_name);
220				invalidateOptionsMenu();
221
222				InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
223
224				View focus = getCurrentFocus();
225
226				if (focus != null) {
227
228					inputManager.hideSoftInputFromWindow(
229							focus.getWindowToken(),
230							InputMethodManager.HIDE_NOT_ALWAYS);
231				}
232			}
233
234			@Override
235			public void onPanelClosed(View arg0) {
236				paneShouldBeOpen = false;
237				if (conversationList.size() > 0) {
238					getActionBar().setDisplayHomeAsUpEnabled(true);
239					getActionBar().setTitle(getSelectedConversation().getName());
240					invalidateOptionsMenu();
241					if (!getSelectedConversation().isRead()) {
242						getSelectedConversation().markRead();
243						updateConversationList();
244					}
245				}
246			}
247
248			@Override
249			public void onPanelSlide(View arg0, float arg1) {
250				// TODO Auto-generated method stub
251
252			}
253		});
254	}
255
256	@Override
257	public boolean onCreateOptionsMenu(Menu menu) {
258		getMenuInflater().inflate(R.menu.conversations, menu);
259		MenuItem menuSecure = (MenuItem) menu.findItem(R.id.action_security);
260		
261		if (spl.isOpen()) {
262			((MenuItem) menu.findItem(R.id.action_archive)).setVisible(false);
263			((MenuItem) menu.findItem(R.id.action_details)).setVisible(false);
264			menuSecure.setVisible(false);
265		} else {
266			((MenuItem) menu.findItem(R.id.action_add)).setVisible(false);
267			if (this.getSelectedConversation()!=null) {
268				if (this.getSelectedConversation().getMode() == Conversation.MODE_MULTI) {
269					((MenuItem) menu.findItem(R.id.action_security)).setVisible(false);
270					menuSecure.setVisible(false);
271					((MenuItem) menu.findItem(R.id.action_archive)).setTitle("Leave conference");
272				} else {
273					if (this.getSelectedConversation().getLatestMessage().getEncryption() != Message.ENCRYPTION_NONE) {
274						menuSecure.setIcon(R.drawable.ic_action_secure);
275					}
276				}
277			}
278		}
279		return true;
280	}
281
282	@Override
283	public boolean onOptionsItemSelected(MenuItem item) {
284		switch (item.getItemId()) {
285		case android.R.id.home:
286			spl.openPane();
287			break;
288		case R.id.action_settings:
289			startActivity(new Intent(this, SettingsActivity.class));
290			break;
291		case R.id.action_accounts:
292			startActivity(new Intent(this, ManageAccountActivity.class));
293			break;
294		case R.id.action_add:
295			startActivity(new Intent(this, NewConversationActivity.class));
296			break;
297		case R.id.action_archive:
298			Conversation conv = getSelectedConversation();
299			conv.setStatus(Conversation.STATUS_ARCHIVED);
300			paneShouldBeOpen = true;
301			spl.openPane();
302			xmppConnectionService.archiveConversation(conv);
303			selectedConversation = conversationList.get(0);
304			break;
305		case R.id.action_details:
306			DialogContactDetails details = new DialogContactDetails();
307			Contact contact = this.getSelectedConversation().getContact();
308			if (contact != null) {
309				contact.setAccount(this.selectedConversation.getAccount());
310				details.setContact(contact);
311				details.show(getFragmentManager(), "details");
312			} else {
313				String jid = getSelectedConversation().getContactJid();
314				AlertDialog.Builder builder = new AlertDialog.Builder(this);
315				builder.setTitle(jid);
316				builder.setMessage("The contact is not in your roster. Would you like to add it.");
317				builder.setNegativeButton("Cancel", null);
318				builder.setPositiveButton("Add",addToRoster);
319				builder.create().show();
320			}
321			break;
322		case R.id.action_security:
323			final Conversation selConv = getSelectedConversation();
324			View menuItemView = findViewById(R.id.action_security);
325			PopupMenu popup = new PopupMenu(this, menuItemView);
326			final ConversationFragment fragment = (ConversationFragment) getFragmentManager().findFragmentByTag("conversation");
327		    if (fragment!=null) {
328				popup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
329					
330					@Override
331					public boolean onMenuItemClick(MenuItem item) {
332						switch (item.getItemId()) {
333						case R.id.encryption_choice_none:
334							selConv.nextMessageEncryption = Message.ENCRYPTION_NONE;
335							item.setChecked(true);
336							break;
337						case R.id.encryption_choice_otr:
338							selConv.nextMessageEncryption = Message.ENCRYPTION_OTR;
339							item.setChecked(true);
340							break;
341						case R.id.encryption_choice_pgp:
342							selConv.nextMessageEncryption = Message.ENCRYPTION_PGP;
343							item.setChecked(true);
344							break;
345						default:
346							selConv.nextMessageEncryption = Message.ENCRYPTION_NONE;
347							break;
348						}
349						fragment.updateChatMsgHint();
350						return true;
351					}
352				});
353			    popup.inflate(R.menu.encryption_choices);
354			    switch (selConv.nextMessageEncryption) {
355				case Message.ENCRYPTION_NONE:
356					popup.getMenu().findItem(R.id.encryption_choice_none).setChecked(true);
357					break;
358				case Message.ENCRYPTION_OTR:
359					popup.getMenu().findItem(R.id.encryption_choice_otr).setChecked(true);
360					break;
361				case Message.ENCRYPTION_PGP:
362					popup.getMenu().findItem(R.id.encryption_choice_pgp).setChecked(true);
363					break;
364				case Message.ENCRYPTION_DECRYPTED:
365					popup.getMenu().findItem(R.id.encryption_choice_pgp).setChecked(true);
366					break;
367				default:
368					popup.getMenu().findItem(R.id.encryption_choice_none).setChecked(true);
369					break;
370				}
371			    popup.show();
372		    }
373
374			break;
375		default:
376			break;
377		}
378		return super.onOptionsItemSelected(item);
379	}
380
381	protected ConversationFragment swapConversationFragment() {
382		ConversationFragment selectedFragment = new ConversationFragment();
383		
384		FragmentTransaction transaction = getFragmentManager()
385				.beginTransaction();
386		transaction.replace(R.id.selected_conversation, selectedFragment,"conversation");
387		transaction.commit();
388		return selectedFragment;
389	}
390
391	@Override
392	public boolean onKeyDown(int keyCode, KeyEvent event) {
393		if (keyCode == KeyEvent.KEYCODE_BACK) {
394			if (!spl.isOpen()) {
395				spl.openPane();
396				return false;
397			}
398		}
399		return super.onKeyDown(keyCode, event);
400	}
401	
402	public void onStart() {
403		super.onStart();
404		NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
405		nm.cancelAll();
406		if (conversationList.size()>=1) {
407			onConvChanged.onConversationListChanged();
408		}
409	}
410	
411	@Override
412	protected void onStop() {
413		Log.d("gultsch","called on stop in conversation activity");
414		if (xmppConnectionServiceBound) {
415        	xmppConnectionService.removeOnConversationListChangedListener();
416		}
417		super.onStop();
418	}
419
420	@Override
421	void onBackendConnected() {
422		
423		xmppConnectionService.setOnConversationListChangedListener(this.onConvChanged);
424		
425		if (conversationList.size()==0) {
426			conversationList.clear();
427			conversationList.addAll(xmppConnectionService
428					.getConversations());
429			
430			this.updateConversationList();
431		}
432
433		if ((getIntent().getAction().equals(Intent.ACTION_VIEW) && (!handledViewIntent))) {
434			if (getIntent().getType().equals(
435					ConversationActivity.VIEW_CONVERSATION)) {
436				handledViewIntent = true;
437
438				String convToView = (String) getIntent().getExtras().get(CONVERSATION);
439
440				for(int i = 0; i < conversationList.size(); ++i) {
441					if (conversationList.get(i).getUuid().equals(convToView)) {
442						selectedConversation = conversationList.get(i);
443					}
444				}
445				paneShouldBeOpen = false;
446				swapConversationFragment();
447			}
448		} else {
449			if (xmppConnectionService.getAccounts().size() == 0) {
450				startActivity(new Intent(this, ManageAccountActivity.class));
451				finish();
452			} else if (conversationList.size() <= 0) {
453				//add no history
454				startActivity(new Intent(this, NewConversationActivity.class));
455				finish();
456			} else {
457				spl.openPane();
458				//find currently loaded fragment
459				ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager().findFragmentByTag("conversation");
460				if (selectedFragment!=null) {
461					Log.d("gultsch","ConversationActivity. found old fragment.");
462					selectedFragment.onBackendConnected();
463				} else {
464					Log.d("gultsch","conversationactivity. no old fragment found. creating new one");
465					selectedConversation = conversationList.get(0);
466					Log.d("gultsch","selected conversation is #"+selectedConversation);
467					swapConversationFragment();
468				}
469			}
470		}
471	}
472}