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