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 conversation.setStatus(Conversation.STATUS_ARCHIVED);
496 showConversationsOverview();
497 xmppConnectionService.archiveConversation(conversation);
498 if (conversationList.size() > 0) {
499 setSelectedConversation(conversationList.get(0));
500 this.mConversationFragment.reInit(getSelectedConversation());
501 } else {
502 setSelectedConversation(null);
503 }
504 }
505
506 @SuppressLint("InflateParams")
507 protected void clearHistoryDialog(final Conversation conversation) {
508 AlertDialog.Builder builder = new AlertDialog.Builder(this);
509 builder.setTitle(getString(R.string.clear_conversation_history));
510 View dialogView = getLayoutInflater().inflate(
511 R.layout.dialog_clear_history, null);
512 final CheckBox endConversationCheckBox = (CheckBox) dialogView
513 .findViewById(R.id.end_conversation_checkbox);
514 builder.setView(dialogView);
515 builder.setNegativeButton(getString(R.string.cancel), null);
516 builder.setPositiveButton(getString(R.string.delete_messages),
517 new OnClickListener() {
518
519 @Override
520 public void onClick(DialogInterface dialog, int which) {
521 ConversationActivity.this.xmppConnectionService.clearConversationHistory(conversation);
522 if (endConversationCheckBox.isChecked()) {
523 endConversation(conversation);
524 } else {
525 updateConversationList();
526 ConversationActivity.this.mConversationFragment.updateMessages();
527 }
528 }
529 });
530 builder.create().show();
531 }
532
533 protected void attachFileDialog() {
534 View menuAttachFile = findViewById(R.id.action_attach_file);
535 if (menuAttachFile == null) {
536 return;
537 }
538 PopupMenu attachFilePopup = new PopupMenu(this, menuAttachFile);
539 attachFilePopup.inflate(R.menu.attachment_choices);
540 if (new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION).resolveActivity(getPackageManager()) == null) {
541 attachFilePopup.getMenu().findItem(R.id.attach_record_voice).setVisible(false);
542 }
543 attachFilePopup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
544
545 @Override
546 public boolean onMenuItemClick(MenuItem item) {
547 switch (item.getItemId()) {
548 case R.id.attach_choose_picture:
549 attachFile(ATTACHMENT_CHOICE_CHOOSE_IMAGE);
550 break;
551 case R.id.attach_take_picture:
552 attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO);
553 break;
554 case R.id.attach_choose_file:
555 attachFile(ATTACHMENT_CHOICE_CHOOSE_FILE);
556 break;
557 case R.id.attach_record_voice:
558 attachFile(ATTACHMENT_CHOICE_RECORD_VOICE);
559 break;
560 }
561 return false;
562 }
563 });
564 attachFilePopup.show();
565 }
566
567 public void verifyOtrSessionDialog(final Conversation conversation, View view) {
568 if (!conversation.hasValidOtrSession() || conversation.getOtrSession().getSessionStatus() != SessionStatus.ENCRYPTED) {
569 Toast.makeText(this, R.string.otr_session_not_started, Toast.LENGTH_LONG).show();
570 return;
571 }
572 if (view == null) {
573 return;
574 }
575 PopupMenu popup = new PopupMenu(this, view);
576 popup.inflate(R.menu.verification_choices);
577 popup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
578 @Override
579 public boolean onMenuItemClick(MenuItem menuItem) {
580 Intent intent = new Intent(ConversationActivity.this, VerifyOTRActivity.class);
581 intent.setAction(VerifyOTRActivity.ACTION_VERIFY_CONTACT);
582 intent.putExtra("contact", conversation.getContact().getJid().toBareJid().toString());
583 intent.putExtra("account", conversation.getAccount().getJid().toBareJid().toString());
584 switch (menuItem.getItemId()) {
585 case R.id.scan_fingerprint:
586 intent.putExtra("mode",VerifyOTRActivity.MODE_SCAN_FINGERPRINT);
587 break;
588 case R.id.ask_question:
589 intent.putExtra("mode",VerifyOTRActivity.MODE_ASK_QUESTION);
590 break;
591 case R.id.manual_verification:
592 intent.putExtra("mode",VerifyOTRActivity.MODE_MANUAL_VERIFICATION);
593 break;
594 }
595 startActivity(intent);
596 return true;
597 }
598 });
599 popup.show();
600 }
601
602 protected void selectEncryptionDialog(final Conversation conversation) {
603 View menuItemView = findViewById(R.id.action_security);
604 if (menuItemView == null) {
605 return;
606 }
607 PopupMenu popup = new PopupMenu(this, menuItemView);
608 final ConversationFragment fragment = (ConversationFragment) getFragmentManager()
609 .findFragmentByTag("conversation");
610 if (fragment != null) {
611 popup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
612
613 @Override
614 public boolean onMenuItemClick(MenuItem item) {
615 switch (item.getItemId()) {
616 case R.id.encryption_choice_none:
617 conversation.setNextEncryption(Message.ENCRYPTION_NONE);
618 item.setChecked(true);
619 break;
620 case R.id.encryption_choice_otr:
621 conversation.setNextEncryption(Message.ENCRYPTION_OTR);
622 item.setChecked(true);
623 break;
624 case R.id.encryption_choice_pgp:
625 if (hasPgp()) {
626 if (conversation.getAccount().getKeys()
627 .has("pgp_signature")) {
628 conversation
629 .setNextEncryption(Message.ENCRYPTION_PGP);
630 item.setChecked(true);
631 } else {
632 announcePgp(conversation.getAccount(),
633 conversation);
634 }
635 } else {
636 showInstallPgpDialog();
637 }
638 break;
639 default:
640 conversation.setNextEncryption(Message.ENCRYPTION_NONE);
641 break;
642 }
643 xmppConnectionService.databaseBackend
644 .updateConversation(conversation);
645 fragment.updateChatMsgHint();
646 return true;
647 }
648 });
649 popup.inflate(R.menu.encryption_choices);
650 MenuItem otr = popup.getMenu().findItem(R.id.encryption_choice_otr);
651 MenuItem none = popup.getMenu().findItem(
652 R.id.encryption_choice_none);
653 if (conversation.getMode() == Conversation.MODE_MULTI) {
654 otr.setEnabled(false);
655 } else {
656 if (forceEncryption()) {
657 none.setVisible(false);
658 }
659 }
660 switch (conversation.getNextEncryption(forceEncryption())) {
661 case Message.ENCRYPTION_NONE:
662 none.setChecked(true);
663 break;
664 case Message.ENCRYPTION_OTR:
665 otr.setChecked(true);
666 break;
667 case Message.ENCRYPTION_PGP:
668 popup.getMenu().findItem(R.id.encryption_choice_pgp)
669 .setChecked(true);
670 break;
671 default:
672 popup.getMenu().findItem(R.id.encryption_choice_none)
673 .setChecked(true);
674 break;
675 }
676 popup.show();
677 }
678 }
679
680 protected void muteConversationDialog(final Conversation conversation) {
681 AlertDialog.Builder builder = new AlertDialog.Builder(this);
682 builder.setTitle(R.string.disable_notifications);
683 final int[] durations = getResources().getIntArray(
684 R.array.mute_options_durations);
685 builder.setItems(R.array.mute_options_descriptions,
686 new OnClickListener() {
687
688 @Override
689 public void onClick(final DialogInterface dialog, final int which) {
690 final long till;
691 if (durations[which] == -1) {
692 till = Long.MAX_VALUE;
693 } else {
694 till = SystemClock.elapsedRealtime()
695 + (durations[which] * 1000);
696 }
697 conversation.setMutedTill(till);
698 ConversationActivity.this.xmppConnectionService.databaseBackend
699 .updateConversation(conversation);
700 updateConversationList();
701 ConversationActivity.this.mConversationFragment.updateMessages();
702 invalidateOptionsMenu();
703 }
704 });
705 builder.create().show();
706 }
707
708 public void unmuteConversation(final Conversation conversation) {
709 conversation.setMutedTill(0);
710 this.xmppConnectionService.databaseBackend.updateConversation(conversation);
711 updateConversationList();
712 ConversationActivity.this.mConversationFragment.updateMessages();
713 invalidateOptionsMenu();
714 }
715
716 @Override
717 public void onBackPressed() {
718 if (!isConversationsOverviewVisable()) {
719 showConversationsOverview();
720 } else {
721 moveTaskToBack(true);
722 }
723 }
724
725 @Override
726 protected void onNewIntent(final Intent intent) {
727 if (xmppConnectionServiceBound) {
728 if (intent != null && VIEW_CONVERSATION.equals(intent.getType())) {
729 handleViewConversationIntent(intent);
730 }
731 } else {
732 setIntent(intent);
733 }
734 }
735
736 @Override
737 public void onStart() {
738 super.onStart();
739 if (this.xmppConnectionServiceBound) {
740 this.onBackendConnected();
741 }
742 if (conversationList.size() >= 1) {
743 this.onConversationUpdate();
744 }
745 }
746
747 @Override
748 public void onPause() {
749 super.onPause();
750 this.mActivityPaused = true;
751 if (this.xmppConnectionServiceBound) {
752 this.xmppConnectionService.getNotificationService().setIsInForeground(false);
753 }
754 }
755
756 @Override
757 public void onResume() {
758 super.onResume();
759 int theme = findTheme();
760 if (this.mTheme != theme) {
761 recreate();
762 }
763 this.mActivityPaused = false;
764 if (this.xmppConnectionServiceBound) {
765 this.xmppConnectionService.getNotificationService().setIsInForeground(true);
766 }
767 }
768
769 @Override
770 public void onSaveInstanceState(final Bundle savedInstanceState) {
771 Conversation conversation = getSelectedConversation();
772 if (conversation != null) {
773 savedInstanceState.putString(STATE_OPEN_CONVERSATION,
774 conversation.getUuid());
775 }
776 savedInstanceState.putBoolean(STATE_PANEL_OPEN,
777 isConversationsOverviewVisable());
778 if (this.mPendingImageUri != null) {
779 savedInstanceState.putString(STATE_PENDING_URI, this.mPendingImageUri.toString());
780 }
781 super.onSaveInstanceState(savedInstanceState);
782 }
783
784 @Override
785 void onBackendConnected() {
786 this.xmppConnectionService.getNotificationService().setIsInForeground(true);
787 updateConversationList();
788 if (xmppConnectionService.getAccounts().size() == 0) {
789 startActivity(new Intent(this, EditAccountActivity.class));
790 } else if (conversationList.size() <= 0) {
791 startActivity(new Intent(this, StartConversationActivity.class));
792 finish();
793 } else if (getIntent() != null
794 && VIEW_CONVERSATION.equals(getIntent().getType())) {
795 handleViewConversationIntent(getIntent());
796 } else if (mOpenConverstaion != null) {
797 selectConversationByUuid(mOpenConverstaion);
798 if (mPanelOpen) {
799 showConversationsOverview();
800 } else {
801 if (isConversationsOverviewHideable()) {
802 openConversation();
803 }
804 }
805 this.mConversationFragment.reInit(getSelectedConversation());
806 mOpenConverstaion = null;
807 } else if (getSelectedConversation() != null) {
808 this.mConversationFragment.updateMessages();
809 } else {
810 showConversationsOverview();
811 mPendingImageUri = null;
812 mPendingFileUri = null;
813 setSelectedConversation(conversationList.get(0));
814 this.mConversationFragment.reInit(getSelectedConversation());
815 }
816
817 if (mPendingImageUri != null) {
818 attachImageToConversation(getSelectedConversation(),mPendingImageUri);
819 mPendingImageUri = null;
820 } else if (mPendingFileUri != null) {
821 attachFileToConversation(getSelectedConversation(),mPendingFileUri);
822 mPendingFileUri = null;
823 }
824 ExceptionHelper.checkForCrash(this, this.xmppConnectionService);
825 setIntent(new Intent());
826 }
827
828 private void handleViewConversationIntent(Intent intent) {
829 String uuid = (String) intent.getExtras().get(CONVERSATION);
830 String text = intent.getExtras().getString(TEXT, "");
831 String nick = intent.getExtras().getString(NICK,null);
832 selectConversationByUuid(uuid);
833 this.mConversationFragment.reInit(getSelectedConversation());
834 if (nick!=null) {
835 this.mConversationFragment.highlightInConference(nick);
836 } else {
837 this.mConversationFragment.appendText(text);
838 }
839 hideConversationsOverview();
840 openConversation();
841 if (mContentView instanceof SlidingPaneLayout) {
842 updateActionBarTitle(true); //fixes bug where slp isn't properly closed yet
843 }
844 }
845
846 private void selectConversationByUuid(String uuid) {
847 for (Conversation aConversationList : conversationList) {
848 if (aConversationList.getUuid().equals(uuid)) {
849 setSelectedConversation(aConversationList);
850 }
851 }
852 }
853
854 @Override
855 protected void unregisterListeners() {
856 super.unregisterListeners();
857 xmppConnectionService.getNotificationService().setOpenConversation(null);
858 }
859
860 @Override
861 protected void onActivityResult(int requestCode, int resultCode,
862 final Intent data) {
863 super.onActivityResult(requestCode, resultCode, data);
864 if (resultCode == RESULT_OK) {
865 if (requestCode == REQUEST_DECRYPT_PGP) {
866 mConversationFragment.hideSnackbar();
867 mConversationFragment.updateMessages();
868 } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_IMAGE) {
869 mPendingImageUri = data.getData();
870 if (xmppConnectionServiceBound) {
871 attachImageToConversation(getSelectedConversation(),mPendingImageUri);
872 mPendingImageUri = null;
873 }
874 } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_FILE || requestCode == ATTACHMENT_CHOICE_RECORD_VOICE) {
875 mPendingFileUri = data.getData();
876 if (xmppConnectionServiceBound) {
877 attachFileToConversation(getSelectedConversation(),mPendingFileUri);
878 mPendingFileUri = null;
879 }
880 } else if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO && mPendingImageUri != null) {
881 if (xmppConnectionServiceBound) {
882 attachImageToConversation(getSelectedConversation(),mPendingImageUri);
883 mPendingImageUri = null;
884 }
885 Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
886 intent.setData(mPendingImageUri);
887 sendBroadcast(intent);
888 }
889 } else {
890 if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO) {
891 mPendingImageUri = null;
892 }
893 }
894 }
895
896 private void attachFileToConversation(Conversation conversation, Uri uri) {
897 prepareFileToast = Toast.makeText(getApplicationContext(),
898 getText(R.string.preparing_file), Toast.LENGTH_LONG);
899 prepareFileToast.show();
900 xmppConnectionService.attachFileToConversation(conversation,uri, new UiCallback<Message>() {
901 @Override
902 public void success(Message message) {
903 hidePrepareFileToast();
904 xmppConnectionService.sendMessage(message);
905 }
906
907 @Override
908 public void error(int errorCode, Message message) {
909 displayErrorDialog(errorCode);
910 }
911
912 @Override
913 public void userInputRequried(PendingIntent pi, Message message) {
914
915 }
916 });
917 }
918
919 private void attachImageToConversation(Conversation conversation, Uri uri) {
920 prepareFileToast = Toast.makeText(getApplicationContext(),
921 getText(R.string.preparing_image), Toast.LENGTH_LONG);
922 prepareFileToast.show();
923 xmppConnectionService.attachImageToConversation(conversation, uri,
924 new UiCallback<Message>() {
925
926 @Override
927 public void userInputRequried(PendingIntent pi,
928 Message object) {
929 hidePrepareFileToast();
930 }
931
932 @Override
933 public void success(Message message) {
934 xmppConnectionService.sendMessage(message);
935 }
936
937 @Override
938 public void error(int error, Message message) {
939 hidePrepareFileToast();
940 displayErrorDialog(error);
941 }
942 });
943 }
944
945 private void hidePrepareFileToast() {
946 if (prepareFileToast != null) {
947 runOnUiThread(new Runnable() {
948
949 @Override
950 public void run() {
951 prepareFileToast.cancel();
952 }
953 });
954 }
955 }
956
957 public void updateConversationList() {
958 xmppConnectionService
959 .populateWithOrderedConversations(conversationList);
960 listAdapter.notifyDataSetChanged();
961 }
962
963 public void runIntent(PendingIntent pi, int requestCode) {
964 try {
965 this.startIntentSenderForResult(pi.getIntentSender(), requestCode,
966 null, 0, 0, 0);
967 } catch (final SendIntentException ignored) {
968 }
969 }
970
971 public void encryptTextMessage(Message message) {
972 xmppConnectionService.getPgpEngine().encrypt(message,
973 new UiCallback<Message>() {
974
975 @Override
976 public void userInputRequried(PendingIntent pi,
977 Message message) {
978 ConversationActivity.this.runIntent(pi,
979 ConversationActivity.REQUEST_SEND_MESSAGE);
980 }
981
982 @Override
983 public void success(Message message) {
984 message.setEncryption(Message.ENCRYPTION_DECRYPTED);
985 xmppConnectionService.sendMessage(message);
986 }
987
988 @Override
989 public void error(int error, Message message) {
990
991 }
992 });
993 }
994
995 public boolean forceEncryption() {
996 return getPreferences().getBoolean("force_encryption", false);
997 }
998
999 public boolean useSendButtonToIndicateStatus() {
1000 return getPreferences().getBoolean("send_button_status", false);
1001 }
1002
1003 public boolean indicateReceived() {
1004 return getPreferences().getBoolean("indicate_received", false);
1005 }
1006
1007 @Override
1008 public void onAccountUpdate() {
1009 runOnUiThread(new Runnable() {
1010
1011 @Override
1012 public void run() {
1013 updateConversationList();
1014 ConversationActivity.this.mConversationFragment.updateMessages();
1015 updateActionBarTitle();
1016 }
1017 });
1018 }
1019
1020 @Override
1021 public void onConversationUpdate() {
1022 runOnUiThread(new Runnable() {
1023
1024 @Override
1025 public void run() {
1026 updateConversationList();
1027 if (conversationList.size() == 0) {
1028 startActivity(new Intent(getApplicationContext(),
1029 StartConversationActivity.class));
1030 finish();
1031 }
1032 ConversationActivity.this.mConversationFragment.updateMessages();
1033 updateActionBarTitle();
1034 }
1035 });
1036 }
1037
1038 @Override
1039 public void onRosterUpdate() {
1040 runOnUiThread(new Runnable() {
1041
1042 @Override
1043 public void run() {
1044 updateConversationList();
1045 ConversationActivity.this.mConversationFragment.updateMessages();
1046 updateActionBarTitle();
1047 }
1048 });
1049 }
1050
1051 @Override
1052 public void OnUpdateBlocklist(Status status) {
1053 invalidateOptionsMenu();
1054 runOnUiThread(new Runnable() {
1055 @Override
1056 public void run() {
1057 ConversationActivity.this.mConversationFragment.updateMessages();
1058 }
1059 });
1060 }
1061
1062 public void unblockConversation(final Blockable conversation) {
1063 xmppConnectionService.sendUnblockRequest(conversation);
1064 }
1065
1066 public void blockConversation(final Blockable conversation) {
1067 xmppConnectionService.sendBlockRequest(conversation);
1068 }
1069}