ConversationActivity.java

   1package eu.siacs.conversations.ui;
   2
   3import java.io.FileNotFoundException;
   4import java.lang.ref.WeakReference;
   5import java.util.ArrayList;
   6import java.util.Hashtable;
   7import java.util.List;
   8
   9import eu.siacs.conversations.R;
  10import eu.siacs.conversations.entities.Account;
  11import eu.siacs.conversations.entities.Contact;
  12import eu.siacs.conversations.entities.Conversation;
  13import eu.siacs.conversations.entities.Message;
  14import eu.siacs.conversations.services.ImageProvider;
  15import eu.siacs.conversations.utils.ExceptionHelper;
  16import eu.siacs.conversations.utils.UIHelper;
  17import android.net.Uri;
  18import android.os.AsyncTask;
  19import android.os.Bundle;
  20import android.preference.PreferenceManager;
  21import android.provider.MediaStore;
  22import android.app.AlertDialog;
  23import android.app.FragmentTransaction;
  24import android.app.PendingIntent;
  25import android.content.Context;
  26import android.content.DialogInterface;
  27import android.content.DialogInterface.OnClickListener;
  28import android.content.IntentSender.SendIntentException;
  29import android.content.Intent;
  30import android.content.SharedPreferences;
  31import android.content.res.Resources;
  32import android.graphics.Bitmap;
  33import android.graphics.Color;
  34import android.graphics.Typeface;
  35import android.graphics.drawable.BitmapDrawable;
  36import android.graphics.drawable.Drawable;
  37import android.support.v4.widget.SlidingPaneLayout;
  38import android.support.v4.widget.SlidingPaneLayout.PanelSlideListener;
  39import android.util.DisplayMetrics;
  40import android.util.Log;
  41import android.view.KeyEvent;
  42import android.view.LayoutInflater;
  43import android.view.Menu;
  44import android.view.MenuItem;
  45import android.view.View;
  46import android.view.ViewGroup;
  47import android.widget.AdapterView;
  48import android.widget.AdapterView.OnItemClickListener;
  49import android.widget.ArrayAdapter;
  50import android.widget.CheckBox;
  51import android.widget.ListView;
  52import android.widget.PopupMenu;
  53import android.widget.PopupMenu.OnMenuItemClickListener;
  54import android.widget.TextView;
  55import android.widget.ImageView;
  56import android.widget.Toast;
  57
  58public class ConversationActivity extends XmppActivity {
  59
  60	public static final String VIEW_CONVERSATION = "viewConversation";
  61	public static final String CONVERSATION = "conversationUuid";
  62	public static final String TEXT = "text";
  63	public static final String PRESENCE = "eu.siacs.conversations.presence";
  64
  65	public static final int REQUEST_SEND_MESSAGE = 0x75441;
  66	public static final int REQUEST_DECRYPT_PGP = 0x76783;
  67	private static final int REQUEST_ATTACH_FILE_DIALOG = 0x48502;
  68	private static final int REQUEST_IMAGE_CAPTURE = 0x33788;
  69	private static final int REQUEST_SEND_PGP_IMAGE = 0x53883;
  70	private static final int REQUEST_ATTACH_FILE = 0x73824;
  71	public static final int REQUEST_ENCRYPT_MESSAGE = 0x378018;
  72
  73	protected SlidingPaneLayout spl;
  74
  75	private List<Conversation> conversationList = new ArrayList<Conversation>();
  76	private Conversation selectedConversation = null;
  77	private ListView listView;
  78	
  79	private boolean paneShouldBeOpen = true;
  80	private boolean useSubject = true;
  81	private ArrayAdapter<Conversation> listAdapter;
  82	
  83	public Message pendingMessage = null;
  84
  85	private OnConversationListChangedListener onConvChanged = new OnConversationListChangedListener() {
  86
  87		@Override
  88		public void onConversationListChanged() {
  89			runOnUiThread(new Runnable() {
  90
  91				@Override
  92				public void run() {
  93					updateConversationList();
  94					if (paneShouldBeOpen) {
  95						if (conversationList.size() >= 1) {
  96							swapConversationFragment();
  97						} else {
  98							startActivity(new Intent(getApplicationContext(),
  99									ContactsActivity.class));
 100							finish();
 101						}
 102					}
 103					ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager()
 104							.findFragmentByTag("conversation");
 105					if (selectedFragment != null) {
 106						selectedFragment.updateMessages();
 107					}
 108				}
 109			});
 110		}
 111	};
 112	
 113	protected ConversationActivity activity = this;
 114	private DisplayMetrics metrics;
 115	private Toast prepareImageToast;
 116
 117	public List<Conversation> getConversationList() {
 118		return this.conversationList;
 119	}
 120
 121	public Conversation getSelectedConversation() {
 122		return this.selectedConversation;
 123	}
 124	
 125	public void setSelectedConversation(Conversation conversation) {
 126		this.selectedConversation = conversation;
 127	}
 128
 129	public ListView getConversationListView() {
 130		return this.listView;
 131	}
 132
 133	public SlidingPaneLayout getSlidingPaneLayout() {
 134		return this.spl;
 135	}
 136
 137	public boolean shouldPaneBeOpen() {
 138		return paneShouldBeOpen;
 139	}
 140
 141	@Override
 142	protected void onCreate(Bundle savedInstanceState) {
 143
 144		metrics = getResources().getDisplayMetrics();
 145		
 146		super.onCreate(savedInstanceState);
 147
 148		setContentView(R.layout.fragment_conversations_overview);
 149
 150		listView = (ListView) findViewById(R.id.list);
 151
 152		this.listAdapter = new ArrayAdapter<Conversation>(this,
 153				R.layout.conversation_list_row, conversationList) {
 154			@Override
 155			public View getView(int position, View view, ViewGroup parent) {
 156				if (view == null) {
 157					LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 158					view = (View) inflater.inflate(
 159							R.layout.conversation_list_row, null);
 160				}
 161				Conversation conv;
 162				if (conversationList.size() > position) {
 163					conv = getItem(position);
 164				} else {
 165					return view;
 166				}
 167				if (!spl.isSlideable()) {
 168					if (conv == getSelectedConversation()) {
 169						view.setBackgroundColor(0xffdddddd);
 170					} else {
 171						view.setBackgroundColor(Color.TRANSPARENT);
 172					}
 173				} else {
 174					view.setBackgroundColor(Color.TRANSPARENT);
 175				}
 176				TextView convName = (TextView) view
 177						.findViewById(R.id.conversation_name);
 178				convName.setText(conv.getName(useSubject));
 179				TextView convLastMsg = (TextView) view
 180						.findViewById(R.id.conversation_lastmsg);
 181				ImageView imagePreview = (ImageView) view.findViewById(R.id.conversation_lastimage);
 182				
 183				Message latestMessage = conv.getLatestMessage();
 184				
 185				if (latestMessage.getType() == Message.TYPE_TEXT) {
 186					if ((latestMessage.getEncryption() != Message.ENCRYPTION_PGP)&&(latestMessage.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED)) {
 187						convLastMsg.setText(conv.getLatestMessage().getBody());
 188					} else {
 189						convLastMsg.setText(getText(R.string.encrypted_message_received));
 190					}
 191					convLastMsg.setVisibility(View.VISIBLE);
 192					imagePreview.setVisibility(View.GONE);
 193				} else if (latestMessage.getType() == Message.TYPE_IMAGE) {
 194					if (latestMessage.getStatus() >= Message.STATUS_RECIEVED) {
 195						convLastMsg.setVisibility(View.GONE);
 196						imagePreview.setVisibility(View.VISIBLE);
 197						loadBitmap(latestMessage, imagePreview);
 198					} else {
 199						convLastMsg.setVisibility(View.VISIBLE);
 200						imagePreview.setVisibility(View.GONE);
 201						if (latestMessage.getStatus() == Message.STATUS_RECEIVED_OFFER) {
 202							convLastMsg.setText(getText(R.string.image_offered_for_download));
 203						} else if (latestMessage.getStatus() == Message.STATUS_RECIEVING) {
 204							convLastMsg.setText(getText(R.string.receiving_image));
 205						} else {
 206							convLastMsg.setText("");
 207						}
 208					}
 209				}
 210				
 211				
 212
 213				if (!conv.isRead()) {
 214					convName.setTypeface(null, Typeface.BOLD);
 215					convLastMsg.setTypeface(null, Typeface.BOLD);
 216				} else {
 217					convName.setTypeface(null, Typeface.NORMAL);
 218					convLastMsg.setTypeface(null, Typeface.NORMAL);
 219				}
 220
 221				((TextView) view.findViewById(R.id.conversation_lastupdate))
 222						.setText(UIHelper.readableTimeDifference(conv
 223								.getLatestMessage().getTimeSent()));
 224
 225				ImageView profilePicture = (ImageView) view
 226						.findViewById(R.id.conversation_image);
 227				profilePicture.setImageBitmap(UIHelper.getContactPicture(
 228						conv, 56, activity.getApplicationContext(), false));
 229				
 230				return view;
 231			}
 232
 233		};
 234
 235		listView.setAdapter(this.listAdapter);
 236
 237		listView.setOnItemClickListener(new OnItemClickListener() {
 238
 239			@Override
 240			public void onItemClick(AdapterView<?> arg0, View clickedView,
 241					int position, long arg3) {
 242				paneShouldBeOpen = false;
 243				if (getSelectedConversation() != conversationList.get(position)) {
 244					setSelectedConversation(conversationList.get(position));
 245					swapConversationFragment(); // .onBackendConnected(conversationList.get(position));
 246				} else {
 247					spl.closePane();
 248				}
 249			}
 250		});
 251		spl = (SlidingPaneLayout) findViewById(R.id.slidingpanelayout);
 252		spl.setParallaxDistance(150);
 253		spl.setShadowResource(R.drawable.es_slidingpane_shadow);
 254		spl.setSliderFadeColor(0);
 255		spl.setPanelSlideListener(new PanelSlideListener() {
 256
 257			@Override
 258			public void onPanelOpened(View arg0) {
 259				paneShouldBeOpen = true;
 260				getActionBar().setDisplayHomeAsUpEnabled(false);
 261				getActionBar().setTitle(R.string.app_name);
 262				invalidateOptionsMenu();
 263				hideKeyboard();
 264			}
 265
 266			@Override
 267			public void onPanelClosed(View arg0) {
 268				paneShouldBeOpen = false;
 269				if ((conversationList.size() > 0)
 270						&& (getSelectedConversation() != null)) {
 271					getActionBar().setDisplayHomeAsUpEnabled(true);
 272					getActionBar().setTitle(
 273							getSelectedConversation().getName(useSubject));
 274					invalidateOptionsMenu();
 275					if (!getSelectedConversation().isRead()) {
 276						getSelectedConversation().markRead();
 277						UIHelper.updateNotification(getApplicationContext(),
 278								getConversationList(), null, false);
 279						listView.invalidateViews();
 280					}
 281				}
 282			}
 283
 284			@Override
 285			public void onPanelSlide(View arg0, float arg1) {
 286				// TODO Auto-generated method stub
 287
 288			}
 289		});
 290	}
 291
 292	@Override
 293	public boolean onCreateOptionsMenu(Menu menu) {
 294		getMenuInflater().inflate(R.menu.conversations, menu);
 295		MenuItem menuSecure = (MenuItem) menu.findItem(R.id.action_security);
 296		MenuItem menuArchive = (MenuItem) menu.findItem(R.id.action_archive);
 297		MenuItem menuMucDetails = (MenuItem) menu
 298				.findItem(R.id.action_muc_details);
 299		MenuItem menuContactDetails = (MenuItem) menu
 300				.findItem(R.id.action_contact_details);
 301		MenuItem menuInviteContacts = (MenuItem) menu
 302				.findItem(R.id.action_invite);
 303		MenuItem menuAttach = (MenuItem) menu.findItem(R.id.action_attach_file);
 304		MenuItem menuClearHistory = (MenuItem) menu.findItem(R.id.action_clear_history);
 305
 306		if ((spl.isOpen() && (spl.isSlideable()))) {
 307			menuArchive.setVisible(false);
 308			menuMucDetails.setVisible(false);
 309			menuContactDetails.setVisible(false);
 310			menuSecure.setVisible(false);
 311			menuInviteContacts.setVisible(false);
 312			menuAttach.setVisible(false);
 313			menuClearHistory.setVisible(false);
 314		} else {
 315			((MenuItem) menu.findItem(R.id.action_add)).setVisible(!spl
 316					.isSlideable());
 317			if (this.getSelectedConversation() != null) {
 318				if (this.getSelectedConversation().getMode() == Conversation.MODE_MULTI) {
 319					menuContactDetails.setVisible(false);
 320					menuSecure.setVisible(false);
 321					menuAttach.setVisible(false);
 322				} else {
 323					menuMucDetails.setVisible(false);
 324					menuInviteContacts.setVisible(false);
 325					if (this.getSelectedConversation().getLatestMessage()
 326							.getEncryption() != Message.ENCRYPTION_NONE) {
 327						menuSecure.setIcon(R.drawable.ic_action_secure);
 328					}
 329				}
 330			}
 331		}
 332		return true;
 333	}
 334	
 335	private void attachFileDialog() {
 336		selectPresence(getSelectedConversation(), new OnPresenceSelected() {
 337			
 338			@Override
 339			public void onPresenceSelected(boolean success, String presence) {
 340				if (success) {
 341					Intent attachFileIntent = new Intent();
 342					attachFileIntent.setType("image/*");
 343					attachFileIntent.setAction(Intent.ACTION_GET_CONTENT);
 344					Intent chooser = Intent.createChooser(attachFileIntent, getString(R.string.attach_file));
 345					startActivityForResult(chooser,	REQUEST_ATTACH_FILE_DIALOG);
 346				}
 347			}
 348
 349			@Override
 350			public void onSendPlainTextInstead() {
 351				
 352			}
 353		},"file");
 354	}
 355	
 356	private void takePicture() {
 357		selectPresence(getSelectedConversation(), new OnPresenceSelected() {
 358			
 359			@Override
 360			public void onPresenceSelected(boolean success, String presence) {
 361				if (success) {
 362					Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
 363					takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, ImageProvider.getIncomingContentUri());
 364					if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
 365						startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
 366					}
 367				}
 368			}
 369
 370			@Override
 371			public void onSendPlainTextInstead() {
 372				// TODO Auto-generated method stub
 373				
 374			}
 375		},"file");
 376
 377	}
 378
 379	private void attachFile() {
 380		final Conversation conversation = getSelectedConversation();
 381		if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
 382			if (hasPgp()) {
 383				if (conversation.getContact().getPgpKeyId()!=0) {
 384					xmppConnectionService.getPgpEngine().hasKey(conversation.getContact(), new UiCallback() {
 385						
 386						@Override
 387						public void userInputRequried(PendingIntent pi) {
 388							ConversationActivity.this.runIntent(pi, REQUEST_ATTACH_FILE);
 389						}
 390						
 391						@Override
 392						public void success() {
 393							attachFileDialog();
 394						}
 395						
 396						@Override
 397						public void error(int error) {
 398							displayErrorDialog(error);
 399							
 400						}
 401					});
 402				} else {
 403					final ConversationFragment fragment = (ConversationFragment) getFragmentManager()
 404							.findFragmentByTag("conversation");
 405					if (fragment != null) {
 406						fragment.showNoPGPKeyDialog(new OnClickListener() {
 407							
 408							@Override
 409							public void onClick(DialogInterface dialog, int which) {
 410								conversation.setNextEncryption(Message.ENCRYPTION_NONE);
 411								attachFileDialog();
 412							}
 413						});
 414					}
 415				}
 416			}
 417		} else if (getSelectedConversation().getNextEncryption() == Message.ENCRYPTION_NONE) {
 418			attachFileDialog();
 419		} else {
 420			AlertDialog.Builder builder = new AlertDialog.Builder(this);
 421			builder.setTitle(getString(R.string.otr_file_transfer));
 422			builder.setMessage(getString(R.string.otr_file_transfer_msg));
 423			builder.setNegativeButton(getString(R.string.cancel), null);
 424			if (conversation.getContact().getPgpKeyId()==0) {
 425				builder.setPositiveButton(getString(R.string.send_unencrypted), new OnClickListener() {
 426					
 427					@Override
 428					public void onClick(DialogInterface dialog, int which) {
 429						conversation.setNextEncryption(Message.ENCRYPTION_NONE);
 430						attachFile();
 431					}
 432				});
 433			} else {
 434				builder.setPositiveButton(getString(R.string.use_pgp_encryption), new OnClickListener() {
 435					
 436					@Override
 437					public void onClick(DialogInterface dialog, int which) {
 438						conversation.setNextEncryption(Message.ENCRYPTION_PGP);
 439						attachFile();
 440					}
 441				});
 442			}
 443			builder.create().show();
 444		}
 445	}
 446	
 447	@Override
 448	public boolean onOptionsItemSelected(MenuItem item) {
 449		switch (item.getItemId()) {
 450		case android.R.id.home:
 451			spl.openPane();
 452			break;
 453		case R.id.action_attach_file:
 454			View menuAttachFile = findViewById(R.id.action_attach_file);
 455			PopupMenu attachFilePopup = new PopupMenu(this, menuAttachFile);
 456			attachFilePopup.inflate(R.menu.attachment_choices);
 457			attachFilePopup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
 458				
 459				@Override
 460				public boolean onMenuItemClick(MenuItem item) {
 461					switch (item.getItemId()) {
 462					case R.id.attach_choose_picture:
 463						attachFile();
 464						break;
 465					case R.id.attach_take_picture:
 466						takePicture();
 467						break;
 468					}
 469					return false;
 470				}
 471			});
 472			attachFilePopup.show();
 473			break;
 474		case R.id.action_add:
 475			startActivity(new Intent(this, ContactsActivity.class));
 476			break;
 477		case R.id.action_archive:
 478			this.endConversation(getSelectedConversation());
 479			break;
 480		case R.id.action_contact_details:
 481			Contact contact = this.getSelectedConversation().getContact();
 482			if (contact != null) {
 483				Intent intent = new Intent(this, ContactDetailsActivity.class);
 484				intent.setAction(ContactDetailsActivity.ACTION_VIEW_CONTACT);
 485				intent.putExtra("uuid", contact.getUuid());
 486				startActivity(intent);
 487			} else {
 488				showAddToRosterDialog(getSelectedConversation());
 489			}
 490			break;
 491		case R.id.action_muc_details:
 492			Intent intent = new Intent(this, MucDetailsActivity.class);
 493			intent.setAction(MucDetailsActivity.ACTION_VIEW_MUC);
 494			intent.putExtra("uuid", getSelectedConversation().getUuid());
 495			startActivity(intent);
 496			break;
 497		case R.id.action_invite:
 498			Intent inviteIntent = new Intent(getApplicationContext(),
 499					ContactsActivity.class);
 500			inviteIntent.setAction("invite");
 501			inviteIntent.putExtra("uuid", getSelectedConversation().getUuid());
 502			startActivity(inviteIntent);
 503			break;
 504		case R.id.action_security:
 505			final Conversation conversation = getSelectedConversation();
 506			View menuItemView = findViewById(R.id.action_security);
 507			PopupMenu popup = new PopupMenu(this, menuItemView);
 508			final ConversationFragment fragment = (ConversationFragment) getFragmentManager()
 509					.findFragmentByTag("conversation");
 510			if (fragment != null) {
 511				popup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
 512
 513					@Override
 514					public boolean onMenuItemClick(MenuItem item) {
 515						switch (item.getItemId()) {
 516						case R.id.encryption_choice_none:
 517							conversation.setNextEncryption(Message.ENCRYPTION_NONE);
 518							item.setChecked(true);
 519							break;
 520						case R.id.encryption_choice_otr:
 521							conversation.setNextEncryption(Message.ENCRYPTION_OTR);
 522							item.setChecked(true);
 523							break;
 524						case R.id.encryption_choice_pgp:
 525							if (hasPgp()) {
 526								if (conversation.getAccount().getKeys().has("pgp_signature")) {
 527									conversation.setNextEncryption(Message.ENCRYPTION_PGP);
 528									item.setChecked(true);
 529								} else {
 530									announcePgp(conversation.getAccount(),conversation);
 531								}
 532							}
 533							break;
 534						default:
 535							conversation.setNextEncryption(Message.ENCRYPTION_NONE);
 536							break;
 537						}
 538						fragment.updateChatMsgHint();
 539						return true;
 540					}
 541				});
 542				popup.inflate(R.menu.encryption_choices);
 543				switch (conversation.getNextEncryption()) {
 544				case Message.ENCRYPTION_NONE:
 545					popup.getMenu().findItem(R.id.encryption_choice_none)
 546							.setChecked(true);
 547					break;
 548				case Message.ENCRYPTION_OTR:
 549					popup.getMenu().findItem(R.id.encryption_choice_otr)
 550							.setChecked(true);
 551					break;
 552				case Message.ENCRYPTION_PGP:
 553					popup.getMenu().findItem(R.id.encryption_choice_pgp)
 554							.setChecked(true);
 555					break;
 556				default:
 557					popup.getMenu().findItem(R.id.encryption_choice_none)
 558							.setChecked(true);
 559					break;
 560				}
 561				popup.show();
 562			}
 563
 564			break;
 565		case R.id.action_clear_history:
 566			clearHistoryDialog(getSelectedConversation());
 567			break;
 568		default:
 569			break;
 570		}
 571		return super.onOptionsItemSelected(item);
 572	}
 573	
 574	private void endConversation(Conversation conversation) {
 575		conversation.setStatus(Conversation.STATUS_ARCHIVED);
 576		paneShouldBeOpen = true;
 577		spl.openPane();
 578		xmppConnectionService.archiveConversation(conversation);
 579		if (conversationList.size() > 0) {
 580			setSelectedConversation(conversationList.get(0));
 581		} else {
 582			setSelectedConversation(null);
 583		}
 584	}
 585
 586	protected void clearHistoryDialog(final Conversation conversation) {
 587		AlertDialog.Builder builder = new AlertDialog.Builder(this);
 588		builder.setTitle(getString(R.string.clear_conversation_history));
 589		View dialogView = getLayoutInflater().inflate(R.layout.dialog_clear_history, null);
 590		final CheckBox endConversationCheckBox = (CheckBox) dialogView.findViewById(R.id.end_conversation_checkbox);
 591		builder.setView(dialogView);
 592		builder.setNegativeButton(getString(R.string.cancel), null);
 593		builder.setPositiveButton(getString(R.string.delete_messages), new OnClickListener() {
 594			
 595			@Override
 596			public void onClick(DialogInterface dialog, int which) {
 597				activity.xmppConnectionService.clearConversationHistory(conversation);
 598				if (endConversationCheckBox.isChecked()) {
 599					endConversation(conversation);
 600				}
 601			}
 602		});
 603		builder.create().show();
 604	}
 605
 606	protected ConversationFragment swapConversationFragment() {
 607		ConversationFragment selectedFragment = new ConversationFragment();
 608
 609		FragmentTransaction transaction = getFragmentManager()
 610				.beginTransaction();
 611		transaction.replace(R.id.selected_conversation, selectedFragment,
 612				"conversation");
 613		transaction.commit();
 614		return selectedFragment;
 615	}
 616
 617	@Override
 618	public boolean onKeyDown(int keyCode, KeyEvent event) {
 619		if (keyCode == KeyEvent.KEYCODE_BACK) {
 620			if (!spl.isOpen()) {
 621				spl.openPane();
 622				return false;
 623			}
 624		}
 625		return super.onKeyDown(keyCode, event);
 626	}
 627
 628	@Override
 629	public void onStart() {
 630		super.onStart();
 631		SharedPreferences preferences = PreferenceManager
 632				.getDefaultSharedPreferences(this);
 633		this.useSubject = preferences.getBoolean("use_subject_in_muc", true);
 634		if (this.xmppConnectionServiceBound) {
 635			this.onBackendConnected();
 636		}
 637		if (conversationList.size() >= 1) {
 638			onConvChanged.onConversationListChanged();
 639		}
 640	}
 641
 642	@Override
 643	protected void onStop() {
 644		if (xmppConnectionServiceBound) {
 645			xmppConnectionService.removeOnConversationListChangedListener();
 646		}
 647		super.onStop();
 648	}
 649
 650	@Override
 651	void onBackendConnected() {
 652		this.registerListener();
 653		if (conversationList.size() == 0) {
 654			updateConversationList();
 655		}
 656
 657		if ((getIntent().getAction() != null)
 658				&& (getIntent().getAction().equals(Intent.ACTION_VIEW) && (!handledViewIntent))) {
 659			if (getIntent().getType().equals(
 660					ConversationActivity.VIEW_CONVERSATION)) {
 661				handledViewIntent = true;
 662
 663				String convToView = (String) getIntent().getExtras().get(
 664						CONVERSATION);
 665
 666				for (int i = 0; i < conversationList.size(); ++i) {
 667					if (conversationList.get(i).getUuid().equals(convToView)) {
 668						setSelectedConversation(conversationList.get(i));
 669					}
 670				}
 671				paneShouldBeOpen = false;
 672				String text = getIntent().getExtras().getString(TEXT, null);
 673				swapConversationFragment().setText(text);
 674			}
 675		} else {
 676			if (xmppConnectionService.getAccounts().size() == 0) {
 677				startActivity(new Intent(this, ManageAccountActivity.class));
 678				finish();
 679			} else if (conversationList.size() <= 0) {
 680				// add no history
 681				startActivity(new Intent(this, ContactsActivity.class));
 682				finish();
 683			} else {
 684				spl.openPane();
 685				// find currently loaded fragment
 686				ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager()
 687						.findFragmentByTag("conversation");
 688				if (selectedFragment != null) {
 689					selectedFragment.onBackendConnected();
 690				} else {
 691					setSelectedConversation(conversationList.get(0));
 692					swapConversationFragment();
 693				}
 694				ExceptionHelper.checkForCrash(this, this.xmppConnectionService);
 695			}
 696		}
 697	}
 698
 699	public void registerListener() {
 700		if (xmppConnectionServiceBound) {
 701			xmppConnectionService
 702					.setOnConversationListChangedListener(this.onConvChanged);
 703		}
 704	}
 705
 706	@Override
 707	protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
 708		super.onActivityResult(requestCode, resultCode, data);
 709		if (resultCode == RESULT_OK) {
 710			if (requestCode == REQUEST_DECRYPT_PGP) {
 711				ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager()
 712						.findFragmentByTag("conversation");
 713				if (selectedFragment != null) {
 714					selectedFragment.hidePgpPassphraseBox();
 715				}
 716			} else if (requestCode == REQUEST_ATTACH_FILE_DIALOG) {
 717				prepareImageToast = Toast.makeText(getApplicationContext(), getText(R.string.preparing_image), Toast.LENGTH_LONG);
 718				final Conversation conversation = getSelectedConversation();
 719				if (conversation.getNextEncryption() == Message.ENCRYPTION_NONE) {
 720					prepareImageToast.show();
 721					this.pendingMessage = xmppConnectionService.attachImageToConversation(conversation, data.getData(),new UiCallback() {
 722						
 723						@Override
 724						public void userInputRequried(PendingIntent pi) {
 725							
 726						}
 727						
 728						@Override
 729						public void success() {
 730							sendPendingImageMessage();
 731						}
 732						
 733						@Override
 734						public void error(int error) {
 735							pendingMessage = null;
 736							displayErrorDialog(error);
 737						}
 738					});
 739				} else if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
 740					prepareImageToast.show();
 741					attachPgpFile(conversation,data.getData());
 742				} else {
 743					Log.d(LOGTAG,"unknown next message encryption: "+conversation.getNextEncryption());
 744				}
 745			} else if (requestCode == REQUEST_SEND_PGP_IMAGE) {
 746				
 747			} else if (requestCode == REQUEST_ATTACH_FILE) {
 748				attachFile();
 749			} else if (requestCode == REQUEST_ANNOUNCE_PGP) {
 750				announcePgp(getSelectedConversation().getAccount(),getSelectedConversation());
 751			} else if (requestCode == REQUEST_ENCRYPT_MESSAGE) {
 752				encryptTextMessage();
 753			} else if (requestCode == REQUEST_IMAGE_CAPTURE) {
 754				this.pendingMessage = xmppConnectionService.attachImageToConversation(getSelectedConversation(), null, new UiCallback() {
 755					
 756					@Override
 757					public void userInputRequried(PendingIntent pi) {
 758						// TODO Auto-generated method stub
 759						
 760					}
 761					
 762					@Override
 763					public void success() {
 764						sendPendingImageMessage();
 765					}
 766					
 767					@Override
 768					public void error(int errorCode) {
 769						// TODO Auto-generated method stub
 770						
 771					}
 772				});
 773			} else {
 774				Log.d(LOGTAG,"unknown result code:"+requestCode);
 775			}
 776		}
 777	}
 778	
 779	private void attachPgpFile(Conversation conversation, Uri uri) {
 780			pendingMessage = xmppConnectionService.attachImageToConversation(conversation, uri, new UiCallback() {
 781				
 782				@Override
 783				public void userInputRequried(PendingIntent pi) {
 784					ConversationActivity.this.runIntent(pi, ConversationActivity.REQUEST_SEND_PGP_IMAGE);
 785				}
 786				
 787				@Override
 788				public void success() {
 789					sendPendingImageMessage();
 790				}
 791				
 792				@Override
 793				public void error(int error) {
 794					pendingMessage = null;
 795					displayErrorDialog(error);
 796				}
 797			});
 798	}
 799
 800	private void sendPendingImageMessage() {
 801		pendingMessage.getConversation().getMessages().add(pendingMessage);
 802		xmppConnectionService.databaseBackend.createMessage(pendingMessage);
 803		xmppConnectionService.sendMessage(pendingMessage, null);
 804		xmppConnectionService.updateUi(pendingMessage.getConversation(), false);
 805		pendingMessage = null;
 806	}
 807	
 808	public void updateConversationList() {
 809		conversationList.clear();
 810		conversationList.addAll(xmppConnectionService.getConversations());
 811		listView.invalidateViews();
 812	}
 813	
 814	public void selectPresence(final Conversation conversation, final OnPresenceSelected listener, String reason) {
 815		Account account = conversation.getAccount();
 816		if (account.getStatus() != Account.STATUS_ONLINE) {
 817			AlertDialog.Builder builder = new AlertDialog.Builder(this);
 818			builder.setTitle(getString(R.string.not_connected));
 819			builder.setIconAttribute(android.R.attr.alertDialogIcon);
 820			if ("otr".equals(reason)) {
 821				builder.setMessage(getString(R.string.you_are_offline,getString(R.string.otr_messages)));
 822			} else if ("file".equals(reason)) {
 823				builder.setMessage(getString(R.string.you_are_offline,getString(R.string.files)));
 824			} else {
 825				builder.setMessage(getString(R.string.you_are_offline_blank));
 826			}
 827			builder.setNegativeButton(getString(R.string.cancel), null);
 828			builder.setPositiveButton(getString(R.string.manage_account), new OnClickListener() {
 829				
 830				@Override
 831				public void onClick(DialogInterface dialog, int which) {
 832					startActivity(new Intent(activity, ManageAccountActivity.class));
 833				}
 834			});
 835			builder.create().show();
 836			listener.onPresenceSelected(false, null);
 837		} else {
 838			Contact contact = conversation.getContact();
 839			if (contact==null) {
 840				showAddToRosterDialog(conversation);
 841				listener.onPresenceSelected(false,null);
 842			} else {
 843				Hashtable<String, Integer> presences = contact.getPresences();
 844				if (presences.size() == 0) {
 845					AlertDialog.Builder builder = new AlertDialog.Builder(this);
 846					builder.setTitle(getString(R.string.contact_offline));
 847					if ("otr".equals(reason)) {
 848						builder.setMessage(getString(R.string.contact_offline_otr));
 849						builder.setPositiveButton(getString(R.string.send_unencrypted), new OnClickListener() {
 850							
 851							@Override
 852							public void onClick(DialogInterface dialog, int which) {
 853								listener.onSendPlainTextInstead();
 854							}
 855						});
 856					} else if ("file".equals(reason)) {
 857						builder.setMessage(getString(R.string.contact_offline_file));
 858					}
 859					builder.setIconAttribute(android.R.attr.alertDialogIcon);
 860					builder.setNegativeButton(getString(R.string.cancel), null);
 861					builder.create().show();
 862					listener.onPresenceSelected(false, null);
 863				} else if (presences.size() == 1) {
 864					String presence = (String) presences.keySet().toArray()[0];
 865					conversation.setNextPresence(presence);
 866					listener.onPresenceSelected(true, presence);
 867				} else {
 868					AlertDialog.Builder builder = new AlertDialog.Builder(this);
 869					builder.setTitle(getString(R.string.choose_presence));
 870					final String[] presencesArray = new String[presences.size()];
 871					presences.keySet().toArray(presencesArray);
 872					builder.setItems(presencesArray,
 873							new DialogInterface.OnClickListener() {
 874	
 875								@Override
 876								public void onClick(DialogInterface dialog,
 877										int which) {
 878									String presence = presencesArray[which];
 879									conversation.setNextPresence(presence);
 880									listener.onPresenceSelected(true,presence);
 881								}
 882							});
 883					builder.create().show();
 884				}
 885			}
 886		}
 887	}
 888	
 889	private void showAddToRosterDialog(final Conversation conversation) {
 890		String jid = conversation.getContactJid();
 891		AlertDialog.Builder builder = new AlertDialog.Builder(this);
 892		builder.setTitle(jid);
 893		builder.setMessage(getString(R.string.not_in_roster));
 894		builder.setNegativeButton(getString(R.string.cancel), null);
 895		builder.setPositiveButton(getString(R.string.add_contact), new DialogInterface.OnClickListener() {
 896
 897			@Override
 898			public void onClick(DialogInterface dialog, int which) {
 899				String jid = conversation.getContactJid();
 900				Account account = getSelectedConversation().getAccount();
 901				String name = jid.split("@")[0];
 902				Contact contact = new Contact(account, name, jid, null);
 903				xmppConnectionService.createContact(contact);
 904			}
 905		});
 906		builder.create().show();
 907	}
 908	
 909	public void runIntent(PendingIntent pi, int requestCode) {
 910		try {
 911			this.startIntentSenderForResult(pi.getIntentSender(),requestCode, null, 0,
 912					0, 0);
 913		} catch (SendIntentException e1) {
 914			Log.d("xmppService","failed to start intent to send message");
 915		}
 916	}
 917	
 918	
 919	class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
 920	    private final WeakReference<ImageView> imageViewReference;
 921	    private Message message = null;
 922
 923	    public BitmapWorkerTask(ImageView imageView) {
 924	        imageViewReference = new WeakReference<ImageView>(imageView);
 925	    }
 926
 927	    @Override
 928	    protected Bitmap doInBackground(Message... params) {
 929	        message = params[0];
 930	        try {
 931				return xmppConnectionService.getFileBackend().getThumbnail(message, (int) (metrics.density * 288),false);
 932			} catch (FileNotFoundException e) {
 933				Log.d("xmppService","file not found!");
 934				return null;
 935			}
 936	    }
 937
 938	    @Override
 939	    protected void onPostExecute(Bitmap bitmap) {
 940	        if (imageViewReference != null && bitmap != null) {
 941	            final ImageView imageView = imageViewReference.get();
 942	            if (imageView != null) {
 943	                imageView.setImageBitmap(bitmap);
 944	                imageView.setBackgroundColor(0x00000000);
 945	            }
 946	        }
 947	    }
 948	}
 949	
 950	public void loadBitmap(Message message, ImageView imageView) {
 951		Bitmap bm;
 952		try {
 953			bm = xmppConnectionService.getFileBackend().getThumbnail(message, (int) (metrics.density * 288), true);
 954		} catch (FileNotFoundException e) {
 955			bm = null;
 956		}
 957		if (bm!=null) {
 958			imageView.setImageBitmap(bm);
 959			imageView.setBackgroundColor(0x00000000);
 960		} else {
 961		    if (cancelPotentialWork(message, imageView)) {
 962		    	imageView.setBackgroundColor(0xff333333);
 963		        final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
 964		        final AsyncDrawable asyncDrawable =
 965		                new AsyncDrawable(getResources(), null, task);
 966		        imageView.setImageDrawable(asyncDrawable);
 967		        task.execute(message);
 968		    }
 969		}
 970	}
 971	
 972	public static boolean cancelPotentialWork(Message message, ImageView imageView) {
 973	    final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
 974
 975	    if (bitmapWorkerTask != null) {
 976	        final Message oldMessage = bitmapWorkerTask.message;
 977	        if (oldMessage == null || message != oldMessage) {
 978	            bitmapWorkerTask.cancel(true);
 979	        } else {
 980	            return false;
 981	        }
 982	    }
 983	    return true;
 984	}
 985	
 986	private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
 987		   if (imageView != null) {
 988		       final Drawable drawable = imageView.getDrawable();
 989		       if (drawable instanceof AsyncDrawable) {
 990		           final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
 991		           return asyncDrawable.getBitmapWorkerTask();
 992		       }
 993		    }
 994		    return null;
 995	}
 996	
 997	static class AsyncDrawable extends BitmapDrawable {
 998	    private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
 999
1000	    public AsyncDrawable(Resources res, Bitmap bitmap,
1001	            BitmapWorkerTask bitmapWorkerTask) {
1002	        super(res, bitmap);
1003	        bitmapWorkerTaskReference =
1004	            new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
1005	    }
1006
1007	    public BitmapWorkerTask getBitmapWorkerTask() {
1008	        return bitmapWorkerTaskReference.get();
1009	    }
1010	}
1011
1012	public void encryptTextMessage() {
1013		xmppConnectionService.getPgpEngine().encrypt(this.pendingMessage, new UiCallback() {
1014
1015					@Override
1016					public void userInputRequried(
1017							PendingIntent pi) {
1018						activity.runIntent(
1019								pi,
1020								ConversationActivity.REQUEST_SEND_MESSAGE);
1021					}
1022
1023					@Override
1024					public void success() {
1025						xmppConnectionService.sendMessage(pendingMessage, null);
1026						pendingMessage = null;
1027						ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager()
1028								.findFragmentByTag("conversation");
1029						if (selectedFragment != null) {
1030							selectedFragment.clearInputField();
1031						}
1032					}
1033
1034					@Override
1035					public void error(int error) {
1036
1037					}
1038				});
1039	}
1040}