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