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