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.xmppConnectionService.updateConversationUi();
534				}
535			});
536		}
537	}
538	
539	private void messageSent() {
540		int size = this.messageList.size();
541		messagesView.setSelection(size - 1);
542		mEditMessage.setText("");
543		updateChatMsgHint();
544	}
545
546	public void updateSendButton() {
547		Conversation c = this.conversation;
548		if (activity.useSendButtonToIndicateStatus() && c != null
549				&& c.getAccount().getStatus() == Account.STATUS_ONLINE) {
550			if (c.getMode() == Conversation.MODE_SINGLE) {
551				switch (c.getContact().getMostAvailableStatus()) {
552				case Presences.CHAT:
553					this.mSendButton
554							.setImageResource(R.drawable.ic_action_send_now_online);
555					break;
556				case Presences.ONLINE:
557					this.mSendButton
558							.setImageResource(R.drawable.ic_action_send_now_online);
559					break;
560				case Presences.AWAY:
561					this.mSendButton
562							.setImageResource(R.drawable.ic_action_send_now_away);
563					break;
564				case Presences.XA:
565					this.mSendButton
566							.setImageResource(R.drawable.ic_action_send_now_away);
567					break;
568				case Presences.DND:
569					this.mSendButton
570							.setImageResource(R.drawable.ic_action_send_now_dnd);
571					break;
572				default:
573					this.mSendButton
574							.setImageResource(R.drawable.ic_action_send_now_offline);
575					break;
576				}
577			} else if (c.getMode() == Conversation.MODE_MULTI) {
578				if (c.getMucOptions().online()) {
579					this.mSendButton
580							.setImageResource(R.drawable.ic_action_send_now_online);
581				} else {
582					this.mSendButton
583							.setImageResource(R.drawable.ic_action_send_now_offline);
584				}
585			} else {
586				this.mSendButton
587						.setImageResource(R.drawable.ic_action_send_now_offline);
588			}
589		} else {
590			this.mSendButton
591					.setImageResource(R.drawable.ic_action_send_now_offline);
592		}
593	}
594
595	protected void updateStatusMessages() {
596		if (conversation.getMode() == Conversation.MODE_SINGLE) {
597			for (int i = this.messageList.size() - 1; i >= 0; --i) {
598				if (this.messageList.get(i).getStatus() == Message.STATUS_RECEIVED) {
599					return;
600				} else {
601					if (this.messageList.get(i).getStatus() == Message.STATUS_SEND_DISPLAYED) {
602						this.messageList.add(i + 1,
603								Message.createStatusMessage(conversation));
604						return;
605					}
606				}
607			}
608		}
609	}
610
611	protected void makeFingerprintWarning(int latestEncryption) {
612		Set<String> knownFingerprints = conversation.getContact()
613				.getOtrFingerprints();
614		if ((latestEncryption == Message.ENCRYPTION_OTR)
615				&& (conversation.hasValidOtrSession()
616						&& (!conversation.isMuted())
617						&& (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) && (!knownFingerprints
618							.contains(conversation.getOtrFingerprint())))) {
619			showSnackbar(R.string.unknown_otr_fingerprint, R.string.verify,
620					new OnClickListener() {
621
622						@Override
623						public void onClick(View v) {
624							if (conversation.getOtrFingerprint() != null) {
625								AlertDialog dialog = UIHelper
626										.getVerifyFingerprintDialog(
627												(ConversationActivity) getActivity(),
628												conversation, snackbar);
629								dialog.show();
630							}
631						}
632					});
633		}
634	}
635
636	protected void showSnackbar(int message, int action,
637			OnClickListener clickListener) {
638		snackbar.setVisibility(View.VISIBLE);
639		snackbar.setOnClickListener(null);
640		snackbarMessage.setText(message);
641		snackbarMessage.setOnClickListener(null);
642		snackbarAction.setText(action);
643		snackbarAction.setOnClickListener(clickListener);
644	}
645
646	protected void hideSnackbar() {
647		snackbar.setVisibility(View.GONE);
648	}
649
650	protected void sendPlainTextMessage(Message message) {
651		ConversationActivity activity = (ConversationActivity) getActivity();
652		activity.xmppConnectionService.sendMessage(message);
653		messageSent();
654	}
655
656	protected void sendPgpMessage(final Message message) {
657		final ConversationActivity activity = (ConversationActivity) getActivity();
658		final XmppConnectionService xmppService = activity.xmppConnectionService;
659		final Contact contact = message.getConversation().getContact();
660		if (activity.hasPgp()) {
661			if (conversation.getMode() == Conversation.MODE_SINGLE) {
662				if (contact.getPgpKeyId() != 0) {
663					xmppService.getPgpEngine().hasKey(contact,
664							new UiCallback<Contact>() {
665
666								@Override
667								public void userInputRequried(PendingIntent pi,
668										Contact contact) {
669									activity.runIntent(
670											pi,
671											ConversationActivity.REQUEST_ENCRYPT_MESSAGE);
672								}
673
674								@Override
675								public void success(Contact contact) {
676									messageSent();
677									activity.encryptTextMessage(message);
678								}
679
680								@Override
681								public void error(int error, Contact contact) {
682
683								}
684							});
685
686				} else {
687					showNoPGPKeyDialog(false,
688							new DialogInterface.OnClickListener() {
689
690								@Override
691								public void onClick(DialogInterface dialog,
692										int which) {
693									conversation
694											.setNextEncryption(Message.ENCRYPTION_NONE);
695									xmppService.databaseBackend
696											.updateConversation(conversation);
697									message.setEncryption(Message.ENCRYPTION_NONE);
698									xmppService.sendMessage(message);
699									messageSent();
700								}
701							});
702				}
703			} else {
704				if (conversation.getMucOptions().pgpKeysInUse()) {
705					if (!conversation.getMucOptions().everybodyHasKeys()) {
706						Toast warning = Toast
707								.makeText(getActivity(),
708										R.string.missing_public_keys,
709										Toast.LENGTH_LONG);
710						warning.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
711						warning.show();
712					}
713					activity.encryptTextMessage(message);
714					messageSent();
715				} else {
716					showNoPGPKeyDialog(true,
717							new DialogInterface.OnClickListener() {
718
719								@Override
720								public void onClick(DialogInterface dialog,
721										int which) {
722									conversation
723											.setNextEncryption(Message.ENCRYPTION_NONE);
724									message.setEncryption(Message.ENCRYPTION_NONE);
725									xmppService.databaseBackend
726											.updateConversation(conversation);
727									xmppService.sendMessage(message);
728									messageSent();
729								}
730							});
731				}
732			}
733		} else {
734			activity.showInstallPgpDialog();
735		}
736	}
737
738	public void showNoPGPKeyDialog(boolean plural,
739			DialogInterface.OnClickListener listener) {
740		AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
741		builder.setIconAttribute(android.R.attr.alertDialogIcon);
742		if (plural) {
743			builder.setTitle(getString(R.string.no_pgp_keys));
744			builder.setMessage(getText(R.string.contacts_have_no_pgp_keys));
745		} else {
746			builder.setTitle(getString(R.string.no_pgp_key));
747			builder.setMessage(getText(R.string.contact_has_no_pgp_key));
748		}
749		builder.setNegativeButton(getString(R.string.cancel), null);
750		builder.setPositiveButton(getString(R.string.send_unencrypted),
751				listener);
752		builder.create().show();
753	}
754
755	protected void sendOtrMessage(final Message message) {
756		final ConversationActivity activity = (ConversationActivity) getActivity();
757		final XmppConnectionService xmppService = activity.xmppConnectionService;
758		if (conversation.hasValidOtrSession()) {
759			activity.xmppConnectionService.sendMessage(message);
760			messageSent();
761		} else {
762			activity.selectPresence(message.getConversation(),
763					new OnPresenceSelected() {
764
765						@Override
766						public void onPresenceSelected() {
767							message.setPresence(conversation.getNextPresence());
768							xmppService.sendMessage(message);
769							messageSent();
770						}
771					});
772		}
773	}
774
775	public void setText(String text) {
776		this.pastedText = text;
777	}
778
779	public void clearInputField() {
780		this.mEditMessage.setText("");
781	}
782}