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