1package eu.siacs.conversations.ui;
2
3import android.annotation.SuppressLint;
4import android.app.ActionBar;
5import android.app.AlertDialog;
6import android.app.FragmentTransaction;
7import android.app.PendingIntent;
8import android.content.DialogInterface;
9import android.content.DialogInterface.OnClickListener;
10import android.content.Intent;
11import android.content.IntentSender.SendIntentException;
12import android.media.MediaActionSound;
13import android.net.Uri;
14import android.os.Bundle;
15import android.os.SystemClock;
16import android.provider.MediaStore;
17import android.support.v4.widget.SlidingPaneLayout;
18import android.support.v4.widget.SlidingPaneLayout.PanelSlideListener;
19import android.view.Menu;
20import android.view.MenuItem;
21import android.view.View;
22import android.widget.AdapterView;
23import android.widget.AdapterView.OnItemClickListener;
24import android.widget.ArrayAdapter;
25import android.widget.CheckBox;
26import android.widget.ListView;
27import android.widget.PopupMenu;
28import android.widget.PopupMenu.OnMenuItemClickListener;
29import android.widget.Toast;
30
31import net.java.otr4j.session.SessionStatus;
32
33import java.util.ArrayList;
34import java.util.List;
35
36import eu.siacs.conversations.R;
37import eu.siacs.conversations.entities.Account;
38import eu.siacs.conversations.entities.Blockable;
39import eu.siacs.conversations.entities.Contact;
40import eu.siacs.conversations.entities.Conversation;
41import eu.siacs.conversations.entities.Message;
42import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
43import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate;
44import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
45import eu.siacs.conversations.ui.adapter.ConversationAdapter;
46import eu.siacs.conversations.utils.ExceptionHelper;
47import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
48
49public class ConversationActivity extends XmppActivity
50 implements OnAccountUpdate, OnConversationUpdate, OnRosterUpdate, OnUpdateBlocklist {
51
52 public static final String VIEW_CONVERSATION = "viewConversation";
53 public static final String CONVERSATION = "conversationUuid";
54 public static final String TEXT = "text";
55 public static final String NICK = "nick";
56
57 public static final int REQUEST_SEND_MESSAGE = 0x0201;
58 public static final int REQUEST_DECRYPT_PGP = 0x0202;
59 public static final int REQUEST_ENCRYPT_MESSAGE = 0x0207;
60 private static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301;
61 private static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302;
62 private static final int ATTACHMENT_CHOICE_CHOOSE_FILE = 0x0303;
63 private static final int ATTACHMENT_CHOICE_RECORD_VOICE = 0x0304;
64 private static final String STATE_OPEN_CONVERSATION = "state_open_conversation";
65 private static final String STATE_PANEL_OPEN = "state_panel_open";
66 private static final String STATE_PENDING_URI = "state_pending_uri";
67
68 private String mOpenConverstaion = null;
69 private boolean mPanelOpen = true;
70 private Uri mPendingImageUri = null;
71 private Uri mPendingFileUri = null;
72
73 private View mContentView;
74
75 private List<Conversation> conversationList = new ArrayList<>();
76 private Conversation mSelectedConversation = null;
77 private ListView listView;
78 private ConversationFragment mConversationFragment;
79
80 private ArrayAdapter<Conversation> listAdapter;
81
82 private Toast prepareFileToast;
83
84 private boolean mActivityPaused = false;
85
86
87 public List<Conversation> getConversationList() {
88 return this.conversationList;
89 }
90
91 public Conversation getSelectedConversation() {
92 return this.mSelectedConversation;
93 }
94
95 public void setSelectedConversation(Conversation conversation) {
96 this.mSelectedConversation = conversation;
97 }
98
99 public void showConversationsOverview() {
100 if (mContentView instanceof SlidingPaneLayout) {
101 SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
102 mSlidingPaneLayout.openPane();
103 }
104 }
105
106 @Override
107 protected String getShareableUri() {
108 Conversation conversation = getSelectedConversation();
109 if (conversation != null) {
110 return conversation.getAccount().getShareableUri();
111 } else {
112 return "";
113 }
114 }
115
116 public void hideConversationsOverview() {
117 if (mContentView instanceof SlidingPaneLayout) {
118 SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
119 mSlidingPaneLayout.closePane();
120 }
121 }
122
123 public boolean isConversationsOverviewHideable() {
124 if (mContentView instanceof SlidingPaneLayout) {
125 SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
126 return mSlidingPaneLayout.isSlideable();
127 } else {
128 return false;
129 }
130 }
131
132 public boolean isConversationsOverviewVisable() {
133 if (mContentView instanceof SlidingPaneLayout) {
134 SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
135 return mSlidingPaneLayout.isOpen();
136 } else {
137 return true;
138 }
139 }
140
141 @Override
142 protected void onCreate(Bundle savedInstanceState) {
143 super.onCreate(savedInstanceState);
144 if (savedInstanceState != null) {mOpenConverstaion = savedInstanceState.getString(
145 STATE_OPEN_CONVERSATION, null);
146 mPanelOpen = savedInstanceState.getBoolean(STATE_PANEL_OPEN, true);
147 String pending = savedInstanceState.getString(STATE_PENDING_URI, null);
148 if (pending != null) {
149 mPendingImageUri = Uri.parse(pending);
150 }
151 }
152
153 setContentView(R.layout.fragment_conversations_overview);
154
155 this.mConversationFragment = new ConversationFragment();
156 FragmentTransaction transaction = getFragmentManager().beginTransaction();
157 transaction.replace(R.id.selected_conversation, this.mConversationFragment, "conversation");
158 transaction.commit();
159
160 listView = (ListView) findViewById(R.id.list);
161 this.listAdapter = new ConversationAdapter(this, conversationList);
162 listView.setAdapter(this.listAdapter);
163
164 if (getActionBar() != null) {
165 getActionBar().setDisplayHomeAsUpEnabled(false);
166 getActionBar().setHomeButtonEnabled(false);
167 }
168
169 listView.setOnItemClickListener(new OnItemClickListener() {
170
171 @Override
172 public void onItemClick(AdapterView<?> arg0, View clickedView,
173 int position, long arg3) {
174 if (getSelectedConversation() != conversationList.get(position)) {
175 setSelectedConversation(conversationList.get(position));
176 ConversationActivity.this.mConversationFragment.reInit(getSelectedConversation());
177 }
178 hideConversationsOverview();
179 }
180 });
181 mContentView = findViewById(R.id.content_view_spl);
182 if (mContentView == null) {
183 mContentView = findViewById(R.id.content_view_ll);
184 }
185 if (mContentView instanceof SlidingPaneLayout) {
186 SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
187 mSlidingPaneLayout.setParallaxDistance(150);
188 mSlidingPaneLayout
189 .setShadowResource(R.drawable.es_slidingpane_shadow);
190 mSlidingPaneLayout.setSliderFadeColor(0);
191 mSlidingPaneLayout.setPanelSlideListener(new PanelSlideListener() {
192
193 @Override
194 public void onPanelOpened(View arg0) {
195 updateActionBarTitle();
196 invalidateOptionsMenu();
197 hideKeyboard();
198 if (xmppConnectionServiceBound) {
199 xmppConnectionService.getNotificationService()
200 .setOpenConversation(null);
201 }
202 closeContextMenu();
203 }
204
205 @Override
206 public void onPanelClosed(View arg0) {
207 openConversation();
208 }
209
210 @Override
211 public void onPanelSlide(View arg0, float arg1) {
212 // TODO Auto-generated method stub
213
214 }
215 });
216 }
217 }
218
219 @Override
220 public void switchToConversation(Conversation conversation) {
221 setSelectedConversation(conversation);
222 runOnUiThread(new Runnable() {
223 @Override
224 public void run() {
225 ConversationActivity.this.mConversationFragment.reInit(getSelectedConversation());
226 openConversation();
227 }
228 });
229 }
230
231 private void updateActionBarTitle() {
232 updateActionBarTitle(isConversationsOverviewHideable() && !isConversationsOverviewVisable());
233 }
234
235 private void updateActionBarTitle(boolean titleShouldBeName) {
236 final ActionBar ab = getActionBar();
237 final Conversation conversation = getSelectedConversation();
238 if (ab != null) {
239 if (titleShouldBeName && conversation != null) {
240 ab.setDisplayHomeAsUpEnabled(true);
241 ab.setHomeButtonEnabled(true);
242 if (conversation.getMode() == Conversation.MODE_SINGLE || useSubjectToIdentifyConference()) {
243 ab.setTitle(conversation.getName());
244 } else {
245 ab.setTitle(conversation.getJid().toBareJid().toString());
246 }
247 } else {
248 ab.setDisplayHomeAsUpEnabled(false);
249 ab.setHomeButtonEnabled(false);
250 ab.setTitle(R.string.app_name);
251 }
252 }
253 }
254
255 private void openConversation() {
256 this.updateActionBarTitle();
257 this.invalidateOptionsMenu();
258 if (xmppConnectionServiceBound) {
259 final Conversation conversation = getSelectedConversation();
260 xmppConnectionService.getNotificationService().setOpenConversation(conversation);
261 sendReadMarkerIfNecessary(conversation);
262 }
263 }
264
265 public void sendReadMarkerIfNecessary(final Conversation conversation) {
266 if (!mActivityPaused && !conversation.isRead()) {
267 xmppConnectionService.sendReadMarker(conversation);
268 }
269 }
270
271 @Override
272 public boolean onCreateOptionsMenu(Menu menu) {
273 getMenuInflater().inflate(R.menu.conversations, menu);
274 final MenuItem menuSecure = menu.findItem(R.id.action_security);
275 final MenuItem menuArchive = menu.findItem(R.id.action_archive);
276 final MenuItem menuMucDetails = menu.findItem(R.id.action_muc_details);
277 final MenuItem menuContactDetails = menu.findItem(R.id.action_contact_details);
278 final MenuItem menuAttach = menu.findItem(R.id.action_attach_file);
279 final MenuItem menuClearHistory = menu.findItem(R.id.action_clear_history);
280 final MenuItem menuAdd = menu.findItem(R.id.action_add);
281 final MenuItem menuInviteContact = menu.findItem(R.id.action_invite);
282 final MenuItem menuMute = menu.findItem(R.id.action_mute);
283 final MenuItem menuUnmute = menu.findItem(R.id.action_unmute);
284 final MenuItem menuBlock = menu.findItem(R.id.action_block);
285 final MenuItem menuUnblock = menu.findItem(R.id.action_unblock);
286
287 if (isConversationsOverviewVisable()
288 && isConversationsOverviewHideable()) {
289 menuArchive.setVisible(false);
290 menuMucDetails.setVisible(false);
291 menuContactDetails.setVisible(false);
292 menuSecure.setVisible(false);
293 menuInviteContact.setVisible(false);
294 menuAttach.setVisible(false);
295 menuClearHistory.setVisible(false);
296 menuMute.setVisible(false);
297 menuUnmute.setVisible(false);
298 menuBlock.setVisible(false);
299 menuUnblock.setVisible(false);
300 } else {
301 menuAdd.setVisible(!isConversationsOverviewHideable());
302 if (this.getSelectedConversation() != null) {
303 if (this.getSelectedConversation().getLatestMessage()
304 .getEncryption() != Message.ENCRYPTION_NONE) {
305 menuSecure.setIcon(R.drawable.ic_action_secure);
306 }
307 if (this.getSelectedConversation().getMode() == Conversation.MODE_MULTI) {
308 menuContactDetails.setVisible(false);
309 menuAttach.setVisible(false);
310 menuBlock.setVisible(false);
311 menuUnblock.setVisible(false);
312 } else {
313 menuMucDetails.setVisible(false);
314 menuInviteContact.setTitle(R.string.conference_with);
315 if (this.getSelectedConversation().isBlocked()) {
316 menuBlock.setVisible(false);
317 } else {
318 menuUnblock.setVisible(false);
319 }
320 final Account account = this.getSelectedConversation().getAccount();
321 if (!(account.isOnlineAndConnected() && account.getXmppConnection().getFeatures().blocking())) {
322 menuBlock.setVisible(false);
323 menuUnblock.setVisible(false);
324 }
325 }
326 if (this.getSelectedConversation().isMuted()) {
327 menuMute.setVisible(false);
328 } else {
329 menuUnmute.setVisible(false);
330 }
331 }
332 }
333 return true;
334 }
335
336 private void selectPresenceToAttachFile(final int attachmentChoice) {
337 selectPresence(getSelectedConversation(), new OnPresenceSelected() {
338
339 @Override
340 public void onPresenceSelected() {
341 Intent intent = new Intent();
342 boolean chooser = false;
343 switch (attachmentChoice) {
344 case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
345 intent.setAction(Intent.ACTION_GET_CONTENT);
346 intent.setType("image/*");
347 chooser = true;
348 break;
349 case ATTACHMENT_CHOICE_TAKE_PHOTO:
350 mPendingImageUri = xmppConnectionService.getFileBackend().getTakePhotoUri();
351 intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
352 intent.putExtra(MediaStore.EXTRA_OUTPUT,mPendingImageUri);
353 break;
354 case ATTACHMENT_CHOICE_CHOOSE_FILE:
355 chooser = true;
356 intent.setType("*/*");
357 intent.addCategory(Intent.CATEGORY_OPENABLE);
358 intent.setAction(Intent.ACTION_GET_CONTENT);
359 break;
360 case ATTACHMENT_CHOICE_RECORD_VOICE:
361 intent.setAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
362 break;
363 }
364 if (intent.resolveActivity(getPackageManager()) != null) {
365 if (chooser) {
366 startActivityForResult(
367 Intent.createChooser(intent,getString(R.string.perform_action_with)),
368 attachmentChoice);
369 } else {
370 startActivityForResult(intent, attachmentChoice);
371 }
372 }
373 }
374 });
375 }
376
377 private void attachFile(final int attachmentChoice) {
378 final Conversation conversation = getSelectedConversation();
379 if (conversation.getNextEncryption(forceEncryption()) == Message.ENCRYPTION_PGP) {
380 if (hasPgp()) {
381 if (conversation.getContact().getPgpKeyId() != 0) {
382 xmppConnectionService.getPgpEngine().hasKey(
383 conversation.getContact(),
384 new UiCallback<Contact>() {
385
386 @Override
387 public void userInputRequried(PendingIntent pi,
388 Contact contact) {
389 ConversationActivity.this.runIntent(pi,
390 attachmentChoice);
391 }
392
393 @Override
394 public void success(Contact contact) {
395 selectPresenceToAttachFile(attachmentChoice);
396 }
397
398 @Override
399 public void error(int error, Contact contact) {
400 displayErrorDialog(error);
401 }
402 });
403 } else {
404 final ConversationFragment fragment = (ConversationFragment) getFragmentManager()
405 .findFragmentByTag("conversation");
406 if (fragment != null) {
407 fragment.showNoPGPKeyDialog(false,
408 new OnClickListener() {
409
410 @Override
411 public void onClick(DialogInterface dialog,
412 int which) {
413 conversation
414 .setNextEncryption(Message.ENCRYPTION_NONE);
415 xmppConnectionService.databaseBackend
416 .updateConversation(conversation);
417 selectPresenceToAttachFile(attachmentChoice);
418 }
419 });
420 }
421 }
422 } else {
423 showInstallPgpDialog();
424 }
425 } else if (getSelectedConversation().getNextEncryption(
426 forceEncryption()) == Message.ENCRYPTION_NONE) {
427 selectPresenceToAttachFile(attachmentChoice);
428 } else {
429 selectPresenceToAttachFile(attachmentChoice);
430 }
431 }
432
433 @Override
434 public boolean onOptionsItemSelected(final MenuItem item) {
435 if (item.getItemId() == android.R.id.home) {
436 showConversationsOverview();
437 return true;
438 } else if (item.getItemId() == R.id.action_add) {
439 startActivity(new Intent(this, StartConversationActivity.class));
440 return true;
441 } else if (getSelectedConversation() != null) {
442 switch (item.getItemId()) {
443 case R.id.action_attach_file:
444 attachFileDialog();
445 break;
446 case R.id.action_archive:
447 this.endConversation(getSelectedConversation());
448 break;
449 case R.id.action_contact_details:
450 Contact contact = this.getSelectedConversation().getContact();
451 if (contact.showInRoster()) {
452 switchToContactDetails(contact);
453 } else {
454 showAddToRosterDialog(getSelectedConversation());
455 }
456 break;
457 case R.id.action_muc_details:
458 Intent intent = new Intent(this,
459 ConferenceDetailsActivity.class);
460 intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC);
461 intent.putExtra("uuid", getSelectedConversation().getUuid());
462 startActivity(intent);
463 break;
464 case R.id.action_invite:
465 inviteToConversation(getSelectedConversation());
466 break;
467 case R.id.action_security:
468 selectEncryptionDialog(getSelectedConversation());
469 break;
470 case R.id.action_clear_history:
471 clearHistoryDialog(getSelectedConversation());
472 break;
473 case R.id.action_mute:
474 muteConversationDialog(getSelectedConversation());
475 break;
476 case R.id.action_unmute:
477 unmuteConversation(getSelectedConversation());
478 break;
479 case R.id.action_block:
480 BlockContactDialog.show(this, xmppConnectionService, getSelectedConversation());
481 break;
482 case R.id.action_unblock:
483 BlockContactDialog.show(this, xmppConnectionService, getSelectedConversation());
484 break;
485 default:
486 break;
487 }
488 return super.onOptionsItemSelected(item);
489 } else {
490 return super.onOptionsItemSelected(item);
491 }
492 }
493
494 public void endConversation(Conversation conversation) {
495 showConversationsOverview();
496 xmppConnectionService.archiveConversation(conversation);
497 if (conversationList.size() > 0) {
498 setSelectedConversation(conversationList.get(0));
499 this.mConversationFragment.reInit(getSelectedConversation());
500 } else {
501 setSelectedConversation(null);
502 }
503 }
504
505 @SuppressLint("InflateParams")
506 protected void clearHistoryDialog(final Conversation conversation) {
507 AlertDialog.Builder builder = new AlertDialog.Builder(this);
508 builder.setTitle(getString(R.string.clear_conversation_history));
509 View dialogView = getLayoutInflater().inflate(
510 R.layout.dialog_clear_history, null);
511 final CheckBox endConversationCheckBox = (CheckBox) dialogView
512 .findViewById(R.id.end_conversation_checkbox);
513 builder.setView(dialogView);
514 builder.setNegativeButton(getString(R.string.cancel), null);
515 builder.setPositiveButton(getString(R.string.delete_messages),
516 new OnClickListener() {
517
518 @Override
519 public void onClick(DialogInterface dialog, int which) {
520 ConversationActivity.this.xmppConnectionService.clearConversationHistory(conversation);
521 if (endConversationCheckBox.isChecked()) {
522 endConversation(conversation);
523 } else {
524 updateConversationList();
525 ConversationActivity.this.mConversationFragment.updateMessages();
526 }
527 }
528 });
529 builder.create().show();
530 }
531
532 protected void attachFileDialog() {
533 View menuAttachFile = findViewById(R.id.action_attach_file);
534 if (menuAttachFile == null) {
535 return;
536 }
537 PopupMenu attachFilePopup = new PopupMenu(this, menuAttachFile);
538 attachFilePopup.inflate(R.menu.attachment_choices);
539 if (new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION).resolveActivity(getPackageManager()) == null) {
540 attachFilePopup.getMenu().findItem(R.id.attach_record_voice).setVisible(false);
541 }
542 attachFilePopup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
543
544 @Override
545 public boolean onMenuItemClick(MenuItem item) {
546 switch (item.getItemId()) {
547 case R.id.attach_choose_picture:
548 attachFile(ATTACHMENT_CHOICE_CHOOSE_IMAGE);
549 break;
550 case R.id.attach_take_picture:
551 attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO);
552 break;
553 case R.id.attach_choose_file:
554 attachFile(ATTACHMENT_CHOICE_CHOOSE_FILE);
555 break;
556 case R.id.attach_record_voice:
557 attachFile(ATTACHMENT_CHOICE_RECORD_VOICE);
558 break;
559 }
560 return false;
561 }
562 });
563 attachFilePopup.show();
564 }
565
566 public void verifyOtrSessionDialog(final Conversation conversation, View view) {
567 if (!conversation.hasValidOtrSession() || conversation.getOtrSession().getSessionStatus() != SessionStatus.ENCRYPTED) {
568 Toast.makeText(this, R.string.otr_session_not_started, Toast.LENGTH_LONG).show();
569 return;
570 }
571 if (view == null) {
572 return;
573 }
574 PopupMenu popup = new PopupMenu(this, view);
575 popup.inflate(R.menu.verification_choices);
576 popup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
577 @Override
578 public boolean onMenuItemClick(MenuItem menuItem) {
579 Intent intent = new Intent(ConversationActivity.this, VerifyOTRActivity.class);
580 intent.setAction(VerifyOTRActivity.ACTION_VERIFY_CONTACT);
581 intent.putExtra("contact", conversation.getContact().getJid().toBareJid().toString());
582 intent.putExtra("account", conversation.getAccount().getJid().toBareJid().toString());
583 switch (menuItem.getItemId()) {
584 case R.id.scan_fingerprint:
585 intent.putExtra("mode",VerifyOTRActivity.MODE_SCAN_FINGERPRINT);
586 break;
587 case R.id.ask_question:
588 intent.putExtra("mode",VerifyOTRActivity.MODE_ASK_QUESTION);
589 break;
590 case R.id.manual_verification:
591 intent.putExtra("mode",VerifyOTRActivity.MODE_MANUAL_VERIFICATION);
592 break;
593 }
594 startActivity(intent);
595 return true;
596 }
597 });
598 popup.show();
599 }
600
601 protected void selectEncryptionDialog(final Conversation conversation) {
602 View menuItemView = findViewById(R.id.action_security);
603 if (menuItemView == null) {
604 return;
605 }
606 PopupMenu popup = new PopupMenu(this, menuItemView);
607 final ConversationFragment fragment = (ConversationFragment) getFragmentManager()
608 .findFragmentByTag("conversation");
609 if (fragment != null) {
610 popup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
611
612 @Override
613 public boolean onMenuItemClick(MenuItem item) {
614 switch (item.getItemId()) {
615 case R.id.encryption_choice_none:
616 conversation.setNextEncryption(Message.ENCRYPTION_NONE);
617 item.setChecked(true);
618 break;
619 case R.id.encryption_choice_otr:
620 conversation.setNextEncryption(Message.ENCRYPTION_OTR);
621 item.setChecked(true);
622 break;
623 case R.id.encryption_choice_pgp:
624 if (hasPgp()) {
625 if (conversation.getAccount().getKeys()
626 .has("pgp_signature")) {
627 conversation
628 .setNextEncryption(Message.ENCRYPTION_PGP);
629 item.setChecked(true);
630 } else {
631 announcePgp(conversation.getAccount(),
632 conversation);
633 }
634 } else {
635 showInstallPgpDialog();
636 }
637 break;
638 default:
639 conversation.setNextEncryption(Message.ENCRYPTION_NONE);
640 break;
641 }
642 xmppConnectionService.databaseBackend
643 .updateConversation(conversation);
644 fragment.updateChatMsgHint();
645 return true;
646 }
647 });
648 popup.inflate(R.menu.encryption_choices);
649 MenuItem otr = popup.getMenu().findItem(R.id.encryption_choice_otr);
650 MenuItem none = popup.getMenu().findItem(
651 R.id.encryption_choice_none);
652 if (conversation.getMode() == Conversation.MODE_MULTI) {
653 otr.setEnabled(false);
654 } else {
655 if (forceEncryption()) {
656 none.setVisible(false);
657 }
658 }
659 switch (conversation.getNextEncryption(forceEncryption())) {
660 case Message.ENCRYPTION_NONE:
661 none.setChecked(true);
662 break;
663 case Message.ENCRYPTION_OTR:
664 otr.setChecked(true);
665 break;
666 case Message.ENCRYPTION_PGP:
667 popup.getMenu().findItem(R.id.encryption_choice_pgp)
668 .setChecked(true);
669 break;
670 default:
671 popup.getMenu().findItem(R.id.encryption_choice_none)
672 .setChecked(true);
673 break;
674 }
675 popup.show();
676 }
677 }
678
679 protected void muteConversationDialog(final Conversation conversation) {
680 AlertDialog.Builder builder = new AlertDialog.Builder(this);
681 builder.setTitle(R.string.disable_notifications);
682 final int[] durations = getResources().getIntArray(
683 R.array.mute_options_durations);
684 builder.setItems(R.array.mute_options_descriptions,
685 new OnClickListener() {
686
687 @Override
688 public void onClick(final DialogInterface dialog, final int which) {
689 final long till;
690 if (durations[which] == -1) {
691 till = Long.MAX_VALUE;
692 } else {
693 till = SystemClock.elapsedRealtime()
694 + (durations[which] * 1000);
695 }
696 conversation.setMutedTill(till);
697 ConversationActivity.this.xmppConnectionService.databaseBackend
698 .updateConversation(conversation);
699 updateConversationList();
700 ConversationActivity.this.mConversationFragment.updateMessages();
701 invalidateOptionsMenu();
702 }
703 });
704 builder.create().show();
705 }
706
707 public void unmuteConversation(final Conversation conversation) {
708 conversation.setMutedTill(0);
709 this.xmppConnectionService.databaseBackend.updateConversation(conversation);
710 updateConversationList();
711 ConversationActivity.this.mConversationFragment.updateMessages();
712 invalidateOptionsMenu();
713 }
714
715 @Override
716 public void onBackPressed() {
717 if (!isConversationsOverviewVisable()) {
718 showConversationsOverview();
719 } else {
720 moveTaskToBack(true);
721 }
722 }
723
724 @Override
725 protected void onNewIntent(final Intent intent) {
726 if (xmppConnectionServiceBound) {
727 if (intent != null && VIEW_CONVERSATION.equals(intent.getType())) {
728 handleViewConversationIntent(intent);
729 }
730 } else {
731 setIntent(intent);
732 }
733 }
734
735 @Override
736 public void onStart() {
737 super.onStart();
738 if (this.xmppConnectionServiceBound) {
739 this.onBackendConnected();
740 }
741 if (conversationList.size() >= 1) {
742 this.onConversationUpdate();
743 }
744 }
745
746 @Override
747 public void onPause() {
748 super.onPause();
749 this.mActivityPaused = true;
750 if (this.xmppConnectionServiceBound) {
751 this.xmppConnectionService.getNotificationService().setIsInForeground(false);
752 }
753 }
754
755 @Override
756 public void onResume() {
757 super.onResume();
758 int theme = findTheme();
759 if (this.mTheme != theme) {
760 recreate();
761 }
762 this.mActivityPaused = false;
763 if (this.xmppConnectionServiceBound) {
764 this.xmppConnectionService.getNotificationService().setIsInForeground(true);
765 }
766 }
767
768 @Override
769 public void onSaveInstanceState(final Bundle savedInstanceState) {
770 Conversation conversation = getSelectedConversation();
771 if (conversation != null) {
772 savedInstanceState.putString(STATE_OPEN_CONVERSATION,
773 conversation.getUuid());
774 }
775 savedInstanceState.putBoolean(STATE_PANEL_OPEN,
776 isConversationsOverviewVisable());
777 if (this.mPendingImageUri != null) {
778 savedInstanceState.putString(STATE_PENDING_URI, this.mPendingImageUri.toString());
779 }
780 super.onSaveInstanceState(savedInstanceState);
781 }
782
783 @Override
784 void onBackendConnected() {
785 this.xmppConnectionService.getNotificationService().setIsInForeground(true);
786 updateConversationList();
787 if (xmppConnectionService.getAccounts().size() == 0) {
788 startActivity(new Intent(this, EditAccountActivity.class));
789 } else if (conversationList.size() <= 0) {
790 startActivity(new Intent(this, StartConversationActivity.class));
791 finish();
792 } else if (getIntent() != null
793 && VIEW_CONVERSATION.equals(getIntent().getType())) {
794 handleViewConversationIntent(getIntent());
795 } else if (mOpenConverstaion != null) {
796 selectConversationByUuid(mOpenConverstaion);
797 if (mPanelOpen) {
798 showConversationsOverview();
799 } else {
800 if (isConversationsOverviewHideable()) {
801 openConversation();
802 }
803 }
804 this.mConversationFragment.reInit(getSelectedConversation());
805 mOpenConverstaion = null;
806 } else if (getSelectedConversation() != null) {
807 this.mConversationFragment.updateMessages();
808 } else {
809 showConversationsOverview();
810 mPendingImageUri = null;
811 mPendingFileUri = null;
812 setSelectedConversation(conversationList.get(0));
813 this.mConversationFragment.reInit(getSelectedConversation());
814 }
815
816 if (mPendingImageUri != null) {
817 attachImageToConversation(getSelectedConversation(),mPendingImageUri);
818 mPendingImageUri = null;
819 } else if (mPendingFileUri != null) {
820 attachFileToConversation(getSelectedConversation(),mPendingFileUri);
821 mPendingFileUri = null;
822 }
823 ExceptionHelper.checkForCrash(this, this.xmppConnectionService);
824 setIntent(new Intent());
825 }
826
827 private void handleViewConversationIntent(Intent intent) {
828 String uuid = (String) intent.getExtras().get(CONVERSATION);
829 String text = intent.getExtras().getString(TEXT, "");
830 String nick = intent.getExtras().getString(NICK,null);
831 selectConversationByUuid(uuid);
832 this.mConversationFragment.reInit(getSelectedConversation());
833 if (nick!=null) {
834 this.mConversationFragment.highlightInConference(nick);
835 } else {
836 this.mConversationFragment.appendText(text);
837 }
838 hideConversationsOverview();
839 openConversation();
840 if (mContentView instanceof SlidingPaneLayout) {
841 updateActionBarTitle(true); //fixes bug where slp isn't properly closed yet
842 }
843 }
844
845 private void selectConversationByUuid(String uuid) {
846 for (Conversation aConversationList : conversationList) {
847 if (aConversationList.getUuid().equals(uuid)) {
848 setSelectedConversation(aConversationList);
849 }
850 }
851 }
852
853 @Override
854 protected void unregisterListeners() {
855 super.unregisterListeners();
856 xmppConnectionService.getNotificationService().setOpenConversation(null);
857 }
858
859 @Override
860 protected void onActivityResult(int requestCode, int resultCode,
861 final Intent data) {
862 super.onActivityResult(requestCode, resultCode, data);
863 if (resultCode == RESULT_OK) {
864 if (requestCode == REQUEST_DECRYPT_PGP) {
865 mConversationFragment.hideSnackbar();
866 mConversationFragment.updateMessages();
867 } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_IMAGE) {
868 mPendingImageUri = data.getData();
869 if (xmppConnectionServiceBound) {
870 attachImageToConversation(getSelectedConversation(),mPendingImageUri);
871 mPendingImageUri = null;
872 }
873 } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_FILE || requestCode == ATTACHMENT_CHOICE_RECORD_VOICE) {
874 mPendingFileUri = data.getData();
875 if (xmppConnectionServiceBound) {
876 attachFileToConversation(getSelectedConversation(),mPendingFileUri);
877 mPendingFileUri = null;
878 }
879 } else if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO && mPendingImageUri != null) {
880 if (xmppConnectionServiceBound) {
881 attachImageToConversation(getSelectedConversation(),mPendingImageUri);
882 mPendingImageUri = null;
883 }
884 Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
885 intent.setData(mPendingImageUri);
886 sendBroadcast(intent);
887 }
888 } else {
889 if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO) {
890 mPendingImageUri = null;
891 }
892 }
893 }
894
895 private void attachFileToConversation(Conversation conversation, Uri uri) {
896 prepareFileToast = Toast.makeText(getApplicationContext(),
897 getText(R.string.preparing_file), Toast.LENGTH_LONG);
898 prepareFileToast.show();
899 xmppConnectionService.attachFileToConversation(conversation,uri, new UiCallback<Message>() {
900 @Override
901 public void success(Message message) {
902 hidePrepareFileToast();
903 xmppConnectionService.sendMessage(message);
904 }
905
906 @Override
907 public void error(int errorCode, Message message) {
908 displayErrorDialog(errorCode);
909 }
910
911 @Override
912 public void userInputRequried(PendingIntent pi, Message message) {
913
914 }
915 });
916 }
917
918 private void attachImageToConversation(Conversation conversation, Uri uri) {
919 prepareFileToast = Toast.makeText(getApplicationContext(),
920 getText(R.string.preparing_image), Toast.LENGTH_LONG);
921 prepareFileToast.show();
922 xmppConnectionService.attachImageToConversation(conversation, uri,
923 new UiCallback<Message>() {
924
925 @Override
926 public void userInputRequried(PendingIntent pi,
927 Message object) {
928 hidePrepareFileToast();
929 }
930
931 @Override
932 public void success(Message message) {
933 xmppConnectionService.sendMessage(message);
934 }
935
936 @Override
937 public void error(int error, Message message) {
938 hidePrepareFileToast();
939 displayErrorDialog(error);
940 }
941 });
942 }
943
944 private void hidePrepareFileToast() {
945 if (prepareFileToast != null) {
946 runOnUiThread(new Runnable() {
947
948 @Override
949 public void run() {
950 prepareFileToast.cancel();
951 }
952 });
953 }
954 }
955
956 public void updateConversationList() {
957 xmppConnectionService
958 .populateWithOrderedConversations(conversationList);
959 listAdapter.notifyDataSetChanged();
960 }
961
962 public void runIntent(PendingIntent pi, int requestCode) {
963 try {
964 this.startIntentSenderForResult(pi.getIntentSender(), requestCode,
965 null, 0, 0, 0);
966 } catch (final SendIntentException ignored) {
967 }
968 }
969
970 public void encryptTextMessage(Message message) {
971 xmppConnectionService.getPgpEngine().encrypt(message,
972 new UiCallback<Message>() {
973
974 @Override
975 public void userInputRequried(PendingIntent pi,
976 Message message) {
977 ConversationActivity.this.runIntent(pi,
978 ConversationActivity.REQUEST_SEND_MESSAGE);
979 }
980
981 @Override
982 public void success(Message message) {
983 message.setEncryption(Message.ENCRYPTION_DECRYPTED);
984 xmppConnectionService.sendMessage(message);
985 }
986
987 @Override
988 public void error(int error, Message message) {
989
990 }
991 });
992 }
993
994 public boolean forceEncryption() {
995 return getPreferences().getBoolean("force_encryption", false);
996 }
997
998 public boolean useSendButtonToIndicateStatus() {
999 return getPreferences().getBoolean("send_button_status", false);
1000 }
1001
1002 public boolean indicateReceived() {
1003 return getPreferences().getBoolean("indicate_received", false);
1004 }
1005
1006 @Override
1007 public void onAccountUpdate() {
1008 runOnUiThread(new Runnable() {
1009
1010 @Override
1011 public void run() {
1012 updateConversationList();
1013 ConversationActivity.this.mConversationFragment.updateMessages();
1014 updateActionBarTitle();
1015 }
1016 });
1017 }
1018
1019 @Override
1020 public void onConversationUpdate() {
1021 runOnUiThread(new Runnable() {
1022
1023 @Override
1024 public void run() {
1025 updateConversationList();
1026 if (conversationList.size() == 0) {
1027 startActivity(new Intent(getApplicationContext(),
1028 StartConversationActivity.class));
1029 finish();
1030 }
1031 ConversationActivity.this.mConversationFragment.updateMessages();
1032 updateActionBarTitle();
1033 }
1034 });
1035 }
1036
1037 @Override
1038 public void onRosterUpdate() {
1039 runOnUiThread(new Runnable() {
1040
1041 @Override
1042 public void run() {
1043 updateConversationList();
1044 ConversationActivity.this.mConversationFragment.updateMessages();
1045 updateActionBarTitle();
1046 }
1047 });
1048 }
1049
1050 @Override
1051 public void OnUpdateBlocklist(Status status) {
1052 runOnUiThread(new Runnable() {
1053 @Override
1054 public void run() {
1055 invalidateOptionsMenu();
1056 ConversationActivity.this.mConversationFragment.updateMessages();
1057 }
1058 });
1059 }
1060
1061 public void unblockConversation(final Blockable conversation) {
1062 xmppConnectionService.sendUnblockRequest(conversation);
1063 }
1064
1065 public void blockConversation(final Blockable conversation) {
1066 xmppConnectionService.sendBlockRequest(conversation);
1067 }
1068}