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