ConversationActivity.java

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