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