ConversationFragment.java

  1package eu.siacs.conversations.ui;
  2
  3import java.util.ArrayList;
  4import java.util.HashMap;
  5import java.util.List;
  6import java.util.Set;
  7
  8import net.java.otr4j.session.SessionStatus;
  9import eu.siacs.conversations.R;
 10import eu.siacs.conversations.crypto.PgpEngine;
 11import eu.siacs.conversations.entities.Contact;
 12import eu.siacs.conversations.entities.Conversation;
 13import eu.siacs.conversations.entities.Message;
 14import eu.siacs.conversations.entities.MucOptions;
 15import eu.siacs.conversations.entities.MucOptions.OnRenameListener;
 16import eu.siacs.conversations.services.ImageProvider;
 17import eu.siacs.conversations.services.XmppConnectionService;
 18import eu.siacs.conversations.utils.UIHelper;
 19import eu.siacs.conversations.xmpp.jingle.JingleConnection;
 20import android.app.AlertDialog;
 21import android.app.Fragment;
 22import android.app.PendingIntent;
 23import android.content.Context;
 24import android.content.DialogInterface;
 25import android.content.Intent;
 26import android.content.IntentSender;
 27import android.content.SharedPreferences;
 28import android.content.IntentSender.SendIntentException;
 29import android.graphics.Bitmap;
 30import android.graphics.Typeface;
 31import android.os.Bundle;
 32import android.preference.PreferenceManager;
 33import android.text.Editable;
 34import android.text.Selection;
 35import android.util.DisplayMetrics;
 36import android.view.Gravity;
 37import android.view.LayoutInflater;
 38import android.view.View;
 39import android.view.View.OnClickListener;
 40import android.view.View.OnLongClickListener;
 41import android.view.ViewGroup;
 42import android.widget.AbsListView.OnScrollListener;
 43import android.widget.AbsListView;
 44import android.widget.ArrayAdapter;
 45import android.widget.Button;
 46import android.widget.EditText;
 47import android.widget.LinearLayout;
 48import android.widget.ListView;
 49import android.widget.ImageButton;
 50import android.widget.ImageView;
 51import android.widget.RelativeLayout;
 52import android.widget.TextView;
 53import android.widget.Toast;
 54
 55public class ConversationFragment extends Fragment {
 56
 57	protected Conversation conversation;
 58	protected ListView messagesView;
 59	protected LayoutInflater inflater;
 60	protected List<Message> messageList = new ArrayList<Message>();
 61	protected ArrayAdapter<Message> messageListAdapter;
 62	protected Contact contact;
 63	protected BitmapCache mBitmapCache = new BitmapCache();
 64	
 65	protected int mPrimaryTextColor;
 66	protected int mSecondaryTextColor;
 67
 68	protected String queuedPqpMessage = null;
 69
 70	private EditText chatMsg;
 71	private String pastedText = null;
 72	private RelativeLayout snackbar;
 73	private TextView snackbarMessage;
 74	private TextView snackbarAction;
 75
 76	protected Bitmap selfBitmap;
 77
 78	private boolean useSubject = true;
 79	private boolean messagesLoaded = false;
 80
 81	private IntentSender askForPassphraseIntent = null;
 82
 83	private OnClickListener sendMsgListener = new OnClickListener() {
 84
 85		@Override
 86		public void onClick(View v) {
 87			if (chatMsg.getText().length() < 1)
 88				return;
 89			Message message = new Message(conversation, chatMsg.getText()
 90					.toString(), conversation.getNextEncryption());
 91			if (conversation.getNextEncryption() == Message.ENCRYPTION_OTR) {
 92				sendOtrMessage(message);
 93			} else if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
 94				sendPgpMessage(message);
 95			} else {
 96				sendPlainTextMessage(message);
 97			}
 98		}
 99	};
