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