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