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