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.utils.ExceptionHelper;
 15import eu.siacs.conversations.utils.UIHelper;
 16import android.net.Uri;
 17import android.os.AsyncTask;
 18import android.os.Bundle;
 19import android.preference.PreferenceManager;
 20import android.app.AlertDialog;
 21import android.app.FragmentTransaction;
 22import android.app.PendingIntent;
 23import android.content.Context;
 24import android.content.DialogInterface;
 25import android.content.DialogInterface.OnClickListener;
 26import android.content.IntentSender.SendIntentException;
 27import android.content.Intent;
 28import android.content.SharedPreferences;
 29import android.content.res.Resources;
 30import android.graphics.Bitmap;
 31import android.graphics.Color;
 32import android.graphics.Typeface;
 33import android.graphics.drawable.BitmapDrawable;
 34import android.graphics.drawable.Drawable;
 35import android.support.v4.widget.SlidingPaneLayout;
 36import android.support.v4.widget.SlidingPaneLayout.PanelSlideListener;
 37import android.util.DisplayMetrics;
 38import android.util.Log;
 39import android.view.KeyEvent;
 40import android.view.LayoutInflater;
 41import android.view.Menu;
 42import android.view.MenuItem;
 43import android.view.View;
 44import android.view.ViewGroup;
 45import android.widget.AdapterView;
 46import android.widget.AdapterView.OnItemClickListener;
 47import android.widget.ArrayAdapter;
 48import android.widget.CheckBox;
 49import android.widget.ListView;
 50import android.widget.PopupMenu;
 51import android.widget.PopupMenu.OnMenuItemClickListener;
 52import android.widget.TextView;
 53import android.widget.ImageView;
 54import android.widget.Toast;
 55
 56public class ConversationActivity extends XmppActivity {
 57
 58	public static final String VIEW_CONVERSATION = "viewConversation";
 59	public static final String CONVERSATION = "conversationUuid";
 60	public static final String TEXT = "text";
 61	public static final String PRESENCE = "eu.siacs.conversations.presence";
 62
 63	public static final int REQUEST_SEND_MESSAGE = 0x75441;
 64	public static final int REQUEST_DECRYPT_PGP = 0x76783;
 65	private static final int REQUEST_ATTACH_FILE_DIALOG = 0x48502;
 66	private static final int REQUEST_SEND_PGP_IMAGE = 0x53883;
 67	private static final int REQUEST_ATTACH_FILE = 0x73824;
 68	public static final int REQUEST_ENCRYPT_MESSAGE = 0x378018;
 69
 70	protected SlidingPaneLayout spl;
 71
 72	private List<Conversation> conversationList = new ArrayList<Conversation>();
 73	private Conversation selectedConversation = null;
 74	private ListView listView;
 75	
 76	private boolean paneShouldBeOpen = true;
 77	private boolean useSubject = true;
 78	private ArrayAdapter<Conversation> listAdapter;
 79	
 80	public Message pendingMessage = null;
 81
 82	private OnConversationListChangedListener onConvChanged = new OnConversationListChangedListener() {
 83
 84		@Override
 85		public void onConversationListChanged() {
 86			runOnUiThread(new Runnable() {
 87
 88				@Override
 89				public void run() {
 90					updateConversationList();
 91					if (paneShouldBeOpen) {
 92						if (conversationList.size() >= 1) {
 93							swapConversationFragment();
 94						} else {
 95							startActivity(new Intent(getApplicationContext(),
 96									ContactsActivity.class));
 97							finish();
 98						}
 99					}
100					ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager()
101							.findFragmentByTag("conversation");
102					if (selectedFragment != null) {
103						selectedFragment.updateMessages();
104					}
105				}
106			});
107		}
108	};
109	
110	protected ConversationActivity activity = this;
111	private DisplayMetrics metrics;
112	private Toast prepareImageToast;
113
114	public List<Conversation> getConversationList() {
115		return this.conversationList;
116	}
117
118	public Conversation getSelectedConversation() {
119		return this.selectedConversation;
120	}
121	
122	public void setSelectedConversation(Conversation conversation) {
123		this.selectedConversation = conversation;
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 (getSelectedConversation() != conversationList.get(position)) {
241					setSelectedConversation(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 UiCallback() {
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(int error) {
372							displayErrorDialog(error);
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		} else {
394			AlertDialog.Builder builder = new AlertDialog.Builder(this);
395			builder.setTitle(getString(R.string.otr_file_transfer));
396			builder.setMessage(getString(R.string.otr_file_transfer_msg));
397			builder.setNegativeButton(getString(R.string.cancel), null);
398			if (conversation.getContact().getPgpKeyId()==0) {
399				builder.setPositiveButton(getString(R.string.send_unencrypted), new OnClickListener() {
400					
401					@Override
402					public void onClick(DialogInterface dialog, int which) {
403						conversation.setNextEncryption(Message.ENCRYPTION_NONE);
404						attachFile();
405					}
406				});
407			} else {
408				builder.setPositiveButton(getString(R.string.use_pgp_encryption), new OnClickListener() {
409					
410					@Override
411					public void onClick(DialogInterface dialog, int which) {
412						conversation.setNextEncryption(Message.ENCRYPTION_PGP);
413						attachFile();
414					}
415				});
416			}
417			builder.create().show();
418		}
419	}
420	
421	@Override
422	public boolean onOptionsItemSelected(MenuItem item) {
423		switch (item.getItemId()) {
424		case android.R.id.home:
425			spl.openPane();
426			break;
427		case R.id.action_attach_file:
428			attachFile();
429			break;
430		case R.id.action_add:
431			startActivity(new Intent(this, ContactsActivity.class));
432			break;
433		case R.id.action_archive:
434			this.endConversation(getSelectedConversation());
435			break;
436		case R.id.action_contact_details:
437			Contact contact = this.getSelectedConversation().getContact();
438			if (contact != null) {
439				Intent intent = new Intent(this, ContactDetailsActivity.class);
440				intent.setAction(ContactDetailsActivity.ACTION_VIEW_CONTACT);
441				intent.putExtra("uuid", contact.getUuid());
442				startActivity(intent);
443			} else {
444				showAddToRosterDialog(getSelectedConversation());
445			}
446			break;
447		case R.id.action_muc_details:
448			Intent intent = new Intent(this, MucDetailsActivity.class);
449			intent.setAction(MucDetailsActivity.ACTION_VIEW_MUC);
450			intent.putExtra("uuid", getSelectedConversation().getUuid());
451			startActivity(intent);
452			break;
453		case R.id.action_invite:
454			Intent inviteIntent = new Intent(getApplicationContext(),
455					ContactsActivity.class);
456			inviteIntent.setAction("invite");
457			inviteIntent.putExtra("uuid", getSelectedConversation().getUuid());
458			startActivity(inviteIntent);
459			break;
460		case R.id.action_security:
461			final Conversation conversation = getSelectedConversation();
462			View menuItemView = findViewById(R.id.action_security);
463			PopupMenu popup = new PopupMenu(this, menuItemView);
464			final ConversationFragment fragment = (ConversationFragment) getFragmentManager()
465					.findFragmentByTag("conversation");
466			if (fragment != null) {
467				popup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
468
469					@Override
470					public boolean onMenuItemClick(MenuItem item) {
471						switch (item.getItemId()) {
472						case R.id.encryption_choice_none:
473							conversation.setNextEncryption(Message.ENCRYPTION_NONE);
474							item.setChecked(true);
475							break;
476						case R.id.encryption_choice_otr:
477							conversation.setNextEncryption(Message.ENCRYPTION_OTR);
478							item.setChecked(true);
479							break;
480						case R.id.encryption_choice_pgp:
481							if (hasPgp()) {
482								if (conversation.getAccount().getKeys().has("pgp_signature")) {
483									conversation.setNextEncryption(Message.ENCRYPTION_PGP);
484									item.setChecked(true);
485								} else {
486									announcePgp(conversation.getAccount(),conversation);
487								}
488							}
489							break;
490						default:
491							conversation.setNextEncryption(Message.ENCRYPTION_NONE);
492							break;
493						}
494						fragment.updateChatMsgHint();
495						return true;
496					}
497				});
498				popup.inflate(R.menu.encryption_choices);
499				switch (conversation.getNextEncryption()) {
500				case Message.ENCRYPTION_NONE:
501					popup.getMenu().findItem(R.id.encryption_choice_none)
502							.setChecked(true);
503					break;
504				case Message.ENCRYPTION_OTR:
505					popup.getMenu().findItem(R.id.encryption_choice_otr)
506							.setChecked(true);
507					break;
508				case Message.ENCRYPTION_PGP:
509					popup.getMenu().findItem(R.id.encryption_choice_pgp)
510							.setChecked(true);
511					break;
512				default:
513					popup.getMenu().findItem(R.id.encryption_choice_none)
514							.setChecked(true);
515					break;
516				}
517				popup.show();
518			}
519
520			break;
521		case R.id.action_clear_history:
522			clearHistoryDialog(getSelectedConversation());
523			break;
524		default:
525			break;
526		}
527		return super.onOptionsItemSelected(item);
528	}
529	
530	private void endConversation(Conversation conversation) {
531		conversation.setStatus(Conversation.STATUS_ARCHIVED);
532		paneShouldBeOpen = true;
533		spl.openPane();
534		xmppConnectionService.archiveConversation(conversation);
535		if (conversationList.size() > 0) {
536			setSelectedConversation(conversationList.get(0));
537		} else {
538			setSelectedConversation(null);
539		}
540	}
541
542	protected void clearHistoryDialog(final Conversation conversation) {
543		AlertDialog.Builder builder = new AlertDialog.Builder(this);
544		builder.setTitle(getString(R.string.clear_conversation_history));
545		View dialogView = getLayoutInflater().inflate(R.layout.dialog_clear_history, null);
546		final CheckBox endConversationCheckBox = (CheckBox) dialogView.findViewById(R.id.end_conversation_checkbox);
547		builder.setView(dialogView);
548		builder.setNegativeButton(getString(R.string.cancel), null);
549		builder.setPositiveButton(getString(R.string.delete_messages), new OnClickListener() {
550			
551			@Override
552			public void onClick(DialogInterface dialog, int which) {
553				activity.xmppConnectionService.clearConversationHistory(conversation);
554				if (endConversationCheckBox.isChecked()) {
555					endConversation(conversation);
556				}
557			}
558		});
559		builder.create().show();
560	}
561
562	protected ConversationFragment swapConversationFragment() {
563		ConversationFragment selectedFragment = new ConversationFragment();
564
565		FragmentTransaction transaction = getFragmentManager()
566				.beginTransaction();
567		transaction.replace(R.id.selected_conversation, selectedFragment,
568				"conversation");
569		transaction.commit();
570		return selectedFragment;
571	}
572
573	@Override
574	public boolean onKeyDown(int keyCode, KeyEvent event) {
575		if (keyCode == KeyEvent.KEYCODE_BACK) {
576			if (!spl.isOpen()) {
577				spl.openPane();
578				return false;
579			}
580		}
581		return super.onKeyDown(keyCode, event);
582	}
583
584	@Override
585	public void onStart() {
586		super.onStart();
587		SharedPreferences preferences = PreferenceManager
588				.getDefaultSharedPreferences(this);
589		this.useSubject = preferences.getBoolean("use_subject_in_muc", true);
590		if (this.xmppConnectionServiceBound) {
591			this.onBackendConnected();
592		}
593		if (conversationList.size() >= 1) {
594			onConvChanged.onConversationListChanged();
595		}
596	}
597
598	@Override
599	protected void onStop() {
600		if (xmppConnectionServiceBound) {
601			xmppConnectionService.removeOnConversationListChangedListener();
602		}
603		super.onStop();
604	}
605
606	@Override
607	void onBackendConnected() {
608		this.registerListener();
609		if (conversationList.size() == 0) {
610			updateConversationList();
611		}
612
613		if ((getIntent().getAction() != null)
614				&& (getIntent().getAction().equals(Intent.ACTION_VIEW) && (!handledViewIntent))) {
615			if (getIntent().getType().equals(
616					ConversationActivity.VIEW_CONVERSATION)) {
617				handledViewIntent = true;
618
619				String convToView = (String) getIntent().getExtras().get(
620						CONVERSATION);
621
622				for (int i = 0; i < conversationList.size(); ++i) {
623					if (conversationList.get(i).getUuid().equals(convToView)) {
624						setSelectedConversation(conversationList.get(i));
625					}
626				}
627				paneShouldBeOpen = false;
628				String text = getIntent().getExtras().getString(TEXT, null);
629				swapConversationFragment().setText(text);
630			}
631		} else {
632			if (xmppConnectionService.getAccounts().size() == 0) {
633				startActivity(new Intent(this, ManageAccountActivity.class));
634				finish();
635			} else if (conversationList.size() <= 0) {
636				// add no history
637				startActivity(new Intent(this, ContactsActivity.class));
638				finish();
639			} else {
640				spl.openPane();
641				// find currently loaded fragment
642				ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager()
643						.findFragmentByTag("conversation");
644				if (selectedFragment != null) {
645					selectedFragment.onBackendConnected();
646				} else {
647					setSelectedConversation(conversationList.get(0));
648					swapConversationFragment();
649				}
650				ExceptionHelper.checkForCrash(this, this.xmppConnectionService);
651			}
652		}
653	}
654
655	public void registerListener() {
656		if (xmppConnectionServiceBound) {
657			xmppConnectionService
658					.setOnConversationListChangedListener(this.onConvChanged);
659		}
660	}
661
662	@Override
663	protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
664		super.onActivityResult(requestCode, resultCode, data);
665		if (resultCode == RESULT_OK) {
666			if (requestCode == REQUEST_DECRYPT_PGP) {
667				ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager()
668						.findFragmentByTag("conversation");
669				if (selectedFragment != null) {
670					selectedFragment.hidePgpPassphraseBox();
671				}
672			} else if (requestCode == REQUEST_ATTACH_FILE_DIALOG) {
673				prepareImageToast = Toast.makeText(getApplicationContext(), getText(R.string.preparing_image), Toast.LENGTH_LONG);
674				final Conversation conversation = getSelectedConversation();
675				if (conversation.getNextEncryption() == Message.ENCRYPTION_NONE) {
676					prepareImageToast.show();
677					this.pendingMessage = xmppConnectionService.attachImageToConversation(conversation, data.getData(),new UiCallback() {
678						
679						@Override
680						public void userInputRequried(PendingIntent pi) {
681							
682						}
683						
684						@Override
685						public void success() {
686							sendPendingImageMessage();
687						}
688						
689						@Override
690						public void error(int error) {
691							pendingMessage = null;
692							displayErrorDialog(error);
693						}
694					});
695				} else if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
696					prepareImageToast.show();
697					attachPgpFile(conversation,data.getData());
698				} else {
699					Log.d(LOGTAG,"unknown next message encryption: "+conversation.getNextEncryption());
700				}
701			} else if (requestCode == REQUEST_SEND_PGP_IMAGE) {
702				
703			} else if (requestCode == REQUEST_ATTACH_FILE) {
704				attachFile();
705			} else if (requestCode == REQUEST_ANNOUNCE_PGP) {
706				announcePgp(getSelectedConversation().getAccount(),getSelectedConversation());
707			} else if (requestCode == REQUEST_ENCRYPT_MESSAGE) {
708				encryptTextMessage();
709			} else {
710				Log.d(LOGTAG,"unknown result code:"+requestCode);
711			}
712		}
713	}
714	
715	private void attachPgpFile(Conversation conversation, Uri uri) {
716			pendingMessage = xmppConnectionService.attachImageToConversation(conversation, uri, new UiCallback() {
717				
718				@Override
719				public void userInputRequried(PendingIntent pi) {
720					ConversationActivity.this.runIntent(pi, ConversationActivity.REQUEST_SEND_PGP_IMAGE);
721				}
722				
723				@Override
724				public void success() {
725					sendPendingImageMessage();
726				}
727				
728				@Override
729				public void error(int error) {
730					pendingMessage = null;
731					displayErrorDialog(error);
732				}
733			});
734	}
735
736	private void sendPendingImageMessage() {
737		pendingMessage.getConversation().getMessages().add(pendingMessage);
738		xmppConnectionService.databaseBackend.createMessage(pendingMessage);
739		xmppConnectionService.sendMessage(pendingMessage, null);
740		xmppConnectionService.updateUi(pendingMessage.getConversation(), false);
741		pendingMessage = null;
742	}
743	
744	public void updateConversationList() {
745		conversationList.clear();
746		conversationList.addAll(xmppConnectionService.getConversations());
747		listView.invalidateViews();
748	}
749	
750	public void selectPresence(final Conversation conversation, final OnPresenceSelected listener, String reason) {
751		Account account = conversation.getAccount();
752		if (account.getStatus() != Account.STATUS_ONLINE) {
753			AlertDialog.Builder builder = new AlertDialog.Builder(this);
754			builder.setTitle(getString(R.string.not_connected));
755			builder.setIconAttribute(android.R.attr.alertDialogIcon);
756			if ("otr".equals(reason)) {
757				builder.setMessage(getString(R.string.you_are_offline,getString(R.string.otr_messages)));
758			} else if ("file".equals(reason)) {
759				builder.setMessage(getString(R.string.you_are_offline,getString(R.string.files)));
760			} else {
761				builder.setMessage(getString(R.string.you_are_offline_blank));
762			}
763			builder.setNegativeButton(getString(R.string.cancel), null);
764			builder.setPositiveButton(getString(R.string.manage_account), new OnClickListener() {
765				
766				@Override
767				public void onClick(DialogInterface dialog, int which) {
768					startActivity(new Intent(activity, ManageAccountActivity.class));
769				}
770			});
771			builder.create().show();
772			listener.onPresenceSelected(false, null);
773		} else {
774			Contact contact = conversation.getContact();
775			if (contact==null) {
776				showAddToRosterDialog(conversation);
777				listener.onPresenceSelected(false,null);
778			} else {
779				Hashtable<String, Integer> presences = contact.getPresences();
780				if (presences.size() == 0) {
781					AlertDialog.Builder builder = new AlertDialog.Builder(this);
782					builder.setTitle(getString(R.string.contact_offline));
783					if ("otr".equals(reason)) {
784						builder.setMessage(getString(R.string.contact_offline_otr));
785						builder.setPositiveButton(getString(R.string.send_unencrypted), new OnClickListener() {
786							
787							@Override
788							public void onClick(DialogInterface dialog, int which) {
789								listener.onSendPlainTextInstead();
790							}
791						});
792					} else if ("file".equals(reason)) {
793						builder.setMessage(getString(R.string.contact_offline_file));
794					}
795					builder.setIconAttribute(android.R.attr.alertDialogIcon);
796					builder.setNegativeButton(getString(R.string.cancel), null);
797					builder.create().show();
798					listener.onPresenceSelected(false, null);
799				} else if (presences.size() == 1) {
800					String presence = (String) presences.keySet().toArray()[0];
801					conversation.setNextPresence(presence);
802					listener.onPresenceSelected(true, presence);
803				} else {
804					AlertDialog.Builder builder = new AlertDialog.Builder(this);
805					builder.setTitle(getString(R.string.choose_presence));
806					final String[] presencesArray = new String[presences.size()];
807					presences.keySet().toArray(presencesArray);
808					builder.setItems(presencesArray,
809							new DialogInterface.OnClickListener() {
810	
811								@Override
812								public void onClick(DialogInterface dialog,
813										int which) {
814									String presence = presencesArray[which];
815									conversation.setNextPresence(presence);
816									listener.onPresenceSelected(true,presence);
817								}
818							});
819					builder.create().show();
820				}
821			}
822		}
823	}
824	
825	private void showAddToRosterDialog(final Conversation conversation) {
826		String jid = conversation.getContactJid();
827		AlertDialog.Builder builder = new AlertDialog.Builder(this);
828		builder.setTitle(jid);
829		builder.setMessage(getString(R.string.not_in_roster));
830		builder.setNegativeButton(getString(R.string.cancel), null);
831		builder.setPositiveButton(getString(R.string.add_contact), new DialogInterface.OnClickListener() {
832
833			@Override
834			public void onClick(DialogInterface dialog, int which) {
835				String jid = conversation.getContactJid();
836				Account account = getSelectedConversation().getAccount();
837				String name = jid.split("@")[0];
838				Contact contact = new Contact(account, name, jid, null);
839				xmppConnectionService.createContact(contact);
840			}
841		});
842		builder.create().show();
843	}
844	
845	public void runIntent(PendingIntent pi, int requestCode) {
846		try {
847			this.startIntentSenderForResult(pi.getIntentSender(),requestCode, null, 0,
848					0, 0);
849		} catch (SendIntentException e1) {
850			Log.d("xmppService","failed to start intent to send message");
851		}
852	}
853	
854	
855	class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
856	    private final WeakReference<ImageView> imageViewReference;
857	    private Message message = null;
858
859	    public BitmapWorkerTask(ImageView imageView) {
860	        imageViewReference = new WeakReference<ImageView>(imageView);
861	    }
862
863	    @Override
864	    protected Bitmap doInBackground(Message... params) {
865	        message = params[0];
866	        try {
867				return xmppConnectionService.getFileBackend().getThumbnail(message, (int) (metrics.density * 288),false);
868			} catch (FileNotFoundException e) {
869				Log.d("xmppService","file not found!");
870				return null;
871			}
872	    }
873
874	    @Override
875	    protected void onPostExecute(Bitmap bitmap) {
876	        if (imageViewReference != null && bitmap != null) {
877	            final ImageView imageView = imageViewReference.get();
878	            if (imageView != null) {
879	                imageView.setImageBitmap(bitmap);
880	                imageView.setBackgroundColor(0x00000000);
881	            }
882	        }
883	    }
884	}
885	
886	public void loadBitmap(Message message, ImageView imageView) {
887		Bitmap bm;
888		try {
889			bm = xmppConnectionService.getFileBackend().getThumbnail(message, (int) (metrics.density * 288), true);
890		} catch (FileNotFoundException e) {
891			bm = null;
892		}
893		if (bm!=null) {
894			imageView.setImageBitmap(bm);
895			imageView.setBackgroundColor(0x00000000);
896		} else {
897		    if (cancelPotentialWork(message, imageView)) {
898		    	imageView.setBackgroundColor(0xff333333);
899		        final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
900		        final AsyncDrawable asyncDrawable =
901		                new AsyncDrawable(getResources(), null, task);
902		        imageView.setImageDrawable(asyncDrawable);
903		        task.execute(message);
904		    }
905		}
906	}
907	
908	public static boolean cancelPotentialWork(Message message, ImageView imageView) {
909	    final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
910
911	    if (bitmapWorkerTask != null) {
912	        final Message oldMessage = bitmapWorkerTask.message;
913	        if (oldMessage == null || message != oldMessage) {
914	            bitmapWorkerTask.cancel(true);
915	        } else {
916	            return false;
917	        }
918	    }
919	    return true;
920	}
921	
922	private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
923		   if (imageView != null) {
924		       final Drawable drawable = imageView.getDrawable();
925		       if (drawable instanceof AsyncDrawable) {
926		           final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
927		           return asyncDrawable.getBitmapWorkerTask();
928		       }
929		    }
930		    return null;
931	}
932	
933	static class AsyncDrawable extends BitmapDrawable {
934	    private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
935
936	    public AsyncDrawable(Resources res, Bitmap bitmap,
937	            BitmapWorkerTask bitmapWorkerTask) {
938	        super(res, bitmap);
939	        bitmapWorkerTaskReference =
940	            new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
941	    }
942
943	    public BitmapWorkerTask getBitmapWorkerTask() {
944	        return bitmapWorkerTaskReference.get();
945	    }
946	}
947
948	public void encryptTextMessage() {
949		xmppConnectionService.getPgpEngine().encrypt(this.pendingMessage, new UiCallback() {
950
951					@Override
952					public void userInputRequried(
953							PendingIntent pi) {
954						activity.runIntent(
955								pi,
956								ConversationActivity.REQUEST_SEND_MESSAGE);
957					}
958
959					@Override
960					public void success() {
961						xmppConnectionService.sendMessage(pendingMessage, null);
962						pendingMessage = null;
963						ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager()
964								.findFragmentByTag("conversation");
965						if (selectedFragment != null) {
966							selectedFragment.clearInputField();
967						}
968					}
969
970					@Override
971					public void error(int error) {
972
973					}
974				});
975	}
976}