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