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