better muc invitations. clearified the creation of ad hoc mucs with an alert dialog

This commit is contained in:
Daniel Gultsch 2014-03-15 15:13:35 +01:00
parent 841c6e04a9
commit 8cd59bb944
12 changed files with 184 additions and 59 deletions

View file

@ -62,8 +62,8 @@
android:windowSoftInputMode="stateHidden" >
</activity>
<activity
android:name="eu.siacs.conversations.ui.NewConversationActivity"
android:label="@string/title_activity_new_conversation"
android:name="eu.siacs.conversations.ui.ContactsActivity"
android:label="@string/title_activity_contacts"
android:parentActivityName="eu.siacs.conversations.ui.ConversationActivity"
android:windowSoftInputMode="stateHidden" >
<meta-data

View file

@ -26,6 +26,10 @@
android:showAsAction="ifRoom"
android:icon="@drawable/ic_action_group"
android:title="@string/action_muc_details" />
<item
android:id="@+id/action_invite"
android:showAsAction="never"
android:title="@string/invite_contacts" />
<item
android:id="@+id/action_archive"

View file

@ -15,4 +15,8 @@
android:id="@+id/action_invite"
android:showAsAction="ifRoom"
android:title="@string/invite_contacts" />
<item
android:id="@+id/action_invite_to_existing"
android:showAsAction="never"
android:title="@string/invite_contacts_to_existing" />
</menu>

View file

@ -10,7 +10,7 @@
<string name="action_muc_details">Conferenece details</string>
<string name="action_secure">Secure conversation</string>
<string name="action_add_account">Add account</string>
<string name="title_activity_new_conversation">New Conversation</string>
<string name="title_activity_contacts">Contacts</string>
<string name="just_now">just now</string>
<string name="sending">sending&#8230;</string>
<string name="announce_pgp">Renew PGP announcement</string>
@ -31,4 +31,13 @@
<string name="no_otr_fingerprint">No OTR Fingerprint generated. Just go ahead an start an encrypted conversation</string>
<string name="start_conversation">Start Conversation</string>
<string name="invite_contacts">Invite Contacts</string>
<string name="invite_contacts_to_existing">Invite to existing conference</string>
<string name="new_conference">Create new conference</string>
<string name="cancel">Cancel</string>
<string name="create_invite">Create \u0026 Invite</string>
<string name="new_conference_explained">Do you want to create a new conference with a randomly generated address and invite the selected contacts to it?</string>
<string name="no_open_mucs">No existing conferences</string>
<string name="invitation_sent">Invitation sent</string>
<string name="account_offline">Account offline</string>
<string name="cant_invite_while_offline">You have to be online to invite people to conferences</string>
</resources>

View file

@ -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<Account> 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<Conversation> 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<Conversation> 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;

View file

@ -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<Contact> rosterContacts = new ArrayList<Contact>();
protected List<Contact> aggregatedContacts = new ArrayList<Contact>();
@ -56,7 +59,10 @@ public class NewConversationActivity extends XmppActivity {
private TextView contactsHeader;
private List<Account> accounts;
private List<Contact> selectedContacts = new ArrayList<Contact>();
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<Conversation> mucs = new ArrayList<Conversation>();
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<Conversation> 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<Contact> 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);

View file

@ -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();

View file

@ -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();
}

View file

@ -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);
}

View file

@ -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);

View file

@ -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();
}
}

View file

@ -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);
}
});
}