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.view.Menu;
19import android.view.MenuItem;
20import android.view.View;
21import android.widget.AdapterView;
22import android.widget.AdapterView.OnItemClickListener;
23import android.widget.ArrayAdapter;
24import android.widget.CheckBox;
25import android.widget.ListView;
26import android.widget.PopupMenu;
27import android.widget.PopupMenu.OnMenuItemClickListener;
28import android.widget.Toast;
29
30import net.java.otr4j.session.SessionStatus;
31
32import java.util.ArrayList;
33import java.util.List;
34
35import eu.siacs.conversations.R;
36import eu.siacs.conversations.entities.Account;
37import eu.siacs.conversations.entities.Blockable;
38import eu.siacs.conversations.entities.Contact;
39import eu.siacs.conversations.entities.Conversation;
40import eu.siacs.conversations.entities.Message;
41import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
42import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate;
43import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
44import eu.siacs.conversations.ui.adapter.ConversationAdapter;
45import eu.siacs.conversations.utils.ExceptionHelper;
46import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
47
48public class ConversationActivity extends XmppActivity
49 implements OnAccountUpdate, OnConversationUpdate, OnRosterUpdate, OnUpdateBlocklist {
50
51 public static final String VIEW_CONVERSATION = "viewConversation";
52 public static final String CONVERSATION = "conversationUuid";
53 public static final String TEXT = "text";
54 public static final String NICK = "nick";
55 public static final String PRESENCE = "eu.siacs.conversations.presence";
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 REQUEST_ATTACH_IMAGE_DIALOG = 0x0203;
61 private static final int REQUEST_IMAGE_CAPTURE = 0x0204;
62 private static final int REQUEST_RECORD_AUDIO = 0x0205;
63 private static final int REQUEST_SEND_PGP_IMAGE = 0x0206;
64 private static final int REQUEST_ATTACH_FILE_DIALOG = 0x0208;
65 private static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301;
66 private static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302;
67 private static final int ATTACHMENT_CHOICE_CHOOSE_FILE = 0x0303;
68 private static final String STATE_OPEN_CONVERSATION = "state_open_conversation";
69 private static final String STATE_PANEL_OPEN = "state_panel_open";
70 private static final String STATE_PENDING_URI = "state_pending_uri";
71
72 private String mOpenConverstaion = null;
73 private boolean mPanelOpen = true;
74 private Uri mPendingImageUri = null;
75 private Uri mPendingFileUri = null;
76
77 private View mContentView;
78
79 private List<Conversation> conversationList = new ArrayList<>();
80 private Conversation mSelectedConversation = null;
81 private ListView listView;
82 private ConversationFragment mConversationFragment;
83
84 private ArrayAdapter<Conversation> listAdapter;
85
86 private Toast prepareFileToast;
87
88 private boolean mActivityPaused = false;
89
90
91 public List<Conversation> getConversationList() {
92 return this.conversationList;
93 }
94
95 public Conversation getSelectedConversation() {
96 return this.mSelectedConversation;
97 }
98
99 public void setSelectedConversation(Conversation conversation) {
100 this.mSelectedConversation = conversation;
101 }
102
103 public ListView getConversationListView() {
104 return this.listView;
105 }
106
107 public void showConversationsOverview() {
108 if (mContentView instanceof SlidingPaneLayout) {
109 SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
110 mSlidingPaneLayout.openPane();
111 }
112 }
113
114 @Override
115 protected String getShareableUri() {
116 Conversation conversation = getSelectedConversation();
117 if (conversation != null) {
118 return conversation.getAccount().getShareableUri();
119 } else {
120 return "";
121 }
122 }
123
124 public void hideConversationsOverview() {
125 if (mContentView instanceof SlidingPaneLayout) {
126 SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
127 mSlidingPaneLayout.closePane();
128 }
129 }
130
131 public boolean isConversationsOverviewHideable() {
132 if (mContentView instanceof SlidingPaneLayout) {
133 SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
134 return mSlidingPaneLayout.isSlideable();
135 } else {
136 return false;
137 }
138 }
139
140 public boolean isConversationsOverviewVisable() {
141 if (mContentView instanceof SlidingPaneLayout) {
142 SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
143 return mSlidingPaneLayout.isOpen();
144 } else {
145 return true;
146 }
147 }
148
149 @Override
150 protected void onCreate(Bundle savedInstanceState) {
151 super.onCreate(savedInstanceState);
152 if (savedInstanceState != null) {mOpenConverstaion = savedInstanceState.getString(
153 STATE_OPEN_CONVERSATION, null);
154 mPanelOpen = savedInstanceState.getBoolean(STATE_PANEL_OPEN, true);
155 String pending = savedInstanceState.getString(STATE_PENDING_URI, null);
156 if (pending != null) {
157 mPendingImageUri = Uri.parse(pending);
158 }
159 }
160
161 setContentView(R.layout.fragment_conversations_overview);
162
163 this.mConversationFragment = new ConversationFragment();
164 FragmentTransaction transaction = getFragmentManager().beginTransaction();
165 transaction.replace(R.id.selected_conversation, this.mConversationFragment, "conversation");
166 transaction.commit();
167
168 listView = (ListView) findViewById(R.id.list);
169 this.listAdapter = new ConversationAdapter(this, conversationList);
170 listView.setAdapter(this.listAdapter);
171
172 if (getActionBar() != null) {
173 getActionBar().setDisplayHomeAsUpEnabled(false);
174 getActionBar().setHomeButtonEnabled(false);
175 }
176
177 listView.setOnItemClickListener(new OnItemClickListener() {
178
179 @Override
180 public void onItemClick(AdapterView<?> arg0, View clickedView,
181 int position, long arg3) {
182 if (getSelectedConversation() != conversationList.get(position)) {
183 setSelectedConversation(conversationList.get(position));
184 ConversationActivity.this.mConversationFragment.reInit(getSelectedConversation());
185 }
186 hideConversationsOverview();
187 }
188 });
189 mContentView = findViewById(R.id.content_view_spl);
190 if (mContentView == null) {
191 mContentView = findViewById(R.id.content_view_ll);
192 }
193 if (mContentView instanceof SlidingPaneLayout) {
194 SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
195 mSlidingPaneLayout.setParallaxDistance(150);
196 mSlidingPaneLayout
197 .setShadowResource(R.drawable.es_slidingpane_shadow);
198 mSlidingPaneLayout.setSliderFadeColor(0);
199 mSlidingPaneLayout.setPanelSlideListener(new PanelSlideListener() {
200
201 @Override
202 public void onPanelOpened(View arg0) {
203 updateActionBarTitle();
204 invalidateOptionsMenu();
205 hideKeyboard();
206 if (xmppConnectionServiceBound) {
207 xmppConnectionService.getNotificationService()
208 .setOpenConversation(null);
209 }
210 closeContextMenu();
211 }
212
213 @Override
214 public void onPanelClosed(View arg0) {
215 openConversation();
216 }
217
218 @Override
219 public void onPanelSlide(View arg0, float arg1) {
220 // TODO Auto-generated method stub
221
222 }
223 });
224 }
225 }
226
227 @Override
228 public void switchToConversation(Conversation conversation) {
229 setSelectedConversation(conversation);
230 runOnUiThread(new Runnable() {
231 @Override
232 public void run() {
233 ConversationActivity.this.mConversationFragment.reInit(getSelectedConversation());
234 openConversation();
235 }
236 });
237 }
238
239 private void updateActionBarTitle() {
240 updateActionBarTitle(isConversationsOverviewHideable() && !isConversationsOverviewVisable());
241 }
242
243 private void updateActionBarTitle(boolean titleShouldBeName) {
244 final ActionBar ab = getActionBar();
245 final Conversation conversation = getSelectedConversation();
246 if (ab != null) {
247 if (titleShouldBeName && conversation != null) {
248 ab.setDisplayHomeAsUpEnabled(true);
249 ab.setHomeButtonEnabled(true);
250 if (conversation.getMode() == Conversation.MODE_SINGLE || useSubjectToIdentifyConference()) {
251 ab.setTitle(conversation.getName());
252 } else {
253 ab.setTitle(conversation.getJid().toBareJid().toString());
254 }
255 } else {
256 ab.setDisplayHomeAsUpEnabled(false);
257 ab.setHomeButtonEnabled(false);
258 ab.setTitle(R.string.app_name);
259 }
260 }
261 }
262
263 private void openConversation() {
264 this.updateActionBarTitle();
265 this.invalidateOptionsMenu();
266 if (xmppConnectionServiceBound) {
267 final Conversation conversation = getSelectedConversation();
268 xmppConnectionService.getNotificationService().setOpenConversation(conversation);
269 sendReadMarkerIfNecessary(conversation);
270 }
271 }
272
273 public void sendReadMarkerIfNecessary(final Conversation conversation) {
274 if (!mActivityPaused && !conversation.isRead()) {
275 xmppConnectionService.sendReadMarker(conversation);
276 }
277 }
278
279 @Override
280 public boolean onCreateOptionsMenu(Menu menu) {
281 getMenuInflater().inflate(R.menu.conversations, menu);
282 final MenuItem menuSecure = menu.findItem(R.id.action_security);
283 final MenuItem menuArchive = menu.findItem(R.id.action_archive);
284 final MenuItem menuMucDetails = menu.findItem(R.id.action_muc_details);
285 final MenuItem menuContactDetails = menu.findItem(R.id.action_contact_details);
286 final MenuItem menuAttach = menu.findItem(R.id.action_attach_file);
287 final MenuItem menuClearHistory = menu.findItem(R.id.action_clear_history);
288 final MenuItem menuAdd = menu.findItem(R.id.action_add);
289 final MenuItem menuInviteContact = menu.findItem(R.id.action_invite);
290 final MenuItem menuMute = menu.findItem(R.id.action_mute);
291 final MenuItem menuUnmute = menu.findItem(R.id.action_unmute);
292 final MenuItem menuBlock = menu.findItem(R.id.action_block);
293 final MenuItem menuUnblock = menu.findItem(R.id.action_unblock);
294
295 if (isConversationsOverviewVisable()
296 && isConversationsOverviewHideable()) {
297 menuArchive.setVisible(false);
298 menuMucDetails.setVisible(false);
299 menuContactDetails.setVisible(false);
300 menuSecure.setVisible(false);
301 menuInviteContact.setVisible(false);
302 menuAttach.setVisible(false);
303 menuClearHistory.setVisible(false);
304 menuMute.setVisible(false);
305 menuUnmute.setVisible(false);
306 menuBlock.setVisible(false);
307 menuUnblock.setVisible(false);
308 } else {
309 menuAdd.setVisible(!isConversationsOverviewHideable());
310 if (this.getSelectedConversation() != null) {
311 if (this.getSelectedConversation().getLatestMessage()
312 .getEncryption() != Message.ENCRYPTION_NONE) {
313 menuSecure.setIcon(R.drawable.ic_action_secure);
314 }
315 if (this.getSelectedConversation().getMode() == Conversation.MODE_MULTI) {
316 menuContactDetails.setVisible(false);
317 menuAttach.setVisible(false);
318 menuBlock.setVisible(false);
319 menuUnblock.setVisible(false);
320 } else {
321 menuMucDetails.setVisible(false);
322 menuInviteContact.setTitle(R.string.conference_with);
323 if (this.getSelectedConversation().isBlocked()) {
324 menuBlock.setVisible(false);
325 } else {
326 menuUnblock.setVisible(false);
327 }
328 final Account account = this.getSelectedConversation().getAccount();
329 if (!(account.isOnlineAndConnected() && account.getXmppConnection().getFeatures().blocking())) {
330 menuBlock.setVisible(false);
331 menuUnblock.setVisible(false);
332 }
333 }
334 if (this.getSelectedConversation().isMuted()) {
335 menuMute.setVisible(false);
336 } else {
337 menuUnmute.setVisible(false);
338 }
339 }
340 }
341 return true;
342 }
343
344 private void selectPresenceToAttachFile(final int attachmentChoice) {
345 selectPresence(getSelectedConversation(), new OnPresenceSelected() {
346
347 @Override
348 public void onPresenceSelected() {
349 if (attachmentChoice == ATTACHMENT_CHOICE_TAKE_PHOTO) {
350 mPendingImageUri = xmppConnectionService.getFileBackend()
351 .getTakePhotoUri();
352 Intent takePictureIntent = new Intent(
353 MediaStore.ACTION_IMAGE_CAPTURE);
354 takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
355 mPendingImageUri);
356 if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
357 startActivityForResult(takePictureIntent,
358 REQUEST_IMAGE_CAPTURE);
359 }
360 } else if (attachmentChoice == ATTACHMENT_CHOICE_CHOOSE_IMAGE) {
361 Intent attachFileIntent = new Intent();
362 attachFileIntent.setType("image/*");
363 attachFileIntent.setAction(Intent.ACTION_GET_CONTENT);
364 Intent chooser = Intent.createChooser(attachFileIntent,
365 getString(R.string.attach_file));
366 startActivityForResult(chooser, REQUEST_ATTACH_IMAGE_DIALOG);
367 } else if (attachmentChoice == ATTACHMENT_CHOICE_CHOOSE_FILE) {
368 Intent attachFileIntent = new Intent();
369 //attachFileIntent.setType("file/*");
370 attachFileIntent.setType("*/*");
371 attachFileIntent.addCategory(Intent.CATEGORY_OPENABLE);
372 attachFileIntent.setAction(Intent.ACTION_GET_CONTENT);
373 Intent chooser = Intent.createChooser(attachFileIntent,
374 getString(R.string.attach_file));
375 startActivityForResult(chooser, REQUEST_ATTACH_FILE_DIALOG);
376 }
377 }
378 });
379 }
380
381 private void attachFile(final int attachmentChoice) {
382 final Conversation conversation = getSelectedConversation();
383 if (conversation.getNextEncryption(forceEncryption()) == Message.ENCRYPTION_PGP) {
384 if (hasPgp()) {
385 if (conversation.getContact().getPgpKeyId() != 0) {
386 xmppConnectionService.getPgpEngine().hasKey(
387 conversation.getContact(),
388 new UiCallback<Contact>() {
389
390 @Override
391 public void userInputRequried(PendingIntent pi,
392 Contact contact) {
393 ConversationActivity.this.runIntent(pi,
394 attachmentChoice);
395 }
396
397 @Override
398 public void success(Contact contact) {
399 selectPresenceToAttachFile(attachmentChoice);
400 }
401
402 @Override
403 public void error(int error, Contact contact) {
404 displayErrorDialog(error);
405 }
406 });
407 } else {
408 final ConversationFragment fragment = (ConversationFragment) getFragmentManager()
409 .findFragmentByTag("conversation");
410 if (fragment != null) {
411 fragment.showNoPGPKeyDialog(false,
412 new OnClickListener() {
413
414 @Override
415 public void onClick(DialogInterface dialog,
416 int which) {
417 conversation
418 .setNextEncryption(Message.ENCRYPTION_NONE);
419 xmppConnectionService.databaseBackend
420 .updateConversation(conversation);
421 selectPresenceToAttachFile(attachmentChoice);
422 }
423 });
424 }
425 }
426 } else {
427 showInstallPgpDialog();
428 }
429 } else if (getSelectedConversation().getNextEncryption(
430 forceEncryption()) == Message.ENCRYPTION_NONE) {
431 selectPresenceToAttachFile(attachmentChoice);
432 } else {
433 selectPresenceToAttachFile(attachmentChoice);
434 }
435 }
436
437 @Override
438 public boolean onOptionsItemSelected(final MenuItem item) {
439 if (item.getItemId() == android.R.id.home) {
440 showConversationsOverview();
441 return true;
442 } else if (item.getItemId() == R.id.action_add) {
443 startActivity(new Intent(this, StartConversationActivity.class));
444 return true;
445 } else if (getSelectedConversation() != null) {
446 switch (item.getItemId()) {
447 case R.id.action_attach_file:
448 attachFileDialog();
449 break;
450 case R.id.action_archive:
451 this.endConversation(getSelectedConversation());
452 break;
453 case R.id.action_contact_details:
454 Contact contact = this.getSelectedConversation().getContact();
455 if (contact.showInRoster()) {
456 switchToContactDetails(contact);
457 } else {
458 showAddToRosterDialog(getSelectedConversation());
459 }
460 break;
461 case R.id.action_muc_details:
462 Intent intent = new Intent(this,
463 ConferenceDetailsActivity.class);
464 intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC);
465 intent.putExtra("uuid", getSelectedConversation().getUuid());
466 startActivity(intent);
467 break;
468 case R.id.action_invite:
469 inviteToConversation(getSelectedConversation());
470 break;
471 case R.id.action_security:
472 selectEncryptionDialog(getSelectedConversation());
473 break;
474 case R.id.action_clear_history:
475 clearHistoryDialog(getSelectedConversation());
476 break;
477 case R.id.action_mute:
478 muteConversationDialog(getSelectedConversation());
479 break;
480 case R.id.action_unmute:
481 unmuteConversation(getSelectedConversation());
482 break;
483 case R.id.action_block:
484 BlockContactDialog.show(this, xmppConnectionService, getSelectedConversation());
485 break;
486 case R.id.action_unblock:
487 BlockContactDialog.show(this, xmppConnectionService, getSelectedConversation());
488 break;
489 default:
490 break;
491 }
492 return super.onOptionsItemSelected(item);
493 } else {
494 return super.onOptionsItemSelected(item);
495 }
496 }
497
498 public void endConversation(Conversation conversation) {
499 conversation.setStatus(Conversation.STATUS_ARCHIVED);
500 showConversationsOverview();
501 xmppConnectionService.archiveConversation(conversation);
502 if (conversationList.size() > 0) {
503 setSelectedConversation(conversationList.get(0));
504 this.mConversationFragment.reInit(getSelectedConversation());
505 } else {
506 setSelectedConversation(null);
507 }
508 }
509
510 @SuppressLint("InflateParams")
511 protected void clearHistoryDialog(final Conversation conversation) {
512 AlertDialog.Builder builder = new AlertDialog.Builder(this);
513 builder.setTitle(getString(R.string.clear_conversation_history));
514 View dialogView = getLayoutInflater().inflate(
515 R.layout.dialog_clear_history, null);
516 final CheckBox endConversationCheckBox = (CheckBox) dialogView
517 .findViewById(R.id.end_conversation_checkbox);
518 builder.setView(dialogView);
519 builder.setNegativeButton(getString(R.string.cancel), null);
520 builder.setPositiveButton(getString(R.string.delete_messages),
521 new OnClickListener() {
522
523 @Override
524 public void onClick(DialogInterface dialog, int which) {
525 ConversationActivity.this.xmppConnectionService.clearConversationHistory(conversation);
526 if (endConversationCheckBox.isChecked()) {
527 endConversation(conversation);
528 } else {
529 updateConversationList();
530 ConversationActivity.this.mConversationFragment.updateMessages();
531 }
532 }
533 });
534 builder.create().show();
535 }
536
537 protected void attachFileDialog() {
538 View menuAttachFile = findViewById(R.id.action_attach_file);
539 if (menuAttachFile == null) {
540 return;
541 }
542 PopupMenu attachFilePopup = new PopupMenu(this, menuAttachFile);
543 attachFilePopup.inflate(R.menu.attachment_choices);
544 attachFilePopup
545 .setOnMenuItemClickListener(new OnMenuItemClickListener() {
546
547 @Override
548 public boolean onMenuItemClick(MenuItem item) {
549 switch (item.getItemId()) {
550 case R.id.attach_choose_picture:
551 attachFile(ATTACHMENT_CHOICE_CHOOSE_IMAGE);
552 break;
553 case R.id.attach_take_picture:
554 attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO);
555 break;
556 case R.id.attach_record_voice:
557 attachFile(ATTACHMENT_CHOICE_CHOOSE_FILE);
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 == REQUEST_ATTACH_IMAGE_DIALOG) {
868 mPendingImageUri = data.getData();
869 if (xmppConnectionServiceBound) {
870 attachImageToConversation(getSelectedConversation(),
871 mPendingImageUri);
872 mPendingImageUri = null;
873 }
874 } else if (requestCode == REQUEST_ATTACH_FILE_DIALOG) {
875 mPendingFileUri = data.getData();
876 if (xmppConnectionServiceBound) {
877 attachFileToConversation(getSelectedConversation(),
878 mPendingFileUri);
879 mPendingFileUri = null;
880 }
881 } else if (requestCode == REQUEST_SEND_PGP_IMAGE) {
882
883 } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_IMAGE) {
884 attachFile(ATTACHMENT_CHOICE_CHOOSE_IMAGE);
885 } else if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO) {
886 attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO);
887 } else if (requestCode == REQUEST_ANNOUNCE_PGP) {
888 announcePgp(getSelectedConversation().getAccount(),
889 getSelectedConversation());
890 } else if (requestCode == REQUEST_ENCRYPT_MESSAGE) {
891 // encryptTextMessage();
892 } else if (requestCode == REQUEST_IMAGE_CAPTURE && mPendingImageUri != null) {
893 if (xmppConnectionServiceBound) {
894 attachImageToConversation(getSelectedConversation(),
895 mPendingImageUri);
896 mPendingImageUri = null;
897 }
898 Intent intent = new Intent(
899 Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
900 intent.setData(mPendingImageUri);
901 sendBroadcast(intent);
902 }
903 } else {
904 if (requestCode == REQUEST_IMAGE_CAPTURE) {
905 mPendingImageUri = null;
906 }
907 }
908 }
909
910 private void attachFileToConversation(Conversation conversation, Uri uri) {
911 prepareFileToast = Toast.makeText(getApplicationContext(),
912 getText(R.string.preparing_file), Toast.LENGTH_LONG);
913 prepareFileToast.show();
914 xmppConnectionService.attachFileToConversation(conversation,uri, new UiCallback<Message>() {
915 @Override
916 public void success(Message message) {
917 hidePrepareFileToast();
918 xmppConnectionService.sendMessage(message);
919 }
920
921 @Override
922 public void error(int errorCode, Message message) {
923 displayErrorDialog(errorCode);
924 }
925
926 @Override
927 public void userInputRequried(PendingIntent pi, Message message) {
928
929 }
930 });
931 }
932
933 private void attachImageToConversation(Conversation conversation, Uri uri) {
934 prepareFileToast = Toast.makeText(getApplicationContext(),
935 getText(R.string.preparing_image), Toast.LENGTH_LONG);
936 prepareFileToast.show();
937 xmppConnectionService.attachImageToConversation(conversation, uri,
938 new UiCallback<Message>() {
939
940 @Override
941 public void userInputRequried(PendingIntent pi,
942 Message object) {
943 hidePrepareFileToast();
944 ConversationActivity.this.runIntent(pi,
945 ConversationActivity.REQUEST_SEND_PGP_IMAGE);
946 }
947
948 @Override
949 public void success(Message message) {
950 xmppConnectionService.sendMessage(message);
951 }
952
953 @Override
954 public void error(int error, Message message) {
955 hidePrepareFileToast();
956 displayErrorDialog(error);
957 }
958 });
959 }
960
961 private void hidePrepareFileToast() {
962 if (prepareFileToast != null) {
963 runOnUiThread(new Runnable() {
964
965 @Override
966 public void run() {
967 prepareFileToast.cancel();
968 }
969 });
970 }
971 }
972
973 public void updateConversationList() {
974 xmppConnectionService
975 .populateWithOrderedConversations(conversationList);
976 listAdapter.notifyDataSetChanged();
977 }
978
979 public void runIntent(PendingIntent pi, int requestCode) {
980 try {
981 this.startIntentSenderForResult(pi.getIntentSender(), requestCode,
982 null, 0, 0, 0);
983 } catch (final SendIntentException ignored) {
984 }
985 }
986
987 public void encryptTextMessage(Message message) {
988 xmppConnectionService.getPgpEngine().encrypt(message,
989 new UiCallback<Message>() {
990
991 @Override
992 public void userInputRequried(PendingIntent pi,
993 Message message) {
994 ConversationActivity.this.runIntent(pi,
995 ConversationActivity.REQUEST_SEND_MESSAGE);
996 }
997
998 @Override
999 public void success(Message message) {
1000 message.setEncryption(Message.ENCRYPTION_DECRYPTED);
1001 xmppConnectionService.sendMessage(message);
1002 }
1003
1004 @Override
1005 public void error(int error, Message message) {
1006
1007 }
1008 });
1009 }
1010
1011 public boolean forceEncryption() {
1012 return getPreferences().getBoolean("force_encryption", false);
1013 }
1014
1015 public boolean useSendButtonToIndicateStatus() {
1016 return getPreferences().getBoolean("send_button_status", false);
1017 }
1018
1019 public boolean indicateReceived() {
1020 return getPreferences().getBoolean("indicate_received", false);
1021 }
1022
1023 @Override
1024 public void onAccountUpdate() {
1025 runOnUiThread(new Runnable() {
1026
1027 @Override
1028 public void run() {
1029 updateConversationList();
1030 ConversationActivity.this.mConversationFragment.updateMessages();
1031 updateActionBarTitle();
1032 }
1033 });
1034 }
1035
1036 @Override
1037 public void onConversationUpdate() {
1038 runOnUiThread(new Runnable() {
1039
1040 @Override
1041 public void run() {
1042 updateConversationList();
1043 if (conversationList.size() == 0) {
1044 startActivity(new Intent(getApplicationContext(),
1045 StartConversationActivity.class));
1046 finish();
1047 }
1048 ConversationActivity.this.mConversationFragment.updateMessages();
1049 updateActionBarTitle();
1050 }
1051 });
1052 }
1053
1054 @Override
1055 public void onRosterUpdate() {
1056 runOnUiThread(new Runnable() {
1057
1058 @Override
1059 public void run() {
1060 updateConversationList();
1061 ConversationActivity.this.mConversationFragment.updateMessages();
1062 updateActionBarTitle();
1063 }
1064 });
1065 }
1066
1067 @Override
1068 public void OnUpdateBlocklist(Status status) {
1069 invalidateOptionsMenu();
1070 runOnUiThread(new Runnable() {
1071 @Override
1072 public void run() {
1073 ConversationActivity.this.mConversationFragment.updateMessages();
1074 }
1075 });
1076 }
1077
1078 public void unblockConversation(final Blockable conversation) {
1079 xmppConnectionService.sendUnblockRequest(conversation);
1080 }
1081
1082 public void blockConversation(final Blockable conversation) {
1083 xmppConnectionService.sendBlockRequest(conversation);
1084 }
1085}