diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 3b598426f..e248145d9 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -62,8 +62,8 @@ android:windowSoftInputMode="stateHidden" > + + diff --git a/res/values/strings.xml b/res/values/strings.xml index f04fca969..eb38e39fd 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -10,7 +10,7 @@ Conferenece details Secure conversation Add account - New Conversation + Contacts just now sending… Renew PGP announcement @@ -31,4 +31,13 @@ No OTR Fingerprint generated. Just go ahead an start an encrypted conversation Start Conversation Invite Contacts + Invite to existing conference + Create new conference + Cancel + Create \u0026 Invite + Do you want to create a new conference with a randomly generated address and invite the selected contacts to it? + No existing conferences + Invitation sent + Account offline + You have to be online to invite people to conferences diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java index f2233130f..815e06afe 100644 --- a/src/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/eu/siacs/conversations/services/XmppConnectionService.java @@ -72,7 +72,7 @@ public class XmppConnectionService extends Service { private static final int PING_MAX_INTERVAL = 300; private static final int PING_MIN_INTERVAL = 10; - private static final int PING_TIMEOUT = 2; + private static final int PING_TIMEOUT = 5; private static final int CONNECT_TIMEOUT = 60; private List accounts; @@ -160,7 +160,7 @@ public class XmppConnectionService extends Service { } } else { - Log.d(LOGTAG, "unparsed message " + packet.toString()); + //Log.d(LOGTAG, "unparsed message " + packet.toString()); } } if ((message == null)||(message.getBody() == null)) { @@ -199,19 +199,6 @@ public class XmppConnectionService extends Service { accountChangedListener.onAccountListChangedListener(); } if (account.getStatus() == Account.STATUS_ONLINE) { - if (account.getXmppConnection().hasFeatureRosterManagment()) { - updateRoster(account, null); - } - connectMultiModeConversations(account); - List conversations = getConversations(); - for (int i = 0; i < conversations.size(); ++i) { - if (conversations.get(i).getAccount() == account) { - sendUnsendMessages(conversations.get(i)); - } - } - if (convChangedListener != null) { - convChangedListener.onConversationListChanged(); - } scheduleWakeupCall(PING_MAX_INTERVAL, true); } else if (account.getStatus() == Account.STATUS_OFFLINE) { if (!account.isOptionSet(Account.OPTION_DISABLED)) { @@ -558,6 +545,19 @@ public class XmppConnectionService extends Service { @Override public void onBind(Account account) { databaseBackend.clearPresences(account); + if (account.getXmppConnection().hasFeatureRosterManagment()) { + updateRoster(account, null); + } + connectMultiModeConversations(account); + List conversations = getConversations(); + for (int i = 0; i < conversations.size(); ++i) { + if (conversations.get(i).getAccount() == account) { + sendUnsendMessages(conversations.get(i)); + } + } + if (convChangedListener != null) { + convChangedListener.onConversationListChanged(); + } } }); return connection; diff --git a/src/eu/siacs/conversations/ui/NewConversationActivity.java b/src/eu/siacs/conversations/ui/ContactsActivity.java similarity index 77% rename from src/eu/siacs/conversations/ui/NewConversationActivity.java rename to src/eu/siacs/conversations/ui/ContactsActivity.java index 9f8f23a4d..1bd6e72fa 100644 --- a/src/eu/siacs/conversations/ui/NewConversationActivity.java +++ b/src/eu/siacs/conversations/ui/ContactsActivity.java @@ -1,9 +1,7 @@ package eu.siacs.conversations.ui; import java.io.UnsupportedEncodingException; -import java.math.BigInteger; import java.net.URLDecoder; -import java.security.SecureRandom; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -13,9 +11,11 @@ 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.CryptoHelper; import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.utils.Validator; import android.os.Bundle; +import android.preference.PreferenceManager; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; @@ -37,14 +37,17 @@ import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.ImageView; +import android.widget.Toast; import android.annotation.SuppressLint; import android.app.AlertDialog; +import android.app.AlertDialog.Builder; import android.content.Context; import android.content.DialogInterface; +import android.content.SharedPreferences; import android.content.DialogInterface.OnClickListener; import android.content.Intent; -public class NewConversationActivity extends XmppActivity { +public class ContactsActivity extends XmppActivity { protected List rosterContacts = new ArrayList(); protected List aggregatedContacts = new ArrayList(); @@ -56,7 +59,10 @@ public class NewConversationActivity extends XmppActivity { private TextView contactsHeader; private List accounts; private List selectedContacts = new ArrayList(); + + private ContactsActivity activity = this; + private boolean useSubject = true; private boolean isActionMode = false; private boolean inviteIntent = false; private ActionMode actionMode = null; @@ -79,18 +85,22 @@ public class NewConversationActivity extends XmppActivity { menu.findItem(R.id.action_start_conversation).setVisible(false); menu.findItem(R.id.action_contact_details).setVisible(false); menu.findItem(R.id.action_invite).setVisible(false); - } else if ((selectedContacts.size() == 1)&&(!inviteIntent)) { + menu.findItem(R.id.action_invite_to_existing).setVisible(false); + } else if ((selectedContacts.size() == 1) && (!inviteIntent)) { menu.findItem(R.id.action_start_conversation).setVisible(true); menu.findItem(R.id.action_contact_details).setVisible(true); menu.findItem(R.id.action_invite).setVisible(false); - } else if (!inviteIntent){ + menu.findItem(R.id.action_invite_to_existing).setVisible(true); + } else if (!inviteIntent) { menu.findItem(R.id.action_start_conversation).setVisible(true); menu.findItem(R.id.action_contact_details).setVisible(false); menu.findItem(R.id.action_invite).setVisible(false); + menu.findItem(R.id.action_invite_to_existing).setVisible(true); } else { menu.findItem(R.id.action_invite).setVisible(true); menu.findItem(R.id.action_start_conversation).setVisible(false); menu.findItem(R.id.action_contact_details).setVisible(false); + menu.findItem(R.id.action_invite_to_existing).setVisible(false); } return true; } @@ -125,11 +135,42 @@ public class NewConversationActivity extends XmppActivity { break; case R.id.action_invite: invite(); - break; + break; + case R.id.action_invite_to_existing: + final List mucs = new ArrayList(); + for(Conversation conv : xmppConnectionService.getConversations()) { + if (conv.getMode() == Conversation.MODE_MULTI) { + mucs.add(conv); + } + } + AlertDialog.Builder builder = new AlertDialog.Builder(activity); + builder.setTitle(getString(R.string.invite_contacts_to_existing)); + if (mucs.size() >= 1) { + String[] options = new String[mucs.size()]; + for(int i = 0; i < options.length; ++i) { + options[i] = mucs.get(i).getName(useSubject); + } + builder.setItems(options, new OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + Conversation conversation = mucs.get(which); + if (isOnline(conversation.getAccount())) { + xmppConnectionService.inviteToConference(conversation, selectedContacts); + Toast.makeText(activity, getString(R.string.invitation_sent), Toast.LENGTH_SHORT).show(); + actionMode.finish(); + } + } + }); + } else { + builder.setMessage(getString(R.string.no_open_mucs)); + } + builder.setNegativeButton(getString(R.string.cancel),null); + builder.create().show(); + break; default: break; } - // TODO Auto-generated method stub return false; } @@ -139,6 +180,20 @@ public class NewConversationActivity extends XmppActivity { } }; + private boolean isOnline(Account account) { + if (account.getStatus() == Account.STATUS_ONLINE) { + return true; + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(getString(R.string.account_offline)); + builder.setMessage(getString(R.string.cant_invite_while_offline)); + builder.setNegativeButton("OK", null); + builder.setIconAttribute(android.R.attr.alertDialogIcon); + builder.create().show(); + return false; + } + } + private void invite() { List conversations = xmppConnectionService .getConversations(); @@ -151,45 +206,64 @@ public class NewConversationActivity extends XmppActivity { } } if (conversation != null) { - xmppConnectionService.inviteToConference(conversation, selectedContacts); + xmppConnectionService.inviteToConference(conversation, + selectedContacts); } finish(); } - + private void startConference() { if (accounts.size() > 1) { getAccountChooser(new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - startConference(accounts.get(which), selectedContacts); + startConference(accounts.get(which)); } }).show(); } else { - startConference(accounts.get(0), selectedContacts); + startConference(accounts.get(0)); } } - private void startConference(Account account, List contacts) { - SecureRandom random = new SecureRandom(); - String mucName = new BigInteger(100, random).toString(32); - String serverName = account.getXmppConnection().getMucServer(); - String jid = mucName + "@" + serverName; - Conversation conversation = xmppConnectionService - .findOrCreateConversation(account, jid, true); - StringBuilder subject = new StringBuilder(); - for (int i = 0; i < selectedContacts.size(); ++i) { - if (i + 1 != selectedContacts.size()) { - subject.append(selectedContacts.get(i).getDisplayName() + ", "); - } else { - subject.append(selectedContacts.get(i).getDisplayName()); - } + private void startConference(final Account account) { + if (isOnline(account)) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(getString(R.string.new_conference)); + builder.setMessage(getString(R.string.new_conference_explained)); + builder.setNegativeButton(getString(R.string.cancel), null); + builder.setPositiveButton(getString(R.string.create_invite), + new OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + String mucName = CryptoHelper.randomMucName(); + String serverName = account.getXmppConnection() + .getMucServer(); + String jid = mucName + "@" + serverName; + Conversation conversation = xmppConnectionService + .findOrCreateConversation(account, jid, true); + StringBuilder subject = new StringBuilder(); + subject.append(account.getUsername() + ", "); + for (int i = 0; i < selectedContacts.size(); ++i) { + if (i + 1 != selectedContacts.size()) { + subject.append(selectedContacts.get(i) + .getDisplayName() + ", "); + } else { + subject.append(selectedContacts.get(i) + .getDisplayName()); + } + } + xmppConnectionService.sendConversationSubject( + conversation, subject.toString()); + xmppConnectionService.inviteToConference(conversation, + selectedContacts); + switchToConversation(conversation, null); + } + }); + builder.create().show(); } - xmppConnectionService.sendConversationSubject(conversation, - subject.toString()); - xmppConnectionService.inviteToConference(conversation, contacts); - switchToConversation(conversation, null); } protected void updateAggregatedContacts() { @@ -246,6 +320,8 @@ public class NewConversationActivity extends XmppActivity { @Override protected void onStart() { super.onStart(); + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity); + this.useSubject = preferences.getBoolean("use_subject_in_muc", true); inviteIntent = "invite".equals(getIntent().getAction()); if (inviteIntent) { contactsHeader.setVisibility(View.GONE); diff --git a/src/eu/siacs/conversations/ui/ConversationActivity.java b/src/eu/siacs/conversations/ui/ConversationActivity.java index ad643c315..a08e07273 100644 --- a/src/eu/siacs/conversations/ui/ConversationActivity.java +++ b/src/eu/siacs/conversations/ui/ConversationActivity.java @@ -74,7 +74,7 @@ public class ConversationActivity extends XmppActivity { if (conversationList.size() >= 1) { swapConversationFragment(); } else { - startActivity(new Intent(getApplicationContext(), NewConversationActivity.class)); + startActivity(new Intent(getApplicationContext(), ContactsActivity.class)); finish(); } } @@ -249,12 +249,14 @@ public class ConversationActivity extends XmppActivity { 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); + MenuItem menuInviteContacts = (MenuItem) menu.findItem(R.id.action_invite); if ((spl.isOpen()&&(spl.isSlideable()))) { menuArchive.setVisible(false); menuMucDetails.setVisible(false); menuContactDetails.setVisible(false); menuSecure.setVisible(false); + menuInviteContacts.setVisible(false); } else { ((MenuItem) menu.findItem(R.id.action_add)).setVisible(!spl.isSlideable()); if (this.getSelectedConversation()!=null) { @@ -263,9 +265,11 @@ public class ConversationActivity extends XmppActivity { menuContactDetails.setVisible(false); menuSecure.setVisible(false); menuArchive.setTitle("Leave conference"); + menuInviteContacts.setVisible(true); } else { menuContactDetails.setVisible(true); menuMucDetails.setVisible(false); + menuInviteContacts.setVisible(false); if (this.getSelectedConversation().getLatestMessage().getEncryption() != Message.ENCRYPTION_NONE) { menuSecure.setIcon(R.drawable.ic_action_secure); } @@ -282,7 +286,7 @@ public class ConversationActivity extends XmppActivity { spl.openPane(); break; case R.id.action_add: - startActivity(new Intent(this, NewConversationActivity.class)); + startActivity(new Intent(this, ContactsActivity.class)); break; case R.id.action_archive: Conversation conv = getSelectedConversation(); @@ -319,6 +323,13 @@ public class ConversationActivity extends XmppActivity { intent.putExtra("uuid", getSelectedConversation().getUuid()); startActivity(intent); break; + case R.id.action_invite: + Intent inviteIntent = new Intent(getApplicationContext(), + ContactsActivity.class); + inviteIntent.setAction("invite"); + inviteIntent.putExtra("uuid",selectedConversation.getUuid()); + startActivity(inviteIntent); + break; case R.id.action_security: final Conversation selConv = getSelectedConversation(); View menuItemView = findViewById(R.id.action_security); @@ -451,7 +462,7 @@ public class ConversationActivity extends XmppActivity { finish(); } else if (conversationList.size() <= 0) { //add no history - startActivity(new Intent(this, NewConversationActivity.class)); + startActivity(new Intent(this, ContactsActivity.class)); finish(); } else { spl.openPane(); diff --git a/src/eu/siacs/conversations/ui/ConversationFragment.java b/src/eu/siacs/conversations/ui/ConversationFragment.java index d72dae8e6..4ae85dc2b 100644 --- a/src/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/eu/siacs/conversations/ui/ConversationFragment.java @@ -326,7 +326,8 @@ public class ConversationFragment extends Fragment { public void onStart() { super.onStart(); ConversationActivity activity = (ConversationActivity) getActivity(); - + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity); + this.useSubject = preferences.getBoolean("use_subject_in_muc", true); if (activity.xmppConnectionServiceBound) { this.onBackendConnected(); } diff --git a/src/eu/siacs/conversations/ui/ManageAccountActivity.java b/src/eu/siacs/conversations/ui/ManageAccountActivity.java index 8798a74cf..b9ed41025 100644 --- a/src/eu/siacs/conversations/ui/ManageAccountActivity.java +++ b/src/eu/siacs/conversations/ui/ManageAccountActivity.java @@ -200,7 +200,7 @@ public class ManageAccountActivity extends XmppActivity { if ((account.getStatus() == Account.STATUS_OFFLINE)||(account.getStatus() == Account.STATUS_TLS_ERROR)) { activity.xmppConnectionService.reconnectAccount(accountList.get(position),true); } else if (account.getStatus() == Account.STATUS_ONLINE) { - activity.startActivity(new Intent(activity.getApplicationContext(),NewConversationActivity.class)); + activity.startActivity(new Intent(activity.getApplicationContext(),ContactsActivity.class)); } else if (account.isOptionSet(Account.OPTION_REGISTER)) { editAccount(account); } diff --git a/src/eu/siacs/conversations/ui/MucDetailsActivity.java b/src/eu/siacs/conversations/ui/MucDetailsActivity.java index 73c182405..8d90fd566 100644 --- a/src/eu/siacs/conversations/ui/MucDetailsActivity.java +++ b/src/eu/siacs/conversations/ui/MucDetailsActivity.java @@ -70,7 +70,7 @@ public class MucDetailsActivity extends XmppActivity { @Override public void onClick(View v) { Intent intent = new Intent(getApplicationContext(), - NewConversationActivity.class); + ContactsActivity.class); intent.setAction("invite"); intent.putExtra("uuid",conversation.getUuid()); startActivity(intent); diff --git a/src/eu/siacs/conversations/utils/CryptoHelper.java b/src/eu/siacs/conversations/utils/CryptoHelper.java index ebbbd9674..550639d23 100644 --- a/src/eu/siacs/conversations/utils/CryptoHelper.java +++ b/src/eu/siacs/conversations/utils/CryptoHelper.java @@ -1,9 +1,14 @@ package eu.siacs.conversations.utils; +import java.security.SecureRandom; +import java.util.Random; + import android.util.Base64; public class CryptoHelper { final protected static char[] hexArray = "0123456789ABCDEF".toCharArray(); + final protected static char[] vowels = "aeiou".toCharArray(); + final protected static char[] consonants ="bcdfghjklmnpqrstvwxyz".toCharArray(); public static String bytesToHex(byte[] bytes) { char[] hexChars = new char[bytes.length * 2]; for ( int j = 0; j < bytes.length; j++ ) { @@ -31,4 +36,21 @@ public class CryptoHelper { return Base64.encodeToString(saslBytes, Base64.DEFAULT); } + + public static String randomMucName() { + Random random = new SecureRandom(); + return randomWord(3,random)+"."+randomWord(7,random); + } + + protected static String randomWord(int lenght,Random random) { + StringBuilder builder = new StringBuilder(lenght); + for(int i = 0; i < lenght; ++i) { + if (i % 2 == 0) { + builder.append(consonants[random.nextInt(consonants.length)]); + } else { + builder.append(vowels[random.nextInt(vowels.length)]); + } + } + return builder.toString(); + } } diff --git a/src/eu/siacs/conversations/xmpp/XmppConnection.java b/src/eu/siacs/conversations/xmpp/XmppConnection.java index dadd310aa..0fbd6f774 100644 --- a/src/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/eu/siacs/conversations/xmpp/XmppConnection.java @@ -220,6 +220,7 @@ public class XmppConnection implements Runnable { tagWriter.writeStanzaAsync(r); } else if (nextTag.isStart("resumed")) { tagReader.readElement(nextTag); + sendPing(); changeStatus(Account.STATUS_ONLINE); Log.d(LOGTAG,account.getJid()+": session resumed"); } else if (nextTag.isStart("r")) { @@ -543,10 +544,6 @@ public class XmppConnection implements Runnable { String resource = packet.findChild("bind").findChild("jid") .getContent().split("/")[1]; account.setResource(resource); - if (bindListener !=null) { - bindListener.onBind(account); - } - account.setStatus(Account.STATUS_ONLINE); if (streamFeatures.hasChild("sm")) { EnablePacket enable = new EnablePacket(); tagWriter.writeStanzaAsync(enable); @@ -554,9 +551,10 @@ public class XmppConnection implements Runnable { sendInitialPresence(); sendServiceDiscoveryInfo(); sendServiceDiscoveryItems(); - if (statusListener != null) { - statusListener.onStatusChanged(account); + if (bindListener !=null) { + bindListener.onBind(account); } + account.setStatus(Account.STATUS_ONLINE); } }); }