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