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