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