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