ConversationActivity.java

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