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					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.getOption(Contact.Options.IN_ROSTER)) {
 478				Intent intent = new Intent(this, ContactDetailsActivity.class);
 479				intent.setAction(ContactDetailsActivity.ACTION_VIEW_CONTACT);
 480				intent.putExtra("account", this.getSelectedConversation().getAccount().getJid());
 481				intent.putExtra("contact",contact.getJid());
 482				startActivity(intent);
 483			} else {
 484				showAddToRosterDialog(getSelectedConversation());
 485			}
 486			break;
 487		case R.id.action_muc_details:
 488			Intent intent = new Intent(this, MucDetailsActivity.class);
 489			intent.setAction(MucDetailsActivity.ACTION_VIEW_MUC);
 490			intent.putExtra("uuid", getSelectedConversation().getUuid());
 491			startActivity(intent);
 492			break;
 493		case R.id.action_invite:
 494			Intent inviteIntent = new Intent(getApplicationContext(),
 495					ContactsActivity.class);
 496			inviteIntent.setAction("invite");
 497			inviteIntent.putExtra("uuid", getSelectedConversation().getUuid());
 498			startActivity(inviteIntent);
 499			break;
 500		case R.id.action_security:
 501			final Conversation conversation = getSelectedConversation();
 502			View menuItemView = findViewById(R.id.action_security);
 503			PopupMenu popup = new PopupMenu(this, menuItemView);
 504			final ConversationFragment fragment = (ConversationFragment) getFragmentManager()
 505					.findFragmentByTag("conversation");
 506			if (fragment != null) {
 507				popup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
 508
 509					@Override
 510					public boolean onMenuItemClick(MenuItem item) {
 511						switch (item.getItemId()) {
 512						case R.id.encryption_choice_none:
 513							conversation.setNextEncryption(Message.ENCRYPTION_NONE);
 514							item.setChecked(true);
 515							break;
 516						case R.id.encryption_choice_otr:
 517							conversation.setNextEncryption(Message.ENCRYPTION_OTR);
 518							item.setChecked(true);
 519							break;
 520						case R.id.encryption_choice_pgp:
 521							if (hasPgp()) {
 522								if (conversation.getAccount().getKeys().has("pgp_signature")) {
 523									conversation.setNextEncryption(Message.ENCRYPTION_PGP);
 524									item.setChecked(true);
 525								} else {
 526									announcePgp(conversation.getAccount(),conversation);
 527								}
 528							}
 529							break;
 530						default:
 531							conversation.setNextEncryption(Message.ENCRYPTION_NONE);
 532							break;
 533						}
 534						fragment.updateChatMsgHint();
 535						return true;
 536					}
 537				});
 538				popup.inflate(R.menu.encryption_choices);
 539				switch (conversation.getNextEncryption()) {
 540				case Message.ENCRYPTION_NONE:
 541					popup.getMenu().findItem(R.id.encryption_choice_none)
 542							.setChecked(true);
 543					break;
 544				case Message.ENCRYPTION_OTR:
 545					popup.getMenu().findItem(R.id.encryption_choice_otr)
 546							.setChecked(true);
 547					break;
 548				case Message.ENCRYPTION_PGP:
 549					popup.getMenu().findItem(R.id.encryption_choice_pgp)
 550							.setChecked(true);
 551					break;
 552				default:
 553					popup.getMenu().findItem(R.id.encryption_choice_none)
 554							.setChecked(true);
 555					break;
 556				}
 557				popup.show();
 558			}
 559
 560			break;
 561		case R.id.action_clear_history:
 562			clearHistoryDialog(getSelectedConversation());
 563			break;
 564		default:
 565			break;
 566		}
 567		return super.onOptionsItemSelected(item);
 568	}
 569	
 570	private void endConversation(Conversation conversation) {
 571		conversation.setStatus(Conversation.STATUS_ARCHIVED);
 572		paneShouldBeOpen = true;
 573		spl.openPane();
 574		xmppConnectionService.archiveConversation(conversation);
 575		if (conversationList.size() > 0) {
 576			setSelectedConversation(conversationList.get(0));
 577		} else {
 578			setSelectedConversation(null);
 579		}
 580	}
 581
 582	protected void clearHistoryDialog(final Conversation conversation) {
 583		AlertDialog.Builder builder = new AlertDialog.Builder(this);
 584		builder.setTitle(getString(R.string.clear_conversation_history));
 585		View dialogView = getLayoutInflater().inflate(R.layout.dialog_clear_history, null);
 586		final CheckBox endConversationCheckBox = (CheckBox) dialogView.findViewById(R.id.end_conversation_checkbox);
 587		builder.setView(dialogView);
 588		builder.setNegativeButton(getString(R.string.cancel), null);
 589		builder.setPositiveButton(getString(R.string.delete_messages), new OnClickListener() {
 590			
 591			@Override
 592			public void onClick(DialogInterface dialog, int which) {
 593				activity.xmppConnectionService.clearConversationHistory(conversation);
 594				if (endConversationCheckBox.isChecked()) {
 595					endConversation(conversation);
 596				}
 597			}
 598		});
 599		builder.create().show();
 600	}
 601
 602	protected ConversationFragment swapConversationFragment() {
 603		ConversationFragment selectedFragment = new ConversationFragment();
 604
 605		FragmentTransaction transaction = getFragmentManager()
 606				.beginTransaction();
 607		transaction.replace(R.id.selected_conversation, selectedFragment,
 608				"conversation");
 609		transaction.commit();
 610		return selectedFragment;
 611	}
 612
 613	@Override
 614	public boolean onKeyDown(int keyCode, KeyEvent event) {
 615		if (keyCode == KeyEvent.KEYCODE_BACK) {
 616			if (!spl.isOpen()) {
 617				spl.openPane();
 618				return false;
 619			}
 620		}
 621		return super.onKeyDown(keyCode, event);
 622	}
 623
 624	@Override
 625	protected void onNewIntent (Intent intent) {
 626		if ((Intent.ACTION_VIEW.equals(intent.getAction())&&(VIEW_CONVERSATION.equals(intent.getType())))) {
 627			String convToView = (String) intent.getExtras().get(
 628					CONVERSATION);
 629
 630			for (int i = 0; i < conversationList.size(); ++i) {
 631				if (conversationList.get(i).getUuid().equals(convToView)) {
 632					setSelectedConversation(conversationList.get(i));
 633				}
 634			}
 635			paneShouldBeOpen = false;
 636			String text = intent.getExtras().getString(TEXT, null);
 637			swapConversationFragment().setText(text);
 638		}
 639	}
 640	
 641	@Override
 642	public void onStart() {
 643		super.onStart();
 644		SharedPreferences preferences = PreferenceManager
 645				.getDefaultSharedPreferences(this);
 646		this.useSubject = preferences.getBoolean("use_subject_in_muc", true);
 647		if (this.xmppConnectionServiceBound) {
 648			this.onBackendConnected();
 649		}
 650		if (conversationList.size() >= 1) {
 651			onConvChanged.onConversationListChanged();
 652		}
 653	}
 654
 655	@Override
 656	protected void onStop() {
 657		if (xmppConnectionServiceBound) {
 658			xmppConnectionService.removeOnConversationListChangedListener();
 659		}
 660		super.onStop();
 661	}
 662
 663	@Override
 664	void onBackendConnected() {
 665		this.registerListener();
 666		if (conversationList.size() == 0) {
 667			updateConversationList();
 668		}
 669
 670		if ((getIntent().getAction() != null)
 671				&& (getIntent().getAction().equals(Intent.ACTION_VIEW) && (!handledViewIntent))) {
 672			if (getIntent().getType().equals(
 673					ConversationActivity.VIEW_CONVERSATION)) {
 674				handledViewIntent = true;
 675
 676				String convToView = (String) getIntent().getExtras().get(
 677						CONVERSATION);
 678
 679				for (int i = 0; i < conversationList.size(); ++i) {
 680					if (conversationList.get(i).getUuid().equals(convToView)) {
 681						setSelectedConversation(conversationList.get(i));
 682					}
 683				}
 684				paneShouldBeOpen = false;
 685				String text = getIntent().getExtras().getString(TEXT, null);
 686				swapConversationFragment().setText(text);
 687			}
 688		} else {
 689			if (xmppConnectionService.getAccounts().size() == 0) {
 690				startActivity(new Intent(this, ManageAccountActivity.class));
 691				finish();
 692			} else if (conversationList.size() <= 0) {
 693				// add no history
 694				startActivity(new Intent(this, ContactsActivity.class));
 695				finish();
 696			} else {
 697				spl.openPane();
 698				// find currently loaded fragment
 699				ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager()
 700						.findFragmentByTag("conversation");
 701				if (selectedFragment != null) {
 702					selectedFragment.onBackendConnected();
 703				} else {
 704					setSelectedConversation(conversationList.get(0));
 705					swapConversationFragment();
 706				}
 707				ExceptionHelper.checkForCrash(this, this.xmppConnectionService);
 708			}
 709		}
 710	}
 711
 712	public void registerListener() {
 713		if (xmppConnectionServiceBound) {
 714			xmppConnectionService
 715					.setOnConversationListChangedListener(this.onConvChanged);
 716		}
 717	}
 718
 719	@Override
 720	protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
 721		super.onActivityResult(requestCode, resultCode, data);
 722		if (resultCode == RESULT_OK) {
 723			if (requestCode == REQUEST_DECRYPT_PGP) {
 724				ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager()
 725						.findFragmentByTag("conversation");
 726				if (selectedFragment != null) {
 727					selectedFragment.hidePgpPassphraseBox();
 728				}
 729			} else if (requestCode == REQUEST_ATTACH_FILE_DIALOG) {
 730				attachImageToConversation(getSelectedConversation(),data.getData());
 731			} else if (requestCode == REQUEST_SEND_PGP_IMAGE) {
 732				
 733			} else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_IMAGE) {
 734				attachFile(ATTACHMENT_CHOICE_CHOOSE_IMAGE);
 735			} else if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO) {
 736				attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO);
 737			} else if (requestCode == REQUEST_ANNOUNCE_PGP) {
 738				announcePgp(getSelectedConversation().getAccount(),getSelectedConversation());
 739			} else if (requestCode == REQUEST_ENCRYPT_MESSAGE) {
 740				encryptTextMessage();
 741			} else if (requestCode == REQUEST_IMAGE_CAPTURE) {
 742				attachImageToConversation(getSelectedConversation(), null);
 743			} else if (requestCode == REQUEST_RECORD_AUDIO) {
 744				Log.d("xmppService",data.getData().toString());
 745				attachAudioToConversation(getSelectedConversation(),data.getData());
 746			} else {
 747				Log.d(LOGTAG,"unknown result code:"+requestCode);
 748			}
 749		}
 750	}
 751	
 752	private void attachAudioToConversation(Conversation conversation, Uri uri) {
 753		
 754	}
 755
 756	private void attachImageToConversation(Conversation conversation, Uri uri) {
 757		prepareImageToast = Toast.makeText(getApplicationContext(), getText(R.string.preparing_image), Toast.LENGTH_LONG);
 758		prepareImageToast.show();
 759		pendingMessage = xmppConnectionService.attachImageToConversation(conversation, uri, new UiCallback() {
 760				
 761			@Override
 762			public void userInputRequried(PendingIntent pi) {
 763				hidePrepareImageToast();
 764				ConversationActivity.this.runIntent(pi, ConversationActivity.REQUEST_SEND_PGP_IMAGE);
 765			}
 766			
 767			@Override
 768			public void success() {
 769				sendPendingImageMessage();
 770				hidePrepareImageToast();
 771			}
 772			
 773			@Override
 774			public void error(int error) {
 775				hidePrepareImageToast();
 776				pendingMessage = null;
 777				displayErrorDialog(error);
 778			}
 779		});
 780	}
 781	
 782	private void hidePrepareImageToast() {
 783		if (prepareImageToast!=null) {
 784			runOnUiThread(new Runnable() {
 785				
 786				@Override
 787				public void run() {
 788					prepareImageToast.cancel();
 789				}
 790			});
 791		}
 792	}
 793
 794	private void sendPendingImageMessage() {
 795		pendingMessage.getConversation().getMessages().add(pendingMessage);
 796		xmppConnectionService.databaseBackend.createMessage(pendingMessage);
 797		xmppConnectionService.sendMessage(pendingMessage, null);
 798		xmppConnectionService.updateUi(pendingMessage.getConversation(), false);
 799		pendingMessage = null;
 800	}
 801	
 802	public void updateConversationList() {
 803		conversationList.clear();
 804		conversationList.addAll(xmppConnectionService.getConversations());
 805		listView.invalidateViews();
 806	}
 807	
 808	public void selectPresence(final Conversation conversation, final OnPresenceSelected listener, String reason) {
 809		Account account = conversation.getAccount();
 810		if (account.getStatus() != Account.STATUS_ONLINE) {
 811			AlertDialog.Builder builder = new AlertDialog.Builder(this);
 812			builder.setTitle(getString(R.string.not_connected));
 813			builder.setIconAttribute(android.R.attr.alertDialogIcon);
 814			if ("otr".equals(reason)) {
 815				builder.setMessage(getString(R.string.you_are_offline,getString(R.string.otr_messages)));
 816			} else if ("file".equals(reason)) {
 817				builder.setMessage(getString(R.string.you_are_offline,getString(R.string.files)));
 818			} else {
 819				builder.setMessage(getString(R.string.you_are_offline_blank));
 820			}
 821			builder.setNegativeButton(getString(R.string.cancel), null);
 822			builder.setPositiveButton(getString(R.string.manage_account), new OnClickListener() {
 823				
 824				@Override
 825				public void onClick(DialogInterface dialog, int which) {
 826					startActivity(new Intent(activity, ManageAccountActivity.class));
 827				}
 828			});
 829			builder.create().show();
 830			listener.onPresenceSelected(false, null);
 831		} else {
 832			Contact contact = conversation.getContact();
 833			if (contact==null) {
 834				showAddToRosterDialog(conversation);
 835				listener.onPresenceSelected(false,null);
 836			} else {
 837				Hashtable<String, Integer> presences = contact.getPresences();
 838				if (presences.size() == 0) {
 839					AlertDialog.Builder builder = new AlertDialog.Builder(this);
 840					builder.setTitle(getString(R.string.contact_offline));
 841					if ("otr".equals(reason)) {
 842						builder.setMessage(getString(R.string.contact_offline_otr));
 843						builder.setPositiveButton(getString(R.string.send_unencrypted), new OnClickListener() {
 844							
 845							@Override
 846							public void onClick(DialogInterface dialog, int which) {
 847								listener.onSendPlainTextInstead();
 848							}
 849						});
 850					} else if ("file".equals(reason)) {
 851						builder.setMessage(getString(R.string.contact_offline_file));
 852					}
 853					builder.setIconAttribute(android.R.attr.alertDialogIcon);
 854					builder.setNegativeButton(getString(R.string.cancel), null);
 855					builder.create().show();
 856					listener.onPresenceSelected(false, null);
 857				} else if (presences.size() == 1) {
 858					String presence = (String) presences.keySet().toArray()[0];
 859					conversation.setNextPresence(presence);
 860					listener.onPresenceSelected(true, presence);
 861				} else {
 862					AlertDialog.Builder builder = new AlertDialog.Builder(this);
 863					builder.setTitle(getString(R.string.choose_presence));
 864					final String[] presencesArray = new String[presences.size()];
 865					presences.keySet().toArray(presencesArray);
 866					builder.setItems(presencesArray,
 867							new DialogInterface.OnClickListener() {
 868	
 869								@Override
 870								public void onClick(DialogInterface dialog,
 871										int which) {
 872									String presence = presencesArray[which];
 873									conversation.setNextPresence(presence);
 874									listener.onPresenceSelected(true,presence);
 875								}
 876							});
 877					builder.create().show();
 878				}
 879			}
 880		}
 881	}
 882	
 883	private void showAddToRosterDialog(final Conversation conversation) {
 884		String jid = conversation.getContactJid();
 885		AlertDialog.Builder builder = new AlertDialog.Builder(this);
 886		builder.setTitle(jid);
 887		builder.setMessage(getString(R.string.not_in_roster));
 888		builder.setNegativeButton(getString(R.string.cancel), null);
 889		builder.setPositiveButton(getString(R.string.add_contact), new DialogInterface.OnClickListener() {
 890
 891			@Override
 892			public void onClick(DialogInterface dialog, int which) {
 893				String jid = conversation.getContactJid();
 894				Account account = getSelectedConversation().getAccount();
 895				Contact contact = account.getRoster().getContact(jid);
 896				xmppConnectionService.createContact(contact);
 897			}
 898		});
 899		builder.create().show();
 900	}
 901	
 902	public void runIntent(PendingIntent pi, int requestCode) {
 903		try {
 904			this.startIntentSenderForResult(pi.getIntentSender(),requestCode, null, 0,
 905					0, 0);
 906		} catch (SendIntentException e1) {
 907			Log.d("xmppService","failed to start intent to send message");
 908		}
 909	}
 910	
 911	
 912	class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
 913	    private final WeakReference<ImageView> imageViewReference;
 914	    private Message message = null;
 915
 916	    public BitmapWorkerTask(ImageView imageView) {
 917	        imageViewReference = new WeakReference<ImageView>(imageView);
 918	    }
 919
 920	    @Override
 921	    protected Bitmap doInBackground(Message... params) {
 922	        message = params[0];
 923	        try {
 924				return xmppConnectionService.getFileBackend().getThumbnail(message, (int) (metrics.density * 288),false);
 925			} catch (FileNotFoundException e) {
 926				Log.d("xmppService","file not found!");
 927				return null;
 928			}
 929	    }
 930
 931	    @Override
 932	    protected void onPostExecute(Bitmap bitmap) {
 933	        if (imageViewReference != null && bitmap != null) {
 934	            final ImageView imageView = imageViewReference.get();
 935	            if (imageView != null) {
 936	                imageView.setImageBitmap(bitmap);
 937	                imageView.setBackgroundColor(0x00000000);
 938	            }
 939	        }
 940	    }
 941	}
 942	
 943	public void loadBitmap(Message message, ImageView imageView) {
 944		Bitmap bm;
 945		try {
 946			bm = xmppConnectionService.getFileBackend().getThumbnail(message, (int) (metrics.density * 288), true);
 947		} catch (FileNotFoundException e) {
 948			bm = null;
 949		}
 950		if (bm!=null) {
 951			imageView.setImageBitmap(bm);
 952			imageView.setBackgroundColor(0x00000000);
 953		} else {
 954		    if (cancelPotentialWork(message, imageView)) {
 955		    	imageView.setBackgroundColor(0xff333333);
 956		        final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
 957		        final AsyncDrawable asyncDrawable =
 958		                new AsyncDrawable(getResources(), null, task);
 959		        imageView.setImageDrawable(asyncDrawable);
 960		        task.execute(message);
 961		    }
 962		}
 963	}
 964	
 965	public static boolean cancelPotentialWork(Message message, ImageView imageView) {
 966	    final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
 967
 968	    if (bitmapWorkerTask != null) {
 969	        final Message oldMessage = bitmapWorkerTask.message;
 970	        if (oldMessage == null || message != oldMessage) {
 971	            bitmapWorkerTask.cancel(true);
 972	        } else {
 973	            return false;
 974	        }
 975	    }
 976	    return true;
 977	}
 978	
 979	private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
 980		   if (imageView != null) {
 981		       final Drawable drawable = imageView.getDrawable();
 982		       if (drawable instanceof AsyncDrawable) {
 983		           final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
 984		           return asyncDrawable.getBitmapWorkerTask();
 985		       }
 986		    }
 987		    return null;
 988	}
 989	
 990	static class AsyncDrawable extends BitmapDrawable {
 991	    private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
 992
 993	    public AsyncDrawable(Resources res, Bitmap bitmap,
 994	            BitmapWorkerTask bitmapWorkerTask) {
 995	        super(res, bitmap);
 996	        bitmapWorkerTaskReference =
 997	            new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
 998	    }
 999
1000	    public BitmapWorkerTask getBitmapWorkerTask() {
1001	        return bitmapWorkerTaskReference.get();
1002	    }
1003	}
1004
1005	public void encryptTextMessage() {
1006		xmppConnectionService.getPgpEngine().encrypt(this.pendingMessage, new UiCallback() {
1007
1008					@Override
1009					public void userInputRequried(
1010							PendingIntent pi) {
1011						activity.runIntent(
1012								pi,
1013								ConversationActivity.REQUEST_SEND_MESSAGE);
1014					}
1015
1016					@Override
1017					public void success() {
1018						xmppConnectionService.sendMessage(pendingMessage, null);
1019						pendingMessage = null;
1020						ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager()
1021								.findFragmentByTag("conversation");
1022						if (selectedFragment != null) {
1023							selectedFragment.clearInputField();
1024						}
1025					}
1026
1027					@Override
1028					public void error(int error) {
1029
1030					}
1031				});
1032	}
1033}