100	protected OnClickListener clickToDecryptListener = new OnClickListener() {
101
102		@Override
103		public void onClick(View v) {
104			if (activity.hasPgp() && askForPassphraseIntent != null) {
105				try {
106					getActivity().startIntentSenderForResult(
107							askForPassphraseIntent,
108							ConversationActivity.REQUEST_DECRYPT_PGP, null, 0,
109							0, 0);
110				} catch (SendIntentException e) {
111					//
112				}
113			}
114		}
115	};
116	
117	private OnClickListener clickToMuc = new OnClickListener() {
118
119		@Override
120		public void onClick(View v) {
121			Intent intent = new Intent(getActivity(), MucDetailsActivity.class);
122			intent.setAction(MucDetailsActivity.ACTION_VIEW_MUC);
123			intent.putExtra("uuid", conversation.getUuid());
124			startActivity(intent);
125		}
126	};
127
128	private OnScrollListener mOnScrollListener = new OnScrollListener() {
129
130		@Override
131		public void onScrollStateChanged(AbsListView view, int scrollState) {
132			// TODO Auto-generated method stub
133
134		}
135
136		@Override
137		public void onScroll(AbsListView view, int firstVisibleItem,
138				int visibleItemCount, int totalItemCount) {
139			if (firstVisibleItem == 0 && messagesLoaded) {
140				long timestamp = messageList.get(0).getTimeSent();
141				messagesLoaded = false;
142				List<Message> messages = activity.xmppConnectionService
143						.getMoreMessages(conversation, timestamp);
144				messageList.addAll(0, messages);
145				messageListAdapter.notifyDataSetChanged();
146				if (messages.size() != 0) {
147					messagesLoaded = true;
148				}
149				messagesView.setSelectionFromTop(messages.size() + 1, 0);
150			}
151		}
152	};
153
154	private ConversationActivity activity;
155
156	public void updateChatMsgHint() {
157		switch (conversation.getNextEncryption()) {
158		case Message.ENCRYPTION_NONE:
159			chatMsg.setHint(getString(R.string.send_plain_text_message));
160			break;
161		case Message.ENCRYPTION_OTR:
162			chatMsg.setHint(getString(R.string.send_otr_message));
163			break;
164		case Message.ENCRYPTION_PGP:
165			chatMsg.setHint(getString(R.string.send_pgp_message));
166			break;
167		default:
168			break;
169		}
170	}
171
172	@Override
173	public View onCreateView(final LayoutInflater inflater,
174			ViewGroup container, Bundle savedInstanceState) {
175
176		final DisplayMetrics metrics = getResources().getDisplayMetrics();
177
178		this.inflater = inflater;
179
180		mPrimaryTextColor = getResources().getColor(R.color.primarytext);
181		mSecondaryTextColor = getResources().getColor(R.color.secondarytext);
182		
183		final View view = inflater.inflate(R.layout.fragment_conversation,
184				container, false);
185		chatMsg = (EditText) view.findViewById(R.id.textinput);
186
187		ImageButton sendButton = (ImageButton) view
188				.findViewById(R.id.textSendButton);
189		sendButton.setOnClickListener(this.sendMsgListener);
190
191		snackbar = (RelativeLayout) view.findViewById(R.id.snackbar);
192		snackbarMessage = (TextView) view.findViewById(R.id.snackbar_message);
193		snackbarAction = (TextView) view.findViewById(R.id.snackbar_action);
194
195		messagesView = (ListView) view.findViewById(R.id.messages_view);
196		messagesView.setOnScrollListener(mOnScrollListener);
197		messagesView.setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL);
198
199		messageListAdapter = new ArrayAdapter<Message>(this.getActivity()
200				.getApplicationContext(), R.layout.message_sent,
201				this.messageList) {
202
203			private static final int SENT = 0;
204			private static final int RECIEVED = 1;
205			private static final int STATUS = 2;
206
207			@Override
208			public int getViewTypeCount() {
209				return 3;
210			}
211
212			@Override
213			public int getItemViewType(int position) {
214				if (getItem(position).getType() == Message.TYPE_STATUS) {
215					return STATUS;
216				} else if (getItem(position).getStatus() <= Message.STATUS_RECIEVED) {
217					return RECIEVED;
218				} else {
219					return SENT;
220				}
221			}
222
223			private void displayStatus(ViewHolder viewHolder, Message message) {
224				String filesize = null;
225				String info = null;
226				boolean error = false;
227				boolean multiReceived = message.getConversation().getMode() == Conversation.MODE_MULTI
228						&& message.getStatus() <= Message.STATUS_RECIEVED;
229				if (message.getType() == Message.TYPE_IMAGE) {
230					String[] fileParams = message.getBody().split(",");
231					try {
232						long size = Long.parseLong(fileParams[0]);
233						filesize = size / 1024 + " KB";
234					} catch (NumberFormatException e) {
235						filesize = "0 KB";
236					}
237				}
238				switch (message.getStatus()) {
239				case Message.STATUS_WAITING:
240					info = getString(R.string.waiting);
241					break;
242				case Message.STATUS_UNSEND:
243					info = getString(R.string.sending);
244					break;
245				case Message.STATUS_OFFERED:
246					info = getString(R.string.offering);
247					break;
248				case Message.STATUS_SEND_FAILED:
249					info = getString(R.string.send_failed);
250					error = true;
251					break;
252				case Message.STATUS_SEND_REJECTED:
253					info = getString(R.string.send_rejected);
254					error = true;
255					break;
256				case Message.STATUS_RECEPTION_FAILED:
257					info = getString(R.string.reception_failed);
258					error = true;
259				default:
260					if (multiReceived) {
261						info = message.getCounterpart();
262					}
263					break;
264				}
265				if (error) {
266					viewHolder.time.setTextColor(0xFFe92727);
267				} else {
268					viewHolder.time.setTextColor(mSecondaryTextColor);
269				}
270				if (message.getEncryption() == Message.ENCRYPTION_NONE) {
271					viewHolder.indicator.setVisibility(View.GONE);
272				} else {
273					viewHolder.indicator.setVisibility(View.VISIBLE);
274				}
275
276				String formatedTime = UIHelper.readableTimeDifference(
277						getContext(), message.getTimeSent());
278				if (message.getStatus() <= Message.STATUS_RECIEVED) {
279					if ((filesize != null) && (info != null)) {
280						viewHolder.time.setText(filesize + " \u00B7 " + info);
281					} else if ((filesize == null) && (info != null)) {
282						viewHolder.time.setText(formatedTime + " \u00B7 "
283								+ info);
284					} else if ((filesize != null) && (info == null)) {
285						viewHolder.time.setText(formatedTime + " \u00B7 "
286								+ filesize);
287					} else {
288						viewHolder.time.setText(formatedTime);
289					}
290				} else {
291					if ((filesize != null) && (info != null)) {
292						viewHolder.time.setText(filesize + " \u00B7 " + info);
293					} else if ((filesize == null) && (info != null)) {
294						if (error) {
295							viewHolder.time.setText(info + " \u00B7 "
296									+ formatedTime);
297						} else {
298							viewHolder.time.setText(info);
299						}
300					} else if ((filesize != null) && (info == null)) {
301						viewHolder.time.setText(filesize + " \u00B7 "
302								+ formatedTime);
303					} else {
304						viewHolder.time.setText(formatedTime);
305					}
306				}
307			}
308
309			private void displayInfoMessage(ViewHolder viewHolder, int r) {
310				if (viewHolder.download_button != null) {
311					viewHolder.download_button.setVisibility(View.GONE);
312				}
313				viewHolder.image.setVisibility(View.GONE);
314				viewHolder.messageBody.setVisibility(View.VISIBLE);
315				viewHolder.messageBody.setText(getString(r));
316				viewHolder.messageBody.setTextColor(0xff33B5E5);
317				viewHolder.messageBody.setTypeface(null, Typeface.ITALIC);
318				viewHolder.messageBody.setTextIsSelectable(false);
319			}
320
321			private void displayDecryptionFailed(ViewHolder viewHolder) {
322				if (viewHolder.download_button != null) {
323					viewHolder.download_button.setVisibility(View.GONE);
324				}
325				viewHolder.image.setVisibility(View.GONE);
326				viewHolder.messageBody.setVisibility(View.VISIBLE);
327				viewHolder.messageBody
328						.setText(getString(R.string.decryption_failed));
329				viewHolder.messageBody.setTextColor(0xFFe92727);
330				viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
331				viewHolder.messageBody.setTextIsSelectable(false);
332			}
333
334			private void displayTextMessage(ViewHolder viewHolder, String text) {
335				if (viewHolder.download_button != null) {
336					viewHolder.download_button.setVisibility(View.GONE);
337				}
338				viewHolder.image.setVisibility(View.GONE);
339				viewHolder.messageBody.setVisibility(View.VISIBLE);
340				if (text != null) {
341					viewHolder.messageBody.setText(text.trim());
342				} else {
343					viewHolder.messageBody.setText("");
344				}
345				viewHolder.messageBody.setTextColor(mPrimaryTextColor);
346				viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
347				viewHolder.messageBody.setTextIsSelectable(true);
348			}
349
350			private void displayImageMessage(ViewHolder viewHolder,
351					final Message message) {
352				if (viewHolder.download_button != null) {
353					viewHolder.download_button.setVisibility(View.GONE);
354				}
355				viewHolder.messageBody.setVisibility(View.GONE);
356				viewHolder.image.setVisibility(View.VISIBLE);
357				String[] fileParams = message.getBody().split(",");
358				if (fileParams.length == 3) {
359					double target = metrics.density * 288;
360					int w = Integer.parseInt(fileParams[1]);
361					int h = Integer.parseInt(fileParams[2]);
362					int scalledW;
363					int scalledH;
364					if (w <= h) {
365						scalledW = (int) (w / ((double) h / target));
366						scalledH = (int) target;
367					} else {
368						scalledW = (int) target;
369						scalledH = (int) (h / ((double) w / target));
370					}
371					viewHolder.image
372							.setLayoutParams(new LinearLayout.LayoutParams(
373									scalledW, scalledH));
374				}
375				activity.loadBitmap(message, viewHolder.image);
376				viewHolder.image.setOnClickListener(new OnClickListener() {
377
378					@Override
379					public void onClick(View v) {
380						Intent intent = new Intent(Intent.ACTION_VIEW);
381						intent.setDataAndType(
382								ImageProvider.getContentUri(message), "image/*");
383						startActivity(intent);
384					}
385				});
386				viewHolder.image
387						.setOnLongClickListener(new OnLongClickListener() {
388
389							@Override
390							public boolean onLongClick(View v) {
391								Intent shareIntent = new Intent();
392								shareIntent.setAction(Intent.ACTION_SEND);
393								shareIntent.putExtra(Intent.EXTRA_STREAM,
394										ImageProvider.getContentUri(message));
395								shareIntent.setType("image/webp");
396								startActivity(Intent.createChooser(shareIntent,
397										getText(R.string.share_with)));
398								return true;
399							}
400						});
401			}
402
403			@Override
404			public View getView(int position, View view, ViewGroup parent) {
405				final Message item = getItem(position);
406				int type = getItemViewType(position);
407				ViewHolder viewHolder;
408				if (view == null) {
409					viewHolder = new ViewHolder();
410					switch (type) {
411					case SENT:
412						view = (View) inflater.inflate(R.layout.message_sent,
413								null);
414						viewHolder.message_box = (LinearLayout) view
415								.findViewById(R.id.message_box);
416						viewHolder.contact_picture = (ImageView) view
417								.findViewById(R.id.message_photo);
418						viewHolder.contact_picture.setImageBitmap(selfBitmap);
419						viewHolder.indicator = (ImageView) view
420								.findViewById(R.id.security_indicator);
421						viewHolder.image = (ImageView) view
422								.findViewById(R.id.message_image);
423						viewHolder.messageBody = (TextView) view
424								.findViewById(R.id.message_body);
425						viewHolder.time = (TextView) view
426								.findViewById(R.id.message_time);
427						view.setTag(viewHolder);
428						break;
429					case RECIEVED:
430						view = (View) inflater.inflate(
431								R.layout.message_recieved, null);
432						viewHolder.message_box = (LinearLayout) view
433								.findViewById(R.id.message_box);
434						viewHolder.contact_picture = (ImageView) view
435								.findViewById(R.id.message_photo);
436
437						viewHolder.download_button = (Button) view
438								.findViewById(R.id.download_button);
439
440						if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
441
442							viewHolder.contact_picture
443									.setImageBitmap(mBitmapCache.get(
444											item.getConversation().getName(
445													useSubject), item
446													.getConversation()
447													.getContact(),
448											getActivity()
449													.getApplicationContext()));
450
451						}
452						viewHolder.indicator = (ImageView) view
453								.findViewById(R.id.security_indicator);
454						viewHolder.image = (ImageView) view
455								.findViewById(R.id.message_image);
456						viewHolder.messageBody = (TextView) view
457								.findViewById(R.id.message_body);
458						viewHolder.time = (TextView) view
459								.findViewById(R.id.message_time);
460						view.setTag(viewHolder);
461						break;
462					case STATUS:
463						view = (View) inflater.inflate(R.layout.message_status,
464								null);
465						viewHolder.contact_picture = (ImageView) view
466								.findViewById(R.id.message_photo);
467						if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
468
469							viewHolder.contact_picture
470									.setImageBitmap(mBitmapCache.get(
471											item.getConversation().getName(
472													useSubject), item
473													.getConversation()
474													.getContact(),
475											getActivity()
476													.getApplicationContext()));
477							viewHolder.contact_picture.setAlpha(128);
478
479						}
480						break;
481					default:
482						viewHolder = null;
483						break;
484					}
485				} else {
486					viewHolder = (ViewHolder) view.getTag();
487				}
488
489				if (type == STATUS) {
490					return view;
491				}
492
493				if (type == RECIEVED) {
494					if (item.getConversation().getMode() == Conversation.MODE_MULTI) {
495						viewHolder.contact_picture.setImageBitmap(mBitmapCache
496								.get(item.getCounterpart(), null, getActivity()
497										.getApplicationContext()));
498						viewHolder.contact_picture
499								.setOnClickListener(new OnClickListener() {
500
501									@Override
502									public void onClick(View v) {
503										highlightInConference(item
504												.getCounterpart());
505									}
506								});
507					}
508				}
509
510				if (item.getType() == Message.TYPE_IMAGE) {
511					if (item.getStatus() == Message.STATUS_RECIEVING) {
512						displayInfoMessage(viewHolder, R.string.receiving_image);
513					} else if (item.getStatus() == Message.STATUS_RECEIVED_OFFER) {
514						viewHolder.image.setVisibility(View.GONE);
515						viewHolder.messageBody.setVisibility(View.GONE);
516						viewHolder.download_button.setVisibility(View.VISIBLE);
517						viewHolder.download_button
518								.setOnClickListener(new OnClickListener() {
519
520									@Override
521									public void onClick(View v) {
522										JingleConnection connection = item
523												.getJingleConnection();
524										if (connection != null) {
525											connection.accept();
526										}
527									}
528								});
529					} else if ((item.getEncryption() == Message.ENCRYPTION_DECRYPTED)
530							|| (item.getEncryption() == Message.ENCRYPTION_NONE)
531							|| (item.getEncryption() == Message.ENCRYPTION_OTR)) {
532						displayImageMessage(viewHolder, item);
533					} else if (item.getEncryption() == Message.ENCRYPTION_PGP) {
534						displayInfoMessage(viewHolder,
535								R.string.encrypted_message);
536					} else {
537						displayDecryptionFailed(viewHolder);
538					}
539				} else {
540					if (item.getEncryption() == Message.ENCRYPTION_PGP) {
541						if (activity.hasPgp()) {
542							displayInfoMessage(viewHolder,
543									R.string.encrypted_message);
544						} else {
545							displayInfoMessage(viewHolder,
546									R.string.install_openkeychain);
547							viewHolder.message_box
548									.setOnClickListener(new OnClickListener() {
549
550										@Override
551										public void onClick(View v) {
552											activity.showInstallPgpDialog();
553										}
554									});
555						}
556					} else if (item.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
557						displayDecryptionFailed(viewHolder);
558					} else {
559						displayTextMessage(viewHolder, item.getBody());
560					}
561				}
562
563				displayStatus(viewHolder, item);
564
565				return view;
566			}
567		};
568		messagesView.setAdapter(messageListAdapter);
569
570		return view;
571	}
572
573	protected void highlightInConference(String nick) {
574		String oldString = chatMsg.getText().toString().trim();
575		if (oldString.isEmpty()) {
576			chatMsg.setText(nick + ": ");
577		} else {
578			chatMsg.setText(oldString + " " + nick + " ");
579		}
580		int position = chatMsg.length();
581		Editable etext = chatMsg.getText();
582		Selection.setSelection(etext, position);
583	}
584
585	protected Bitmap findSelfPicture() {
586		SharedPreferences sharedPref = PreferenceManager
587				.getDefaultSharedPreferences(getActivity()
588						.getApplicationContext());
589		boolean showPhoneSelfContactPicture = sharedPref.getBoolean(
590				"show_phone_selfcontact_picture", true);
591
592		return UIHelper.getSelfContactPicture(conversation.getAccount(), 48,
593				showPhoneSelfContactPicture, getActivity());
594	}
595
596	@Override
597	public void onStart() {
598		super.onStart();
599		this.activity = (ConversationActivity) getActivity();
600		SharedPreferences preferences = PreferenceManager
601				.getDefaultSharedPreferences(activity);
602		this.useSubject = preferences.getBoolean("use_subject_in_muc", true);
603		if (activity.xmppConnectionServiceBound) {
604			this.onBackendConnected();
605		}
606	}
607
608	@Override
609	public void onStop() {
610		super.onStop();
611		if (this.conversation != null) {
612			this.conversation.setNextMessage(chatMsg.getText().toString());
613		}
614	}
615
616	public void onBackendConnected() {
617		this.conversation = activity.getSelectedConversation();
618		if (this.conversation == null) {
619			return;
620		}
621		String oldString = conversation.getNextMessage().trim();
622		if (this.pastedText == null) {
623			this.chatMsg.setText(oldString);
624		} else {
625
626			if (oldString.isEmpty()) {
627				chatMsg.setText(pastedText);
628			} else {
629				chatMsg.setText(oldString + " " + pastedText);
630			}
631			pastedText = null;
632		}
633		int position = chatMsg.length();
634		Editable etext = chatMsg.getText();
635		Selection.setSelection(etext, position);
636		this.selfBitmap = findSelfPicture();
637		updateMessages();
638		if (activity.getSlidingPaneLayout().isSlideable()) {
639			if (!activity.shouldPaneBeOpen()) {
640				activity.getSlidingPaneLayout().closePane();
641				activity.getActionBar().setDisplayHomeAsUpEnabled(true);
642				activity.getActionBar().setHomeButtonEnabled(true);
643				activity.getActionBar().setTitle(
644						conversation.getName(useSubject));
645				activity.invalidateOptionsMenu();
646			}
647		}
648		if (conversation.getMode() == Conversation.MODE_MULTI) {
649			activity.xmppConnectionService
650					.setOnRenameListener(new OnRenameListener() {
651
652						@Override
653						public void onRename(final boolean success) {
654							activity.xmppConnectionService
655									.updateConversation(conversation);
656							getActivity().runOnUiThread(new Runnable() {
657
658								@Override
659								public void run() {
660									if (success) {
661										Toast.makeText(
662												getActivity(),
663												getString(R.string.your_nick_has_been_changed),
664												Toast.LENGTH_SHORT).show();
665									} else {
666										Toast.makeText(
667												getActivity(),
668												getString(R.string.nick_in_use),
669												Toast.LENGTH_SHORT).show();
670									}
671								}
672							});
673						}
674					});
675		}
676	}
677
678	private void decryptMessage(Message message) {
679		PgpEngine engine = activity.xmppConnectionService.getPgpEngine();
680		if (engine != null) {
681			engine.decrypt(message, new UiCallback<Message>() {
682
683				@Override
684				public void userInputRequried(PendingIntent pi, Message message) {
685					askForPassphraseIntent = pi.getIntentSender();
686					showSnackbar(R.string.openpgp_messages_found,R.string.decrypt,clickToDecryptListener);
687				}
688
689				@Override
690				public void success(Message message) {
691					activity.xmppConnectionService.databaseBackend
692							.updateMessage(message);
693					updateMessages();
694				}
695
696				@Override
697				public void error(int error, Message message) {
698					message.setEncryption(Message.ENCRYPTION_DECRYPTION_FAILED);
699					// updateMessages();
700				}
701			});
702		}
703	}
704
705	public void updateMessages() {
706		if (getView() == null) {
707			return;
708		}
709		ConversationActivity activity = (ConversationActivity) getActivity();
710		if (this.conversation != null) {
711			for (Message message : this.conversation.getMessages()) {
712				if ((message.getEncryption() == Message.ENCRYPTION_PGP)
713						&& ((message.getStatus() == Message.STATUS_RECIEVED) || (message
714								.getStatus() == Message.STATUS_SEND))) {
715					decryptMessage(message);
716					break;
717				}
718			}
719			if (this.conversation.getMessages().size() == 0) {
720				this.messageList.clear();
721				messagesLoaded = false;
722			} else {
723				for (Message message : this.conversation.getMessages()) {
724					if (!this.messageList.contains(message)) {
725						this.messageList.add(message);
726					}
727				}
728				messagesLoaded = true;
729				updateStatusMessages();
730			}
731			this.messageListAdapter.notifyDataSetChanged();
732			if (conversation.getMode() == Conversation.MODE_SINGLE) {
733				if (messageList.size() >= 1) {
734					makeFingerprintWarning(conversation.getLatestEncryption());
735				}
736			} else {
737				if (conversation.getMucOptions().getError() != 0) {
738					showSnackbar(R.string.nick_in_use, R.string.edit,clickToMuc);
739					if (conversation.getMucOptions().getError() == MucOptions.ERROR_NICK_IN_USE) {
740						showSnackbar(R.string.nick_in_use, R.string.edit,clickToMuc);
741					}
742				}
743			}
744			getActivity().invalidateOptionsMenu();
745			updateChatMsgHint();
746			if (!activity.shouldPaneBeOpen()) {
747				activity.xmppConnectionService.markRead(conversation);
748				// TODO update notifications
749				UIHelper.updateNotification(getActivity(),
750						activity.getConversationList(), null, false);
751				activity.updateConversationList();
752			}
753		}
754	}
755
756	private void messageSent() {
757		int size = this.messageList.size();
758		if (size >= 1) {
759			messagesView.setSelection(size - 1);
760		}
761		chatMsg.setText("");
762	}
763
764	protected void updateStatusMessages() {
765		boolean addedStatusMsg = false;
766		if (conversation.getMode() == Conversation.MODE_SINGLE) {
767			for (int i = this.messageList.size() - 1; i >= 0; --i) {
768				if (addedStatusMsg) {
769					if (this.messageList.get(i).getType() == Message.TYPE_STATUS) {
770						this.messageList.remove(i);
771						--i;
772					}
773				} else {
774					if (this.messageList.get(i).getStatus() == Message.STATUS_RECIEVED) {
775						addedStatusMsg = true;
776					} else {
777						if (this.messageList.get(i).getStatus() == Message.STATUS_SEND_DISPLAYED) {
778							this.messageList.add(i + 1,
779									Message.createStatusMessage(conversation));
780							addedStatusMsg = true;
781						}
782					}
783				}
784			}
785		}
786	}
787
788	protected void makeFingerprintWarning(int latestEncryption) {
789		Set<String> knownFingerprints = conversation.getContact()
790				.getOtrFingerprints();
791		if ((latestEncryption == Message.ENCRYPTION_OTR)
792				&& (conversation.hasValidOtrSession()
793						&& (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) && (!knownFingerprints
794							.contains(conversation.getOtrFingerprint())))) {
795			showSnackbar(R.string.unknown_otr_fingerprint, R.string.verify, new OnClickListener() {
796
797				@Override
798				public void onClick(View v) {
799					if (conversation.getOtrFingerprint() != null) {
800						AlertDialog dialog = UIHelper.getVerifyFingerprintDialog(
801								(ConversationActivity) getActivity(), conversation,
802								snackbar);
803						dialog.show();
804					}
805				}
806			});
807		}
808	}
809	
810	protected void showSnackbar(int message, int action, OnClickListener clickListener) {
811		snackbar.setVisibility(View.VISIBLE);
812		snackbarMessage.setText(message);
813		snackbarAction.setText(action);
814		snackbarAction.setOnClickListener(clickListener);
815	}
816	
817	protected void hideSnackbar() {
818		snackbar.setVisibility(View.GONE);
819	}
820
821	protected void sendPlainTextMessage(Message message) {
822		ConversationActivity activity = (ConversationActivity) getActivity();
823		activity.xmppConnectionService.sendMessage(message);
824		messageSent();
825	}
826
827	protected void sendPgpMessage(final Message message) {
828		final ConversationActivity activity = (ConversationActivity) getActivity();
829		final XmppConnectionService xmppService = activity.xmppConnectionService;
830		final Contact contact = message.getConversation().getContact();
831		if (activity.hasPgp()) {
832			if (conversation.getMode() == Conversation.MODE_SINGLE) {
833				if (contact.getPgpKeyId() != 0) {
834					xmppService.getPgpEngine().hasKey(contact,
835							new UiCallback<Contact>() {
836
837								@Override
838								public void userInputRequried(PendingIntent pi,
839										Contact contact) {
840									activity.runIntent(
841											pi,
842											ConversationActivity.REQUEST_ENCRYPT_MESSAGE);
843								}
844
845								@Override
846								public void success(Contact contact) {
847									messageSent();
848									activity.encryptTextMessage(message);
849								}
850
851								@Override
852								public void error(int error, Contact contact) {
853
854								}
855							});
856
857				} else {
858					showNoPGPKeyDialog(false,
859							new DialogInterface.OnClickListener() {
860
861								@Override
862								public void onClick(DialogInterface dialog,
863										int which) {
864									conversation
865											.setNextEncryption(Message.ENCRYPTION_NONE);
866									message.setEncryption(Message.ENCRYPTION_NONE);
867									xmppService.sendMessage(message);
868									messageSent();
869								}
870							});
871				}
872			} else {
873				if (conversation.getMucOptions().pgpKeysInUse()) {
874					if (!conversation.getMucOptions().everybodyHasKeys()) {
875						Toast warning = Toast
876								.makeText(getActivity(),
877										R.string.missing_public_keys,
878										Toast.LENGTH_LONG);
879						warning.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
880						warning.show();
881					}
882					activity.encryptTextMessage(message);
883					messageSent();
884				} else {
885					showNoPGPKeyDialog(true,
886							new DialogInterface.OnClickListener() {
887
888								@Override
889								public void onClick(DialogInterface dialog,
890										int which) {
891									conversation
892											.setNextEncryption(Message.ENCRYPTION_NONE);
893									message.setEncryption(Message.ENCRYPTION_NONE);
894									xmppService.sendMessage(message);
895									messageSent();
896								}
897							});
898				}
899			}
900		} else {
901			activity.showInstallPgpDialog();
902		}
903	}
904
905	public void showNoPGPKeyDialog(boolean plural,
906			DialogInterface.OnClickListener listener) {
907		AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
908		builder.setIconAttribute(android.R.attr.alertDialogIcon);
909		if (plural) {
910			builder.setTitle(getString(R.string.no_pgp_keys));
911			builder.setMessage(getText(R.string.contacts_have_no_pgp_keys));
912		} else {
913			builder.setTitle(getString(R.string.no_pgp_key));
914			builder.setMessage(getText(R.string.contact_has_no_pgp_key));
915		}
916		builder.setNegativeButton(getString(R.string.cancel), null);
917		builder.setPositiveButton(getString(R.string.send_unencrypted),
918				listener);
919		builder.create().show();
920	}
921
922	protected void sendOtrMessage(final Message message) {
923		final ConversationActivity activity = (ConversationActivity) getActivity();
924		final XmppConnectionService xmppService = activity.xmppConnectionService;
925		if (conversation.hasValidOtrSession()) {
926			activity.xmppConnectionService.sendMessage(message);
927			messageSent();
928		} else {
929			activity.selectPresence(message.getConversation(),
930					new OnPresenceSelected() {
931
932						@Override
933						public void onPresenceSelected() {
934							message.setPresence(conversation.getNextPresence());
935							xmppService.sendMessage(message);
936							messageSent();
937						}
938					});
939		}
940	}
941
942	private static class ViewHolder {
943
944		protected LinearLayout message_box;
945		protected Button download_button;
946		protected ImageView image;
947		protected ImageView indicator;
948		protected TextView time;
949		protected TextView messageBody;
950		protected ImageView contact_picture;
951
952	}
953
954	private class BitmapCache {
955		private HashMap<String, Bitmap> bitmaps = new HashMap<String, Bitmap>();
956
957		public Bitmap get(String name, Contact contact, Context context) {
958			if (bitmaps.containsKey(name)) {
959				return bitmaps.get(name);
960			} else {
961				Bitmap bm;
962				if (contact != null) {
963					bm = UIHelper
964							.getContactPicture(contact, 48, context, false);
965				} else {
966					bm = UIHelper.getContactPicture(name, 48, context, false);
967				}
968				bitmaps.put(name, bm);
969				return bm;
970			}
971		}
972	}
973
974	public void setText(String text) {
975		this.pastedText = text;
976	}
977
978	public void clearInputField() {
979		this.chatMsg.setText("");
980	}
981}