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