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