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