ConversationActivity.java

  1package eu.siacs.conversations.ui;
  2
  3import java.io.FileNotFoundException;
  4import java.lang.ref.WeakReference;
  5import java.util.ArrayList;
  6import java.util.List;
  7import java.util.concurrent.RejectedExecutionException;
  8
  9import eu.siacs.conversations.R;
 10import eu.siacs.conversations.entities.Contact;
 11import eu.siacs.conversations.entities.Conversation;
 12import eu.siacs.conversations.entities.Message;
 13import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate;
 14import eu.siacs.conversations.ui.adapter.ConversationAdapter;
 15import eu.siacs.conversations.utils.ExceptionHelper;
 16import eu.siacs.conversations.utils.UIHelper;
 17import android.net.Uri;
 18import android.os.AsyncTask;
 19import android.os.Bundle;
 20import android.preference.PreferenceManager;
 21import android.provider.MediaStore;
 22import android.app.ActionBar;
 23import android.app.AlertDialog;
 24import android.app.FragmentTransaction;
 25import android.app.PendingIntent;
 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.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.view.KeyEvent;
 39import android.view.Menu;
 40import android.view.MenuItem;
 41import android.view.View;
 42import android.widget.AdapterView;
 43import android.widget.AdapterView.OnItemClickListener;
 44import android.widget.ArrayAdapter;
 45import android.widget.CheckBox;
 46import android.widget.ListView;
 47import android.widget.PopupMenu;
 48import android.widget.PopupMenu.OnMenuItemClickListener;
 49import android.widget.ImageView;
 50import android.widget.Toast;
 51
 52public class ConversationActivity extends XmppActivity {
 53
 54	public static final String VIEW_CONVERSATION = "viewConversation";
 55	public static final String CONVERSATION = "conversationUuid";
 56	public static final String TEXT = "text";
 57	public static final String PRESENCE = "eu.siacs.conversations.presence";
 58
 59	public static final int REQUEST_SEND_MESSAGE = 0x0201;
 60	public static final int REQUEST_DECRYPT_PGP = 0x0202;
 61	private static final int REQUEST_ATTACH_FILE_DIALOG = 0x0203;
 62	private static final int REQUEST_IMAGE_CAPTURE = 0x0204;
 63	private static final int REQUEST_RECORD_AUDIO = 0x0205;
 64	private static final int REQUEST_SEND_PGP_IMAGE = 0x0206;
 65	public static final int REQUEST_ENCRYPT_MESSAGE = 0x0207;
 66
 67	private static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301;
 68	private static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302;
 69	private static final int ATTACHMENT_CHOICE_RECORD_VOICE = 0x0303;
 70
 71	protected SlidingPaneLayout spl;
 72
 73	private List<Conversation> conversationList = new ArrayList<Conversation>();
 74	private Conversation selectedConversation = null;
 75	private ListView listView;
 76
 77	private boolean paneShouldBeOpen = true;
 78	private boolean useSubject = true;
 79	private boolean showLastseen = false;
 80	private ArrayAdapter<Conversation> listAdapter;
 81
 82	private OnConversationUpdate onConvChanged = new OnConversationUpdate() {
 83
 84		@Override
 85		public void onConversationUpdate() {
 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									StartConversationActivity.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	private Uri pendingImageUri = null;
115
116	public List<Conversation> getConversationList() {
117		return this.conversationList;
118	}
119
120	public Conversation getSelectedConversation() {
121		return this.selectedConversation;
122	}
123
124	public void setSelectedConversation(Conversation conversation) {
125		this.selectedConversation = conversation;
126	}
127
128	public ListView getConversationListView() {
129		return this.listView;
130	}
131
132	public SlidingPaneLayout getSlidingPaneLayout() {
133		return this.spl;
134	}
135
136	public boolean shouldPaneBeOpen() {
137		return paneShouldBeOpen;
138	}
139
140	@Override
141	protected void onCreate(Bundle savedInstanceState) {
142
143		metrics = getResources().getDisplayMetrics();
144
145		super.onCreate(savedInstanceState);
146
147		setContentView(R.layout.fragment_conversations_overview);
148
149		listView = (ListView) findViewById(R.id.list);
150
151		getActionBar().setDisplayHomeAsUpEnabled(false);
152		getActionBar().setHomeButtonEnabled(false);
153
154		this.listAdapter = new ConversationAdapter(this, conversationList);
155		listView.setAdapter(this.listAdapter);
156
157		listView.setOnItemClickListener(new OnItemClickListener() {
158
159			@Override
160			public void onItemClick(AdapterView<?> arg0, View clickedView,
161					int position, long arg3) {
162				paneShouldBeOpen = false;
163				if (getSelectedConversation() != conversationList.get(position)) {
164					setSelectedConversation(conversationList.get(position));
165					swapConversationFragment();
166				} else {
167					spl.closePane();
168				}
169			}
170		});
171		spl = (SlidingPaneLayout) findViewById(R.id.slidingpanelayout);
172		spl.setParallaxDistance(150);
173		spl.setShadowResource(R.drawable.es_slidingpane_shadow);
174		spl.setSliderFadeColor(0);
175		spl.setPanelSlideListener(new PanelSlideListener() {
176
177			@Override
178			public void onPanelOpened(View arg0) {
179				paneShouldBeOpen = true;
180				ActionBar ab = getActionBar();
181				if (ab != null) {
182					ab.setDisplayHomeAsUpEnabled(false);
183					ab.setHomeButtonEnabled(false);
184					ab.setTitle(R.string.app_name);
185				}
186				invalidateOptionsMenu();
187				hideKeyboard();
188			}
189
190			@Override
191			public void onPanelClosed(View arg0) {
192				paneShouldBeOpen = false;
193				if ((conversationList.size() > 0)
194						&& (getSelectedConversation() != null)) {
195					ActionBar ab = getActionBar();
196					if (ab != null) {
197						ab.setDisplayHomeAsUpEnabled(true);
198						ab.setHomeButtonEnabled(true);
199						ab.setTitle(getSelectedConversation().getName(
200								useSubject));
201					}
202					invalidateOptionsMenu();
203					if (!getSelectedConversation().isRead()) {
204						xmppConnectionService
205								.markRead(getSelectedConversation());
206						UIHelper.updateNotification(getApplicationContext(),
207								getConversationList(), null, false);
208						listView.invalidateViews();
209					}
210				}
211			}
212
213			@Override
214			public void onPanelSlide(View arg0, float arg1) {
215				// TODO Auto-generated method stub
216
217			}
218		});
219	}
220
221	@Override
222	public boolean onCreateOptionsMenu(Menu menu) {
223		getMenuInflater().inflate(R.menu.conversations, menu);
224		MenuItem menuSecure = (MenuItem) menu.findItem(R.id.action_security);
225		MenuItem menuArchive = (MenuItem) menu.findItem(R.id.action_archive);
226		MenuItem menuMucDetails = (MenuItem) menu
227				.findItem(R.id.action_muc_details);
228		MenuItem menuContactDetails = (MenuItem) menu
229				.findItem(R.id.action_contact_details);
230		MenuItem menuAttach = (MenuItem) menu.findItem(R.id.action_attach_file);
231		MenuItem menuClearHistory = (MenuItem) menu
232				.findItem(R.id.action_clear_history);
233		MenuItem menuAdd = (MenuItem) menu.findItem(R.id.action_add);
234		MenuItem menuInviteContact = (MenuItem) menu
235				.findItem(R.id.action_invite);
236
237		if ((spl.isOpen() && (spl.isSlideable()))) {
238			menuArchive.setVisible(false);
239			menuMucDetails.setVisible(false);
240			menuContactDetails.setVisible(false);
241			menuSecure.setVisible(false);
242			menuInviteContact.setVisible(false);
243			menuAttach.setVisible(false);
244			menuClearHistory.setVisible(false);
245		} else {
246			menuAdd.setVisible(!spl.isSlideable());
247			if (this.getSelectedConversation() != null) {
248				if (this.getSelectedConversation().getLatestMessage()
249						.getEncryption() != Message.ENCRYPTION_NONE) {
250					menuSecure.setIcon(R.drawable.ic_action_secure);
251				}
252				if (this.getSelectedConversation().getMode() == Conversation.MODE_MULTI) {
253					menuContactDetails.setVisible(false);
254					menuAttach.setVisible(false);
255				} else {
256					menuMucDetails.setVisible(false);
257					menuInviteContact.setVisible(false);
258				}
259			}
260		}
261		return true;
262	}
263
264	private void selectPresenceToAttachFile(final int attachmentChoice) {
265		selectPresence(getSelectedConversation(), new OnPresenceSelected() {
266
267			@Override
268			public void onPresenceSelected() {
269				if (attachmentChoice == ATTACHMENT_CHOICE_TAKE_PHOTO) {
270					pendingImageUri = xmppConnectionService.getFileBackend()
271							.getTakePhotoUri();
272					Intent takePictureIntent = new Intent(
273							MediaStore.ACTION_IMAGE_CAPTURE);
274					takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
275							pendingImageUri);
276					if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
277						startActivityForResult(takePictureIntent,
278								REQUEST_IMAGE_CAPTURE);
279					}
280				} else if (attachmentChoice == ATTACHMENT_CHOICE_CHOOSE_IMAGE) {
281					Intent attachFileIntent = new Intent();
282					attachFileIntent.setType("image/*");
283					attachFileIntent.setAction(Intent.ACTION_GET_CONTENT);
284					Intent chooser = Intent.createChooser(attachFileIntent,
285							getString(R.string.attach_file));
286					startActivityForResult(chooser, REQUEST_ATTACH_FILE_DIALOG);
287				} else if (attachmentChoice == ATTACHMENT_CHOICE_RECORD_VOICE) {
288					Intent intent = new Intent(
289							MediaStore.Audio.Media.RECORD_SOUND_ACTION);
290					startActivityForResult(intent, REQUEST_RECORD_AUDIO);
291				}
292			}
293		});
294	}
295
296	private void attachFile(final int attachmentChoice) {
297		final Conversation conversation = getSelectedConversation();
298		if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
299			if (hasPgp()) {
300				if (conversation.getContact().getPgpKeyId() != 0) {
301					xmppConnectionService.getPgpEngine().hasKey(
302							conversation.getContact(),
303							new UiCallback<Contact>() {
304
305								@Override
306								public void userInputRequried(PendingIntent pi,
307										Contact contact) {
308									ConversationActivity.this.runIntent(pi,
309											attachmentChoice);
310								}
311
312								@Override
313								public void success(Contact contact) {
314									selectPresenceToAttachFile(attachmentChoice);
315								}
316
317								@Override
318								public void error(int error, Contact contact) {
319									displayErrorDialog(error);
320								}
321							});
322				} else {
323					final ConversationFragment fragment = (ConversationFragment) getFragmentManager()
324							.findFragmentByTag("conversation");
325					if (fragment != null) {
326						fragment.showNoPGPKeyDialog(false,
327								new OnClickListener() {
328
329									@Override
330									public void onClick(DialogInterface dialog,
331											int which) {
332										conversation
333												.setNextEncryption(Message.ENCRYPTION_NONE);
334										selectPresenceToAttachFile(attachmentChoice);
335									}
336								});
337					}
338				}
339			} else {
340				showInstallPgpDialog();
341			}
342		} else if (getSelectedConversation().getNextEncryption() == Message.ENCRYPTION_NONE) {
343			selectPresenceToAttachFile(attachmentChoice);
344		} else {
345			selectPresenceToAttachFile(attachmentChoice);
346		}
347	}
348
349	@Override
350	public boolean onOptionsItemSelected(MenuItem item) {
351		switch (item.getItemId()) {
352		case android.R.id.home:
353			spl.openPane();
354			return true;
355		case R.id.action_attach_file:
356			View menuAttachFile = findViewById(R.id.action_attach_file);
357			if (menuAttachFile == null) {
358				break;
359			}
360			PopupMenu attachFilePopup = new PopupMenu(this, menuAttachFile);
361			attachFilePopup.inflate(R.menu.attachment_choices);
362			attachFilePopup
363					.setOnMenuItemClickListener(new OnMenuItemClickListener() {
364
365						@Override
366						public boolean onMenuItemClick(MenuItem item) {
367							switch (item.getItemId()) {
368							case R.id.attach_choose_picture:
369								attachFile(ATTACHMENT_CHOICE_CHOOSE_IMAGE);
370								break;
371							case R.id.attach_take_picture:
372								attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO);
373								break;
374							case R.id.attach_record_voice:
375								attachFile(ATTACHMENT_CHOICE_RECORD_VOICE);
376								break;
377							}
378							return false;
379						}
380					});
381			attachFilePopup.show();
382			break;
383		case R.id.action_add:
384			startActivity(new Intent(this, StartConversationActivity.class));
385			break;
386		case R.id.action_archive:
387			this.endConversation(getSelectedConversation());
388			break;
389		case R.id.action_contact_details:
390			Contact contact = this.getSelectedConversation().getContact();
391			if (contact.showInRoster()) {
392				switchToContactDetails(contact);
393			} else {
394				showAddToRosterDialog(getSelectedConversation());
395			}
396			break;
397		case R.id.action_muc_details:
398			Intent intent = new Intent(this, ConferenceDetailsActivity.class);
399			intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC);
400			intent.putExtra("uuid", getSelectedConversation().getUuid());
401			startActivity(intent);
402			break;
403		case R.id.action_invite:
404			inviteToConversation(getSelectedConversation());
405			break;
406		case R.id.action_security:
407			final Conversation conversation = getSelectedConversation();
408			View menuItemView = findViewById(R.id.action_security);
409			if (menuItemView == null) {
410				break;
411			}
412			PopupMenu popup = new PopupMenu(this, menuItemView);
413			final ConversationFragment fragment = (ConversationFragment) getFragmentManager()
414					.findFragmentByTag("conversation");
415			if (fragment != null) {
416				popup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
417
418					@Override
419					public boolean onMenuItemClick(MenuItem item) {
420						switch (item.getItemId()) {
421						case R.id.encryption_choice_none:
422							conversation
423									.setNextEncryption(Message.ENCRYPTION_NONE);
424							item.setChecked(true);
425							break;
426						case R.id.encryption_choice_otr:
427							conversation
428									.setNextEncryption(Message.ENCRYPTION_OTR);
429							item.setChecked(true);
430							break;
431						case R.id.encryption_choice_pgp:
432							if (hasPgp()) {
433								if (conversation.getAccount().getKeys()
434										.has("pgp_signature")) {
435									conversation
436											.setNextEncryption(Message.ENCRYPTION_PGP);
437									item.setChecked(true);
438								} else {
439									announcePgp(conversation.getAccount(),
440											conversation);
441								}
442							} else {
443								showInstallPgpDialog();
444							}
445							break;
446						default:
447							conversation
448									.setNextEncryption(Message.ENCRYPTION_NONE);
449							break;
450						}
451						fragment.updateChatMsgHint();
452						return true;
453					}
454				});
455				popup.inflate(R.menu.encryption_choices);
456				MenuItem otr = popup.getMenu().findItem(
457						R.id.encryption_choice_otr);
458				if (conversation.getMode() == Conversation.MODE_MULTI) {
459					otr.setEnabled(false);
460				}
461				switch (conversation.getNextEncryption()) {
462				case Message.ENCRYPTION_NONE:
463					popup.getMenu().findItem(R.id.encryption_choice_none)
464							.setChecked(true);
465					break;
466				case Message.ENCRYPTION_OTR:
467					otr.setChecked(true);
468					break;
469				case Message.ENCRYPTION_PGP:
470					popup.getMenu().findItem(R.id.encryption_choice_pgp)
471							.setChecked(true);
472					break;
473				default:
474					popup.getMenu().findItem(R.id.encryption_choice_none)
475							.setChecked(true);
476					break;
477				}
478				popup.show();
479			}
480
481			break;
482		case R.id.action_clear_history:
483			clearHistoryDialog(getSelectedConversation());
484			break;
485		default:
486			break;
487		}
488		return super.onOptionsItemSelected(item);
489	}
490
491	public void endConversation(Conversation conversation) {
492		conversation.setStatus(Conversation.STATUS_ARCHIVED);
493		paneShouldBeOpen = true;
494		spl.openPane();
495		xmppConnectionService.archiveConversation(conversation);
496		if (conversationList.size() > 0) {
497			setSelectedConversation(conversationList.get(0));
498		} else {
499			setSelectedConversation(null);
500		}
501	}
502
503	protected void clearHistoryDialog(final Conversation conversation) {
504		AlertDialog.Builder builder = new AlertDialog.Builder(this);
505		builder.setTitle(getString(R.string.clear_conversation_history));
506		View dialogView = getLayoutInflater().inflate(
507				R.layout.dialog_clear_history, null);
508		final CheckBox endConversationCheckBox = (CheckBox) dialogView
509				.findViewById(R.id.end_conversation_checkbox);
510		builder.setView(dialogView);
511		builder.setNegativeButton(getString(R.string.cancel), null);
512		builder.setPositiveButton(getString(R.string.delete_messages),
513				new OnClickListener() {
514
515					@Override
516					public void onClick(DialogInterface dialog, int which) {
517						activity.xmppConnectionService
518								.clearConversationHistory(conversation);
519						if (endConversationCheckBox.isChecked()) {
520							endConversation(conversation);
521						}
522					}
523				});
524		builder.create().show();
525	}
526
527	protected ConversationFragment swapConversationFragment() {
528		ConversationFragment selectedFragment = new ConversationFragment();
529		if (!isFinishing()) {
530
531			FragmentTransaction transaction = getFragmentManager()
532					.beginTransaction();
533			transaction.replace(R.id.selected_conversation, selectedFragment,
534					"conversation");
535
536			transaction.commitAllowingStateLoss();
537		}
538		return selectedFragment;
539	}
540
541	@Override
542	public boolean onKeyDown(int keyCode, KeyEvent event) {
543		if (keyCode == KeyEvent.KEYCODE_BACK) {
544			if (!spl.isOpen()) {
545				spl.openPane();
546				return false;
547			}
548		}
549		return super.onKeyDown(keyCode, event);
550	}
551
552	@Override
553	protected void onNewIntent(Intent intent) {
554		if (xmppConnectionServiceBound) {
555			if ((Intent.ACTION_VIEW.equals(intent.getAction()) && (VIEW_CONVERSATION
556					.equals(intent.getType())))) {
557				String convToView = (String) intent.getExtras().get(
558						CONVERSATION);
559				updateConversationList();
560				for (int i = 0; i < conversationList.size(); ++i) {
561					if (conversationList.get(i).getUuid().equals(convToView)) {
562						setSelectedConversation(conversationList.get(i));
563						break;
564					}
565				}
566				paneShouldBeOpen = false;
567				String text = intent.getExtras().getString(TEXT, null);
568				swapConversationFragment().setText(text);
569			}
570		} else {
571			handledViewIntent = false;
572			setIntent(intent);
573		}
574	}
575
576	@Override
577	public void onStart() {
578		super.onStart();
579		SharedPreferences preferences = PreferenceManager
580				.getDefaultSharedPreferences(this);
581		this.useSubject = preferences.getBoolean("use_subject_in_muc", true);
582		this.showLastseen = preferences.getBoolean("show_last_seen", false);
583		if (this.xmppConnectionServiceBound) {
584			this.onBackendConnected();
585		}
586		if (conversationList.size() >= 1) {
587			onConvChanged.onConversationUpdate();
588		}
589	}
590
591	@Override
592	protected void onStop() {
593		if (xmppConnectionServiceBound) {
594			xmppConnectionService.removeOnConversationListChangedListener();
595		}
596		super.onStop();
597	}
598
599	@Override
600	void onBackendConnected() {
601		this.registerListener();
602		if (conversationList.size() == 0) {
603			updateConversationList();
604		}
605
606		if (getSelectedConversation() != null && pendingImageUri != null) {
607			attachImageToConversation(getSelectedConversation(),
608					pendingImageUri);
609			pendingImageUri = null;
610		} else {
611			pendingImageUri = null;
612		}
613
614		if ((getIntent().getAction() != null)
615				&& (getIntent().getAction().equals(Intent.ACTION_VIEW) && (!handledViewIntent))) {
616			if (getIntent().getType().equals(
617					ConversationActivity.VIEW_CONVERSATION)) {
618				handledViewIntent = true;
619
620				String convToView = (String) getIntent().getExtras().get(
621						CONVERSATION);
622
623				for (int i = 0; i < conversationList.size(); ++i) {
624					if (conversationList.get(i).getUuid().equals(convToView)) {
625						setSelectedConversation(conversationList.get(i));
626					}
627				}
628				paneShouldBeOpen = false;
629				String text = getIntent().getExtras().getString(TEXT, null);
630				swapConversationFragment().setText(text);
631			}
632		} else {
633			if (xmppConnectionService.getAccounts().size() == 0) {
634				startActivity(new Intent(this, EditAccountActivity.class));
635			} else if (conversationList.size() <= 0) {
636				// add no history
637				startActivity(new Intent(this, StartConversationActivity.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,
664			final Intent data) {
665		super.onActivityResult(requestCode, resultCode, data);
666		if (resultCode == RESULT_OK) {
667			if (requestCode == REQUEST_DECRYPT_PGP) {
668				ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager()
669						.findFragmentByTag("conversation");
670				if (selectedFragment != null) {
671					selectedFragment.hideSnackbar();
672				}
673			} else if (requestCode == REQUEST_ATTACH_FILE_DIALOG) {
674				pendingImageUri = data.getData();
675				if (xmppConnectionServiceBound) {
676					attachImageToConversation(getSelectedConversation(),
677							pendingImageUri);
678					pendingImageUri = null;
679				}
680			} else if (requestCode == REQUEST_SEND_PGP_IMAGE) {
681
682			} else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_IMAGE) {
683				attachFile(ATTACHMENT_CHOICE_CHOOSE_IMAGE);
684			} else if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO) {
685				attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO);
686			} else if (requestCode == REQUEST_ANNOUNCE_PGP) {
687				announcePgp(getSelectedConversation().getAccount(),
688						getSelectedConversation());
689			} else if (requestCode == REQUEST_ENCRYPT_MESSAGE) {
690				// encryptTextMessage();
691			} else if (requestCode == REQUEST_IMAGE_CAPTURE) {
692				if (xmppConnectionServiceBound) {
693					attachImageToConversation(getSelectedConversation(),
694							pendingImageUri);
695					pendingImageUri = null;
696				}
697				Intent intent = new Intent(
698						Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
699				intent.setData(pendingImageUri);
700				sendBroadcast(intent);
701			} else if (requestCode == REQUEST_RECORD_AUDIO) {
702				attachAudioToConversation(getSelectedConversation(),
703						data.getData());
704			}
705		}
706	}
707
708	private void attachAudioToConversation(Conversation conversation, Uri uri) {
709
710	}
711
712	private void attachImageToConversation(Conversation conversation, Uri uri) {
713		prepareImageToast = Toast.makeText(getApplicationContext(),
714				getText(R.string.preparing_image), Toast.LENGTH_LONG);
715		prepareImageToast.show();
716		xmppConnectionService.attachImageToConversation(conversation, uri,
717				new UiCallback<Message>() {
718
719					@Override
720					public void userInputRequried(PendingIntent pi,
721							Message object) {
722						hidePrepareImageToast();
723						ConversationActivity.this.runIntent(pi,
724								ConversationActivity.REQUEST_SEND_PGP_IMAGE);
725					}
726
727					@Override
728					public void success(Message message) {
729						xmppConnectionService.sendMessage(message);
730					}
731
732					@Override
733					public void error(int error, Message message) {
734						hidePrepareImageToast();
735						displayErrorDialog(error);
736					}
737				});
738	}
739
740	private void hidePrepareImageToast() {
741		if (prepareImageToast != null) {
742			runOnUiThread(new Runnable() {
743
744				@Override
745				public void run() {
746					prepareImageToast.cancel();
747				}
748			});
749		}
750	}
751
752	public void updateConversationList() {
753		xmppConnectionService
754				.populateWithOrderedConversations(conversationList);
755		listView.invalidateViews();
756	}
757
758	public boolean showLastseen() {
759		if (getSelectedConversation() == null) {
760			return false;
761		} else {
762			return this.showLastseen
763					&& getSelectedConversation().getMode() == Conversation.MODE_SINGLE;
764		}
765	}
766
767	public void runIntent(PendingIntent pi, int requestCode) {
768		try {
769			this.startIntentSenderForResult(pi.getIntentSender(), requestCode,
770					null, 0, 0, 0);
771		} catch (SendIntentException e1) {
772		}
773	}
774
775	class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
776		private final WeakReference<ImageView> imageViewReference;
777		private Message message = null;
778
779		public BitmapWorkerTask(ImageView imageView) {
780			imageViewReference = new WeakReference<ImageView>(imageView);
781		}
782
783		@Override
784		protected Bitmap doInBackground(Message... params) {
785			message = params[0];
786			try {
787				return xmppConnectionService.getFileBackend().getThumbnail(
788						message, (int) (metrics.density * 288), false);
789			} catch (FileNotFoundException e) {
790				return null;
791			}
792		}
793
794		@Override
795		protected void onPostExecute(Bitmap bitmap) {
796			if (imageViewReference != null && bitmap != null) {
797				final ImageView imageView = imageViewReference.get();
798				if (imageView != null) {
799					imageView.setImageBitmap(bitmap);
800					imageView.setBackgroundColor(0x00000000);
801				}
802			}
803		}
804	}
805
806	public void loadBitmap(Message message, ImageView imageView) {
807		Bitmap bm;
808		try {
809			bm = xmppConnectionService.getFileBackend().getThumbnail(message,
810					(int) (metrics.density * 288), true);
811		} catch (FileNotFoundException e) {
812			bm = null;
813		}
814		if (bm != null) {
815			imageView.setImageBitmap(bm);
816			imageView.setBackgroundColor(0x00000000);
817		} else {
818			if (cancelPotentialWork(message, imageView)) {
819				imageView.setBackgroundColor(0xff333333);
820				final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
821				final AsyncDrawable asyncDrawable = new AsyncDrawable(
822						getResources(), null, task);
823				imageView.setImageDrawable(asyncDrawable);
824				try {
825					task.execute(message);
826				} catch (RejectedExecutionException e) {
827					return;
828				}
829			}
830		}
831	}
832
833	public static boolean cancelPotentialWork(Message message,
834			ImageView imageView) {
835		final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
836
837		if (bitmapWorkerTask != null) {
838			final Message oldMessage = bitmapWorkerTask.message;
839			if (oldMessage == null || message != oldMessage) {
840				bitmapWorkerTask.cancel(true);
841			} else {
842				return false;
843			}
844		}
845		return true;
846	}
847
848	private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
849		if (imageView != null) {
850			final Drawable drawable = imageView.getDrawable();
851			if (drawable instanceof AsyncDrawable) {
852				final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
853				return asyncDrawable.getBitmapWorkerTask();
854			}
855		}
856		return null;
857	}
858
859	static class AsyncDrawable extends BitmapDrawable {
860		private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
861
862		public AsyncDrawable(Resources res, Bitmap bitmap,
863				BitmapWorkerTask bitmapWorkerTask) {
864			super(res, bitmap);
865			bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>(
866					bitmapWorkerTask);
867		}
868
869		public BitmapWorkerTask getBitmapWorkerTask() {
870			return bitmapWorkerTaskReference.get();
871		}
872	}
873
874	public void encryptTextMessage(Message message) {
875		xmppConnectionService.getPgpEngine().encrypt(message,
876				new UiCallback<Message>() {
877
878					@Override
879					public void userInputRequried(PendingIntent pi,
880							Message message) {
881						activity.runIntent(pi,
882								ConversationActivity.REQUEST_SEND_MESSAGE);
883					}
884
885					@Override
886					public void success(Message message) {
887						message.setEncryption(Message.ENCRYPTION_DECRYPTED);
888						xmppConnectionService.sendMessage(message);
889					}
890
891					@Override
892					public void error(int error, Message message) {
893
894					}
895				});
896	}
897}