ConversationActivity.java

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