ConversationFragment.java

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