ConversationFragment.java

  1package eu.siacs.conversations.ui;
  2
  3import java.util.ArrayList;
  4import java.util.List;
  5import java.util.Set;
  6import java.util.concurrent.ConcurrentLinkedQueue;
  7
  8import net.java.otr4j.session.SessionStatus;
  9import eu.siacs.conversations.R;
 10import eu.siacs.conversations.crypto.PgpEngine;
 11import eu.siacs.conversations.entities.Account;
 12import eu.siacs.conversations.entities.Contact;
 13import eu.siacs.conversations.entities.Conversation;
 14import eu.siacs.conversations.entities.Message;
 15import eu.siacs.conversations.entities.MucOptions;
 16import eu.siacs.conversations.entities.Presences;
 17import eu.siacs.conversations.services.XmppConnectionService;
 18import eu.siacs.conversations.ui.EditMessage.OnEnterPressed;
 19import eu.siacs.conversations.ui.XmppActivity.OnPresenceSelected;
 20import eu.siacs.conversations.ui.XmppActivity.OnValueEdited;
 21import eu.siacs.conversations.ui.adapter.MessageAdapter;
 22import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureClicked;
 23import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureLongClicked;
 24import eu.siacs.conversations.utils.UIHelper;
 25import android.app.AlertDialog;
 26import android.app.Fragment;
 27import android.app.PendingIntent;
 28import android.content.Context;
 29import android.content.DialogInterface;
 30import android.content.Intent;
 31import android.content.IntentSender;
 32import android.content.IntentSender.SendIntentException;
 33import android.os.Bundle;
 34import android.text.Editable;
 35import android.text.Selection;
 36import android.view.Gravity;
 37import android.view.KeyEvent;
 38import android.view.LayoutInflater;
 39import android.view.View;
 40import android.view.View.OnClickListener;
 41import android.view.ViewGroup;
 42import android.view.inputmethod.EditorInfo;
 43import android.view.inputmethod.InputMethodManager;
 44import android.widget.AbsListView.OnScrollListener;
 45import android.widget.TextView.OnEditorActionListener;
 46import android.widget.AbsListView;
 47
 48import android.widget.ListView;
 49import android.widget.ImageButton;
 50import android.widget.RelativeLayout;
 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 MessageAdapter messageListAdapter;
 61	protected Contact contact;
 62
 63	protected String queuedPqpMessage = null;
 64
 65	private EditMessage mEditMessage;
 66	private ImageButton mSendButton;
 67	private String pastedText = null;
 68	private RelativeLayout snackbar;
 69	private TextView snackbarMessage;
 70	private TextView snackbarAction;
 71
 72	private boolean messagesLoaded = false;
 73
 74	private IntentSender askForPassphraseIntent = null;
 75	
 76	
 77	private ConcurrentLinkedQueue<Message> mEncryptedMessages = new ConcurrentLinkedQueue<Message>();
 78	private boolean mDecryptJobRunning = false;
 79
 80	private OnEditorActionListener mEditorActionListener = new OnEditorActionListener() {
 81
 82		@Override
 83		public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
 84			if (actionId == EditorInfo.IME_ACTION_SEND) {
 85				InputMethodManager imm = (InputMethodManager) v.getContext()
 86						.getSystemService(Context.INPUT_METHOD_SERVICE);
 87				imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
 88				sendMessage();
 89				return true;
 90			} else {
 91				return false;
 92			}
 93		}
 94	};
 95
 96	private OnClickListener mSendButtonListener = new OnClickListener() {
 97
 98		@Override
 99		public void onClick(View v) {
100			sendMessage();
101		}
102	};
103	protected OnClickListener clickToDecryptListener = new OnClickListener() {
104
105		@Override
106		public void onClick(View v) {
107			if (activity.hasPgp() && askForPassphraseIntent != null) {
108				try {
109					getActivity().startIntentSenderForResult(
110							askForPassphraseIntent,
111							ConversationActivity.REQUEST_DECRYPT_PGP, null, 0,
112							0, 0);
113				} catch (SendIntentException e) {
114					//
115				}
116			}
117		}
118	};
119
120	private OnClickListener clickToMuc = new OnClickListener() {
121
122		@Override
123		public void onClick(View v) {
124			Intent intent = new Intent(getActivity(),
125					ConferenceDetailsActivity.class);
126			intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC);
127			intent.putExtra("uuid", conversation.getUuid());
128			startActivity(intent);
129		}
130	};
131
132	private OnClickListener leaveMuc = new OnClickListener() {
133
134		@Override
135		public void onClick(View v) {
136			activity.endConversation(conversation);
137		}
138	};
139
140	private OnClickListener joinMuc = new OnClickListener() {
141
142		@Override
143		public void onClick(View v) {
144			activity.xmppConnectionService.joinMuc(conversation);
145		}
146	};
147
148	private OnClickListener enterPassword = new OnClickListener() {
149
150		@Override
151		public void onClick(View v) {
152			MucOptions muc = conversation.getMucOptions();
153			String password = muc.getPassword();
154			if (password == null) {
155				password = "";
156			}
157			activity.quickPasswordEdit(password, new OnValueEdited() {
158
159				@Override
160				public void onValueEdited(String value) {
161					activity.xmppConnectionService.providePasswordForMuc(
162							conversation, value);
163				}
164			});
165		}
166	};
167
168	private OnScrollListener mOnScrollListener = new OnScrollListener() {
169
170		@Override
171		public void onScrollStateChanged(AbsListView view, int scrollState) {
172			// TODO Auto-generated method stub
173
174		}
175
176		@Override
177		public void onScroll(AbsListView view, int firstVisibleItem,
178				int visibleItemCount, int totalItemCount) {
179			if (firstVisibleItem == 0 && messagesLoaded) {
180				long timestamp = messageList.get(0).getTimeSent();
181				messagesLoaded = false;
182				int size = activity.xmppConnectionService.loadMoreMessages(
183						conversation, timestamp);
184				messageList.clear();
185				messageList.addAll(conversation.getMessages());
186				updateStatusMessages();
187				messageListAdapter.notifyDataSetChanged();
188				if (size != 0) {
189					messagesLoaded = true;
190				}
191				messagesView.setSelectionFromTop(size + 1, 0);
192			}
193		}
194	};
195
196	private ConversationActivity activity;
197
198	private void sendMessage() {
199		if (this.conversation == null) {
200			return;
201		}
202		if (mEditMessage.getText().length() < 1) {
203			if (this.conversation.getMode() == Conversation.MODE_MULTI) {
204				conversation.setNextPresence(null);
205				updateChatMsgHint();
206			}
207			return;
208		}
209		Message message = new Message(conversation, mEditMessage.getText()
210				.toString(), conversation.getNextEncryption(activity
211				.forceEncryption()));
212		if (conversation.getMode() == Conversation.MODE_MULTI) {
213			if (conversation.getNextPresence() != null) {
214				message.setPresence(conversation.getNextPresence());
215				message.setType(Message.TYPE_PRIVATE);
216				conversation.setNextPresence(null);
217			}
218		}
219		if (conversation.getNextEncryption(activity.forceEncryption()) == Message.ENCRYPTION_OTR) {
220			sendOtrMessage(message);
221		} else if (conversation.getNextEncryption(activity.forceEncryption()) == Message.ENCRYPTION_PGP) {
222			sendPgpMessage(message);
223		} else {
224			sendPlainTextMessage(message);
225		}
226	}
227
228	public void updateChatMsgHint() {
229		if (conversation.getMode() == Conversation.MODE_MULTI
230				&& conversation.getNextPresence() != null) {
231			this.mEditMessage.setHint(getString(
232					R.string.send_private_message_to,
233					conversation.getNextPresence()));
234		} else {
235			switch (conversation.getNextEncryption(activity.forceEncryption())) {
236			case Message.ENCRYPTION_NONE:
237				mEditMessage
238						.setHint(getString(R.string.send_plain_text_message));
239				break;
240			case Message.ENCRYPTION_OTR:
241				mEditMessage.setHint(getString(R.string.send_otr_message));
242				break;
243			case Message.ENCRYPTION_PGP:
244				mEditMessage.setHint(getString(R.string.send_pgp_message));
245				break;
246			default:
247				break;
248			}
249		}
250	}
251
252	@Override
253	public View onCreateView(final LayoutInflater inflater,
254			ViewGroup container, Bundle savedInstanceState) {
255		final View view = inflater.inflate(R.layout.fragment_conversation,
256				container, false);
257		mEditMessage = (EditMessage) view.findViewById(R.id.textinput);
258		mEditMessage.setOnClickListener(new OnClickListener() {
259
260			@Override
261			public void onClick(View v) {
262				activity.hideConversationsOverview();
263			}
264		});
265		mEditMessage.setOnEditorActionListener(mEditorActionListener);
266		mEditMessage.setOnEnterPressedListener(new OnEnterPressed() {
267
268			@Override
269			public void onEnterPressed() {
270				sendMessage();
271			}
272		});
273
274		mSendButton = (ImageButton) view.findViewById(R.id.textSendButton);
275		mSendButton.setOnClickListener(this.mSendButtonListener);
276
277		snackbar = (RelativeLayout) view.findViewById(R.id.snackbar);
278		snackbarMessage = (TextView) view.findViewById(R.id.snackbar_message);
279		snackbarAction = (TextView) view.findViewById(R.id.snackbar_action);
280
281		messagesView = (ListView) view.findViewById(R.id.messages_view);
282		messagesView.setOnScrollListener(mOnScrollListener);
283		messagesView.setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL);
284		messageListAdapter = new MessageAdapter(
285				(ConversationActivity) getActivity(), this.messageList);
286		messageListAdapter
287				.setOnContactPictureClicked(new OnContactPictureClicked() {
288
289					@Override
290					public void onContactPictureClicked(Message message) {
291						if (message.getStatus() <= Message.STATUS_RECEIVED) {
292							if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
293								if (message.getPresence() != null) {
294									highlightInConference(message.getPresence());
295								} else {
296									highlightInConference(message
297											.getCounterpart());
298								}
299							} else {
300								Contact contact = message.getConversation()
301										.getContact();
302								if (contact.showInRoster()) {
303									activity.switchToContactDetails(contact);
304								} else {
305									activity.showAddToRosterDialog(message
306											.getConversation());
307								}
308							}
309						}
310					}
311				});
312		messageListAdapter
313				.setOnContactPictureLongClicked(new OnContactPictureLongClicked() {
314
315					@Override
316					public void onContactPictureLongClicked(Message message) {
317						if (message.getStatus() <= Message.STATUS_RECEIVED) {
318							if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
319								if (message.getPresence() != null) {
320									privateMessageWith(message.getPresence());
321								} else {
322									privateMessageWith(message.getCounterpart());
323								}
324							}
325						}
326					}
327				});
328		messagesView.setAdapter(messageListAdapter);
329
330		return view;
331	}
332
333	protected void privateMessageWith(String counterpart) {
334		this.mEditMessage.setText("");
335		this.conversation.setNextPresence(counterpart);
336		updateChatMsgHint();
337	}
338
339	protected void highlightInConference(String nick) {
340		String oldString = mEditMessage.getText().toString().trim();
341		if (oldString.isEmpty() || mEditMessage.getSelectionStart() == 0) {
342			mEditMessage.getText().insert(0, nick + ": ");
343		} else {
344			if (mEditMessage.getText().charAt(
345					mEditMessage.getSelectionStart() - 1) != ' ') {
346				nick = " " + nick;
347			}
348			mEditMessage.getText().insert(mEditMessage.getSelectionStart(),
349					nick + " ");
350		}
351	}
352
353	@Override
354	public void onStart() {
355		super.onStart();
356		this.activity = (ConversationActivity) getActivity();
357		if (activity.xmppConnectionServiceBound) {
358			this.onBackendConnected();
359		}
360	}
361
362	@Override
363	public void onStop() {
364		mDecryptJobRunning = false;
365		super.onStop();
366		if (this.conversation != null) {
367			this.conversation.setNextMessage(mEditMessage.getText().toString());
368		}
369	}
370
371	public void onBackendConnected() {
372		this.activity = (ConversationActivity) getActivity();
373		this.conversation = activity.getSelectedConversation();
374		if (this.conversation == null) {
375			return;
376		}
377		String oldString = conversation.getNextMessage().trim();
378		if (this.pastedText == null) {
379			this.mEditMessage.setText(oldString);
380		} else {
381
382			if (oldString.isEmpty()) {
383				mEditMessage.setText(pastedText);
384			} else {
385				mEditMessage.setText(oldString + " " + pastedText);
386			}
387			pastedText = null;
388		}
389		int position = mEditMessage.length();
390		Editable etext = mEditMessage.getText();
391		Selection.setSelection(etext, position);
392		if (activity.isConversationsOverviewHideable()) {
393			if (!activity.shouldPaneBeOpen()) {
394				activity.hideConversationsOverview();
395				activity.openConversation(conversation);
396			}
397		}
398		if (this.conversation.getMode() == Conversation.MODE_MULTI) {
399			conversation.setNextPresence(null);
400		}
401		updateMessages();
402	}
403
404	public void updateMessages() {
405		if (getView() == null) {
406			return;
407		}
408		hideSnackbar();
409		final ConversationActivity activity = (ConversationActivity) getActivity();
410		if (this.conversation != null) {
411			final Contact contact = this.conversation.getContact();
412			if (this.conversation.isMuted()) {
413				showSnackbar(R.string.notifications_disabled, R.string.enable,
414						new OnClickListener() {
415
416							@Override
417							public void onClick(View v) {
418								conversation.setMutedTill(0);
419								activity.xmppConnectionService.databaseBackend
420										.updateConversation(conversation);
421								updateMessages();
422							}
423						});
424			} else if (!contact.showInRoster()
425					&& contact
426							.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
427				showSnackbar(R.string.contact_added_you, R.string.add_back,
428						new OnClickListener() {
429
430							@Override
431							public void onClick(View v) {
432								activity.xmppConnectionService
433										.createContact(contact);
434								activity.switchToContactDetails(contact);
435							}
436						});
437			}
438			for (Message message : this.conversation.getMessages()) {
439				if ((message.getEncryption() == Message.ENCRYPTION_PGP)
440						&& ((message.getStatus() == Message.STATUS_RECEIVED) || (message
441								.getStatus() == Message.STATUS_SEND))) {
442					if (!mEncryptedMessages.contains(message)) {
443						mEncryptedMessages.add(message);
444					}
445				}
446			}
447			decryptNext();
448			this.messageList.clear();
449			if (this.conversation.getMessages().size() == 0) {
450				messagesLoaded = false;
451			} else {
452				this.messageList.addAll(this.conversation.getMessages());
453				messagesLoaded = true;
454				updateStatusMessages();
455			}
456			this.messageListAdapter.notifyDataSetChanged();
457			if (conversation.getMode() == Conversation.MODE_SINGLE) {
458				if (messageList.size() >= 1) {
459					makeFingerprintWarning(conversation.getLatestEncryption());
460				}
461			} else {
462				if (!conversation.getMucOptions().online()
463						&& conversation.getAccount().getStatus() == Account.STATUS_ONLINE) {
464					int error = conversation.getMucOptions().getError();
465					switch (error) {
466					case MucOptions.ERROR_NICK_IN_USE:
467						showSnackbar(R.string.nick_in_use, R.string.edit,
468								clickToMuc);
469						break;
470					case MucOptions.ERROR_ROOM_NOT_FOUND:
471						showSnackbar(R.string.conference_not_found,
472								R.string.leave, leaveMuc);
473						break;
474					case MucOptions.ERROR_PASSWORD_REQUIRED:
475						showSnackbar(R.string.conference_requires_password,
476								R.string.enter_password, enterPassword);
477						break;
478					case MucOptions.ERROR_BANNED:
479						showSnackbar(R.string.conference_banned,
480								R.string.leave, leaveMuc);
481						break;
482					case MucOptions.ERROR_MEMBERS_ONLY:
483						showSnackbar(R.string.conference_members_only,
484								R.string.leave, leaveMuc);
485						break;
486					case MucOptions.KICKED_FROM_ROOM:
487						showSnackbar(R.string.conference_kicked, R.string.join,
488								joinMuc);
489						break;
490					default:
491						break;
492					}
493				}
494			}
495			getActivity().invalidateOptionsMenu();
496			updateChatMsgHint();
497			if (!activity.shouldPaneBeOpen()) {
498				activity.xmppConnectionService.markRead(conversation, true);
499				activity.updateConversationList();
500			}
501			this.updateSendButton();
502		}
503	}
504
505	private void decryptNext() {
506		Message next = this.mEncryptedMessages.peek();
507		PgpEngine engine = activity.xmppConnectionService.getPgpEngine();
508			
509		if (next!=null && engine != null && !mDecryptJobRunning) {
510			mDecryptJobRunning = true;
511			engine.decrypt(next, new UiCallback<Message>() {
512
513				@Override
514				public void userInputRequried(PendingIntent pi, Message message) {
515					mDecryptJobRunning = false;
516					askForPassphraseIntent = pi.getIntentSender();
517					showSnackbar(R.string.openpgp_messages_found,
518							R.string.decrypt, clickToDecryptListener);
519				}
520
521				@Override
522				public void success(Message message) {
523					mDecryptJobRunning = false;
524					mEncryptedMessages.remove();
525					activity.xmppConnectionService.updateMessage(message);
526				}
527
528				@Override
529				public void error(int error, Message message) {
530					message.setEncryption(Message.ENCRYPTION_DECRYPTION_FAILED);
531					mDecryptJobRunning = false;
532					mEncryptedMessages.remove();
533					activity.updateConversationList();
534					activity.xmppConnectionService.updateConversationUi();
535				}
536			});
537		}
538	}
539	
540	private void messageSent() {
541		int size = this.messageList.size();
542		messagesView.setSelection(size - 1);
543		mEditMessage.setText("");
544		updateChatMsgHint();
545	}
546
547	public void updateSendButton() {
548		Conversation c = this.conversation;
549		if (activity.useSendButtonToIndicateStatus() && c != null
550				&& c.getAccount().getStatus() == Account.STATUS_ONLINE) {
551			if (c.getMode() == Conversation.MODE_SINGLE) {
552				switch (c.getContact().getMostAvailableStatus()) {
553				case Presences.CHAT:
554					this.mSendButton
555							.setImageResource(R.drawable.ic_action_send_now_online);
556					break;
557				case Presences.ONLINE:
558					this.mSendButton
559							.setImageResource(R.drawable.ic_action_send_now_online);
560					break;
561				case Presences.AWAY:
562					this.mSendButton
563							.setImageResource(R.drawable.ic_action_send_now_away);
564					break;
565				case Presences.XA:
566					this.mSendButton
567							.setImageResource(R.drawable.ic_action_send_now_away);
568					break;
569				case Presences.DND:
570					this.mSendButton
571							.setImageResource(R.drawable.ic_action_send_now_dnd);
572					break;
573				default:
574					this.mSendButton
575							.setImageResource(R.drawable.ic_action_send_now_offline);
576					break;
577				}
578			} else if (c.getMode() == Conversation.MODE_MULTI) {
579				if (c.getMucOptions().online()) {
580					this.mSendButton
581							.setImageResource(R.drawable.ic_action_send_now_online);
582				} else {
583					this.mSendButton
584							.setImageResource(R.drawable.ic_action_send_now_offline);
585				}
586			} else {
587				this.mSendButton
588						.setImageResource(R.drawable.ic_action_send_now_offline);
589			}
590		} else {
591			this.mSendButton
592					.setImageResource(R.drawable.ic_action_send_now_offline);
593		}
594	}
595
596	protected void updateStatusMessages() {
597		if (conversation.getMode() == Conversation.MODE_SINGLE) {
598			for (int i = this.messageList.size() - 1; i >= 0; --i) {
599				if (this.messageList.get(i).getStatus() == Message.STATUS_RECEIVED) {
600					return;
601				} else {
602					if (this.messageList.get(i).getStatus() == Message.STATUS_SEND_DISPLAYED) {
603						this.messageList.add(i + 1,
604								Message.createStatusMessage(conversation));
605						return;
606					}
607				}
608			}
609		}
610	}
611
612	protected void makeFingerprintWarning(int latestEncryption) {
613		Set<String> knownFingerprints = conversation.getContact()
614				.getOtrFingerprints();
615		if ((latestEncryption == Message.ENCRYPTION_OTR)
616				&& (conversation.hasValidOtrSession()
617						&& (!conversation.isMuted())
618						&& (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) && (!knownFingerprints
619							.contains(conversation.getOtrFingerprint())))) {
620			showSnackbar(R.string.unknown_otr_fingerprint, R.string.verify,
621					new OnClickListener() {
622
623						@Override
624						public void onClick(View v) {
625							if (conversation.getOtrFingerprint() != null) {
626								AlertDialog dialog = UIHelper
627										.getVerifyFingerprintDialog(
628												(ConversationActivity) getActivity(),
629												conversation, snackbar);
630								dialog.show();
631							}
632						}
633					});
634		}
635	}
636
637	protected void showSnackbar(int message, int action,
638			OnClickListener clickListener) {
639		snackbar.setVisibility(View.VISIBLE);
640		snackbar.setOnClickListener(null);
641		snackbarMessage.setText(message);
642		snackbarMessage.setOnClickListener(null);
643		snackbarAction.setText(action);
644		snackbarAction.setOnClickListener(clickListener);
645	}
646
647	protected void hideSnackbar() {
648		snackbar.setVisibility(View.GONE);
649	}
650
651	protected void sendPlainTextMessage(Message message) {
652		ConversationActivity activity = (ConversationActivity) getActivity();
653		activity.xmppConnectionService.sendMessage(message);
654		messageSent();
655	}
656
657	protected void sendPgpMessage(final Message message) {
658		final ConversationActivity activity = (ConversationActivity) getActivity();
659		final XmppConnectionService xmppService = activity.xmppConnectionService;
660		final Contact contact = message.getConversation().getContact();
661		if (activity.hasPgp()) {
662			if (conversation.getMode() == Conversation.MODE_SINGLE) {
663				if (contact.getPgpKeyId() != 0) {
664					xmppService.getPgpEngine().hasKey(contact,
665							new UiCallback<Contact>() {
666
667								@Override
668								public void userInputRequried(PendingIntent pi,
669										Contact contact) {
670									activity.runIntent(
671											pi,
672											ConversationActivity.REQUEST_ENCRYPT_MESSAGE);
673								}
674
675								@Override
676								public void success(Contact contact) {
677									messageSent();
678									activity.encryptTextMessage(message);
679								}
680
681								@Override
682								public void error(int error, Contact contact) {
683
684								}
685							});
686
687				} else {
688					showNoPGPKeyDialog(false,
689							new DialogInterface.OnClickListener() {
690
691								@Override
692								public void onClick(DialogInterface dialog,
693										int which) {
694									conversation
695											.setNextEncryption(Message.ENCRYPTION_NONE);
696									xmppService.databaseBackend
697											.updateConversation(conversation);
698									message.setEncryption(Message.ENCRYPTION_NONE);
699									xmppService.sendMessage(message);
700									messageSent();
701								}
702							});
703				}
704			} else {
705				if (conversation.getMucOptions().pgpKeysInUse()) {
706					if (!conversation.getMucOptions().everybodyHasKeys()) {
707						Toast warning = Toast
708								.makeText(getActivity(),
709										R.string.missing_public_keys,
710										Toast.LENGTH_LONG);
711						warning.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
712						warning.show();
713					}
714					activity.encryptTextMessage(message);
715					messageSent();
716				} else {
717					showNoPGPKeyDialog(true,
718							new DialogInterface.OnClickListener() {
719
720								@Override
721								public void onClick(DialogInterface dialog,
722										int which) {
723									conversation
724											.setNextEncryption(Message.ENCRYPTION_NONE);
725									message.setEncryption(Message.ENCRYPTION_NONE);
726									xmppService.databaseBackend
727											.updateConversation(conversation);
728									xmppService.sendMessage(message);
729									messageSent();
730								}
731							});
732				}
733			}
734		} else {
735			activity.showInstallPgpDialog();
736		}
737	}
738
739	public void showNoPGPKeyDialog(boolean plural,
740			DialogInterface.OnClickListener listener) {
741		AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
742		builder.setIconAttribute(android.R.attr.alertDialogIcon);
743		if (plural) {
744			builder.setTitle(getString(R.string.no_pgp_keys));
745			builder.setMessage(getText(R.string.contacts_have_no_pgp_keys));
746		} else {
747			builder.setTitle(getString(R.string.no_pgp_key));
748			builder.setMessage(getText(R.string.contact_has_no_pgp_key));
749		}
750		builder.setNegativeButton(getString(R.string.cancel), null);
751		builder.setPositiveButton(getString(R.string.send_unencrypted),
752				listener);
753		builder.create().show();
754	}
755
756	protected void sendOtrMessage(final Message message) {
757		final ConversationActivity activity = (ConversationActivity) getActivity();
758		final XmppConnectionService xmppService = activity.xmppConnectionService;
759		if (conversation.hasValidOtrSession()) {
760			activity.xmppConnectionService.sendMessage(message);
761			messageSent();
762		} else {
763			activity.selectPresence(message.getConversation(),
764					new OnPresenceSelected() {
765
766						@Override
767						public void onPresenceSelected() {
768							message.setPresence(conversation.getNextPresence());
769							xmppService.sendMessage(message);
770							messageSent();
771						}
772					});
773		}
774	}
775
776	public void setText(String text) {
777		this.pastedText = text;
778	}
779
780	public void clearInputField() {
781		this.mEditMessage.setText("");
782	}
783}