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