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			private void displayStatus(ViewHolder viewHolder, Message message) {
196				String filesize = null;
197				String info = null;
198				boolean error = false;
199				if (message.getType() == Message.TYPE_IMAGE) {
200					String[] fileParams = message.getBody().split(",");
201					try {
202						long size = Long.parseLong(fileParams[0]);
203						filesize = size / 1024 + " KB";
204					} catch (NumberFormatException e) {
205						filesize = "0 KB";
206					}
207				}
208				switch (message.getStatus()) {
209				case Message.STATUS_UNSEND:
210					info = getString(R.string.sending);
211					break;
212				case Message.STATUS_OFFERED:
213					info = getString(R.string.offering);
214					break;
215				case Message.STATUS_SEND_FAILED:
216					info = getString(R.string.send_failed);
217					error = true;
218					break;
219				case Message.STATUS_SEND_REJECTED:
220					info = getString(R.string.send_rejected);
221					error = true;
222					break;
223				default:
224					if ((message.getConversation().getMode() == Conversation.MODE_MULTI)
225							&& (message.getStatus() <= Message.STATUS_RECIEVED)) {
226						info = message.getCounterpart();
227					}
228					break;
229				}
230				if (error) {
231					viewHolder.time.setTextColor(0xFFe92727);
232				} else {
233					viewHolder.time.setTextColor(0xFF8e8e8e);
234				}
235				if (message.getEncryption() == Message.ENCRYPTION_NONE) {
236					viewHolder.indicator.setVisibility(View.GONE);
237				} else {
238					viewHolder.indicator.setVisibility(View.VISIBLE);
239				}
240
241				String formatedTime = UIHelper.readableTimeDifference(message
242						.getTimeSent());
243				if (message.getStatus() <= Message.STATUS_RECIEVED) {
244					if ((filesize != null) && (info != null)) {
245						viewHolder.time.setText(filesize + " \u00B7 " + info);
246					} else if ((filesize == null) && (info != null)) {
247						viewHolder.time.setText(formatedTime + " \u00B7 "
248								+ info);
249					} else if ((filesize != null) && (info == null)) {
250						viewHolder.time.setText(formatedTime + " \u00B7 "
251								+ filesize);
252					} else {
253						viewHolder.time.setText(formatedTime);
254					}
255				} else {
256					if ((filesize != null) && (info != null)) {
257						viewHolder.time.setText(filesize + " \u00B7 " + info);
258					} else if ((filesize == null) && (info != null)) {
259						viewHolder.time.setText(info + " \u00B7 "
260								+ formatedTime);
261					} else if ((filesize != null) && (info == null)) {
262						viewHolder.time.setText(filesize + " \u00B7 "
263								+ formatedTime);
264					} else {
265						viewHolder.time.setText(formatedTime);
266					}
267				}
268			}
269
270			private void displayInfoMessage(ViewHolder viewHolder, int r) {
271				viewHolder.download_button.setVisibility(View.GONE);
272				viewHolder.image.setVisibility(View.GONE);
273				viewHolder.messageBody.setVisibility(View.VISIBLE);
274				viewHolder.messageBody.setText(getString(r));
275				viewHolder.messageBody.setTextColor(0xff33B5E5);
276				viewHolder.messageBody.setTypeface(null, Typeface.ITALIC);
277			}
278
279			private void displayDecryptionFailed(ViewHolder viewHolder) {
280				viewHolder.download_button.setVisibility(View.GONE);
281				viewHolder.image.setVisibility(View.GONE);
282				viewHolder.messageBody.setVisibility(View.VISIBLE);
283				viewHolder.messageBody
284						.setText(getString(R.string.decryption_failed));
285				viewHolder.messageBody.setTextColor(0xFFe92727);
286				viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
287			}
288
289			private void displayTextMessage(ViewHolder viewHolder, String text) {
290				if (viewHolder.download_button != null) {
291					viewHolder.download_button.setVisibility(View.GONE);
292				}
293				viewHolder.image.setVisibility(View.GONE);
294				viewHolder.messageBody.setVisibility(View.VISIBLE);
295				if (text != null) {
296					viewHolder.messageBody.setText(text.trim());
297				} else {
298					viewHolder.messageBody.setText("");
299				}
300				viewHolder.messageBody.setTextColor(0xff333333);
301				viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
302			}
303
304			private void displayImageMessage(ViewHolder viewHolder,
305					final Message message) {
306				if (viewHolder.download_button != null) {
307					viewHolder.download_button.setVisibility(View.GONE);
308				}
309				viewHolder.messageBody.setVisibility(View.GONE);
310				viewHolder.image.setVisibility(View.VISIBLE);
311				String[] fileParams = message.getBody().split(",");
312				if (fileParams.length == 3) {
313					double target = metrics.density * 288;
314					int w = Integer.parseInt(fileParams[1]);
315					int h = Integer.parseInt(fileParams[2]);
316					int scalledW;
317					int scalledH;
318					if (w <= h) {
319						scalledW = (int) (w / ((double) h / target));
320						scalledH = (int) target;
321					} else {
322						scalledW = (int) target;
323						scalledH = (int) (h / ((double) w / target));
324					}
325					viewHolder.image
326							.setLayoutParams(new LinearLayout.LayoutParams(
327									scalledW, scalledH));
328				}
329				activity.loadBitmap(message, viewHolder.image);
330				viewHolder.image.setOnClickListener(new OnClickListener() {
331
332					@Override
333					public void onClick(View v) {
334						Uri uri = Uri
335								.parse("content://eu.siacs.conversations.images/"
336										+ message.getConversationUuid()
337										+ "/"
338										+ message.getUuid());
339						Log.d("xmppService",
340								"staring intent with uri:" + uri.toString());
341						Intent intent = new Intent(Intent.ACTION_VIEW);
342						intent.setDataAndType(uri, "image/*");
343						startActivity(intent);
344					}
345				});
346			}
347
348			@Override
349			public View getView(int position, View view, ViewGroup parent) {
350				final Message item = getItem(position);
351				int type = getItemViewType(position);
352				ViewHolder viewHolder;
353				if (view == null) {
354					viewHolder = new ViewHolder();
355					switch (type) {
356					case SENT:
357						view = (View) inflater.inflate(R.layout.message_sent,
358								null);
359						viewHolder.contact_picture = (ImageView) view
360								.findViewById(R.id.message_photo);
361						viewHolder.contact_picture.setImageBitmap(selfBitmap);
362						break;
363					case RECIEVED:
364						view = (View) inflater.inflate(
365								R.layout.message_recieved, null);
366						viewHolder.contact_picture = (ImageView) view
367								.findViewById(R.id.message_photo);
368
369						viewHolder.download_button = (Button) view
370								.findViewById(R.id.download_button);
371
372						if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
373
374							viewHolder.contact_picture
375									.setImageBitmap(mBitmapCache.get(
376											item.getConversation().getName(
377													useSubject), item
378													.getConversation()
379													.getContact(),
380											getActivity()
381													.getApplicationContext()));
382
383						}
384						break;
385					default:
386						viewHolder = null;
387						break;
388					}
389					viewHolder.indicator = (ImageView) view
390							.findViewById(R.id.security_indicator);
391					viewHolder.image = (ImageView) view
392							.findViewById(R.id.message_image);
393					viewHolder.messageBody = (TextView) view
394							.findViewById(R.id.message_body);
395					viewHolder.time = (TextView) view
396							.findViewById(R.id.message_time);
397					view.setTag(viewHolder);
398				} else {
399					viewHolder = (ViewHolder) view.getTag();
400				}
401
402				if (type == RECIEVED) {
403					if (item.getConversation().getMode() == Conversation.MODE_MULTI) {
404						if (item.getCounterpart() != null) {
405							viewHolder.contact_picture
406									.setImageBitmap(mBitmapCache.get(item
407											.getCounterpart(), null,
408											getActivity()
409													.getApplicationContext()));
410						} else {
411							viewHolder.contact_picture
412									.setImageBitmap(mBitmapCache.get(
413											item.getConversation().getName(
414													useSubject), null,
415											getActivity()
416													.getApplicationContext()));
417						}
418					}
419				}
420
421				if (item.getType() == Message.TYPE_IMAGE) {
422					if (item.getStatus() == Message.STATUS_RECIEVING) {
423						displayInfoMessage(viewHolder, R.string.receiving_image);
424					} else if (item.getStatus() == Message.STATUS_RECEIVED_OFFER) {
425						viewHolder.image.setVisibility(View.GONE);
426						viewHolder.messageBody.setVisibility(View.GONE);
427						viewHolder.download_button.setVisibility(View.VISIBLE);
428						viewHolder.download_button
429								.setOnClickListener(new OnClickListener() {
430
431									@Override
432									public void onClick(View v) {
433										JingleConnection connection = item
434												.getJingleConnection();
435										if (connection != null) {
436											connection.accept();
437										} else {
438											Log.d("xmppService",
439													"attached jingle connection was null");
440										}
441									}
442								});
443					} else if ((item.getEncryption() == Message.ENCRYPTION_DECRYPTED)
444							|| (item.getEncryption() == Message.ENCRYPTION_NONE)) {
445						displayImageMessage(viewHolder, item);
446					} else if (item.getEncryption() == Message.ENCRYPTION_PGP) {
447						displayInfoMessage(viewHolder,
448								R.string.encrypted_message);
449					} else {
450						displayDecryptionFailed(viewHolder);
451					}
452				} else {
453					if (item.getEncryption() == Message.ENCRYPTION_PGP) {
454						displayInfoMessage(viewHolder,
455								R.string.encrypted_message);
456					} else if (item.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
457						displayDecryptionFailed(viewHolder);
458					} else {
459						displayTextMessage(viewHolder, item.getBody());
460					}
461				}
462
463				displayStatus(viewHolder, item);
464
465				return view;
466			}
467		};
468		messagesView.setAdapter(messageListAdapter);
469
470		return view;
471	}
472
473	protected Bitmap findSelfPicture() {
474		SharedPreferences sharedPref = PreferenceManager
475				.getDefaultSharedPreferences(getActivity()
476						.getApplicationContext());
477		boolean showPhoneSelfContactPicture = sharedPref.getBoolean(
478				"show_phone_selfcontact_picture", true);
479
480		return UIHelper.getSelfContactPicture(conversation.getAccount(), 48,
481				showPhoneSelfContactPicture, getActivity());
482	}
483
484	@Override
485	public void onStart() {
486		super.onStart();
487		this.activity = (ConversationActivity) getActivity();
488		SharedPreferences preferences = PreferenceManager
489				.getDefaultSharedPreferences(activity);
490		this.useSubject = preferences.getBoolean("use_subject_in_muc", true);
491		if (activity.xmppConnectionServiceBound) {
492			this.onBackendConnected();
493		}
494	}
495
496	@Override
497	public void onStop() {
498		super.onStop();
499		if (this.conversation!=null) {
500			this.conversation.setNextMessage(chatMsg.getText().toString());
501		}
502	}
503
504	public void onBackendConnected() {
505		this.conversation = activity.getSelectedConversation();
506		if (this.conversation == null) {
507			return;
508		}
509		if (this.pastedText == null) {
510			this.chatMsg.setText(conversation.getNextMessage());
511		} else {
512			chatMsg.setText(conversation.getNextMessage() + " " + pastedText);
513			pastedText = null;
514		}
515		int position = chatMsg.length();
516		Editable etext = chatMsg.getText();
517		Selection.setSelection(etext, position);
518		this.selfBitmap = findSelfPicture();
519		updateMessages();
520		if (activity.getSlidingPaneLayout().isSlideable()) {
521			if (!activity.shouldPaneBeOpen()) {
522				activity.getSlidingPaneLayout().closePane();
523				activity.getActionBar().setDisplayHomeAsUpEnabled(true);
524				activity.getActionBar().setTitle(
525						conversation.getName(useSubject));
526				activity.invalidateOptionsMenu();
527
528			}
529		}
530		if (conversation.getMode() == Conversation.MODE_MULTI) {
531			activity.xmppConnectionService
532					.setOnRenameListener(new OnRenameListener() {
533
534						@Override
535						public void onRename(final boolean success) {
536							activity.xmppConnectionService
537									.updateConversation(conversation);
538							getActivity().runOnUiThread(new Runnable() {
539
540								@Override
541								public void run() {
542									if (success) {
543										Toast.makeText(
544												getActivity(),
545												getString(R.string.your_nick_has_been_changed),
546												Toast.LENGTH_SHORT).show();
547									} else {
548										Toast.makeText(
549												getActivity(),
550												getString(R.string.nick_in_use),
551												Toast.LENGTH_SHORT).show();
552									}
553								}
554							});
555						}
556					});
557		}
558	}
559
560	private void decryptMessage(final Message message) {
561		PgpEngine engine = activity.xmppConnectionService.getPgpEngine();
562		if (engine != null) {
563			engine.decrypt(message, new OnPgpEngineResult() {
564
565				@Override
566				public void userInputRequried(PendingIntent pi) {
567					askForPassphraseIntent = pi.getIntentSender();
568					pgpInfo.setVisibility(View.VISIBLE);
569				}
570
571				@Override
572				public void success() {
573					activity.xmppConnectionService.databaseBackend
574							.updateMessage(message);
575					updateMessages();
576				}
577
578				@Override
579				public void error(OpenPgpError openPgpError) {
580					Log.d("xmppService",
581							"decryption error" + openPgpError.getMessage());
582					message.setEncryption(Message.ENCRYPTION_DECRYPTION_FAILED);
583					// updateMessages();
584				}
585			});
586		} else {
587			pgpInfo.setVisibility(View.VISIBLE);
588		}
589	}
590
591	public void updateMessages() {
592		if (getView()==null) {
593			return;
594		}
595		ConversationActivity activity = (ConversationActivity) getActivity();
596		if (this.conversation != null) {
597			for (Message message : this.conversation.getMessages()) {
598				if ((message.getEncryption() == Message.ENCRYPTION_PGP)
599						&& (message.getStatus() == Message.STATUS_RECIEVED)) {
600					decryptMessage(message);
601					break;
602				}
603			}
604			this.messageList.clear();
605			this.messageList.addAll(this.conversation.getMessages());
606			this.messageListAdapter.notifyDataSetChanged();
607			if (conversation.getMode() == Conversation.MODE_SINGLE) {
608				if (messageList.size() >= 1) {
609					makeFingerprintWarning(conversation.getLatestEncryption());
610				}
611			} else {
612				if (conversation.getMucOptions().getError() != 0) {
613					mucError.setVisibility(View.VISIBLE);
614					if (conversation.getMucOptions().getError() == MucOptions.ERROR_NICK_IN_USE) {
615						mucErrorText.setText(getString(R.string.nick_in_use));
616					}
617				} else {
618					mucError.setVisibility(View.GONE);
619				}
620			}
621			getActivity().invalidateOptionsMenu();
622			updateChatMsgHint();
623			int size = this.messageList.size();
624			if (size >= 1)
625				messagesView.setSelection(size - 1);
626			if (!activity.shouldPaneBeOpen()) {
627				conversation.markRead();
628				// TODO update notifications
629				UIHelper.updateNotification(getActivity(),
630						activity.getConversationList(), null, false);
631				activity.updateConversationList();
632			}
633		}
634	}
635
636	protected void makeFingerprintWarning(int latestEncryption) {
637		final LinearLayout fingerprintWarning = (LinearLayout) getView()
638				.findViewById(R.id.new_fingerprint);
639		if (conversation.getContact() != null) {
640			Set<String> knownFingerprints = conversation.getContact()
641					.getOtrFingerprints();
642			if ((latestEncryption == Message.ENCRYPTION_OTR)
643					&& (conversation.hasValidOtrSession()
644							&& (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) && (!knownFingerprints
645								.contains(conversation.getOtrFingerprint())))) {
646				fingerprintWarning.setVisibility(View.VISIBLE);
647				TextView fingerprint = (TextView) getView().findViewById(
648						R.id.otr_fingerprint);
649				fingerprint.setText(conversation.getOtrFingerprint());
650				fingerprintWarning.setOnClickListener(new OnClickListener() {
651
652					@Override
653					public void onClick(View v) {
654						AlertDialog dialog = UIHelper
655								.getVerifyFingerprintDialog(
656										(ConversationActivity) getActivity(),
657										conversation, fingerprintWarning);
658						dialog.show();
659					}
660				});
661			} else {
662				fingerprintWarning.setVisibility(View.GONE);
663			}
664		} else {
665			fingerprintWarning.setVisibility(View.GONE);
666		}
667	}
668
669	protected void sendPlainTextMessage(Message message) {
670		ConversationActivity activity = (ConversationActivity) getActivity();
671		activity.xmppConnectionService.sendMessage(message, null);
672		chatMsg.setText("");
673	}
674
675	protected void sendPgpMessage(final Message message) {
676		activity.pendingMessage = message;
677		final ConversationActivity activity = (ConversationActivity) getActivity();
678		final XmppConnectionService xmppService = activity.xmppConnectionService;
679		final Contact contact = message.getConversation().getContact();
680		if (activity.hasPgp()) {
681			if (contact.getPgpKeyId() != 0) {
682				xmppService.getPgpEngine().hasKey(contact,
683						new OnPgpEngineResult() {
684
685							@Override
686							public void userInputRequried(PendingIntent pi) {
687								activity.runIntent(
688										pi,
689										ConversationActivity.REQUEST_ENCRYPT_MESSAGE);
690							}
691
692							@Override
693							public void success() {
694								activity.encryptTextMessage();
695							}
696
697							@Override
698							public void error(OpenPgpError openPgpError) {
699								Log.d("xmppService", "openpgp error"
700										+ openPgpError.getMessage());
701							}
702						});
703
704			} else {
705				showNoPGPKeyDialog(new DialogInterface.OnClickListener() {
706
707					@Override
708					public void onClick(DialogInterface dialog,
709							int which) {
710						conversation
711								.setNextEncryption(Message.ENCRYPTION_NONE);
712						message.setEncryption(Message.ENCRYPTION_NONE);
713						xmppService.sendMessage(message, null);
714						chatMsg.setText("");
715					}
716				});
717			}
718		}
719	}
720	
721	public void showNoPGPKeyDialog(DialogInterface.OnClickListener listener) {
722		AlertDialog.Builder builder = new AlertDialog.Builder(
723				getActivity());
724		builder.setTitle(getString(R.string.no_pgp_key));
725		builder.setIconAttribute(android.R.attr.alertDialogIcon);
726		builder.setMessage(getText(R.string.contact_has_no_pgp_key));
727		builder.setNegativeButton(getString(R.string.cancel), null);
728		builder.setPositiveButton(getString(R.string.send_unencrypted),listener);
729		builder.create().show();
730	}
731
732	protected void sendOtrMessage(final Message message) {
733		ConversationActivity activity = (ConversationActivity) getActivity();
734		final XmppConnectionService xmppService = activity.xmppConnectionService;
735		if (conversation.hasValidOtrSession()) {
736			activity.xmppConnectionService.sendMessage(message, null);
737			chatMsg.setText("");
738		} else {
739			activity.selectPresence(message.getConversation(),
740					new OnPresenceSelected() {
741
742						@Override
743						public void onPresenceSelected(boolean success,
744								String presence) {
745							if (success) {
746								xmppService.sendMessage(message, presence);
747								chatMsg.setText("");
748							}
749						}
750
751						@Override
752						public void onSendPlainTextInstead() {
753							message.setEncryption(Message.ENCRYPTION_NONE);
754							xmppService.sendMessage(message, null);
755							chatMsg.setText("");
756						}
757					}, "otr");
758		}
759	}
760
761	private static class ViewHolder {
762
763		protected Button download_button;
764		protected ImageView image;
765		protected ImageView indicator;
766		protected TextView time;
767		protected TextView messageBody;
768		protected ImageView contact_picture;
769
770	}
771
772	private class BitmapCache {
773		private HashMap<String, Bitmap> bitmaps = new HashMap<String, Bitmap>();
774		private Bitmap error = null;
775
776		public Bitmap get(String name, Contact contact, Context context) {
777			if (bitmaps.containsKey(name)) {
778				return bitmaps.get(name);
779			} else {
780				Bitmap bm;
781				if (contact != null) {
782					bm = UIHelper
783							.getContactPicture(contact, 48, context, false);
784				} else {
785					bm = UIHelper.getContactPicture(name, 48, context, false);
786				}
787				bitmaps.put(name, bm);
788				return bm;
789			}
790		}
791	}
792
793	public void setText(String text) {
794		this.pastedText = text;
795	}
796
797	public void clearInputField() {
798		this.chatMsg.setText("");
799	}
800}