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