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