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