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/webp");
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							viewHolder.contact_picture.setAlpha(128);
423
424						}
425						break;
426					default:
427						viewHolder = null;
428						break;
429					}
430				} else {
431					viewHolder = (ViewHolder) view.getTag();
432				}
433				
434				if (type == STATUS) {
435					return view;
436				}
437
438				if (type == RECIEVED) {
439					if (item.getConversation().getMode() == Conversation.MODE_MULTI) {
440						viewHolder.contact_picture.setImageBitmap(mBitmapCache
441								.get(item.getCounterpart(), null, getActivity()
442										.getApplicationContext()));
443						viewHolder.contact_picture
444								.setOnClickListener(new OnClickListener() {
445
446									@Override
447									public void onClick(View v) {
448										highlightInConference(item
449												.getCounterpart());
450									}
451								});
452					}
453				}
454
455				if (item.getType() == Message.TYPE_IMAGE) {
456					if (item.getStatus() == Message.STATUS_RECIEVING) {
457						displayInfoMessage(viewHolder, R.string.receiving_image);
458					} else if (item.getStatus() == Message.STATUS_RECEIVED_OFFER) {
459						viewHolder.image.setVisibility(View.GONE);
460						viewHolder.messageBody.setVisibility(View.GONE);
461						viewHolder.download_button.setVisibility(View.VISIBLE);
462						viewHolder.download_button
463								.setOnClickListener(new OnClickListener() {
464
465									@Override
466									public void onClick(View v) {
467										JingleConnection connection = item
468												.getJingleConnection();
469										if (connection != null) {
470											connection.accept();
471										} else {
472											Log.d("xmppService",
473													"attached jingle connection was null");
474										}
475									}
476								});
477					} else if ((item.getEncryption() == Message.ENCRYPTION_DECRYPTED)
478							|| (item.getEncryption() == Message.ENCRYPTION_NONE)) {
479						displayImageMessage(viewHolder, item);
480					} else if (item.getEncryption() == Message.ENCRYPTION_PGP) {
481						displayInfoMessage(viewHolder,
482								R.string.encrypted_message);
483					} else {
484						displayDecryptionFailed(viewHolder);
485					}
486				} else {
487					if (item.getEncryption() == Message.ENCRYPTION_PGP) {
488						displayInfoMessage(viewHolder,
489								R.string.encrypted_message);
490					} else if (item.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
491						displayDecryptionFailed(viewHolder);
492					} else {
493						displayTextMessage(viewHolder, item.getBody());
494					}
495				}
496
497				displayStatus(viewHolder, item);
498
499				return view;
500			}
501		};
502		messagesView.setAdapter(messageListAdapter);
503
504		return view;
505	}
506
507	protected void highlightInConference(String nick) {
508		String oldString = chatMsg.getText().toString().trim();
509		if (oldString.isEmpty()) {
510			chatMsg.setText(nick + ": ");
511		} else {
512			chatMsg.setText(oldString + " " + nick + " ");
513		}
514		int position = chatMsg.length();
515		Editable etext = chatMsg.getText();
516		Selection.setSelection(etext, position);
517	}
518
519	protected Bitmap findSelfPicture() {
520		SharedPreferences sharedPref = PreferenceManager
521				.getDefaultSharedPreferences(getActivity()
522						.getApplicationContext());
523		boolean showPhoneSelfContactPicture = sharedPref.getBoolean(
524				"show_phone_selfcontact_picture", true);
525
526		return UIHelper.getSelfContactPicture(conversation.getAccount(), 48,
527				showPhoneSelfContactPicture, getActivity());
528	}
529
530	@Override
531	public void onStart() {
532		super.onStart();
533		this.activity = (ConversationActivity) getActivity();
534		SharedPreferences preferences = PreferenceManager
535				.getDefaultSharedPreferences(activity);
536		this.useSubject = preferences.getBoolean("use_subject_in_muc", true);
537		if (activity.xmppConnectionServiceBound) {
538			this.onBackendConnected();
539		}
540	}
541
542	@Override
543	public void onStop() {
544		super.onStop();
545		if (this.conversation != null) {
546			this.conversation.setNextMessage(chatMsg.getText().toString());
547		}
548	}
549
550	public void onBackendConnected() {
551		this.conversation = activity.getSelectedConversation();
552		if (this.conversation == null) {
553			return;
554		}
555		String oldString = conversation.getNextMessage().trim();
556		if (this.pastedText == null) {
557			this.chatMsg.setText(oldString);
558		} else {
559
560			if (oldString.isEmpty()) {
561				chatMsg.setText(pastedText);
562			} else {
563				chatMsg.setText(oldString + " " + pastedText);
564			}
565			pastedText = null;
566		}
567		int position = chatMsg.length();
568		Editable etext = chatMsg.getText();
569		Selection.setSelection(etext, position);
570		this.selfBitmap = findSelfPicture();
571		updateMessages();
572		if (activity.getSlidingPaneLayout().isSlideable()) {
573			if (!activity.shouldPaneBeOpen()) {
574				activity.getSlidingPaneLayout().closePane();
575				activity.getActionBar().setDisplayHomeAsUpEnabled(true);
576				activity.getActionBar().setHomeButtonEnabled(true);
577				activity.getActionBar().setTitle(
578						conversation.getName(useSubject));
579				activity.invalidateOptionsMenu();
580
581			}
582		}
583		if (conversation.getMode() == Conversation.MODE_MULTI) {
584			activity.xmppConnectionService
585					.setOnRenameListener(new OnRenameListener() {
586
587						@Override
588						public void onRename(final boolean success) {
589							activity.xmppConnectionService
590									.updateConversation(conversation);
591							getActivity().runOnUiThread(new Runnable() {
592
593								@Override
594								public void run() {
595									if (success) {
596										Toast.makeText(
597												getActivity(),
598												getString(R.string.your_nick_has_been_changed),
599												Toast.LENGTH_SHORT).show();
600									} else {
601										Toast.makeText(
602												getActivity(),
603												getString(R.string.nick_in_use),
604												Toast.LENGTH_SHORT).show();
605									}
606								}
607							});
608						}
609					});
610		}
611	}
612
613	private void decryptMessage(final Message message) {
614		PgpEngine engine = activity.xmppConnectionService.getPgpEngine();
615		if (engine != null) {
616			engine.decrypt(message, new UiCallback() {
617
618				@Override
619				public void userInputRequried(PendingIntent pi) {
620					askForPassphraseIntent = pi.getIntentSender();
621					pgpInfo.setVisibility(View.VISIBLE);
622				}
623
624				@Override
625				public void success() {
626					activity.xmppConnectionService.databaseBackend
627							.updateMessage(message);
628					updateMessages();
629				}
630
631				@Override
632				public void error(int error) {
633					message.setEncryption(Message.ENCRYPTION_DECRYPTION_FAILED);
634					// updateMessages();
635				}
636			});
637		} else {
638			pgpInfo.setVisibility(View.VISIBLE);
639		}
640	}
641
642	public void updateMessages() {
643		if (getView() == null) {
644			return;
645		}
646		ConversationActivity activity = (ConversationActivity) getActivity();
647		if (this.conversation != null) {
648			for (Message message : this.conversation.getMessages()) {
649				if ((message.getEncryption() == Message.ENCRYPTION_PGP)
650						&& ((message.getStatus() == Message.STATUS_RECIEVED) || (message
651								.getStatus() == Message.STATUS_SEND))) {
652					decryptMessage(message);
653					break;
654				}
655			}
656			this.messageList.clear();
657			this.messageList.addAll(this.conversation.getMessages());
658			updateStatusMessages();
659			this.messageListAdapter.notifyDataSetChanged();
660			if (conversation.getMode() == Conversation.MODE_SINGLE) {
661				if (messageList.size() >= 1) {
662					makeFingerprintWarning(conversation.getLatestEncryption());
663				}
664			} else {
665				if (conversation.getMucOptions().getError() != 0) {
666					mucError.setVisibility(View.VISIBLE);
667					if (conversation.getMucOptions().getError() == MucOptions.ERROR_NICK_IN_USE) {
668						mucErrorText.setText(getString(R.string.nick_in_use));
669					}
670				} else {
671					mucError.setVisibility(View.GONE);
672				}
673			}
674			getActivity().invalidateOptionsMenu();
675			updateChatMsgHint();
676			int size = this.messageList.size();
677			if (size >= 1)
678				messagesView.setSelection(size - 1);
679			if (!activity.shouldPaneBeOpen()) {
680				activity.xmppConnectionService.markRead(conversation);
681				// TODO update notifications
682				UIHelper.updateNotification(getActivity(),
683						activity.getConversationList(), null, false);
684				activity.updateConversationList();
685			}
686		}
687	}
688	
689	protected void updateStatusMessages() {
690		if (conversation.getMode() == Conversation.MODE_SINGLE) {
691			for(int i = this.messageList.size() - 1; i >= 0; --i) {
692				if (this.messageList.get(i).getStatus() == Message.STATUS_RECIEVED) {
693					return;
694				} else {
695					if (this.messageList.get(i).getStatus() == Message.STATUS_SEND_DISPLAYED) {
696						this.messageList.add(i+1, Message.createStatusMessage(conversation));
697						return;
698					}
699				}
700			}
701		}
702	}
703
704	protected void makeFingerprintWarning(int latestEncryption) {
705		final LinearLayout fingerprintWarning = (LinearLayout) getView()
706				.findViewById(R.id.new_fingerprint);
707		Set<String> knownFingerprints = conversation.getContact()
708				.getOtrFingerprints();
709		if ((latestEncryption == Message.ENCRYPTION_OTR)
710				&& (conversation.hasValidOtrSession()
711						&& (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) && (!knownFingerprints
712							.contains(conversation.getOtrFingerprint())))) {
713			fingerprintWarning.setVisibility(View.VISIBLE);
714			TextView fingerprint = (TextView) getView().findViewById(
715					R.id.otr_fingerprint);
716			fingerprint.setText(conversation.getOtrFingerprint());
717			fingerprintWarning.setOnClickListener(new OnClickListener() {
718
719				@Override
720				public void onClick(View v) {
721					AlertDialog dialog = UIHelper.getVerifyFingerprintDialog(
722							(ConversationActivity) getActivity(), conversation,
723							fingerprintWarning);
724					dialog.show();
725				}
726			});
727		} else {
728			fingerprintWarning.setVisibility(View.GONE);
729		}
730	}
731
732	protected void sendPlainTextMessage(Message message) {
733		ConversationActivity activity = (ConversationActivity) getActivity();
734		activity.xmppConnectionService.sendMessage(message, null);
735		chatMsg.setText("");
736	}
737
738	protected void sendPgpMessage(final Message message) {
739		activity.pendingMessage = message;
740		final ConversationActivity activity = (ConversationActivity) getActivity();
741		final XmppConnectionService xmppService = activity.xmppConnectionService;
742		final Contact contact = message.getConversation().getContact();
743		if (activity.hasPgp()) {
744			if (conversation.getMode() == Conversation.MODE_SINGLE) {
745				if (contact.getPgpKeyId() != 0) {
746					xmppService.getPgpEngine().hasKey(contact,
747							new UiCallback() {
748
749								@Override
750								public void userInputRequried(PendingIntent pi) {
751									activity.runIntent(
752											pi,
753											ConversationActivity.REQUEST_ENCRYPT_MESSAGE);
754								}
755
756								@Override
757								public void success() {
758									activity.encryptTextMessage();
759								}
760
761								@Override
762								public void error(int error) {
763
764								}
765							});
766
767				} else {
768					showNoPGPKeyDialog(false,
769							new DialogInterface.OnClickListener() {
770
771								@Override
772								public void onClick(DialogInterface dialog,
773										int which) {
774									conversation
775											.setNextEncryption(Message.ENCRYPTION_NONE);
776									message.setEncryption(Message.ENCRYPTION_NONE);
777									xmppService.sendMessage(message, null);
778									chatMsg.setText("");
779								}
780							});
781				}
782			} else {
783				if (conversation.getMucOptions().pgpKeysInUse()) {
784					if (!conversation.getMucOptions().everybodyHasKeys()) {
785						Toast warning = Toast
786								.makeText(getActivity(),
787										R.string.missing_public_keys,
788										Toast.LENGTH_LONG);
789						warning.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
790						warning.show();
791					}
792					activity.encryptTextMessage();
793				} else {
794					showNoPGPKeyDialog(true,
795							new DialogInterface.OnClickListener() {
796
797								@Override
798								public void onClick(DialogInterface dialog,
799										int which) {
800									conversation
801											.setNextEncryption(Message.ENCRYPTION_NONE);
802									message.setEncryption(Message.ENCRYPTION_NONE);
803									xmppService.sendMessage(message, null);
804									chatMsg.setText("");
805								}
806							});
807				}
808			}
809		}
810	}
811
812	public void showNoPGPKeyDialog(boolean plural,
813			DialogInterface.OnClickListener listener) {
814		AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
815		builder.setIconAttribute(android.R.attr.alertDialogIcon);
816		if (plural) {
817			builder.setTitle(getString(R.string.no_pgp_keys));
818			builder.setMessage(getText(R.string.contacts_have_no_pgp_keys));
819		} else {
820			builder.setTitle(getString(R.string.no_pgp_key));
821			builder.setMessage(getText(R.string.contact_has_no_pgp_key));
822		}
823		builder.setNegativeButton(getString(R.string.cancel), null);
824		builder.setPositiveButton(getString(R.string.send_unencrypted),
825				listener);
826		builder.create().show();
827	}
828
829	protected void sendOtrMessage(final Message message) {
830		ConversationActivity activity = (ConversationActivity) getActivity();
831		final XmppConnectionService xmppService = activity.xmppConnectionService;
832		if (conversation.hasValidOtrSession()) {
833			activity.xmppConnectionService.sendMessage(message, null);
834			chatMsg.setText("");
835		} else {
836			activity.selectPresence(message.getConversation(),
837					new OnPresenceSelected() {
838
839						@Override
840						public void onPresenceSelected(boolean success,
841								String presence) {
842							if (success) {
843								xmppService.sendMessage(message, presence);
844								chatMsg.setText("");
845							}
846						}
847
848						@Override
849						public void onSendPlainTextInstead() {
850							message.setEncryption(Message.ENCRYPTION_NONE);
851							xmppService.sendMessage(message, null);
852							chatMsg.setText("");
853						}
854					}, "otr");
855		}
856	}
857
858	private static class ViewHolder {
859
860		protected Button download_button;
861		protected ImageView image;
862		protected ImageView indicator;
863		protected TextView time;
864		protected TextView messageBody;
865		protected ImageView contact_picture;
866
867	}
868
869	private class BitmapCache {
870		private HashMap<String, Bitmap> bitmaps = new HashMap<String, Bitmap>();
871
872		public Bitmap get(String name, Contact contact, Context context) {
873			if (bitmaps.containsKey(name)) {
874				return bitmaps.get(name);
875			} else {
876				Bitmap bm;
877				if (contact != null) {
878					bm = UIHelper
879							.getContactPicture(contact, 48, context, false);
880				} else {
881					bm = UIHelper.getContactPicture(name, 48, context, false);
882				}
883				bitmaps.put(name, bm);
884				return bm;
885			}
886		}
887	}
888
889	public void setText(String text) {
890		this.pastedText = text;
891	}
892
893	public void clearInputField() {
894		this.chatMsg.setText("");
895	}
896}