aboutsummaryrefslogtreecommitdiffstats
path: root/src/eu/siacs/conversations/ui
diff options
context:
space:
mode:
Diffstat (limited to 'src/eu/siacs/conversations/ui')
-rw-r--r--src/eu/siacs/conversations/ui/ConversationActivity.java481
-rw-r--r--src/eu/siacs/conversations/ui/ConversationFragment.java602
-rw-r--r--src/eu/siacs/conversations/ui/DialogContactDetails.java218
-rw-r--r--src/eu/siacs/conversations/ui/EditAccount.java138
-rw-r--r--src/eu/siacs/conversations/ui/ManageAccountActivity.java312
-rw-r--r--src/eu/siacs/conversations/ui/NewConversationActivity.java332
-rw-r--r--src/eu/siacs/conversations/ui/OnAccountListChangedListener.java5
-rw-r--r--src/eu/siacs/conversations/ui/OnConversationListChangedListener.java5
-rw-r--r--src/eu/siacs/conversations/ui/OnRosterFetchedListener.java9
-rw-r--r--src/eu/siacs/conversations/ui/SettingsActivity.java16
-rw-r--r--src/eu/siacs/conversations/ui/SettingsFragment.java15
-rw-r--r--src/eu/siacs/conversations/ui/XmppActivity.java52
12 files changed, 2185 insertions, 0 deletions
diff --git a/src/eu/siacs/conversations/ui/ConversationActivity.java b/src/eu/siacs/conversations/ui/ConversationActivity.java
new file mode 100644
index 00000000..7660b8fd
--- /dev/null
+++ b/src/eu/siacs/conversations/ui/ConversationActivity.java
@@ -0,0 +1,481 @@
+package eu.siacs.conversations.ui;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Contact;
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.utils.UIHelper;
+import android.net.Uri;
+import android.os.Bundle;
+import android.app.AlertDialog;
+import android.app.FragmentTransaction;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.graphics.Typeface;
+import android.support.v4.widget.SlidingPaneLayout;
+import android.support.v4.widget.SlidingPaneLayout.PanelSlideListener;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.PopupMenu;
+import android.widget.PopupMenu.OnMenuItemClickListener;
+import android.widget.TextView;
+import android.widget.ImageView;
+
+public class ConversationActivity extends XmppActivity {
+
+ public static final String VIEW_CONVERSATION = "viewConversation";
+ public static final String CONVERSATION = "conversationUuid";
+
+ public static final int REQUEST_SEND_MESSAGE = 0x75441;
+ public static final int REQUEST_DECRYPT_PGP = 0x76783;
+
+ protected SlidingPaneLayout spl;
+
+ private List<Conversation> conversationList = new ArrayList<Conversation>();
+ private Conversation selectedConversation = null;
+ private ListView listView;
+
+ private boolean paneShouldBeOpen = true;
+ private ArrayAdapter<Conversation> listAdapter;
+
+ private OnConversationListChangedListener onConvChanged = new OnConversationListChangedListener() {
+
+ @Override
+ public void onConversationListChanged() {
+ conversationList.clear();
+ conversationList.addAll(xmppConnectionService
+ .getConversations());
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ updateConversationList();
+ if(paneShouldBeOpen) {
+ if (conversationList.size() >= 1) {
+ swapConversationFragment();
+ } else {
+ startActivity(new Intent(getApplicationContext(), NewConversationActivity.class));
+ finish();
+ }
+ }
+ ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager().findFragmentByTag("conversation");
+ if (selectedFragment!=null) {
+ selectedFragment.updateMessages();
+ }
+ }
+ });
+ }
+ };
+
+ private DialogInterface.OnClickListener addToRoster = new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ String jid = getSelectedConversation().getContactJid();
+ Account account = getSelectedConversation().getAccount();
+ String name = jid.split("@")[0];
+ Contact contact = new Contact(account, name, jid, null);
+ xmppConnectionService.createContact(contact);
+ }
+ };
+
+ public List<Conversation> getConversationList() {
+ return this.conversationList;
+ }
+
+ public Conversation getSelectedConversation() {
+ return this.selectedConversation;
+ }
+
+ public ListView getConversationListView() {
+ return this.listView;
+ }
+
+ public SlidingPaneLayout getSlidingPaneLayout() {
+ return this.spl;
+ }
+
+ public boolean shouldPaneBeOpen() {
+ return paneShouldBeOpen;
+ }
+
+ public void updateConversationList() {
+ if (conversationList.size() >= 1) {
+ Collections.sort(this.conversationList, new Comparator<Conversation>() {
+ @Override
+ public int compare(Conversation lhs, Conversation rhs) {
+ return (int) (rhs.getLatestMessage().getTimeSent() - lhs.getLatestMessage().getTimeSent());
+ }
+ });
+ }
+ this.listView.invalidateViews();
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.fragment_conversations_overview);
+
+ listView = (ListView) findViewById(R.id.list);
+
+ this.listAdapter = new ArrayAdapter<Conversation>(this,
+ R.layout.conversation_list_row, conversationList) {
+ @Override
+ public View getView(int position, View view, ViewGroup parent) {
+ if (view == null) {
+ LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ view = (View) inflater.inflate(
+ R.layout.conversation_list_row, null);
+ }
+ Conversation conv = getItem(position);
+ TextView convName = (TextView) view.findViewById(R.id.conversation_name);
+ convName.setText(conv.getName());
+ TextView convLastMsg = (TextView) view.findViewById(R.id.conversation_lastmsg);
+ convLastMsg.setText(conv.getLatestMessage().getBody());
+
+ if(!conv.isRead()) {
+ convName.setTypeface(null,Typeface.BOLD);
+ convLastMsg.setTypeface(null,Typeface.BOLD);
+ } else {
+ convName.setTypeface(null,Typeface.NORMAL);
+ convLastMsg.setTypeface(null,Typeface.NORMAL);
+ }
+
+ ((TextView) view.findViewById(R.id.conversation_lastupdate))
+ .setText(UIHelper.readableTimeDifference(getItem(position).getLatestMessage().getTimeSent()));
+
+ Uri profilePhoto = getItem(position).getProfilePhotoUri();
+ ImageView imageView = (ImageView) view.findViewById(R.id.conversation_image);
+ if (profilePhoto!=null) {
+ imageView.setImageURI(profilePhoto);
+ } else {
+ imageView.setImageBitmap(UIHelper.getUnknownContactPicture(getItem(position).getName(),200));
+ }
+
+
+ ((ImageView) view.findViewById(R.id.conversation_image))
+ .setImageURI(getItem(position).getProfilePhotoUri());
+ return view;
+ }
+
+ };
+
+ listView.setAdapter(this.listAdapter);
+
+ listView.setOnItemClickListener(new OnItemClickListener() {
+
+ @Override
+ public void onItemClick(AdapterView<?> arg0, View clickedView,
+ int position, long arg3) {
+ paneShouldBeOpen = false;
+ if (selectedConversation != conversationList.get(position)) {
+ selectedConversation = conversationList.get(position);
+ swapConversationFragment(); //.onBackendConnected(conversationList.get(position));
+ } else {
+ spl.closePane();
+ }
+ }
+ });
+ spl = (SlidingPaneLayout) findViewById(R.id.slidingpanelayout);
+ spl.setParallaxDistance(150);
+ spl.setShadowResource(R.drawable.es_slidingpane_shadow);
+ spl.setSliderFadeColor(0);
+ spl.setPanelSlideListener(new PanelSlideListener() {
+
+ @Override
+ public void onPanelOpened(View arg0) {
+ paneShouldBeOpen = true;
+ getActionBar().setDisplayHomeAsUpEnabled(false);
+ getActionBar().setTitle(R.string.app_name);
+ invalidateOptionsMenu();
+
+ InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+
+ View focus = getCurrentFocus();
+
+ if (focus != null) {
+
+ inputManager.hideSoftInputFromWindow(
+ focus.getWindowToken(),
+ InputMethodManager.HIDE_NOT_ALWAYS);
+ }
+ }
+
+ @Override
+ public void onPanelClosed(View arg0) {
+ paneShouldBeOpen = false;
+ if (conversationList.size() > 0) {
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ getActionBar().setTitle(getSelectedConversation().getName());
+ invalidateOptionsMenu();
+ if (!getSelectedConversation().isRead()) {
+ getSelectedConversation().markRead();
+ updateConversationList();
+ }
+ }
+ }
+
+ @Override
+ public void onPanelSlide(View arg0, float arg1) {
+ // TODO Auto-generated method stub
+
+ }
+ });
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.conversations, menu);
+ MenuItem menuSecure = (MenuItem) menu.findItem(R.id.action_security);
+ MenuItem menuArchive = (MenuItem) menu.findItem(R.id.action_archive);
+ MenuItem menuMucDetails = (MenuItem) menu.findItem(R.id.action_muc_details);
+ MenuItem menuContactDetails = (MenuItem) menu.findItem(R.id.action_contact_details);
+
+ if (spl.isOpen()) {
+ menuArchive.setVisible(false);
+ menuMucDetails.setVisible(false);
+ menuContactDetails.setVisible(false);
+ menuSecure.setVisible(false);
+ } else {
+ ((MenuItem) menu.findItem(R.id.action_add)).setVisible(false);
+ if (this.getSelectedConversation()!=null) {
+ if (this.getSelectedConversation().getMode() == Conversation.MODE_MULTI) {
+ menuMucDetails.setVisible(true);
+ menuContactDetails.setVisible(false);
+ menuSecure.setVisible(false);
+ menuArchive.setTitle("Leave conference");
+ } else {
+ menuContactDetails.setVisible(true);
+ menuMucDetails.setVisible(false);
+ if (this.getSelectedConversation().getLatestMessage().getEncryption() != Message.ENCRYPTION_NONE) {
+ menuSecure.setIcon(R.drawable.ic_action_secure);
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ spl.openPane();
+ break;
+ case R.id.action_settings:
+ startActivity(new Intent(this, SettingsActivity.class));
+ break;
+ case R.id.action_accounts:
+ startActivity(new Intent(this, ManageAccountActivity.class));
+ break;
+ case R.id.action_add:
+ startActivity(new Intent(this, NewConversationActivity.class));
+ break;
+ case R.id.action_archive:
+ Conversation conv = getSelectedConversation();
+ conv.setStatus(Conversation.STATUS_ARCHIVED);
+ paneShouldBeOpen = true;
+ spl.openPane();
+ xmppConnectionService.archiveConversation(conv);
+ selectedConversation = conversationList.get(0);
+ break;
+ case R.id.action_contact_details:
+ DialogContactDetails details = new DialogContactDetails();
+ Contact contact = this.getSelectedConversation().getContact();
+ if (contact != null) {
+ contact.setAccount(this.selectedConversation.getAccount());
+ details.setContact(contact);
+ details.show(getFragmentManager(), "details");
+ } else {
+ String jid = getSelectedConversation().getContactJid();
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(jid);
+ builder.setMessage("The contact is not in your roster. Would you like to add it.");
+ builder.setNegativeButton("Cancel", null);
+ builder.setPositiveButton("Add",addToRoster);
+ builder.create().show();
+ }
+ break;
+ case R.id.action_security:
+ final Conversation selConv = getSelectedConversation();
+ View menuItemView = findViewById(R.id.action_security);
+ PopupMenu popup = new PopupMenu(this, menuItemView);
+ final ConversationFragment fragment = (ConversationFragment) getFragmentManager().findFragmentByTag("conversation");
+ if (fragment!=null) {
+ popup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.encryption_choice_none:
+ selConv.nextMessageEncryption = Message.ENCRYPTION_NONE;
+ item.setChecked(true);
+ break;
+ case R.id.encryption_choice_otr:
+ selConv.nextMessageEncryption = Message.ENCRYPTION_OTR;
+ item.setChecked(true);
+ break;
+ case R.id.encryption_choice_pgp:
+ selConv.nextMessageEncryption = Message.ENCRYPTION_PGP;
+ item.setChecked(true);
+ break;
+ default:
+ selConv.nextMessageEncryption = Message.ENCRYPTION_NONE;
+ break;
+ }
+ fragment.updateChatMsgHint();
+ return true;
+ }
+ });
+ popup.inflate(R.menu.encryption_choices);
+ switch (selConv.nextMessageEncryption) {
+ case Message.ENCRYPTION_NONE:
+ popup.getMenu().findItem(R.id.encryption_choice_none).setChecked(true);
+ break;
+ case Message.ENCRYPTION_OTR:
+ popup.getMenu().findItem(R.id.encryption_choice_otr).setChecked(true);
+ break;
+ case Message.ENCRYPTION_PGP:
+ popup.getMenu().findItem(R.id.encryption_choice_pgp).setChecked(true);
+ break;
+ case Message.ENCRYPTION_DECRYPTED:
+ popup.getMenu().findItem(R.id.encryption_choice_pgp).setChecked(true);
+ break;
+ default:
+ popup.getMenu().findItem(R.id.encryption_choice_none).setChecked(true);
+ break;
+ }
+ popup.show();
+ }
+
+ break;
+ default:
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ protected ConversationFragment swapConversationFragment() {
+ ConversationFragment selectedFragment = new ConversationFragment();
+
+ FragmentTransaction transaction = getFragmentManager()
+ .beginTransaction();
+ transaction.replace(R.id.selected_conversation, selectedFragment,"conversation");
+ transaction.commit();
+ return selectedFragment;
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ if (!spl.isOpen()) {
+ spl.openPane();
+ return false;
+ }
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ public void onStart() {
+ super.onStart();
+ NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ nm.cancelAll();
+ if (conversationList.size()>=1) {
+ onConvChanged.onConversationListChanged();
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ Log.d("gultsch","called on stop in conversation activity");
+ if (xmppConnectionServiceBound) {
+ xmppConnectionService.removeOnConversationListChangedListener();
+ }
+ super.onStop();
+ }
+
+ @Override
+ void onBackendConnected() {
+
+ xmppConnectionService.setOnConversationListChangedListener(this.onConvChanged);
+
+ if (conversationList.size()==0) {
+ conversationList.clear();
+ conversationList.addAll(xmppConnectionService
+ .getConversations());
+
+ this.updateConversationList();
+ }
+
+ if ((getIntent().getAction().equals(Intent.ACTION_VIEW) && (!handledViewIntent))) {
+ if (getIntent().getType().equals(
+ ConversationActivity.VIEW_CONVERSATION)) {
+ handledViewIntent = true;
+
+ String convToView = (String) getIntent().getExtras().get(CONVERSATION);
+
+ for(int i = 0; i < conversationList.size(); ++i) {
+ if (conversationList.get(i).getUuid().equals(convToView)) {
+ selectedConversation = conversationList.get(i);
+ }
+ }
+ paneShouldBeOpen = false;
+ swapConversationFragment();
+ }
+ } else {
+ if (xmppConnectionService.getAccounts().size() == 0) {
+ startActivity(new Intent(this, ManageAccountActivity.class));
+ finish();
+ } else if (conversationList.size() <= 0) {
+ //add no history
+ startActivity(new Intent(this, NewConversationActivity.class));
+ finish();
+ } else {
+ spl.openPane();
+ //find currently loaded fragment
+ ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager().findFragmentByTag("conversation");
+ if (selectedFragment!=null) {
+ Log.d("gultsch","ConversationActivity. found old fragment.");
+ selectedFragment.onBackendConnected();
+ } else {
+ Log.d("gultsch","conversationactivity. no old fragment found. creating new one");
+ selectedConversation = conversationList.get(0);
+ Log.d("gultsch","selected conversation is #"+selectedConversation);
+ swapConversationFragment();
+ }
+ }
+ }
+ }
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (resultCode == RESULT_OK) {
+ if (requestCode == REQUEST_DECRYPT_PGP) {
+ ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager().findFragmentByTag("conversation");
+ if (selectedFragment!=null) {
+ selectedFragment.hidePgpPassphraseBox();
+ }
+ }
+ }
+ }
+}
diff --git a/src/eu/siacs/conversations/ui/ConversationFragment.java b/src/eu/siacs/conversations/ui/ConversationFragment.java
new file mode 100644
index 00000000..ff06fafc
--- /dev/null
+++ b/src/eu/siacs/conversations/ui/ConversationFragment.java
@@ -0,0 +1,602 @@
+package eu.siacs.conversations.ui;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import net.java.otr4j.session.SessionStatus;
+
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.crypto.PgpEngine.OpenPgpException;
+import eu.siacs.conversations.crypto.PgpEngine.UserInputRequiredException;
+import eu.siacs.conversations.entities.Contact;
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.utils.PhoneHelper;
+import eu.siacs.conversations.utils.UIHelper;
+import android.app.AlertDialog;
+import android.app.Fragment;
+import android.content.DialogInterface;
+import android.content.IntentSender;
+import android.content.SharedPreferences;
+import android.content.IntentSender.SendIntentException;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Typeface;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+public class ConversationFragment extends Fragment {
+
+ protected Conversation conversation;
+ protected ListView messagesView;
+ protected LayoutInflater inflater;
+ protected List<Message> messageList = new ArrayList<Message>();
+ protected ArrayAdapter<Message> messageListAdapter;
+ protected Contact contact;
+ protected BitmapCache mBitmapCache = new BitmapCache();
+
+ protected String queuedPqpMessage = null;
+
+ private EditText chatMsg;
+
+ protected Bitmap selfBitmap;
+
+ private IntentSender askForPassphraseIntent = null;
+
+ private OnClickListener sendMsgListener = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (chatMsg.getText().length() < 1)
+ return;
+ Message message = new Message(conversation, chatMsg.getText()
+ .toString(), conversation.nextMessageEncryption);
+ if (conversation.nextMessageEncryption == Message.ENCRYPTION_OTR) {
+ sendOtrMessage(message);
+ } else if (conversation.nextMessageEncryption == Message.ENCRYPTION_PGP) {
+ sendPgpMessage(message);
+ } else {
+ sendPlainTextMessage(message);
+ }
+ }
+ };
+ protected OnClickListener clickToDecryptListener = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ Log.d("gultsch","clicked to decrypt");
+ if (askForPassphraseIntent!=null) {
+ try {
+ getActivity().startIntentSenderForResult(askForPassphraseIntent, ConversationActivity.REQUEST_DECRYPT_PGP, null, 0, 0, 0);
+ } catch (SendIntentException e) {
+ Log.d("gultsch","couldnt fire intent");
+ }
+ }
+ }
+ };
+ private LinearLayout pgpInfo;
+
+ public void hidePgpPassphraseBox() {
+ pgpInfo.setVisibility(View.GONE);
+ }
+
+ public void updateChatMsgHint() {
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ chatMsg.setHint("Send message to conference");
+ } else {
+ switch (conversation.nextMessageEncryption) {
+ case Message.ENCRYPTION_NONE:
+ chatMsg.setHint("Send plain text message");
+ break;
+ case Message.ENCRYPTION_OTR:
+ chatMsg.setHint("Send OTR encrypted message");
+ break;
+ case Message.ENCRYPTION_PGP:
+ chatMsg.setHint("Send openPGP encryted messeage");
+ break;
+ case Message.ENCRYPTION_DECRYPTED:
+ chatMsg.setHint("Send openPGP encryted messeage");
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ @Override
+ public View onCreateView(final LayoutInflater inflater,
+ ViewGroup container, Bundle savedInstanceState) {
+
+ this.inflater = inflater;
+
+ final View view = inflater.inflate(R.layout.fragment_conversation,
+ container, false);
+ chatMsg = (EditText) view.findViewById(R.id.textinput);
+ ImageButton sendButton = (ImageButton) view
+ .findViewById(R.id.textSendButton);
+ sendButton.setOnClickListener(this.sendMsgListener);
+
+ pgpInfo = (LinearLayout) view.findViewById(R.id.pgp_keyentry);
+ pgpInfo.setOnClickListener(clickToDecryptListener);
+
+ messagesView = (ListView) view.findViewById(R.id.messages_view);
+
+ messageListAdapter = new ArrayAdapter<Message>(this.getActivity()
+ .getApplicationContext(), R.layout.message_sent,
+ this.messageList) {
+
+ private static final int SENT = 0;
+ private static final int RECIEVED = 1;
+ private static final int ERROR = 2;
+
+ @Override
+ public int getViewTypeCount() {
+ return 3;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if (getItem(position).getStatus() == Message.STATUS_RECIEVED) {
+ return RECIEVED;
+ } else if (getItem(position).getStatus() == Message.STATUS_ERROR) {
+ return ERROR;
+ } else {
+ return SENT;
+ }
+ }
+
+ @Override
+ public View getView(int position, View view, ViewGroup parent) {
+ Message item = getItem(position);
+ int type = getItemViewType(position);
+ ViewHolder viewHolder;
+ if (view == null) {
+ viewHolder = new ViewHolder();
+ switch (type) {
+ case SENT:
+ view = (View) inflater.inflate(R.layout.message_sent,
+ null);
+ viewHolder.imageView = (ImageView) view
+ .findViewById(R.id.message_photo);
+ viewHolder.imageView.setImageBitmap(selfBitmap);
+ break;
+ case RECIEVED:
+ view = (View) inflater.inflate(
+ R.layout.message_recieved, null);
+ viewHolder.imageView = (ImageView) view
+ .findViewById(R.id.message_photo);
+ if (item.getConversation().getMode() == Conversation.MODE_SINGLE) {
+ Uri uri = item.getConversation()
+ .getProfilePhotoUri();
+ if (uri != null) {
+ viewHolder.imageView
+ .setImageBitmap(mBitmapCache.get(item
+ .getConversation().getName(),
+ uri));
+ } else {
+ viewHolder.imageView
+ .setImageBitmap(mBitmapCache.get(item
+ .getConversation().getName(),
+ null));
+ }
+ }
+ break;
+ case ERROR:
+ view = (View) inflater.inflate(R.layout.message_error,
+ null);
+ viewHolder.imageView = (ImageView) view
+ .findViewById(R.id.message_photo);
+ viewHolder.imageView.setImageBitmap(mBitmapCache
+ .getError());
+ break;
+ default:
+ viewHolder = null;
+ break;
+ }
+ viewHolder.messageBody = (TextView) view
+ .findViewById(R.id.message_body);
+ viewHolder.time = (TextView) view
+ .findViewById(R.id.message_time);
+ view.setTag(viewHolder);
+ } else {
+ viewHolder = (ViewHolder) view.getTag();
+ }
+ if (type == RECIEVED) {
+ if (item.getConversation().getMode() == Conversation.MODE_MULTI) {
+ if (item.getCounterpart() != null) {
+ viewHolder.imageView.setImageBitmap(mBitmapCache
+ .get(item.getCounterpart(), null));
+ } else {
+ viewHolder.imageView
+ .setImageBitmap(mBitmapCache.get(item
+ .getConversation().getName(), null));
+ }
+ }
+ }
+ String body = item.getBody();
+ if (body != null) {
+ if (item.getEncryption() == Message.ENCRYPTION_PGP) {
+ viewHolder.messageBody.setText(getString(R.string.encrypted_message));
+ viewHolder.messageBody.setTextColor(0xff33B5E5);
+ viewHolder.messageBody.setTypeface(null,Typeface.ITALIC);
+ } else {
+ viewHolder.messageBody.setText(body.trim());
+ viewHolder.messageBody.setTextColor(0xff000000);
+ viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
+ }
+ }
+ if (item.getStatus() == Message.STATUS_UNSEND) {
+ viewHolder.time.setTypeface(null, Typeface.ITALIC);
+ viewHolder.time.setText("sending\u2026");
+ } else {
+ viewHolder.time.setTypeface(null, Typeface.NORMAL);
+ if ((item.getConversation().getMode() == Conversation.MODE_SINGLE)
+ || (type != RECIEVED)) {
+ viewHolder.time.setText(UIHelper
+ .readableTimeDifference(item.getTimeSent()));
+ } else {
+ viewHolder.time.setText(item.getCounterpart()
+ + " \u00B7 "
+ + UIHelper.readableTimeDifference(item
+ .getTimeSent()));
+ }
+ }
+ return view;
+ }
+ };
+ messagesView.setAdapter(messageListAdapter);
+
+ return view;
+ }
+
+ protected Bitmap findSelfPicture() {
+ SharedPreferences sharedPref = PreferenceManager
+ .getDefaultSharedPreferences(getActivity()
+ .getApplicationContext());
+ boolean showPhoneSelfContactPicture = sharedPref.getBoolean(
+ "show_phone_selfcontact_picture", true);
+
+ Bitmap self = null;
+
+ if (showPhoneSelfContactPicture) {
+ Uri selfiUri = PhoneHelper.getSefliUri(getActivity());
+ if (selfiUri != null) {
+ try {
+ self = BitmapFactory.decodeStream(getActivity()
+ .getContentResolver().openInputStream(selfiUri));
+ } catch (FileNotFoundException e) {
+ self = null;
+ }
+ }
+ }
+ if (self == null) {
+ self = UIHelper.getUnknownContactPicture(conversation.getAccount()
+ .getJid(), 200);
+ }
+
+ final Bitmap selfBitmap = self;
+ return selfBitmap;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ ConversationActivity activity = (ConversationActivity) getActivity();
+
+ if (activity.xmppConnectionServiceBound) {
+ this.onBackendConnected();
+ }
+ }
+
+ public void onBackendConnected() {
+ final ConversationActivity activity = (ConversationActivity) getActivity();
+ this.conversation = activity.getSelectedConversation();
+ this.selfBitmap = findSelfPicture();
+ updateMessages();
+ // rendering complete. now go tell activity to close pane
+ if (!activity.shouldPaneBeOpen()) {
+ activity.getSlidingPaneLayout().closePane();
+ activity.getActionBar().setDisplayHomeAsUpEnabled(true);
+ activity.getActionBar().setTitle(conversation.getName());
+ activity.invalidateOptionsMenu();
+ if (!conversation.isRead()) {
+ conversation.markRead();
+ activity.updateConversationList();
+ }
+ }
+ if (queuedPqpMessage != null) {
+ this.conversation.nextMessageEncryption = Message.ENCRYPTION_PGP;
+ Message message = new Message(conversation, queuedPqpMessage,
+ Message.ENCRYPTION_PGP);
+ sendPgpMessage(message);
+ }
+ }
+
+ public void updateMessages() {
+ ConversationActivity activity = (ConversationActivity) getActivity();
+ List<Message> encryptedMessages = new LinkedList<Message>();
+ for(Message message : this.conversation.getMessages()) {
+ if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+ encryptedMessages.add(message);
+ }
+ }
+ if (encryptedMessages.size() > 0) {
+ DecryptMessage task = new DecryptMessage();
+ Message[] msgs = new Message[encryptedMessages.size()];
+ task.execute(encryptedMessages.toArray(msgs));
+ }
+ this.messageList.clear();
+ this.messageList.addAll(this.conversation.getMessages());
+ this.messageListAdapter.notifyDataSetChanged();
+ if (messageList.size() >= 1) {
+ int latestEncryption = this.conversation.getLatestMessage()
+ .getEncryption();
+ if (latestEncryption== Message.ENCRYPTION_DECRYPTED) {
+ conversation.nextMessageEncryption = Message.ENCRYPTION_PGP;
+ } else {
+ conversation.nextMessageEncryption = latestEncryption;
+ }
+ makeFingerprintWarning(latestEncryption);
+ }
+ getActivity().invalidateOptionsMenu();
+ updateChatMsgHint();
+ int size = this.messageList.size();
+ if (size >= 1)
+ messagesView.setSelection(size - 1);
+ if (!activity.shouldPaneBeOpen()) {
+ conversation.markRead();
+ activity.updateConversationList();
+ }
+ }
+
+ protected void makeFingerprintWarning(int latestEncryption) {
+ final LinearLayout fingerprintWarning = (LinearLayout) getView()
+ .findViewById(R.id.new_fingerprint);
+ if (conversation.getContact() != null) {
+ Set<String> knownFingerprints = conversation.getContact()
+ .getOtrFingerprints();
+ if ((latestEncryption == Message.ENCRYPTION_OTR)
+ && (conversation.hasValidOtrSession()
+ && (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) && (!knownFingerprints
+ .contains(conversation.getOtrFingerprint())))) {
+ fingerprintWarning.setVisibility(View.VISIBLE);
+ TextView fingerprint = (TextView) getView().findViewById(
+ R.id.otr_fingerprint);
+ fingerprint.setText(conversation.getOtrFingerprint());
+ fingerprintWarning.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ AlertDialog dialog = UIHelper
+ .getVerifyFingerprintDialog(
+ (ConversationActivity) getActivity(),
+ conversation, fingerprintWarning);
+ dialog.show();
+ }
+ });
+ } else {
+ fingerprintWarning.setVisibility(View.GONE);
+ }
+ } else {
+ fingerprintWarning.setVisibility(View.GONE);
+ }
+ }
+
+ protected void sendPlainTextMessage(Message message) {
+ ConversationActivity activity = (ConversationActivity) getActivity();
+ activity.xmppConnectionService.sendMessage(message, null);
+ chatMsg.setText("");
+ }
+
+ protected void sendPgpMessage(final Message message) {
+ ConversationActivity activity = (ConversationActivity) getActivity();
+ final XmppConnectionService xmppService = activity.xmppConnectionService;
+ Contact contact = message.getConversation().getContact();
+ if (contact.getPgpKeyId() != 0) {
+ xmppService.sendMessage(message, null);
+ chatMsg.setText("");
+ } else {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setTitle("No openPGP key found");
+ builder.setIconAttribute(android.R.attr.alertDialogIcon);
+ builder.setMessage("There is no openPGP key assoziated with this contact");
+ builder.setNegativeButton("Cancel", null);
+ builder.setPositiveButton("Send plain text",
+ new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ conversation.nextMessageEncryption = Message.ENCRYPTION_NONE;
+ message.setEncryption(Message.ENCRYPTION_NONE);
+ xmppService.sendMessage(message, null);
+ chatMsg.setText("");
+ }
+ });
+ builder.create().show();
+ }
+ }
+
+ public void resendPgpMessage(String msg) {
+ this.queuedPqpMessage = msg;
+ }
+
+ protected void sendOtrMessage(final Message message) {
+ ConversationActivity activity = (ConversationActivity) getActivity();
+ final XmppConnectionService xmppService = activity.xmppConnectionService;
+ if (conversation.hasValidOtrSession()) {
+ activity.xmppConnectionService.sendMessage(message, null);
+ chatMsg.setText("");
+ } else {
+ Hashtable<String, Integer> presences;
+ if (conversation.getContact() != null) {
+ presences = conversation.getContact().getPresences();
+ } else {
+ presences = null;
+ }
+ if ((presences == null) || (presences.size() == 0)) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(
+ getActivity());
+ builder.setTitle("Contact is offline");
+ builder.setIconAttribute(android.R.attr.alertDialogIcon);
+ builder.setMessage("Sending OTR encrypted messages to an offline contact is impossible.");
+ builder.setPositiveButton("Send plain text",
+ new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog,
+ int which) {
+ conversation.nextMessageEncryption = Message.ENCRYPTION_NONE;
+ message.setEncryption(Message.ENCRYPTION_NONE);
+ xmppService.sendMessage(message, null);
+ chatMsg.setText("");
+ }
+ });
+ builder.setNegativeButton("Cancel", null);
+ builder.create().show();
+ } else if (presences.size() == 1) {
+ xmppService.sendMessage(message, (String) presences.keySet()
+ .toArray()[0]);
+ chatMsg.setText("");
+ } else {
+ AlertDialog.Builder builder = new AlertDialog.Builder(
+ getActivity());
+ builder.setTitle("Choose Presence");
+ final String[] presencesArray = new String[presences.size()];
+ presences.keySet().toArray(presencesArray);
+ builder.setItems(presencesArray,
+ new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog,
+ int which) {
+ xmppService.sendMessage(message,
+ presencesArray[which]);
+ chatMsg.setText("");
+ }
+ });
+ builder.create().show();
+ }
+ }
+ }
+
+ private static class ViewHolder {
+
+ protected TextView time;
+ protected TextView messageBody;
+ protected ImageView imageView;
+
+ }
+
+ private class BitmapCache {
+ private HashMap<String, Bitmap> bitmaps = new HashMap<String, Bitmap>();
+ private Bitmap error = null;
+
+ public Bitmap get(String name, Uri uri) {
+ if (bitmaps.containsKey(name)) {
+ return bitmaps.get(name);
+ } else {
+ Bitmap bm;
+ if (uri != null) {
+ try {
+ bm = BitmapFactory.decodeStream(getActivity()
+ .getContentResolver().openInputStream(uri));
+ } catch (FileNotFoundException e) {
+ bm = UIHelper.getUnknownContactPicture(name, 200);
+ }
+ } else {
+ bm = UIHelper.getUnknownContactPicture(name, 200);
+ }
+ bitmaps.put(name, bm);
+ return bm;
+ }
+ }
+
+ public Bitmap getError() {
+ if (error == null) {
+ error = UIHelper.getErrorPicture(200);
+ }
+ return error;
+ }
+ }
+
+ class DecryptMessage extends AsyncTask<Message, Void, Boolean> {
+
+ @Override
+ protected Boolean doInBackground(Message... params) {
+ final ConversationActivity activity = (ConversationActivity) getActivity();
+ askForPassphraseIntent = null;
+ for(int i = 0; i < params.length; ++i) {
+ if (params[i].getEncryption() == Message.ENCRYPTION_PGP) {
+ String body = params[i].getBody();
+ String decrypted = null;
+ try {
+ if (activity==null) {
+ return false;
+ }
+ Log.d("gultsch","calling to decrypt message id #"+params[i].getUuid());
+ decrypted = activity.xmppConnectionService.getPgpEngine().decrypt(body);
+ } catch (UserInputRequiredException e) {
+ askForPassphraseIntent = e.getPendingIntent().getIntentSender();
+ activity.runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ pgpInfo.setVisibility(View.VISIBLE);
+ }
+ });
+
+ return false;
+
+ } catch (OpenPgpException e) {
+ Log.d("gultsch","error decrypting pgp");
+ }
+ if (decrypted!=null) {
+ params[i].setBody(decrypted);
+ params[i].setEncryption(Message.ENCRYPTION_DECRYPTED);
+ activity.xmppConnectionService.updateMessage(params[i]);
+ }
+ if (activity!=null) {
+ activity.runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ messageListAdapter.notifyDataSetChanged();
+ }
+ });
+ }
+ }
+ if (activity!=null) {
+ activity.runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ activity.updateConversationList();
+ }
+ });
+ }
+ }
+ return true;
+ }
+
+ }
+}
diff --git a/src/eu/siacs/conversations/ui/DialogContactDetails.java b/src/eu/siacs/conversations/ui/DialogContactDetails.java
new file mode 100644
index 00000000..1210bc3c
--- /dev/null
+++ b/src/eu/siacs/conversations/ui/DialogContactDetails.java
@@ -0,0 +1,218 @@
+package eu.siacs.conversations.ui;
+
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Contact;
+import eu.siacs.conversations.entities.Presences;
+import eu.siacs.conversations.utils.UIHelper;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.ContactsContract.CommonDataKinds;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Intents;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.CheckBox;
+import android.widget.QuickContactBadge;
+import android.widget.TextView;
+
+public class DialogContactDetails extends DialogFragment {
+
+ private Contact contact = null;
+ boolean displayingInRoster = false;
+
+ private DialogContactDetails mDetailsDialog = this;
+ private XmppActivity activity;
+
+ private CheckBox send;
+ private CheckBox receive;
+
+ private DialogInterface.OnClickListener askRemoveFromRoster = new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setTitle("Delete from roster");
+ builder.setMessage("Do you want to delete "+contact.getJid()+" from your roster. The conversation assoziated with this account will not be removed.");
+ builder.setNegativeButton("Cancel", null);
+ builder.setPositiveButton("Delete",removeFromRoster);
+ builder.create().show();
+ }
+ };
+
+ private DialogInterface.OnClickListener removeFromRoster = new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ activity.xmppConnectionService.deleteContact(contact);
+ mDetailsDialog.dismiss();
+ }
+ };
+
+ private DialogInterface.OnClickListener addToPhonebook = new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
+ intent.setType(Contacts.CONTENT_ITEM_TYPE);
+ intent.putExtra(Intents.Insert.IM_HANDLE,contact.getJid());
+ intent.putExtra(Intents.Insert.IM_PROTOCOL,CommonDataKinds.Im.PROTOCOL_JABBER);
+ intent.putExtra("finishActivityOnSaveCompleted", true);
+ getActivity().startActivityForResult(intent,0);
+ mDetailsDialog.dismiss();
+ }
+ };
+
+ private DialogInterface.OnClickListener updateSubscriptions = new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ boolean needsUpdating = false;
+ if (contact.getSubscriptionOption(Contact.Subscription.FROM)) {
+ if (!send.isChecked()) {
+ contact.resetSubscriptionOption(Contact.Subscription.FROM);
+ contact.resetSubscriptionOption(Contact.Subscription.PREEMPTIVE_GRANT);
+ activity.xmppConnectionService.stopPresenceUpdatesTo(contact);
+ needsUpdating=true;
+ }
+ } else {
+ if (contact.getSubscriptionOption(Contact.Subscription.PREEMPTIVE_GRANT)) {
+ if (!send.isChecked()) {
+ contact.resetSubscriptionOption(Contact.Subscription.PREEMPTIVE_GRANT);
+ needsUpdating=true;
+ }
+ } else {
+ if (send.isChecked()) {
+ contact.setSubscriptionOption(Contact.Subscription.PREEMPTIVE_GRANT);
+ needsUpdating=true;
+ }
+ }
+ }
+ if (contact.getSubscriptionOption(Contact.Subscription.TO)) {
+ if (!receive.isChecked()) {
+ contact.resetSubscriptionOption(Contact.Subscription.TO);
+ activity.xmppConnectionService.stopPresenceUpdatesFrom(contact);
+ needsUpdating=true;
+ }
+ } else {
+ if (contact.getSubscriptionOption(Contact.Subscription.ASKING)) {
+ if (!receive.isChecked()) {
+ contact.resetSubscriptionOption(Contact.Subscription.ASKING);
+ activity.xmppConnectionService.stopPresenceUpdatesFrom(contact);
+ needsUpdating=true;
+ }
+ } else {
+ if (receive.isChecked()) {
+ contact.setSubscriptionOption(Contact.Subscription.ASKING);
+ activity.xmppConnectionService.requestPresenceUpdatesFrom(contact);
+ needsUpdating=true;
+ }
+ }
+ }
+ if (needsUpdating) {
+ activity.xmppConnectionService.updateContact(contact);
+ }
+ }
+ };
+
+ public void setContact(Contact contact) {
+ this.contact = contact;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ this.activity = (XmppActivity) getActivity();
+ AlertDialog.Builder builder = new AlertDialog.Builder(this.activity);
+ LayoutInflater inflater = getActivity().getLayoutInflater();
+ View view = inflater.inflate(R.layout.dialog_contact_details, null);
+ TextView contactJid = (TextView) view.findViewById(R.id.details_contactjid);
+ TextView accountJid = (TextView) view.findViewById(R.id.details_account);
+ TextView status = (TextView) view.findViewById(R.id.details_contactstatus);
+ send = (CheckBox) view.findViewById(R.id.details_send_presence);
+ receive = (CheckBox) view.findViewById(R.id.details_receive_presence);
+ //ImageView contactPhoto = (ImageView) view.findViewById(R.id.details_contact_picture);
+ QuickContactBadge badge = (QuickContactBadge) view.findViewById(R.id.details_contact_badge);
+
+ if (contact.getSubscriptionOption(Contact.Subscription.FROM)) {
+ send.setChecked(true);
+ } else {
+ send.setText("Preemptively grant subscription request");
+ if (contact.getSubscriptionOption(Contact.Subscription.PREEMPTIVE_GRANT)) {
+ send.setChecked(true);
+ } else {
+ send.setChecked(false);
+ }
+ }
+ if (contact.getSubscriptionOption(Contact.Subscription.TO)) {
+ receive.setChecked(true);
+ } else {
+ receive.setText("Request presence updates");
+ if (contact.getSubscriptionOption(Contact.Subscription.ASKING)) {
+ receive.setChecked(true);
+ } else {
+ receive.setChecked(false);
+ }
+ }
+
+ switch (contact.getMostAvailableStatus()) {
+ case Presences.CHAT:
+ status.setText("free to chat");
+ status.setTextColor(0xFF83b600);
+ break;
+ case Presences.ONLINE:
+ status.setText("online");
+ status.setTextColor(0xFF83b600);
+ break;
+ case Presences.AWAY:
+ status.setText("away");
+ status.setTextColor(0xFFffa713);
+ break;
+ case Presences.XA:
+ status.setText("extended away");
+ status.setTextColor(0xFFffa713);
+ break;
+ case Presences.DND:
+ status.setText("do not disturb");
+ status.setTextColor(0xFFe92727);
+ break;
+ case Presences.OFFLINE:
+ status.setText("offline");
+ status.setTextColor(0xFFe92727);
+ break;
+ default:
+ status.setText("offline");
+ status.setTextColor(0xFFe92727);
+ break;
+ }
+ contactJid.setText(contact.getJid());
+ accountJid.setText(contact.getAccount().getJid());
+
+ UIHelper.prepareContactBadge(getActivity(), badge, contact);
+
+ if (contact.getSystemAccount()==null) {
+ badge.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setTitle("Add to phone book");
+ builder.setMessage("Do you want to add "+contact.getJid()+" to your phones contact list?");
+ builder.setNegativeButton("Cancel", null);
+ builder.setPositiveButton("Add",addToPhonebook);
+ builder.create().show();
+ }
+ });
+ }
+
+ builder.setView(view);
+ builder.setTitle(contact.getDisplayName());
+
+ builder.setNeutralButton("Done", this.updateSubscriptions);
+ builder.setPositiveButton("Remove from roster", this.askRemoveFromRoster);
+ return builder.create();
+ }
+}
diff --git a/src/eu/siacs/conversations/ui/EditAccount.java b/src/eu/siacs/conversations/ui/EditAccount.java
new file mode 100644
index 00000000..3ec74174
--- /dev/null
+++ b/src/eu/siacs/conversations/ui/EditAccount.java
@@ -0,0 +1,138 @@
+package eu.siacs.conversations.ui;
+
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.utils.Validator;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.EditText;
+import android.widget.TextView;
+
+public class EditAccount extends DialogFragment {
+
+ protected Account account;
+
+ public void setAccount(Account account) {
+ this.account = account;
+ }
+
+ public interface EditAccountListener {
+ public void onAccountEdited(Account account);
+ }
+
+ protected EditAccountListener listener = null;
+
+ public void setEditAccountListener(EditAccountListener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ LayoutInflater inflater = getActivity().getLayoutInflater();
+ View view = inflater.inflate(R.layout.edit_account_dialog, null);
+ final EditText jidText = (EditText) view.findViewById(R.id.account_jid);
+ final TextView confirmPwDesc = (TextView) view
+ .findViewById(R.id.account_confirm_password_desc);
+ CheckBox useTLS = (CheckBox) view.findViewById(R.id.account_usetls);
+
+ final EditText password = (EditText) view
+ .findViewById(R.id.account_password);
+ final EditText passwordConfirm = (EditText) view
+ .findViewById(R.id.account_password_confirm2);
+ final CheckBox registerAccount = (CheckBox) view
+ .findViewById(R.id.edit_account_register_new);
+
+ final String okButtonDesc;
+
+ if (account != null) {
+ builder.setTitle("Edit account");
+ registerAccount.setVisibility(View.GONE);
+ jidText.setText(account.getJid());
+ password.setText(account.getPassword());
+ okButtonDesc = "Edit";
+ if (account.isOptionSet(Account.OPTION_USETLS)) {
+ useTLS.setChecked(true);
+ } else {
+ useTLS.setChecked(false);
+ }
+ } else {
+ builder.setTitle("Add account");
+ okButtonDesc = "Add";
+ }
+
+ registerAccount
+ .setOnCheckedChangeListener(new OnCheckedChangeListener() {
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView,
+ boolean isChecked) {
+ AlertDialog d = (AlertDialog) getDialog();
+ Button positiveButton = (Button) d
+ .getButton(Dialog.BUTTON_POSITIVE);
+ if (isChecked) {
+ positiveButton.setText("Register");
+ passwordConfirm.setVisibility(View.VISIBLE);
+ confirmPwDesc.setVisibility(View.VISIBLE);
+ } else {
+ passwordConfirm.setVisibility(View.GONE);
+ positiveButton.setText("Add");
+ confirmPwDesc.setVisibility(View.GONE);
+ }
+ }
+ });
+
+ builder.setView(view);
+ builder.setNeutralButton("Cancel", null);
+ builder.setPositiveButton(okButtonDesc, null);
+ return builder.create();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ final AlertDialog d = (AlertDialog) getDialog();
+ Button positiveButton = (Button) d.getButton(Dialog.BUTTON_POSITIVE);
+ positiveButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ EditText jidEdit = (EditText) d.findViewById(R.id.account_jid);
+ String jid = jidEdit.getText().toString();
+ EditText passwordEdit = (EditText) d
+ .findViewById(R.id.account_password);
+ String password = passwordEdit.getText().toString();
+ CheckBox useTLS = (CheckBox) d.findViewById(R.id.account_usetls);
+ String username;
+ String server;
+ if (Validator.isValidJid(jid)) {
+ String[] parts = jid.split("@");
+ username = parts[0];
+ server = parts[1];
+ } else {
+ jidEdit.setError("Invalid Jabber ID");
+ return;
+ }
+ if (account != null) {
+ account.setPassword(password);
+ account.setUsername(username);
+ account.setServer(server);
+ } else {
+ account = new Account(username, server, password);
+ }
+ account.setOption(Account.OPTION_USETLS, useTLS.isChecked());
+ if (listener != null) {
+ listener.onAccountEdited(account);
+ d.dismiss();
+ }
+ }
+ });
+ }
+}
diff --git a/src/eu/siacs/conversations/ui/ManageAccountActivity.java b/src/eu/siacs/conversations/ui/ManageAccountActivity.java
new file mode 100644
index 00000000..22b82e77
--- /dev/null
+++ b/src/eu/siacs/conversations/ui/ManageAccountActivity.java
@@ -0,0 +1,312 @@
+package eu.siacs.conversations.ui;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.crypto.PgpEngine;
+import eu.siacs.conversations.crypto.PgpEngine.UserInputRequiredException;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.ui.EditAccount.EditAccountListener;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.content.IntentSender.SendIntentException;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.ActionMode;
+import android.view.ActionMode.Callback;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView.OnItemLongClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+public class ManageAccountActivity extends XmppActivity implements ActionMode.Callback {
+
+ public static final int REQUEST_ANNOUNCE_PGP = 0x73731;
+
+ protected boolean isActionMode = false;
+ protected ActionMode actionMode;
+ protected Account selectedAccountForActionMode = null;
+
+ protected List<Account> accountList = new ArrayList<Account>();
+ protected ListView accountListView;
+ protected ArrayAdapter<Account> accountListViewAdapter;
+ protected OnAccountListChangedListener accountChanged = new OnAccountListChangedListener() {
+
+ @Override
+ public void onAccountListChangedListener() {
+ Log.d("xmppService", "ui on account list changed listener");
+ accountList.clear();
+ accountList.addAll(xmppConnectionService.getAccounts());
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ if (accountList.size() == 1) {
+ startActivity(new Intent(getApplicationContext(),
+ NewConversationActivity.class));
+ }
+ accountListViewAdapter.notifyDataSetChanged();
+ }
+ });
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.manage_accounts);
+
+ accountListView = (ListView) findViewById(R.id.account_list);
+ accountListViewAdapter = new ArrayAdapter<Account>(
+ getApplicationContext(), R.layout.account_row, this.accountList) {
+ @Override
+ public View getView(int position, View view, ViewGroup parent) {
+ Account account = getItem(position);
+ if (view == null) {
+ LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ view = (View) inflater.inflate(R.layout.account_row, null);
+ }
+ ((TextView) view.findViewById(R.id.account_jid))
+ .setText(account.getJid());
+ TextView statusView = (TextView) view
+ .findViewById(R.id.account_status);
+ switch (account.getStatus()) {
+ case Account.STATUS_DISABLED:
+ statusView.setText("temporarily disabled");
+ statusView.setTextColor(0xFF1da9da);
+ break;
+ case Account.STATUS_ONLINE:
+ statusView.setText("online");
+ statusView.setTextColor(0xFF83b600);
+ break;
+ case Account.STATUS_OFFLINE:
+ statusView.setText("offline");
+ statusView.setTextColor(0xFFe92727);
+ break;
+ case Account.STATUS_UNAUTHORIZED:
+ statusView.setText("unauthorized");
+ statusView.setTextColor(0xFFe92727);
+ break;
+ case Account.STATUS_SERVER_NOT_FOUND:
+ statusView.setText("server not found");
+ statusView.setTextColor(0xFFe92727);
+ break;
+ default:
+ break;
+ }
+
+ return view;
+ }
+ };
+ final Activity activity = this;
+ accountListView.setAdapter(this.accountListViewAdapter);
+ accountListView.setOnItemClickListener(new OnItemClickListener() {
+
+ @Override
+ public void onItemClick(AdapterView<?> arg0, View view,
+ int position, long arg3) {
+ if (!isActionMode) {
+ EditAccount dialog = new EditAccount();
+ dialog.setAccount(accountList.get(position));
+ dialog.setEditAccountListener(new EditAccountListener() {
+
+ @Override
+ public void onAccountEdited(Account account) {
+ xmppConnectionService.updateAccount(account);
+ }
+ });
+ dialog.show(getFragmentManager(), "edit_account");
+ } else {
+ selectedAccountForActionMode = accountList.get(position);
+ actionMode.invalidate();
+ }
+ }
+ });
+ accountListView.setOnItemLongClickListener(new OnItemLongClickListener() {
+
+ @Override
+ public boolean onItemLongClick(AdapterView<?> arg0, View view,
+ int position, long arg3) {
+ if (!isActionMode) {
+ accountListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+ accountListView.setItemChecked(position,true);
+ selectedAccountForActionMode = accountList.get(position);
+ actionMode = activity.startActionMode((Callback) activity);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ });
+ }
+
+ @Override
+ protected void onStop() {
+ if (xmppConnectionServiceBound) {
+ xmppConnectionService.removeOnAccountListChangedListener();
+ }
+ super.onStop();
+ }
+
+ @Override
+ void onBackendConnected() {
+ xmppConnectionService.setOnAccountListChangedListener(accountChanged);
+ this.accountList.clear();
+ this.accountList.addAll(xmppConnectionService.getAccounts());
+ accountListViewAdapter.notifyDataSetChanged();
+ if (this.accountList.size() == 0) {
+ getActionBar().setDisplayHomeAsUpEnabled(false);
+ addAccount();
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.manageaccounts, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_settings:
+ startActivity(new Intent(this, SettingsActivity.class));
+ break;
+ case R.id.action_add_account:
+ addAccount();
+ break;
+ default:
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ protected void addAccount() {
+ final Activity activity = this;
+ EditAccount dialog = new EditAccount();
+ dialog.setEditAccountListener(new EditAccountListener() {
+
+ @Override
+ public void onAccountEdited(Account account) {
+ xmppConnectionService.createAccount(account);
+ activity.getActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+ });
+ dialog.show(getFragmentManager(), "add_account");
+ }
+
+ @Override
+ public boolean onActionItemClicked(final ActionMode mode, MenuItem item) {
+ if (item.getItemId()==R.id.account_disable) {
+ selectedAccountForActionMode.setOption(Account.OPTION_DISABLED, true);
+ xmppConnectionService.updateAccount(selectedAccountForActionMode);
+ mode.finish();
+ } else if (item.getItemId()==R.id.account_enable) {
+ selectedAccountForActionMode.setOption(Account.OPTION_DISABLED, false);
+ xmppConnectionService.updateAccount(selectedAccountForActionMode);
+ mode.finish();
+ } else if (item.getItemId()==R.id.account_delete) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle("Are you sure?");
+ builder.setIconAttribute(android.R.attr.alertDialogIcon);
+ builder.setMessage("If you delete your account your entire conversation history will be lost");
+ builder.setPositiveButton("Delete", new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ xmppConnectionService.deleteAccount(selectedAccountForActionMode);
+ selectedAccountForActionMode = null;
+ mode.finish();
+ }
+ });
+ builder.setNegativeButton("Cancel",null);
+ builder.create().show();
+ } else if (item.getItemId()==R.id.announce_pgp) {
+ mode.finish();
+ try {
+ xmppConnectionService.generatePgpAnnouncement(selectedAccountForActionMode);
+ } catch (PgpEngine.UserInputRequiredException e) {
+ try {
+ startIntentSenderForResult(e.getPendingIntent().getIntentSender(), REQUEST_ANNOUNCE_PGP, null, 0, 0, 0);
+ } catch (SendIntentException e1) {
+ Log.d("gultsch","sending intent failed");
+ }
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ MenuInflater inflater = mode.getMenuInflater();
+ inflater.inflate(R.menu.manageaccounts_context, menu);
+ return true;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ if (selectedAccountForActionMode.isOptionSet(Account.OPTION_DISABLED)) {
+ menu.findItem(R.id.account_enable).setVisible(true);
+ menu.findItem(R.id.account_disable).setVisible(false);
+ } else {
+ menu.findItem(R.id.account_disable).setVisible(true);
+ menu.findItem(R.id.account_enable).setVisible(false);
+ }
+ return true;
+ }
+
+ @Override
+ public void onActionModeStarted(ActionMode mode) {
+ super.onActionModeStarted(mode);
+ this.isActionMode = true;
+ }
+
+ @Override
+ public void onActionModeFinished(ActionMode mode) {
+ super.onActionModeFinished(mode);
+ this.isActionMode = false;
+ accountListView.clearChoices();
+ accountListView.requestLayout();
+ accountListView.post(new Runnable() {
+ @Override
+ public void run() {
+ accountListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
+ }
+ });
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (resultCode == RESULT_OK) {
+ if (requestCode == REQUEST_ANNOUNCE_PGP) {
+ try {
+ xmppConnectionService.generatePgpAnnouncement(selectedAccountForActionMode);
+ } catch (UserInputRequiredException e) {
+ Log.d("gultsch","already came back. ignoring");
+ }
+ }
+ }
+ }
+}
diff --git a/src/eu/siacs/conversations/ui/NewConversationActivity.java b/src/eu/siacs/conversations/ui/NewConversationActivity.java
new file mode 100644
index 00000000..628a3047
--- /dev/null
+++ b/src/eu/siacs/conversations/ui/NewConversationActivity.java
@@ -0,0 +1,332 @@
+package eu.siacs.conversations.ui;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Contact;
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.utils.UIHelper;
+import eu.siacs.conversations.utils.Validator;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView.OnItemLongClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.ListView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.ImageView;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+
+public class NewConversationActivity extends XmppActivity {
+
+ protected List<Contact> phoneContacts = new ArrayList<Contact>();
+ protected List<Contact> rosterContacts = new ArrayList<Contact>();
+ protected List<Contact> aggregatedContacts = new ArrayList<Contact>();
+ protected ListView contactsView;
+ protected ArrayAdapter<Contact> contactsAdapter;
+
+ protected EditText search;
+ protected String searchString = "";
+ private TextView contactsHeader;
+ private List<Account> accounts;
+
+ protected void updateAggregatedContacts() {
+
+ aggregatedContacts.clear();
+ for (Contact contact : rosterContacts) {
+ if (contact.match(searchString))
+ aggregatedContacts.add(contact);
+ }
+
+ Collections.sort(aggregatedContacts, new Comparator<Contact>() {
+
+ @SuppressLint("DefaultLocale")
+ @Override
+ public int compare(Contact lhs, Contact rhs) {
+ return lhs.getDisplayName().toLowerCase()
+ .compareTo(rhs.getDisplayName().toLowerCase());
+ }
+ });
+
+ if (aggregatedContacts.size() == 0) {
+
+ if (Validator.isValidJid(searchString)) {
+ String name = searchString.split("@")[0];
+ Contact newContact = new Contact(null, name, searchString, null);
+ newContact.flagAsNotInRoster();
+ aggregatedContacts.add(newContact);
+ contactsHeader.setText("Create new contact");
+ } else {
+ contactsHeader.setText("Contacts");
+ }
+ } else {
+ contactsHeader.setText("Contacts");
+ }
+
+ contactsAdapter.notifyDataSetChanged();
+ contactsView.setScrollX(0);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_new_conversation);
+
+ contactsHeader = (TextView) findViewById(R.id.contacts_header);
+
+ search = (EditText) findViewById(R.id.new_conversation_search);
+ search.addTextChangedListener(new TextWatcher() {
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before,
+ int count) {
+ searchString = search.getText().toString();
+ updateAggregatedContacts();
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count,
+ int after) {
+ // TODO Auto-generated method stub
+
+ }
+ });
+
+ contactsView = (ListView) findViewById(R.id.contactList);
+ contactsAdapter = new ArrayAdapter<Contact>(getApplicationContext(),
+ R.layout.contact, aggregatedContacts) {
+ @Override
+ public View getView(int position, View view, ViewGroup parent) {
+ LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ Contact contact = getItem(position);
+ if (view == null) {
+ view = (View) inflater.inflate(R.layout.contact, null);
+ }
+
+ ((TextView) view.findViewById(R.id.contact_display_name))
+ .setText(getItem(position).getDisplayName());
+ TextView contactJid = (TextView) view
+ .findViewById(R.id.contact_jid);
+ contactJid.setText(contact.getJid());
+ String profilePhoto = getItem(position).getProfilePhoto();
+ ImageView imageView = (ImageView) view
+ .findViewById(R.id.contact_photo);
+ if (profilePhoto != null) {
+ imageView.setImageURI(Uri.parse(profilePhoto));
+ } else {
+ imageView.setImageBitmap(UIHelper.getUnknownContactPicture(
+ getItem(position).getDisplayName(), 90));
+ }
+ return view;
+ }
+ };
+ contactsView.setAdapter(contactsAdapter);
+ final Activity activity = this;
+ contactsView.setOnItemClickListener(new OnItemClickListener() {
+
+ @Override
+ public void onItemClick(AdapterView<?> arg0, final View view,
+ int pos, long arg3) {
+ final Contact clickedContact = aggregatedContacts.get(pos);
+
+ if ((clickedContact.getAccount()==null)&&(accounts.size()>1)) {
+ String[] accountList = new String[accounts.size()];
+ for (int i = 0; i < accounts.size(); ++i) {
+ accountList[i] = accounts.get(i).getJid();
+ }
+
+ AlertDialog.Builder accountChooser = new AlertDialog.Builder(
+ activity);
+ accountChooser.setTitle("Choose account");
+ accountChooser.setItems(accountList, new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ clickedContact.setAccount(accounts.get(which));
+ showIsMucDialogIfNeeded(clickedContact);
+ }
+ });
+ accountChooser.create().show();
+ } else {
+ if (clickedContact.getAccount()==null) {
+ clickedContact.setAccount(accounts.get(0));
+ }
+ showIsMucDialogIfNeeded(clickedContact);
+ }
+ }
+ });
+ contactsView.setOnItemLongClickListener(new OnItemLongClickListener() {
+
+ @Override
+ public boolean onItemLongClick(AdapterView<?> arg0, View arg1,
+ int pos, long arg3) {
+ Contact clickedContact = aggregatedContacts.get(pos);
+ DialogContactDetails dialog = new DialogContactDetails();
+ dialog.setContact(clickedContact);
+ dialog.show(getFragmentManager(), "details");
+ return true;
+ }
+ });
+ }
+
+ public void showIsMucDialogIfNeeded(final Contact clickedContact) {
+ if (clickedContact.couldBeMuc()) {
+ AlertDialog.Builder dialog = new AlertDialog.Builder(this);
+ dialog.setTitle("Multi User Conference");
+ dialog.setMessage("Are you trying to join a conference?");
+ dialog.setPositiveButton("Yes", new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ startConversation(clickedContact, clickedContact.getAccount(),true);
+ }
+ });
+ dialog.setNegativeButton("No", new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ startConversation(clickedContact, clickedContact.getAccount(),false);
+ }
+ });
+ dialog.create().show();
+ } else {
+ startConversation(clickedContact, clickedContact.getAccount(),false);
+ }
+ }
+
+ public void startConversation(Contact contact, Account account, boolean muc) {
+ if (!contact.isInRoster()) {
+ xmppConnectionService.createContact(contact);
+ }
+ Conversation conversation = xmppConnectionService
+ .findOrCreateConversation(account, contact.getJid(), muc);
+
+ Intent viewConversationIntent = new Intent(this,
+ ConversationActivity.class);
+ viewConversationIntent.setAction(Intent.ACTION_VIEW);
+ viewConversationIntent.putExtra(ConversationActivity.CONVERSATION,
+ conversation.getUuid());
+ viewConversationIntent.setType(ConversationActivity.VIEW_CONVERSATION);
+ viewConversationIntent.setFlags(viewConversationIntent.getFlags()
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(viewConversationIntent);
+ }
+
+ @Override
+ void onBackendConnected() {
+ if (xmppConnectionService.getConversationCount() == 0) {
+ getActionBar().setDisplayHomeAsUpEnabled(false);
+ getActionBar().setHomeButtonEnabled(false);
+ }
+ this.accounts = xmppConnectionService.getAccounts();
+ this.rosterContacts.clear();
+ for (int i = 0; i < accounts.size(); ++i) {
+ xmppConnectionService.getRoster(accounts.get(i),
+ new OnRosterFetchedListener() {
+
+ @Override
+ public void onRosterFetched(List<Contact> roster) {
+ rosterContacts.addAll(roster);
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ updateAggregatedContacts();
+ }
+ });
+
+ }
+ });
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.newconversation, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_settings:
+ startActivity(new Intent(this, SettingsActivity.class));
+ break;
+ case R.id.action_accounts:
+ startActivity(new Intent(this, ManageAccountActivity.class));
+ break;
+ case R.id.action_refresh_contacts:
+ refreshContacts();
+ break;
+ default:
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void refreshContacts() {
+ final ProgressBar progress = (ProgressBar) findViewById(R.id.progressBar1);
+ final EditText searchBar = (EditText) findViewById(R.id.new_conversation_search);
+ final TextView contactsHeader = (TextView) findViewById(R.id.contacts_header);
+ final ListView contactList = (ListView) findViewById(R.id.contactList);
+ searchBar.setVisibility(View.GONE);
+ contactsHeader.setVisibility(View.GONE);
+ contactList.setVisibility(View.GONE);
+ progress.setVisibility(View.VISIBLE);
+ this.accounts = xmppConnectionService.getAccounts();
+ this.rosterContacts.clear();
+ for (int i = 0; i < accounts.size(); ++i) {
+ if (accounts.get(i).getStatus() == Account.STATUS_ONLINE) {
+ xmppConnectionService.updateRoster(accounts.get(i),
+ new OnRosterFetchedListener() {
+
+ @Override
+ public void onRosterFetched(
+ final List<Contact> roster) {
+ runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ rosterContacts.addAll(roster);
+ progress.setVisibility(View.GONE);
+ searchBar.setVisibility(View.VISIBLE);
+ contactList.setVisibility(View.VISIBLE);
+ contactList.setVisibility(View.VISIBLE);
+ updateAggregatedContacts();
+ }
+ });
+ }
+ });
+ }
+ }
+ }
+}
diff --git a/src/eu/siacs/conversations/ui/OnAccountListChangedListener.java b/src/eu/siacs/conversations/ui/OnAccountListChangedListener.java
new file mode 100644
index 00000000..98ef445e
--- /dev/null
+++ b/src/eu/siacs/conversations/ui/OnAccountListChangedListener.java
@@ -0,0 +1,5 @@
+package eu.siacs.conversations.ui;
+
+public interface OnAccountListChangedListener {
+ public void onAccountListChangedListener();
+}
diff --git a/src/eu/siacs/conversations/ui/OnConversationListChangedListener.java b/src/eu/siacs/conversations/ui/OnConversationListChangedListener.java
new file mode 100644
index 00000000..2a922e21
--- /dev/null
+++ b/src/eu/siacs/conversations/ui/OnConversationListChangedListener.java
@@ -0,0 +1,5 @@
+package eu.siacs.conversations.ui;
+
+public interface OnConversationListChangedListener {
+ public void onConversationListChanged();
+}
diff --git a/src/eu/siacs/conversations/ui/OnRosterFetchedListener.java b/src/eu/siacs/conversations/ui/OnRosterFetchedListener.java
new file mode 100644
index 00000000..d69ce35b
--- /dev/null
+++ b/src/eu/siacs/conversations/ui/OnRosterFetchedListener.java
@@ -0,0 +1,9 @@
+package eu.siacs.conversations.ui;
+
+import java.util.List;
+
+import eu.siacs.conversations.entities.Contact;
+
+public interface OnRosterFetchedListener {
+ public void onRosterFetched(List<Contact> roster);
+}
diff --git a/src/eu/siacs/conversations/ui/SettingsActivity.java b/src/eu/siacs/conversations/ui/SettingsActivity.java
new file mode 100644
index 00000000..abaf8c68
--- /dev/null
+++ b/src/eu/siacs/conversations/ui/SettingsActivity.java
@@ -0,0 +1,16 @@
+package eu.siacs.conversations.ui;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class SettingsActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Display the fragment as the main content.
+ getFragmentManager().beginTransaction()
+ .replace(android.R.id.content, new SettingsFragment()).commit();
+ }
+
+}
diff --git a/src/eu/siacs/conversations/ui/SettingsFragment.java b/src/eu/siacs/conversations/ui/SettingsFragment.java
new file mode 100644
index 00000000..7e1c3698
--- /dev/null
+++ b/src/eu/siacs/conversations/ui/SettingsFragment.java
@@ -0,0 +1,15 @@
+package eu.siacs.conversations.ui;
+
+import eu.siacs.conversations.R;
+import android.os.Bundle;
+import android.preference.PreferenceFragment;
+
+public class SettingsFragment extends PreferenceFragment {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Load the preferences from an XML resource
+ addPreferencesFromResource(R.xml.preferences);
+ }
+}
diff --git a/src/eu/siacs/conversations/ui/XmppActivity.java b/src/eu/siacs/conversations/ui/XmppActivity.java
new file mode 100644
index 00000000..5114e640
--- /dev/null
+++ b/src/eu/siacs/conversations/ui/XmppActivity.java
@@ -0,0 +1,52 @@
+package eu.siacs.conversations.ui;
+
+import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.services.XmppConnectionService.XmppConnectionBinder;
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+
+public abstract class XmppActivity extends Activity {
+ public XmppConnectionService xmppConnectionService;
+ public boolean xmppConnectionServiceBound = false;
+ protected boolean handledViewIntent = false;
+ protected ServiceConnection mConnection = new ServiceConnection() {
+
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ XmppConnectionBinder binder = (XmppConnectionBinder) service;
+ xmppConnectionService = binder.getService();
+ xmppConnectionServiceBound = true;
+ onBackendConnected();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName arg0) {
+ xmppConnectionServiceBound = false;
+ }
+ };
+
+ @Override
+ protected void onStart() {
+ startService(new Intent(this, XmppConnectionService.class));
+ super.onStart();
+ if (!xmppConnectionServiceBound) {
+ Intent intent = new Intent(this, XmppConnectionService.class);
+ bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (xmppConnectionServiceBound) {
+ unbindService(mConnection);
+ xmppConnectionServiceBound = false;
+ }
+ }
+
+ abstract void onBackendConnected();
+}