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