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(getContext(), 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					menuAttach.setVisible(false);
 325				} else {
 326					menuMucDetails.setVisible(false);
 327					menuInviteContacts.setVisible(false);
 328					if (this.getSelectedConversation().getLatestMessage()
 329							.getEncryption() != Message.ENCRYPTION_NONE) {
 330						menuSecure.setIcon(R.drawable.ic_action_secure);
 331					}
 332				}
 333			}
 334		}
 335		return true;
 336	}
 337	
 338	private void selectPresenceToAttachFile(final int attachmentChoice) {
 339		selectPresence(getSelectedConversation(), new OnPresenceSelected() {
 340			
 341			@Override
 342			public void onPresenceSelected(boolean success, String presence) {
 343				if (success) {
 344					if (attachmentChoice==ATTACHMENT_CHOICE_TAKE_PHOTO) {
 345						Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
 346						takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, ImageProvider.getIncomingContentUri());
 347						if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
 348							startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
 349						}
 350					} else if (attachmentChoice==ATTACHMENT_CHOICE_CHOOSE_IMAGE) {
 351						Intent attachFileIntent = new Intent();
 352						attachFileIntent.setType("image/*");
 353						attachFileIntent.setAction(Intent.ACTION_GET_CONTENT);
 354						Intent chooser = Intent.createChooser(attachFileIntent, getString(R.string.attach_file));
 355						startActivityForResult(chooser,	REQUEST_ATTACH_FILE_DIALOG);
 356					} else if (attachmentChoice==ATTACHMENT_CHOICE_RECORD_VOICE) {
 357						Intent intent = new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
 358						startActivityForResult(intent, REQUEST_RECORD_AUDIO);
 359					}
 360				}
 361			}
 362
 363			@Override
 364			public void onSendPlainTextInstead() {
 365				// TODO Auto-generated method stub
 366				
 367			}
 368		},"file");
 369	}
 370
 371	private void attachFile(final int attachmentChoice) {
 372		final Conversation conversation = getSelectedConversation();
 373		if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
 374			if (hasPgp()) {
 375				if (conversation.getContact().getPgpKeyId()!=0) {
 376					xmppConnectionService.getPgpEngine().hasKey(conversation.getContact(), new UiCallback() {
 377						
 378						@Override
 379						public void userInputRequried(PendingIntent pi) {
 380							ConversationActivity.this.runIntent(pi, attachmentChoice);
 381						}
 382						
 383						@Override
 384						public void success() {
 385							selectPresenceToAttachFile(attachmentChoice);
 386						}
 387						
 388						@Override
 389						public void error(int error) {
 390							displayErrorDialog(error);
 391						}
 392					});
 393				} else {
 394					final ConversationFragment fragment = (ConversationFragment) getFragmentManager()
 395							.findFragmentByTag("conversation");
 396					if (fragment != null) {
 397						fragment.showNoPGPKeyDialog(new OnClickListener() {
 398							
 399							@Override
 400							public void onClick(DialogInterface dialog, int which) {
 401								conversation.setNextEncryption(Message.ENCRYPTION_NONE);
 402								selectPresenceToAttachFile(attachmentChoice);
 403							}
 404						});
 405					}
 406				}
 407			}
 408		} else if (getSelectedConversation().getNextEncryption() == Message.ENCRYPTION_NONE) {
 409			selectPresenceToAttachFile(attachmentChoice);
 410		} else {
 411			AlertDialog.Builder builder = new AlertDialog.Builder(this);
 412			builder.setTitle(getString(R.string.otr_file_transfer));
 413			builder.setMessage(getString(R.string.otr_file_transfer_msg));
 414			builder.setNegativeButton(getString(R.string.cancel), null);
 415			if (conversation.getContact().getPgpKeyId()==0) {
 416				builder.setPositiveButton(getString(R.string.send_unencrypted), new OnClickListener() {
 417					
 418					@Override
 419					public void onClick(DialogInterface dialog, int which) {
 420						conversation.setNextEncryption(Message.ENCRYPTION_NONE);
 421						attachFile(attachmentChoice);
 422					}
 423				});
 424			} else {
 425				builder.setPositiveButton(getString(R.string.use_pgp_encryption), new OnClickListener() {
 426					
 427					@Override
 428					public void onClick(DialogInterface dialog, int which) {
 429						conversation.setNextEncryption(Message.ENCRYPTION_PGP);
 430						attachFile(attachmentChoice);
 431					}
 432				});
 433			}
 434			builder.create().show();
 435		}
 436	}
 437	
 438	@Override
 439	public boolean onOptionsItemSelected(MenuItem item) {
 440		switch (item.getItemId()) {
 441		case android.R.id.home:
 442			spl.openPane();
 443			break;
 444		case R.id.action_attach_file:
 445			View menuAttachFile = findViewById(R.id.action_attach_file);
 446			PopupMenu attachFilePopup = new PopupMenu(this, menuAttachFile);
 447			attachFilePopup.inflate(R.menu.attachment_choices);
 448			attachFilePopup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
 449				
 450				@Override
 451				public boolean onMenuItemClick(MenuItem item) {
 452					switch (item.getItemId()) {
 453					case R.id.attach_choose_picture:
 454						attachFile(ATTACHMENT_CHOICE_CHOOSE_IMAGE);
 455						break;
 456					case R.id.attach_take_picture:
 457						attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO);
 458						break;
 459					case R.id.attach_record_voice:
 460						attachFile(ATTACHMENT_CHOICE_RECORD_VOICE);
 461						break;
 462					}
 463					return false;
 464				}
 465			});
 466			attachFilePopup.show();
 467			break;
 468		case R.id.action_add:
 469			startActivity(new Intent(this, ContactsActivity.class));
 470			break;
 471		case R.id.action_archive:
 472			this.endConversation(getSelectedConversation());
 473			break;
 474		case R.id.action_contact_details:
 475			Contact contact = this.getSelectedConversation().getContact();
 476			if (contact.showInRoster()) {
 477				Intent intent = new Intent(this, ContactDetailsActivity.class);
 478				intent.setAction(ContactDetailsActivity.ACTION_VIEW_CONTACT);
 479				intent.putExtra("account", this.getSelectedConversation().getAccount().getJid());
 480				intent.putExtra("contact",contact.getJid());
 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				MenuItem otr = popup.getMenu().findItem(R.id.encryption_choice_otr);
 539				if (conversation.getMode() == Conversation.MODE_MULTI) {
 540					otr.setVisible(false);
 541				}
 542				switch (conversation.getNextEncryption()) {
 543				case Message.ENCRYPTION_NONE:
 544					popup.getMenu().findItem(R.id.encryption_choice_none)
 545							.setChecked(true);
 546					break;
 547				case Message.ENCRYPTION_OTR:
 548					otr.setChecked(true);
 549					break;
 550				case Message.ENCRYPTION_PGP:
 551					popup.getMenu().findItem(R.id.encryption_choice_pgp)
 552							.setChecked(true);
 553					break;
 554				default:
 555					popup.getMenu().findItem(R.id.encryption_choice_none)
 556							.setChecked(true);
 557					break;
 558				}
 559				popup.show();
 560			}
 561
 562			break;
 563		case R.id.action_clear_history:
 564			clearHistoryDialog(getSelectedConversation());
 565			break;
 566		default:
 567			break;
 568		}
 569		return super.onOptionsItemSelected(item);
 570	}
 571	
 572	private void endConversation(Conversation conversation) {
 573		conversation.setStatus(Conversation.STATUS_ARCHIVED);
 574		paneShouldBeOpen = true;
 575		spl.openPane();
 576		xmppConnectionService.archiveConversation(conversation);
 577		if (conversationList.size() > 0) {
 578			setSelectedConversation(conversationList.get(0));
 579		} else {
 580			setSelectedConversation(null);
 581		}
 582	}
 583
 584	protected void clearHistoryDialog(final Conversation conversation) {
 585		AlertDialog.Builder builder = new AlertDialog.Builder(this);
 586		builder.setTitle(getString(R.string.clear_conversation_history));
 587		View dialogView = getLayoutInflater().inflate(R.layout.dialog_clear_history, null);
 588		final CheckBox endConversationCheckBox = (CheckBox) dialogView.findViewById(R.id.end_conversation_checkbox);
 589		builder.setView(dialogView);
 590		builder.setNegativeButton(getString(R.string.cancel), null);
 591		builder.setPositiveButton(getString(R.string.delete_messages), new OnClickListener() {
 592			
 593			@Override
 594			public void onClick(DialogInterface dialog, int which) {
 595				activity.xmppConnectionService.clearConversationHistory(conversation);
 596				if (endConversationCheckBox.isChecked()) {
 597					endConversation(conversation);
 598				}
 599			}
 600		});
 601		builder.create().show();
 602	}
 603
 604	protected ConversationFragment swapConversationFragment() {
 605		ConversationFragment selectedFragment = new ConversationFragment();
 606
 607		FragmentTransaction transaction = getFragmentManager()
 608				.beginTransaction();
 609		transaction.replace(R.id.selected_conversation, selectedFragment,
 610				"conversation");
 611		transaction.commit();
 612		return selectedFragment;
 613	}
 614
 615	@Override
 616	public boolean onKeyDown(int keyCode, KeyEvent event) {
 617		if (keyCode == KeyEvent.KEYCODE_BACK) {
 618			if (!spl.isOpen()) {
 619				spl.openPane();
 620				return false;
 621			}
 622		}
 623		return super.onKeyDown(keyCode, event);
 624	}
 625
 626	@Override
 627	protected void onNewIntent (Intent intent) {
 628		if ((Intent.ACTION_VIEW.equals(intent.getAction())&&(VIEW_CONVERSATION.equals(intent.getType())))) {
 629			String convToView = (String) intent.getExtras().get(
 630					CONVERSATION);
 631
 632			for (int i = 0; i < conversationList.size(); ++i) {
 633				if (conversationList.get(i).getUuid().equals(convToView)) {
 634					setSelectedConversation(conversationList.get(i));
 635				}
 636			}
 637			paneShouldBeOpen = false;
 638			String text = intent.getExtras().getString(TEXT, null);
 639			swapConversationFragment().setText(text);
 640		}
 641	}
 642	
 643	@Override
 644	public void onStart() {
 645		super.onStart();
 646		SharedPreferences preferences = PreferenceManager
 647				.getDefaultSharedPreferences(this);
 648		this.useSubject = preferences.getBoolean("use_subject_in_muc", true);
 649		if (this.xmppConnectionServiceBound) {
 650			this.onBackendConnected();
 651		}
 652		if (conversationList.size() >= 1) {
 653			onConvChanged.onConversationListChanged();
 654		}
 655	}
 656
 657	@Override
 658	protected void onStop() {
 659		if (xmppConnectionServiceBound) {
 660			xmppConnectionService.removeOnConversationListChangedListener();
 661		}
 662		super.onStop();
 663	}
 664
 665	@Override
 666	void onBackendConnected() {
 667		this.registerListener();
 668		if (conversationList.size() == 0) {
 669			updateConversationList();
 670		}
 671
 672		if ((getIntent().getAction() != null)
 673				&& (getIntent().getAction().equals(Intent.ACTION_VIEW) && (!handledViewIntent))) {
 674			if (getIntent().getType().equals(
 675					ConversationActivity.VIEW_CONVERSATION)) {
 676				handledViewIntent = true;
 677
 678				String convToView = (String) getIntent().getExtras().get(
 679						CONVERSATION);
 680
 681				for (int i = 0; i < conversationList.size(); ++i) {
 682					if (conversationList.get(i).getUuid().equals(convToView)) {
 683						setSelectedConversation(conversationList.get(i));
 684					}
 685				}
 686				paneShouldBeOpen = false;
 687				String text = getIntent().getExtras().getString(TEXT, null);
 688				swapConversationFragment().setText(text);
 689			}
 690		} else {
 691			if (xmppConnectionService.getAccounts().size() == 0) {
 692				startActivity(new Intent(this, ManageAccountActivity.class));
 693				finish();
 694			} else if (conversationList.size() <= 0) {
 695				// add no history
 696				startActivity(new Intent(this, ContactsActivity.class));
 697				finish();
 698			} else {
 699				spl.openPane();
 700				// find currently loaded fragment
 701				ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager()
 702						.findFragmentByTag("conversation");
 703				if (selectedFragment != null) {
 704					selectedFragment.onBackendConnected();
 705				} else {
 706					setSelectedConversation(conversationList.get(0));
 707					swapConversationFragment();
 708				}
 709				ExceptionHelper.checkForCrash(this, this.xmppConnectionService);
 710			}
 711		}
 712	}
 713
 714	public void registerListener() {
 715		if (xmppConnectionServiceBound) {
 716			xmppConnectionService
 717					.setOnConversationListChangedListener(this.onConvChanged);
 718		}
 719	}
 720
 721	@Override
 722	protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
 723		super.onActivityResult(requestCode, resultCode, data);
 724		if (resultCode == RESULT_OK) {
 725			if (requestCode == REQUEST_DECRYPT_PGP) {
 726				ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager()
 727						.findFragmentByTag("conversation");
 728				if (selectedFragment != null) {
 729					selectedFragment.hidePgpPassphraseBox();
 730				}
 731			} else if (requestCode == REQUEST_ATTACH_FILE_DIALOG) {
 732				attachImageToConversation(getSelectedConversation(),data.getData());
 733			} else if (requestCode == REQUEST_SEND_PGP_IMAGE) {
 734				
 735			} else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_IMAGE) {
 736				attachFile(ATTACHMENT_CHOICE_CHOOSE_IMAGE);
 737			} else if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO) {
 738				attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO);
 739			} else if (requestCode == REQUEST_ANNOUNCE_PGP) {
 740				announcePgp(getSelectedConversation().getAccount(),getSelectedConversation());
 741			} else if (requestCode == REQUEST_ENCRYPT_MESSAGE) {
 742				encryptTextMessage();
 743			} else if (requestCode == REQUEST_IMAGE_CAPTURE) {
 744				attachImageToConversation(getSelectedConversation(), null);
 745			} else if (requestCode == REQUEST_RECORD_AUDIO) {
 746				Log.d("xmppService",data.getData().toString());
 747				attachAudioToConversation(getSelectedConversation(),data.getData());
 748			} else {
 749				Log.d(LOGTAG,"unknown result code:"+requestCode);
 750			}
 751		}
 752	}
 753	
 754	private void attachAudioToConversation(Conversation conversation, Uri uri) {
 755		
 756	}
 757
 758	private void attachImageToConversation(Conversation conversation, Uri uri) {
 759		prepareImageToast = Toast.makeText(getApplicationContext(), getText(R.string.preparing_image), Toast.LENGTH_LONG);
 760		prepareImageToast.show();
 761		pendingMessage = xmppConnectionService.attachImageToConversation(conversation, uri, new UiCallback() {
 762				
 763			@Override
 764			public void userInputRequried(PendingIntent pi) {
 765				hidePrepareImageToast();
 766				ConversationActivity.this.runIntent(pi, ConversationActivity.REQUEST_SEND_PGP_IMAGE);
 767			}
 768			
 769			@Override
 770			public void success() {
 771				sendPendingImageMessage();
 772				hidePrepareImageToast();
 773			}
 774			
 775			@Override
 776			public void error(int error) {
 777				hidePrepareImageToast();
 778				pendingMessage = null;
 779				displayErrorDialog(error);
 780			}
 781		});
 782	}
 783	
 784	private void hidePrepareImageToast() {
 785		if (prepareImageToast!=null) {
 786			runOnUiThread(new Runnable() {
 787				
 788				@Override
 789				public void run() {
 790					prepareImageToast.cancel();
 791				}
 792			});
 793		}
 794	}
 795
 796	private void sendPendingImageMessage() {
 797		pendingMessage.getConversation().getMessages().add(pendingMessage);
 798		xmppConnectionService.databaseBackend.createMessage(pendingMessage);
 799		xmppConnectionService.sendMessage(pendingMessage, null);
 800		xmppConnectionService.updateUi(pendingMessage.getConversation(), false);
 801		pendingMessage = null;
 802	}
 803	
 804	public void updateConversationList() {
 805		conversationList.clear();
 806		conversationList.addAll(xmppConnectionService.getConversations());
 807		listView.invalidateViews();
 808	}
 809	
 810	public void selectPresence(final Conversation conversation, final OnPresenceSelected listener, String reason) {
 811		Account account = conversation.getAccount();
 812		if (account.getStatus() != Account.STATUS_ONLINE) {
 813			AlertDialog.Builder builder = new AlertDialog.Builder(this);
 814			builder.setTitle(getString(R.string.not_connected));
 815			builder.setIconAttribute(android.R.attr.alertDialogIcon);
 816			if ("otr".equals(reason)) {
 817				builder.setMessage(getString(R.string.you_are_offline,getString(R.string.otr_messages)));
 818			} else if ("file".equals(reason)) {
 819				builder.setMessage(getString(R.string.you_are_offline,getString(R.string.files)));
 820			} else {
 821				builder.setMessage(getString(R.string.you_are_offline_blank));
 822			}
 823			builder.setNegativeButton(getString(R.string.cancel), null);
 824			builder.setPositiveButton(getString(R.string.manage_account), new OnClickListener() {
 825				
 826				@Override
 827				public void onClick(DialogInterface dialog, int which) {
 828					startActivity(new Intent(activity, ManageAccountActivity.class));
 829				}
 830			});
 831			builder.create().show();
 832			listener.onPresenceSelected(false, null);
 833		} else {
 834			Contact contact = conversation.getContact();
 835			if (contact==null) {
 836				showAddToRosterDialog(conversation);
 837				listener.onPresenceSelected(false,null);
 838			} else {
 839				Hashtable<String, Integer> presences = contact.getPresences();
 840				if (presences.size() == 0) {
 841					AlertDialog.Builder builder = new AlertDialog.Builder(this);
 842					builder.setTitle(getString(R.string.contact_offline));
 843					if ("otr".equals(reason)) {
 844						builder.setMessage(getString(R.string.contact_offline_otr));
 845						builder.setPositiveButton(getString(R.string.send_unencrypted), new OnClickListener() {
 846							
 847							@Override
 848							public void onClick(DialogInterface dialog, int which) {
 849								listener.onSendPlainTextInstead();
 850							}
 851						});
 852					} else if ("file".equals(reason)) {
 853						builder.setMessage(getString(R.string.contact_offline_file));
 854					}
 855					builder.setIconAttribute(android.R.attr.alertDialogIcon);
 856					builder.setNegativeButton(getString(R.string.cancel), null);
 857					builder.create().show();
 858					listener.onPresenceSelected(false, null);
 859				} else if (presences.size() == 1) {
 860					String presence = (String) presences.keySet().toArray()[0];
 861					conversation.setNextPresence(presence);
 862					listener.onPresenceSelected(true, presence);
 863				} else {
 864					AlertDialog.Builder builder = new AlertDialog.Builder(this);
 865					builder.setTitle(getString(R.string.choose_presence));
 866					final String[] presencesArray = new String[presences.size()];
 867					presences.keySet().toArray(presencesArray);
 868					builder.setItems(presencesArray,
 869							new DialogInterface.OnClickListener() {
 870	
 871								@Override
 872								public void onClick(DialogInterface dialog,
 873										int which) {
 874									String presence = presencesArray[which];
 875									conversation.setNextPresence(presence);
 876									listener.onPresenceSelected(true,presence);
 877								}
 878							});
 879					builder.create().show();
 880				}
 881			}
 882		}
 883	}
 884	
 885	private void showAddToRosterDialog(final Conversation conversation) {
 886		String jid = conversation.getContactJid();
 887		AlertDialog.Builder builder = new AlertDialog.Builder(this);
 888		builder.setTitle(jid);
 889		builder.setMessage(getString(R.string.not_in_roster));
 890		builder.setNegativeButton(getString(R.string.cancel), null);
 891		builder.setPositiveButton(getString(R.string.add_contact), new DialogInterface.OnClickListener() {
 892
 893			@Override
 894			public void onClick(DialogInterface dialog, int which) {
 895				String jid = conversation.getContactJid();
 896				Account account = getSelectedConversation().getAccount();
 897				Contact contact = account.getRoster().getContact(jid);
 898				xmppConnectionService.createContact(contact);
 899			}
 900		});
 901		builder.create().show();
 902	}
 903	
 904	public void runIntent(PendingIntent pi, int requestCode) {
 905		try {
 906			this.startIntentSenderForResult(pi.getIntentSender(),requestCode, null, 0,
 907					0, 0);
 908		} catch (SendIntentException e1) {
 909			Log.d("xmppService","failed to start intent to send message");
 910		}
 911	}
 912	
 913	
 914	class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
 915	    private final WeakReference<ImageView> imageViewReference;
 916	    private Message message = null;
 917
 918	    public BitmapWorkerTask(ImageView imageView) {
 919	        imageViewReference = new WeakReference<ImageView>(imageView);
 920	    }
 921
 922	    @Override
 923	    protected Bitmap doInBackground(Message... params) {
 924	        message = params[0];
 925	        try {
 926				return xmppConnectionService.getFileBackend().getThumbnail(message, (int) (metrics.density * 288),false);
 927			} catch (FileNotFoundException e) {
 928				Log.d("xmppService","file not found!");
 929				return null;
 930			}
 931	    }
 932
 933	    @Override
 934	    protected void onPostExecute(Bitmap bitmap) {
 935	        if (imageViewReference != null && bitmap != null) {
 936	            final ImageView imageView = imageViewReference.get();
 937	            if (imageView != null) {
 938	                imageView.setImageBitmap(bitmap);
 939	                imageView.setBackgroundColor(0x00000000);
 940	            }
 941	        }
 942	    }
 943	}
 944	
 945	public void loadBitmap(Message message, ImageView imageView) {
 946		Bitmap bm;
 947		try {
 948			bm = xmppConnectionService.getFileBackend().getThumbnail(message, (int) (metrics.density * 288), true);
 949		} catch (FileNotFoundException e) {
 950			bm = null;
 951		}
 952		if (bm!=null) {
 953			imageView.setImageBitmap(bm);
 954			imageView.setBackgroundColor(0x00000000);
 955		} else {
 956		    if (cancelPotentialWork(message, imageView)) {
 957		    	imageView.setBackgroundColor(0xff333333);
 958		        final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
 959		        final AsyncDrawable asyncDrawable =
 960		                new AsyncDrawable(getResources(), null, task);
 961		        imageView.setImageDrawable(asyncDrawable);
 962		        task.execute(message);
 963		    }
 964		}
 965	}
 966	
 967	public static boolean cancelPotentialWork(Message message, ImageView imageView) {
 968	    final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
 969
 970	    if (bitmapWorkerTask != null) {
 971	        final Message oldMessage = bitmapWorkerTask.message;
 972	        if (oldMessage == null || message != oldMessage) {
 973	            bitmapWorkerTask.cancel(true);
 974	        } else {
 975	            return false;
 976	        }
 977	    }
 978	    return true;
 979	}
 980	
 981	private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
 982		   if (imageView != null) {
 983		       final Drawable drawable = imageView.getDrawable();
 984		       if (drawable instanceof AsyncDrawable) {
 985		           final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
 986		           return asyncDrawable.getBitmapWorkerTask();
 987		       }
 988		    }
 989		    return null;
 990	}
 991	
 992	static class AsyncDrawable extends BitmapDrawable {
 993	    private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
 994
 995	    public AsyncDrawable(Resources res, Bitmap bitmap,
 996	            BitmapWorkerTask bitmapWorkerTask) {
 997	        super(res, bitmap);
 998	        bitmapWorkerTaskReference =
 999	            new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
1000	    }
1001
1002	    public BitmapWorkerTask getBitmapWorkerTask() {
1003	        return bitmapWorkerTaskReference.get();
1004	    }
1005	}
1006
1007	public void encryptTextMessage() {
1008		xmppConnectionService.getPgpEngine().encrypt(this.pendingMessage, new UiCallback() {
1009
1010					@Override
1011					public void userInputRequried(
1012							PendingIntent pi) {
1013						activity.runIntent(
1014								pi,
1015								ConversationActivity.REQUEST_SEND_MESSAGE);
1016					}
1017
1018					@Override
1019					public void success() {
1020						xmppConnectionService.sendMessage(pendingMessage, null);
1021						pendingMessage = null;
1022						ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager()
1023								.findFragmentByTag("conversation");
1024						if (selectedFragment != null) {
1025							selectedFragment.clearInputField();
1026						}
1027					}
1028
1029					@Override
1030					public void error(int error) {
1031
1032					}
1033				});
1034	}
1035}