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 }
658
659 private void decryptMessage(Message message) {
660 PgpEngine engine = activity.xmppConnectionService.getPgpEngine();
661 if (engine != null) {
662 engine.decrypt(message, new UiCallback<Message>() {
663
664 @Override
665 public void userInputRequried(PendingIntent pi, Message message) {
666 askForPassphraseIntent = pi.getIntentSender();
667 showSnackbar(R.string.openpgp_messages_found,R.string.decrypt,clickToDecryptListener);
668 }
669
670 @Override
671 public void success(Message message) {
672 activity.xmppConnectionService.databaseBackend
673 .updateMessage(message);
674 updateMessages();
675 }
676
677 @Override
678 public void error(int error, Message message) {
679 message.setEncryption(Message.ENCRYPTION_DECRYPTION_FAILED);
680 // updateMessages();
681 }
682 });
683 }
684 }
685
686 public void updateMessages() {
687 if (getView() == null) {
688 return;
689 }
690 ConversationActivity activity = (ConversationActivity) getActivity();
691 if (this.conversation != null) {
692 for (Message message : this.conversation.getMessages()) {
693 if ((message.getEncryption() == Message.ENCRYPTION_PGP)
694 && ((message.getStatus() == Message.STATUS_RECIEVED) || (message
695 .getStatus() == Message.STATUS_SEND))) {
696 decryptMessage(message);
697 break;
698 }
699 }
700 if (this.conversation.getMessages().size() == 0) {
701 this.messageList.clear();
702 messagesLoaded = false;
703 } else {
704 for (Message message : this.conversation.getMessages()) {
705 if (!this.messageList.contains(message)) {
706 this.messageList.add(message);
707 }
708 }
709 messagesLoaded = true;
710 updateStatusMessages();
711 }
712 this.messageListAdapter.notifyDataSetChanged();
713 if (conversation.getMode() == Conversation.MODE_SINGLE) {
714 if (messageList.size() >= 1) {
715 makeFingerprintWarning(conversation.getLatestEncryption());
716 }
717 } else {
718 if (conversation.getMucOptions().getError() != 0) {
719 showSnackbar(R.string.nick_in_use, R.string.edit,clickToMuc);
720 if (conversation.getMucOptions().getError() == MucOptions.ERROR_NICK_IN_USE) {
721 showSnackbar(R.string.nick_in_use, R.string.edit,clickToMuc);
722 }
723 }
724 }
725 getActivity().invalidateOptionsMenu();
726 updateChatMsgHint();
727 if (!activity.shouldPaneBeOpen()) {
728 activity.xmppConnectionService.markRead(conversation);
729 // TODO update notifications
730 UIHelper.updateNotification(getActivity(),
731 activity.getConversationList(), null, false);
732 activity.updateConversationList();
733 }
734 }
735 }
736
737 private void messageSent() {
738 int size = this.messageList.size();
739 if (size >= 1) {
740 messagesView.setSelection(size - 1);
741 }
742 chatMsg.setText("");
743 }
744
745 protected void updateStatusMessages() {
746 boolean addedStatusMsg = false;
747 if (conversation.getMode() == Conversation.MODE_SINGLE) {
748 for (int i = this.messageList.size() - 1; i >= 0; --i) {
749 if (addedStatusMsg) {
750 if (this.messageList.get(i).getType() == Message.TYPE_STATUS) {
751 this.messageList.remove(i);
752 --i;
753 }
754 } else {
755 if (this.messageList.get(i).getStatus() == Message.STATUS_RECIEVED) {
756 addedStatusMsg = true;
757 } else {
758 if (this.messageList.get(i).getStatus() == Message.STATUS_SEND_DISPLAYED) {
759 this.messageList.add(i + 1,
760 Message.createStatusMessage(conversation));
761 addedStatusMsg = true;
762 }
763 }
764 }
765 }
766 }
767 }
768
769 protected void makeFingerprintWarning(int latestEncryption) {
770 Set<String> knownFingerprints = conversation.getContact()
771 .getOtrFingerprints();
772 if ((latestEncryption == Message.ENCRYPTION_OTR)
773 && (conversation.hasValidOtrSession()
774 && (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) && (!knownFingerprints
775 .contains(conversation.getOtrFingerprint())))) {
776 showSnackbar(R.string.unknown_otr_fingerprint, R.string.verify, new OnClickListener() {
777
778 @Override
779 public void onClick(View v) {
780 if (conversation.getOtrFingerprint() != null) {
781 AlertDialog dialog = UIHelper.getVerifyFingerprintDialog(
782 (ConversationActivity) getActivity(), conversation,
783 snackbar);
784 dialog.show();
785 }
786 }
787 });
788 }
789 }
790
791 protected void showSnackbar(int message, int action, OnClickListener clickListener) {
792 snackbar.setVisibility(View.VISIBLE);
793 snackbarMessage.setText(message);
794 snackbarAction.setText(action);
795 snackbarAction.setOnClickListener(clickListener);
796 }
797
798 protected void hideSnackbar() {
799 snackbar.setVisibility(View.GONE);
800 }
801
802 protected void sendPlainTextMessage(Message message) {
803 ConversationActivity activity = (ConversationActivity) getActivity();
804 activity.xmppConnectionService.sendMessage(message);
805 messageSent();
806 }
807
808 protected void sendPgpMessage(final Message message) {
809 final ConversationActivity activity = (ConversationActivity) getActivity();
810 final XmppConnectionService xmppService = activity.xmppConnectionService;
811 final Contact contact = message.getConversation().getContact();
812 if (activity.hasPgp()) {
813 if (conversation.getMode() == Conversation.MODE_SINGLE) {
814 if (contact.getPgpKeyId() != 0) {
815 xmppService.getPgpEngine().hasKey(contact,
816 new UiCallback<Contact>() {
817
818 @Override
819 public void userInputRequried(PendingIntent pi,
820 Contact contact) {
821 activity.runIntent(
822 pi,
823 ConversationActivity.REQUEST_ENCRYPT_MESSAGE);
824 }
825
826 @Override
827 public void success(Contact contact) {
828 messageSent();
829 activity.encryptTextMessage(message);
830 }
831
832 @Override
833 public void error(int error, Contact contact) {
834
835 }
836 });
837
838 } else {
839 showNoPGPKeyDialog(false,
840 new DialogInterface.OnClickListener() {
841
842 @Override
843 public void onClick(DialogInterface dialog,
844 int which) {
845 conversation
846 .setNextEncryption(Message.ENCRYPTION_NONE);
847 message.setEncryption(Message.ENCRYPTION_NONE);
848 xmppService.sendMessage(message);
849 messageSent();
850 }
851 });
852 }
853 } else {
854 if (conversation.getMucOptions().pgpKeysInUse()) {
855 if (!conversation.getMucOptions().everybodyHasKeys()) {
856 Toast warning = Toast
857 .makeText(getActivity(),
858 R.string.missing_public_keys,
859 Toast.LENGTH_LONG);
860 warning.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
861 warning.show();
862 }
863 activity.encryptTextMessage(message);
864 messageSent();
865 } else {
866 showNoPGPKeyDialog(true,
867 new DialogInterface.OnClickListener() {
868
869 @Override
870 public void onClick(DialogInterface dialog,
871 int which) {
872 conversation
873 .setNextEncryption(Message.ENCRYPTION_NONE);
874 message.setEncryption(Message.ENCRYPTION_NONE);
875 xmppService.sendMessage(message);
876 messageSent();
877 }
878 });
879 }
880 }
881 } else {
882 activity.showInstallPgpDialog();
883 }
884 }
885
886 public void showNoPGPKeyDialog(boolean plural,
887 DialogInterface.OnClickListener listener) {
888 AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
889 builder.setIconAttribute(android.R.attr.alertDialogIcon);
890 if (plural) {
891 builder.setTitle(getString(R.string.no_pgp_keys));
892 builder.setMessage(getText(R.string.contacts_have_no_pgp_keys));
893 } else {
894 builder.setTitle(getString(R.string.no_pgp_key));
895 builder.setMessage(getText(R.string.contact_has_no_pgp_key));
896 }
897 builder.setNegativeButton(getString(R.string.cancel), null);
898 builder.setPositiveButton(getString(R.string.send_unencrypted),
899 listener);
900 builder.create().show();
901 }
902
903 protected void sendOtrMessage(final Message message) {
904 final ConversationActivity activity = (ConversationActivity) getActivity();
905 final XmppConnectionService xmppService = activity.xmppConnectionService;
906 if (conversation.hasValidOtrSession()) {
907 activity.xmppConnectionService.sendMessage(message);
908 messageSent();
909 } else {
910 activity.selectPresence(message.getConversation(),
911 new OnPresenceSelected() {
912
913 @Override
914 public void onPresenceSelected() {
915 message.setPresence(conversation.getNextPresence());
916 xmppService.sendMessage(message);
917 messageSent();
918 }
919 });
920 }
921 }
922
923 private static class ViewHolder {
924
925 protected LinearLayout message_box;
926 protected Button download_button;
927 protected ImageView image;
928 protected ImageView indicator;
929 protected TextView time;
930 protected TextView messageBody;
931 protected ImageView contact_picture;
932
933 }
934
935 private class BitmapCache {
936 private HashMap<String, Bitmap> bitmaps = new HashMap<String, Bitmap>();
937
938 public Bitmap get(String name, Contact contact, Context context) {
939 if (bitmaps.containsKey(name)) {
940 return bitmaps.get(name);
941 } else {
942 Bitmap bm;
943 if (contact != null) {
944 bm = UIHelper
945 .getContactPicture(contact, 48, context, false);
946 } else {
947 bm = UIHelper.getContactPicture(name, 48, context, false);
948 }
949 bitmaps.put(name, bm);
950 return bm;
951 }
952 }
953 }
954
955 public void setText(String text) {
956 this.pastedText = text;
957 }
958
959 public void clearInputField() {
960 this.chatMsg.setText("");
961 }
962}