ConversationFragment.java

  1package eu.siacs.conversations.ui;
  2
  3import java.io.FileNotFoundException;
  4import java.util.ArrayList;
  5import java.util.HashMap;
  6import java.util.Hashtable;
  7import java.util.LinkedList;
  8import java.util.List;
  9import java.util.Set;
 10
 11import net.java.otr4j.session.SessionStatus;
 12
 13import eu.siacs.conversations.R;
 14import eu.siacs.conversations.crypto.PgpEngine.OpenPgpException;
 15import eu.siacs.conversations.crypto.PgpEngine.UserInputRequiredException;
 16import eu.siacs.conversations.entities.Account;
 17import eu.siacs.conversations.entities.Contact;
 18import eu.siacs.conversations.entities.Conversation;
 19import eu.siacs.conversations.entities.Message;
 20import eu.siacs.conversations.entities.MucOptions;
 21import eu.siacs.conversations.entities.MucOptions.OnRenameListener;
 22import eu.siacs.conversations.services.XmppConnectionService;
 23import eu.siacs.conversations.utils.UIHelper;
 24import android.app.AlertDialog;
 25import android.app.Fragment;
 26import android.content.Context;
 27import android.content.DialogInterface;
 28import android.content.Intent;
 29import android.content.IntentSender;
 30import android.content.SharedPreferences;
 31import android.content.IntentSender.SendIntentException;
 32import android.graphics.Bitmap;
 33import android.graphics.Typeface;
 34import android.net.Uri;
 35import android.os.AsyncTask;
 36import android.os.Bundle;
 37import android.preference.PreferenceManager;
 38import android.util.DisplayMetrics;
 39import android.util.Log;
 40import android.view.LayoutInflater;
 41import android.view.View;
 42import android.view.View.OnClickListener;
 43import android.view.ViewGroup;
 44import android.widget.ArrayAdapter;
 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.nextMessageEncryption);
 82			if (conversation.nextMessageEncryption == Message.ENCRYPTION_OTR) {
 83				sendOtrMessage(message);
 84			} else if (conversation.nextMessageEncryption == 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 (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		if (conversation.getMode() == Conversation.MODE_MULTI) {
129			chatMsg.setHint(getString(R.string.send_message_to_conference));
130		} else {
131			switch (conversation.nextMessageEncryption) {
132			case Message.ENCRYPTION_NONE:
133				chatMsg.setHint(getString(R.string.send_plain_text_message));
134				break;
135			case Message.ENCRYPTION_OTR:
136				chatMsg.setHint(getString(R.string.send_otr_message));
137				break;
138			case Message.ENCRYPTION_PGP:
139				chatMsg.setHint(getString(R.string.send_pgp_message));
140				break;
141			case Message.ENCRYPTION_DECRYPTED:
142				chatMsg.setHint(getString(R.string.send_pgp_message));
143				break;
144			default:
145				break;
146			}
147		}
148	}
149
150	@Override
151	public View onCreateView(final LayoutInflater inflater,
152			ViewGroup container, Bundle savedInstanceState) {
153
154		final DisplayMetrics metrics = getResources().getDisplayMetrics();
155		
156		this.inflater = inflater;
157
158		final View view = inflater.inflate(R.layout.fragment_conversation,
159				container, false);
160		chatMsg = (EditText) view.findViewById(R.id.textinput);
161		
162		if (pastedText!=null) {
163			chatMsg.setText(pastedText);
164		}
165		
166		ImageButton sendButton = (ImageButton) view
167				.findViewById(R.id.textSendButton);
168		sendButton.setOnClickListener(this.sendMsgListener);
169
170		pgpInfo = (LinearLayout) view.findViewById(R.id.pgp_keyentry);
171		pgpInfo.setOnClickListener(clickToDecryptListener);
172		mucError = (LinearLayout) view.findViewById(R.id.muc_error);
173		mucError.setOnClickListener(clickToMuc);
174		mucErrorText = (TextView) view.findViewById(R.id.muc_error_msg);
175
176		messagesView = (ListView) view.findViewById(R.id.messages_view);
177
178		messageListAdapter = new ArrayAdapter<Message>(this.getActivity()
179				.getApplicationContext(), R.layout.message_sent,
180				this.messageList) {
181
182			private static final int SENT = 0;
183			private static final int RECIEVED = 1;
184
185			@Override
186			public int getViewTypeCount() {
187				return 2;
188			}
189
190			@Override
191			public int getItemViewType(int position) {
192				if (getItem(position).getStatus() <= Message.STATUS_RECIEVED) {
193					return RECIEVED;
194				} else {
195					return SENT;
196				}
197			}
198
199			@Override
200			public View getView(int position, View view, ViewGroup parent) {
201				final Message item = getItem(position);
202				int type = getItemViewType(position);
203				ViewHolder viewHolder;
204				if (view == null) {
205					viewHolder = new ViewHolder();
206					switch (type) {
207					case SENT:
208						view = (View) inflater.inflate(R.layout.message_sent,
209								null);
210						viewHolder.contact_picture = (ImageView) view
211								.findViewById(R.id.message_photo);
212						viewHolder.contact_picture.setImageBitmap(selfBitmap);
213						break;
214					case RECIEVED:
215						view = (View) inflater.inflate(
216								R.layout.message_recieved, null);
217						viewHolder.contact_picture = (ImageView) view
218								.findViewById(R.id.message_photo);
219						
220						if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
221
222							viewHolder.contact_picture.setImageBitmap(mBitmapCache
223									.get(item.getConversation().getName(useSubject), item
224											.getConversation().getContact(),
225											getActivity()
226													.getApplicationContext()));
227
228						}
229						break;
230					default:
231						viewHolder = null;
232						break;
233					}
234					viewHolder.indicator = (ImageView) view.findViewById(R.id.security_indicator);
235					viewHolder.image = (ImageView) view.findViewById(R.id.message_image);
236					viewHolder.messageBody = (TextView) view
237							.findViewById(R.id.message_body);
238					viewHolder.time = (TextView) view
239							.findViewById(R.id.message_time);
240					view.setTag(viewHolder);
241				} else {
242					viewHolder = (ViewHolder) view.getTag();
243				}
244				if (type == RECIEVED) {
245					if (item.getConversation().getMode() == Conversation.MODE_MULTI) {
246						if (item.getCounterpart() != null) {
247							viewHolder.contact_picture.setImageBitmap(mBitmapCache
248									.get(item.getCounterpart(), null,
249											getActivity()
250													.getApplicationContext()));
251						} else {
252							viewHolder.contact_picture.setImageBitmap(mBitmapCache
253									.get(item.getConversation().getName(useSubject),
254											null, getActivity()
255													.getApplicationContext()));
256						}
257					}
258				}
259				
260				if (item.getEncryption() == Message.ENCRYPTION_NONE) {
261					viewHolder.indicator.setVisibility(View.GONE);
262				} else {
263					viewHolder.indicator.setVisibility(View.VISIBLE);
264				}
265				
266				
267				if (item.getType() == Message.TYPE_IMAGE) {
268					if (item.getStatus() == Message.STATUS_PREPARING) {
269						viewHolder.image.setVisibility(View.GONE);
270						viewHolder.messageBody.setVisibility(View.VISIBLE);
271						viewHolder.messageBody.setText(getString(R.string.preparing_image));
272						viewHolder.messageBody.setTextColor(0xff33B5E5);
273						viewHolder.messageBody.setTypeface(null,Typeface.ITALIC);
274					} else if (item.getStatus() == Message.STATUS_RECIEVING) {
275						viewHolder.image.setVisibility(View.GONE);
276						viewHolder.messageBody.setVisibility(View.GONE);
277						viewHolder.messageBody.setVisibility(View.VISIBLE);
278						viewHolder.messageBody.setText(getString(R.string.receiving_image));
279						viewHolder.messageBody.setTextColor(0xff33B5E5);
280						viewHolder.messageBody.setTypeface(null,Typeface.ITALIC);
281					} else {
282						try {
283							Bitmap thumbnail = activity.xmppConnectionService.getFileBackend().getThumbnailFromMessage(item,(int) (metrics.density * 288));
284							viewHolder.image.setImageBitmap(thumbnail);
285							viewHolder.messageBody.setVisibility(View.GONE);
286							viewHolder.image.setVisibility(View.VISIBLE);
287							viewHolder.image.setOnClickListener(new OnClickListener() {
288								
289								@Override
290								public void onClick(View v) {
291									Uri uri = Uri.parse("content://eu.siacs.conversations.images/"+item.getConversationUuid()+"/"+item.getUuid());
292									Log.d("xmppService","staring intent with uri:"+uri.toString());
293									Intent intent = new Intent(Intent.ACTION_VIEW);
294								    intent.setDataAndType(uri, "image/*");
295								    startActivity(intent);
296								}
297							});
298						} catch (FileNotFoundException e) {
299							viewHolder.image.setVisibility(View.GONE);
300							viewHolder.messageBody.setText("error loading image file");
301							viewHolder.messageBody.setVisibility(View.VISIBLE);
302						}
303					}
304				} else {
305					viewHolder.image.setVisibility(View.GONE);
306					viewHolder.messageBody.setVisibility(View.VISIBLE);
307					String body = item.getBody();
308					if (body != null) {
309						if (item.getEncryption() == Message.ENCRYPTION_PGP) {
310							viewHolder.messageBody
311									.setText(getString(R.string.encrypted_message));
312							viewHolder.messageBody.setTextColor(0xff33B5E5);
313							viewHolder.messageBody.setTypeface(null,
314									Typeface.ITALIC);
315						} else if ((item.getEncryption() == Message.ENCRYPTION_OTR)||(item.getEncryption() == Message.ENCRYPTION_DECRYPTED)) {
316							viewHolder.messageBody.setText(body.trim());
317							viewHolder.messageBody.setTextColor(0xff333333);
318							viewHolder.messageBody.setTypeface(null,
319									Typeface.NORMAL);
320						} else {
321							viewHolder.messageBody.setText(body.trim());
322							viewHolder.messageBody.setTextColor(0xff333333);
323							viewHolder.messageBody.setTypeface(null,
324									Typeface.NORMAL);
325						}
326					}
327				}
328				switch (item.getStatus()) {
329				case Message.STATUS_UNSEND:
330					viewHolder.time.setTypeface(null, Typeface.ITALIC);
331					viewHolder.time.setTextColor(0xFF8e8e8e);
332					viewHolder.time.setText("sending\u2026");
333					break;
334				case Message.STATUS_SEND_FAILED:
335					viewHolder.time.setText(getString(R.string.send_failed) + " \u00B7 " + UIHelper.readableTimeDifference(item
336							.getTimeSent()));
337					viewHolder.time.setTextColor(0xFFe92727);
338					viewHolder.time.setTypeface(null,Typeface.NORMAL);
339					break;
340				case Message.STATUS_SEND_REJECTED:
341					viewHolder.time.setText(getString(R.string.send_rejected));
342					viewHolder.time.setTextColor(0xFFe92727);
343					viewHolder.time.setTypeface(null,Typeface.NORMAL);
344					break;
345				default:
346					viewHolder.time.setTypeface(null, Typeface.NORMAL);
347					viewHolder.time.setTextColor(0xFF8e8e8e);
348					if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
349						viewHolder.time.setText(UIHelper
350								.readableTimeDifference(item.getTimeSent()));
351					} else {
352						viewHolder.time.setText(item.getCounterpart()
353								+ " \u00B7 "
354								+ UIHelper.readableTimeDifference(item
355										.getTimeSent()));
356					}
357					break;
358				}
359				return view;
360			}
361		};
362		messagesView.setAdapter(messageListAdapter);
363
364		return view;
365	}
366
367	protected Bitmap findSelfPicture() {
368		SharedPreferences sharedPref = PreferenceManager
369				.getDefaultSharedPreferences(getActivity()
370						.getApplicationContext());
371		boolean showPhoneSelfContactPicture = sharedPref.getBoolean(
372				"show_phone_selfcontact_picture", true);
373
374		return UIHelper.getSelfContactPicture(conversation.getAccount(), 48,
375				showPhoneSelfContactPicture, getActivity());
376	}
377
378	@Override
379	public void onStart() {
380		super.onStart();
381		this.activity = (ConversationActivity) getActivity();
382		SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
383		this.useSubject = preferences.getBoolean("use_subject_in_muc", true);
384		if (activity.xmppConnectionServiceBound) {
385			this.onBackendConnected();
386		}
387	}
388
389	public void onBackendConnected() {
390		this.conversation = activity.getSelectedConversation();
391		if (this.conversation == null) {
392			return;
393		}
394		this.selfBitmap = findSelfPicture();
395		updateMessages();
396		// rendering complete. now go tell activity to close pane
397		if (activity.getSlidingPaneLayout().isSlideable()) {
398			if (!activity.shouldPaneBeOpen()) {
399				activity.getSlidingPaneLayout().closePane();
400				activity.getActionBar().setDisplayHomeAsUpEnabled(true);
401				activity.getActionBar().setTitle(conversation.getName(useSubject));
402				activity.invalidateOptionsMenu();
403
404			}
405		}
406		if (queuedPqpMessage != null) {
407			this.conversation.nextMessageEncryption = Message.ENCRYPTION_PGP;
408			Message message = new Message(conversation, queuedPqpMessage,
409					Message.ENCRYPTION_PGP);
410			sendPgpMessage(message);
411		}
412		if (conversation.getMode() == Conversation.MODE_MULTI) {
413			activity.xmppConnectionService
414					.setOnRenameListener(new OnRenameListener() {
415
416						@Override
417						public void onRename(final boolean success) {
418							activity.xmppConnectionService.updateConversation(conversation);
419							getActivity().runOnUiThread(new Runnable() {
420
421								@Override
422								public void run() {
423									if (success) {
424										Toast.makeText(
425												getActivity(),
426												getString(R.string.your_nick_has_been_changed),
427												Toast.LENGTH_SHORT).show();
428									} else {
429										Toast.makeText(getActivity(),
430												getString(R.string.nick_in_use),
431												Toast.LENGTH_SHORT).show();
432									}
433								}
434							});
435						}
436					});
437		}
438	}
439
440	public void updateMessages() {
441		ConversationActivity activity = (ConversationActivity) getActivity();
442		if (this.conversation != null) {
443			List<Message> encryptedMessages = new LinkedList<Message>();
444			for (Message message : this.conversation.getMessages()) {
445				if (message.getEncryption() == Message.ENCRYPTION_PGP) {
446					encryptedMessages.add(message);
447				}
448			}
449			if (encryptedMessages.size() > 0) {
450				DecryptMessage task = new DecryptMessage();
451				Message[] msgs = new Message[encryptedMessages.size()];
452				task.execute(encryptedMessages.toArray(msgs));
453			}
454			this.messageList.clear();
455			this.messageList.addAll(this.conversation.getMessages());
456			this.messageListAdapter.notifyDataSetChanged();
457			if (conversation.getMode() == Conversation.MODE_SINGLE) {
458				if (messageList.size() >= 1) {
459					int latestEncryption = this.conversation.getLatestMessage()
460							.getEncryption();
461					if (latestEncryption == Message.ENCRYPTION_DECRYPTED) {
462						conversation.nextMessageEncryption = Message.ENCRYPTION_PGP;
463					} else {
464						conversation.nextMessageEncryption = latestEncryption;
465					}
466					makeFingerprintWarning(latestEncryption);
467				}
468			} else {
469				if (conversation.getMucOptions().getError() != 0) {
470					mucError.setVisibility(View.VISIBLE);
471					if (conversation.getMucOptions().getError() == MucOptions.ERROR_NICK_IN_USE) {
472						mucErrorText.setText(getString(R.string.nick_in_use));
473					}
474				} else {
475					mucError.setVisibility(View.GONE);
476				}
477			}
478			getActivity().invalidateOptionsMenu();
479			updateChatMsgHint();
480			int size = this.messageList.size();
481			if (size >= 1)
482				messagesView.setSelection(size - 1);
483			if (!activity.shouldPaneBeOpen()) {
484				conversation.markRead();
485				// TODO update notifications
486				UIHelper.updateNotification(getActivity(),
487						activity.getConversationList(), null, false);
488				activity.updateConversationList();
489			}
490		}
491	}
492
493	protected void makeFingerprintWarning(int latestEncryption) {
494		final LinearLayout fingerprintWarning = (LinearLayout) getView()
495				.findViewById(R.id.new_fingerprint);
496		if (conversation.getContact() != null) {
497			Set<String> knownFingerprints = conversation.getContact()
498					.getOtrFingerprints();
499			if ((latestEncryption == Message.ENCRYPTION_OTR)
500					&& (conversation.hasValidOtrSession()
501							&& (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) && (!knownFingerprints
502								.contains(conversation.getOtrFingerprint())))) {
503				fingerprintWarning.setVisibility(View.VISIBLE);
504				TextView fingerprint = (TextView) getView().findViewById(
505						R.id.otr_fingerprint);
506				fingerprint.setText(conversation.getOtrFingerprint());
507				fingerprintWarning.setOnClickListener(new OnClickListener() {
508
509					@Override
510					public void onClick(View v) {
511						AlertDialog dialog = UIHelper
512								.getVerifyFingerprintDialog(
513										(ConversationActivity) getActivity(),
514										conversation, fingerprintWarning);
515						dialog.show();
516					}
517				});
518			} else {
519				fingerprintWarning.setVisibility(View.GONE);
520			}
521		} else {
522			fingerprintWarning.setVisibility(View.GONE);
523		}
524	}
525
526	protected void sendPlainTextMessage(Message message) {
527		ConversationActivity activity = (ConversationActivity) getActivity();
528		activity.xmppConnectionService.sendMessage(message, null);
529		chatMsg.setText("");
530	}
531
532	protected void sendPgpMessage(final Message message) {
533		ConversationActivity activity = (ConversationActivity) getActivity();
534		final XmppConnectionService xmppService = activity.xmppConnectionService;
535		Contact contact = message.getConversation().getContact();
536		Account account = message.getConversation().getAccount();
537		if (activity.hasPgp()) {
538			if (contact.getPgpKeyId() != 0) {
539				try {
540					message.setEncryptedBody(xmppService.getPgpEngine().encrypt(account, contact.getPgpKeyId(), message.getBody()));
541					xmppService.sendMessage(message, null);
542					chatMsg.setText("");
543				} catch (UserInputRequiredException e) {
544					try {
545						getActivity().startIntentSenderForResult(e.getPendingIntent().getIntentSender(),
546								ConversationActivity.REQUEST_SEND_MESSAGE, null, 0,
547								0, 0);
548					} catch (SendIntentException e1) {
549						Log.d("xmppService","failed to start intent to send message");
550					}
551				} catch (OpenPgpException e) {
552					Log.d("xmppService","error encrypting with pgp: "+e.getOpenPgpError().getMessage());
553				}
554			} else {
555				AlertDialog.Builder builder = new AlertDialog.Builder(
556						getActivity());
557				builder.setTitle("No openPGP key found");
558				builder.setIconAttribute(android.R.attr.alertDialogIcon);
559				builder.setMessage("There is no openPGP key associated with this contact");
560				builder.setNegativeButton("Cancel", null);
561				builder.setPositiveButton("Send plain text",
562						new DialogInterface.OnClickListener() {
563
564							@Override
565							public void onClick(DialogInterface dialog,
566									int which) {
567								conversation.nextMessageEncryption = Message.ENCRYPTION_NONE;
568								message.setEncryption(Message.ENCRYPTION_NONE);
569								xmppService.sendMessage(message, null);
570								chatMsg.setText("");
571							}
572						});
573				builder.create().show();
574			}
575		}
576	}
577
578	protected void sendOtrMessage(final Message message) {
579		ConversationActivity activity = (ConversationActivity) getActivity();
580		final XmppConnectionService xmppService = activity.xmppConnectionService;
581		if (conversation.hasValidOtrSession()) {
582			activity.xmppConnectionService.sendMessage(message, null);
583			chatMsg.setText("");
584		} else {
585			Hashtable<String, Integer> presences;
586			if (conversation.getContact() != null) {
587				presences = conversation.getContact().getPresences();
588			} else {
589				presences = null;
590			}
591			if ((presences == null) || (presences.size() == 0)) {
592				AlertDialog.Builder builder = new AlertDialog.Builder(
593						getActivity());
594				builder.setTitle("Contact is offline");
595				builder.setIconAttribute(android.R.attr.alertDialogIcon);
596				builder.setMessage("Sending OTR encrypted messages to an offline contact is impossible.");
597				builder.setPositiveButton("Send plain text",
598						new DialogInterface.OnClickListener() {
599
600							@Override
601							public void onClick(DialogInterface dialog,
602									int which) {
603								conversation.nextMessageEncryption = Message.ENCRYPTION_NONE;
604								message.setEncryption(Message.ENCRYPTION_NONE);
605								xmppService.sendMessage(message, null);
606								chatMsg.setText("");
607							}
608						});
609				builder.setNegativeButton("Cancel", null);
610				builder.create().show();
611			} else if (presences.size() == 1) {
612				xmppService.sendMessage(message, (String) presences.keySet()
613						.toArray()[0]);
614				chatMsg.setText("");
615			} else {
616				AlertDialog.Builder builder = new AlertDialog.Builder(
617						getActivity());
618				builder.setTitle("Choose Presence");
619				final String[] presencesArray = new String[presences.size()];
620				presences.keySet().toArray(presencesArray);
621				builder.setItems(presencesArray,
622						new DialogInterface.OnClickListener() {
623
624							@Override
625							public void onClick(DialogInterface dialog,
626									int which) {
627								xmppService.sendMessage(message,
628										presencesArray[which]);
629								chatMsg.setText("");
630							}
631						});
632				builder.create().show();
633			}
634		}
635	}
636
637	private static class ViewHolder {
638
639		protected ImageView image;
640		protected ImageView indicator;
641		protected TextView time;
642		protected TextView messageBody;
643		protected ImageView contact_picture;
644
645	}
646
647	private class BitmapCache {
648		private HashMap<String, Bitmap> bitmaps = new HashMap<String, Bitmap>();
649		private Bitmap error = null;
650
651		public Bitmap get(String name, Contact contact, Context context) {
652			if (bitmaps.containsKey(name)) {
653				return bitmaps.get(name);
654			} else {
655				Bitmap bm;
656				if (contact != null){
657					bm = UIHelper.getContactPicture(contact, 48, context, false);
658				} else {
659					bm = UIHelper.getContactPicture(name, 48, context, false);
660				}
661				bitmaps.put(name, bm);
662				return bm;
663			}
664		}
665
666		public Bitmap getError() {
667			if (error == null) {
668				error = UIHelper.getErrorPicture(200);
669			}
670			return error;
671		}
672	}
673
674	class DecryptMessage extends AsyncTask<Message, Void, Boolean> {
675
676		@Override
677		protected Boolean doInBackground(Message... params) {
678			final ConversationActivity activity = (ConversationActivity) getActivity();
679			askForPassphraseIntent = null;
680			for (int i = 0; i < params.length; ++i) {
681				if (params[i].getEncryption() == Message.ENCRYPTION_PGP) {
682					String body = params[i].getBody();
683					String decrypted = null;
684					if (activity == null) {
685						return false;
686					} else if (!activity.xmppConnectionServiceBound) {
687						return false;
688					}
689					try {
690						decrypted = activity.xmppConnectionService
691								.getPgpEngine().decrypt(conversation.getAccount(),body);
692					} catch (UserInputRequiredException e) {
693						askForPassphraseIntent = e.getPendingIntent()
694								.getIntentSender();
695						activity.runOnUiThread(new Runnable() {
696
697							@Override
698							public void run() {
699								pgpInfo.setVisibility(View.VISIBLE);
700							}
701						});
702
703						return false;
704
705					} catch (OpenPgpException e) {
706						Log.d("gultsch", "error decrypting pgp");
707					}
708					if (decrypted != null) {
709						params[i].setBody(decrypted);
710						params[i].setEncryption(Message.ENCRYPTION_DECRYPTED);
711						activity.xmppConnectionService.updateMessage(params[i]);
712					}
713					if (activity != null) {
714						activity.runOnUiThread(new Runnable() {
715
716							@Override
717							public void run() {
718								messageListAdapter.notifyDataSetChanged();
719							}
720						});
721					}
722				}
723				if (activity != null) {
724					activity.runOnUiThread(new Runnable() {
725
726						@Override
727						public void run() {
728							activity.updateConversationList();
729						}
730					});
731				}
732			}
733			return true;
734		}
735
736	}
737
738	public void setText(String text) {
739		this.pastedText = text;
740	}
741}