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 (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(getContext(), message
234						.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.getCounterpart());
401									}
402								});
403					}
404				}
405
406				if (item.getType() == Message.TYPE_IMAGE) {
407					if (item.getStatus() == Message.STATUS_RECIEVING) {
408						displayInfoMessage(viewHolder, R.string.receiving_image);
409					} else if (item.getStatus() == Message.STATUS_RECEIVED_OFFER) {
410						viewHolder.image.setVisibility(View.GONE);
411						viewHolder.messageBody.setVisibility(View.GONE);
412						viewHolder.download_button.setVisibility(View.VISIBLE);
413						viewHolder.download_button
414								.setOnClickListener(new OnClickListener() {
415
416									@Override
417									public void onClick(View v) {
418										JingleConnection connection = item
419												.getJingleConnection();
420										if (connection != null) {
421											connection.accept();
422										} else {
423											Log.d("xmppService",
424													"attached jingle connection was null");
425										}
426									}
427								});
428					} else if ((item.getEncryption() == Message.ENCRYPTION_DECRYPTED)
429							|| (item.getEncryption() == Message.ENCRYPTION_NONE)) {
430						displayImageMessage(viewHolder, item);
431					} else if (item.getEncryption() == Message.ENCRYPTION_PGP) {
432						displayInfoMessage(viewHolder,
433								R.string.encrypted_message);
434					} else {
435						displayDecryptionFailed(viewHolder);
436					}
437				} else {
438					if (item.getEncryption() == Message.ENCRYPTION_PGP) {
439						displayInfoMessage(viewHolder,
440								R.string.encrypted_message);
441					} else if (item.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
442						displayDecryptionFailed(viewHolder);
443					} else {
444						displayTextMessage(viewHolder, item.getBody());
445					}
446				}
447
448				displayStatus(viewHolder, item);
449
450				return view;
451			}
452		};
453		messagesView.setAdapter(messageListAdapter);
454
455		return view;
456	}
457
458	protected void highlightInConference(String nick) {
459		String oldString = chatMsg.getText().toString().trim();
460		if (oldString.isEmpty()) {
461			chatMsg.setText(nick+": ");
462		} else {
463			chatMsg.setText(oldString+" "+nick+" ");
464		}
465		int position = chatMsg.length();
466		Editable etext = chatMsg.getText();
467		Selection.setSelection(etext, position);
468	}
469	
470	protected Bitmap findSelfPicture() {
471		SharedPreferences sharedPref = PreferenceManager
472				.getDefaultSharedPreferences(getActivity()
473						.getApplicationContext());
474		boolean showPhoneSelfContactPicture = sharedPref.getBoolean(
475				"show_phone_selfcontact_picture", true);
476
477		return UIHelper.getSelfContactPicture(conversation.getAccount(), 48,
478				showPhoneSelfContactPicture, getActivity());
479	}
480
481	@Override
482	public void onStart() {
483		super.onStart();
484		this.activity = (ConversationActivity) getActivity();
485		SharedPreferences preferences = PreferenceManager
486				.getDefaultSharedPreferences(activity);
487		this.useSubject = preferences.getBoolean("use_subject_in_muc", true);
488		if (activity.xmppConnectionServiceBound) {
489			this.onBackendConnected();
490		}
491	}
492
493	@Override
494	public void onStop() {
495		super.onStop();
496		if (this.conversation != null) {
497			this.conversation.setNextMessage(chatMsg.getText().toString());
498		}
499	}
500
501	public void onBackendConnected() {
502		this.conversation = activity.getSelectedConversation();
503		if (this.conversation == null) {
504			return;
505		}
506		String oldString = conversation.getNextMessage().trim();
507		if (this.pastedText == null) {
508			this.chatMsg.setText(oldString);
509		} else {
510			
511			if (oldString.isEmpty()) {
512				chatMsg.setText(pastedText);
513			} else {
514				chatMsg.setText(oldString + " " + pastedText);
515			}
516			pastedText = null;
517		}
518		int position = chatMsg.length();
519		Editable etext = chatMsg.getText();
520		Selection.setSelection(etext, position);
521		this.selfBitmap = findSelfPicture();
522		updateMessages();
523		if (activity.getSlidingPaneLayout().isSlideable()) {
524			if (!activity.shouldPaneBeOpen()) {
525				activity.getSlidingPaneLayout().closePane();
526				activity.getActionBar().setDisplayHomeAsUpEnabled(true);
527				activity.getActionBar().setHomeButtonEnabled(true);
528				activity.getActionBar().setTitle(
529						conversation.getName(useSubject));
530				activity.invalidateOptionsMenu();
531
532			}
533		}
534		if (conversation.getMode() == Conversation.MODE_MULTI) {
535			activity.xmppConnectionService
536					.setOnRenameListener(new OnRenameListener() {
537
538						@Override
539						public void onRename(final boolean success) {
540							activity.xmppConnectionService
541									.updateConversation(conversation);
542							getActivity().runOnUiThread(new Runnable() {
543
544								@Override
545								public void run() {
546									if (success) {
547										Toast.makeText(
548												getActivity(),
549												getString(R.string.your_nick_has_been_changed),
550												Toast.LENGTH_SHORT).show();
551									} else {
552										Toast.makeText(
553												getActivity(),
554												getString(R.string.nick_in_use),
555												Toast.LENGTH_SHORT).show();
556									}
557								}
558							});
559						}
560					});
561		}
562	}
563
564	private void decryptMessage(final Message message) {
565		PgpEngine engine = activity.xmppConnectionService.getPgpEngine();
566		if (engine != null) {
567			engine.decrypt(message, new UiCallback() {
568
569				@Override
570				public void userInputRequried(PendingIntent pi) {
571					askForPassphraseIntent = pi.getIntentSender();
572					pgpInfo.setVisibility(View.VISIBLE);
573				}
574
575				@Override
576				public void success() {
577					activity.xmppConnectionService.databaseBackend
578							.updateMessage(message);
579					updateMessages();
580				}
581
582				@Override
583				public void error(int error) {
584					message.setEncryption(Message.ENCRYPTION_DECRYPTION_FAILED);
585					// updateMessages();
586				}
587			});
588		} else {
589			pgpInfo.setVisibility(View.VISIBLE);
590		}
591	}
592
593	public void updateMessages() {
594		if (getView() == null) {
595			return;
596		}
597		ConversationActivity activity = (ConversationActivity) getActivity();
598		if (this.conversation != null) {
599			for (Message message : this.conversation.getMessages()) {
600				if ((message.getEncryption() == Message.ENCRYPTION_PGP)
601						&& ((message.getStatus() == Message.STATUS_RECIEVED) || (message
602								.getStatus() == Message.STATUS_SEND))) {
603					decryptMessage(message);
604					break;
605				}
606			}
607			this.messageList.clear();
608			this.messageList.addAll(this.conversation.getMessages());
609			this.messageListAdapter.notifyDataSetChanged();
610			if (conversation.getMode() == Conversation.MODE_SINGLE) {
611				if (messageList.size() >= 1) {
612					makeFingerprintWarning(conversation.getLatestEncryption());
613				}
614			} else {
615				if (conversation.getMucOptions().getError() != 0) {
616					mucError.setVisibility(View.VISIBLE);
617					if (conversation.getMucOptions().getError() == MucOptions.ERROR_NICK_IN_USE) {
618						mucErrorText.setText(getString(R.string.nick_in_use));
619					}
620				} else {
621					mucError.setVisibility(View.GONE);
622				}
623			}
624			getActivity().invalidateOptionsMenu();
625			updateChatMsgHint();
626			int size = this.messageList.size();
627			if (size >= 1)
628				messagesView.setSelection(size - 1);
629			if (!activity.shouldPaneBeOpen()) {
630				conversation.markRead();
631				// TODO update notifications
632				UIHelper.updateNotification(getActivity(),
633						activity.getConversationList(), null, false);
634				activity.updateConversationList();
635			}
636		}
637	}
638
639	protected void makeFingerprintWarning(int latestEncryption) {
640		final LinearLayout fingerprintWarning = (LinearLayout) getView()
641				.findViewById(R.id.new_fingerprint);
642		Set<String> knownFingerprints = conversation.getContact()
643				.getOtrFingerprints();
644		if ((latestEncryption == Message.ENCRYPTION_OTR)
645				&& (conversation.hasValidOtrSession()
646						&& (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) && (!knownFingerprints
647							.contains(conversation.getOtrFingerprint())))) {
648			fingerprintWarning.setVisibility(View.VISIBLE);
649			TextView fingerprint = (TextView) getView().findViewById(
650					R.id.otr_fingerprint);
651			fingerprint.setText(conversation.getOtrFingerprint());
652			fingerprintWarning.setOnClickListener(new OnClickListener() {
653
654				@Override
655				public void onClick(View v) {
656					AlertDialog dialog = UIHelper.getVerifyFingerprintDialog(
657							(ConversationActivity) getActivity(), conversation,
658							fingerprintWarning);
659					dialog.show();
660				}
661			});
662		} else {
663			fingerprintWarning.setVisibility(View.GONE);
664		}
665	}
666
667	protected void sendPlainTextMessage(Message message) {
668		ConversationActivity activity = (ConversationActivity) getActivity();
669		activity.xmppConnectionService.sendMessage(message, null);
670		chatMsg.setText("");
671	}
672
673	protected void sendPgpMessage(final Message message) {
674		activity.pendingMessage = message;
675		final ConversationActivity activity = (ConversationActivity) getActivity();
676		final XmppConnectionService xmppService = activity.xmppConnectionService;
677		final Contact contact = message.getConversation().getContact();
678		if (activity.hasPgp()) {
679			if (conversation.getMode() == Conversation.MODE_SINGLE) {
680				if (contact.getPgpKeyId() != 0) {
681					xmppService.getPgpEngine().hasKey(contact, new UiCallback() {
682	
683						@Override
684						public void userInputRequried(PendingIntent pi) {
685							activity.runIntent(pi,
686									ConversationActivity.REQUEST_ENCRYPT_MESSAGE);
687						}
688	
689						@Override
690						public void success() {
691							activity.encryptTextMessage();
692						}
693	
694						@Override
695						public void error(int error) {
696	
697						}
698					});
699	
700				} else {
701					showNoPGPKeyDialog(false,new DialogInterface.OnClickListener() {
702	
703						@Override
704						public void onClick(DialogInterface dialog, int which) {
705							conversation.setNextEncryption(Message.ENCRYPTION_NONE);
706							message.setEncryption(Message.ENCRYPTION_NONE);
707							xmppService.sendMessage(message, null);
708							chatMsg.setText("");
709						}
710					});
711				}
712			} else {
713				if (conversation.getMucOptions().pgpKeysInUse()) {
714					if (!conversation.getMucOptions().everybodyHasKeys()) {
715						Toast warning = Toast.makeText(getActivity(), R.string.missing_public_keys, Toast.LENGTH_LONG);
716						warning.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
717						warning.show();
718					}
719					activity.encryptTextMessage();
720				} else {
721					showNoPGPKeyDialog(true,new DialogInterface.OnClickListener() {
722						
723						@Override
724						public void onClick(DialogInterface dialog, int which) {
725							conversation.setNextEncryption(Message.ENCRYPTION_NONE);
726							message.setEncryption(Message.ENCRYPTION_NONE);
727							xmppService.sendMessage(message, null);
728							chatMsg.setText("");
729						}
730					});
731				}
732			}
733		}
734	}
735
736	public void showNoPGPKeyDialog(boolean plural, DialogInterface.OnClickListener listener) {
737		AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
738		builder.setIconAttribute(android.R.attr.alertDialogIcon);
739		if (plural) {
740			builder.setTitle(getString(R.string.no_pgp_keys));
741			builder.setMessage(getText(R.string.contacts_have_no_pgp_keys));
742		} else {
743			builder.setTitle(getString(R.string.no_pgp_key));
744			builder.setMessage(getText(R.string.contact_has_no_pgp_key));
745		}
746		builder.setNegativeButton(getString(R.string.cancel), null);
747		builder.setPositiveButton(getString(R.string.send_unencrypted),
748				listener);
749		builder.create().show();
750	}
751
752	protected void sendOtrMessage(final Message message) {
753		ConversationActivity activity = (ConversationActivity) getActivity();
754		final XmppConnectionService xmppService = activity.xmppConnectionService;
755		if (conversation.hasValidOtrSession()) {
756			activity.xmppConnectionService.sendMessage(message, null);
757			chatMsg.setText("");
758		} else {
759			activity.selectPresence(message.getConversation(),
760					new OnPresenceSelected() {
761
762						@Override
763						public void onPresenceSelected(boolean success,
764								String presence) {
765							if (success) {
766								xmppService.sendMessage(message, presence);
767								chatMsg.setText("");
768							}
769						}
770
771						@Override
772						public void onSendPlainTextInstead() {
773							message.setEncryption(Message.ENCRYPTION_NONE);
774							xmppService.sendMessage(message, null);
775							chatMsg.setText("");
776						}
777					}, "otr");
778		}
779	}
780
781	private static class ViewHolder {
782
783		protected Button download_button;
784		protected ImageView image;
785		protected ImageView indicator;
786		protected TextView time;
787		protected TextView messageBody;
788		protected ImageView contact_picture;
789
790	}
791
792	private class BitmapCache {
793		private HashMap<String, Bitmap> bitmaps = new HashMap<String, Bitmap>();
794
795		public Bitmap get(String name, Contact contact, Context context) {
796			if (bitmaps.containsKey(name)) {
797				return bitmaps.get(name);
798			} else {
799				Bitmap bm;
800				if (contact != null) {
801					bm = UIHelper
802							.getContactPicture(contact, 48, context, false);
803				} else {
804					bm = UIHelper.getContactPicture(name, 48, context, false);
805				}
806				bitmaps.put(name, bm);
807				return bm;
808			}
809		}
810	}
811
812	public void setText(String text) {
813		this.pastedText = text;
814	}
815
816	public void clearInputField() {
817		this.chatMsg.setText("");
818	}
819}