hide lock icon in channels; modify muc user context

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java        | 34 
src/main/java/eu/siacs/conversations/entities/Conversation.java                | 24 
src/main/java/eu/siacs/conversations/entities/MucOptions.java                  |  2 
src/main/java/eu/siacs/conversations/services/XmppConnectionService.java       |  6 
src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java         |  2 
src/main/java/eu/siacs/conversations/ui/ConversationFragment.java              | 15 
src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java         |  2 
src/main/java/eu/siacs/conversations/ui/util/ConversationMenuConfigurator.java | 12 
src/main/java/eu/siacs/conversations/ui/util/MucDetailsContextMenuHelper.java  | 11 
src/main/res/layout/activity_muc_details.xml                                   |  1 
src/main/res/menu/muc_details_context.xml                                      | 69 
src/main/res/values/strings.xml                                                |  7 
12 files changed, 91 insertions(+), 94 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java 🔗

@@ -840,40 +840,6 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
 		});
 	}
 
-	public enum AxolotlCapability {
-		FULL,
-		MISSING_PRESENCE,
-		MISSING_KEYS,
-		WRONG_CONFIGURATION,
-		NO_MEMBERS
-	}
-
-	public boolean isConversationAxolotlCapable(Conversation conversation) {
-		return conversation.isSingleOrPrivateAndNonAnonymous();
-	}
-
-	public Pair<AxolotlCapability, Jid> isConversationAxolotlCapableDetailed(Conversation conversation) {
-		if (conversation.isSingleOrPrivateAndNonAnonymous()) {
-			final List<Jid> jids = getCryptoTargets(conversation);
-			for (Jid jid : jids) {
-				if (!hasAny(jid) && (!deviceIds.containsKey(jid) || deviceIds.get(jid).isEmpty())) {
-					if (conversation.getAccount().getRoster().getContact(jid).mutualPresenceSubscription()) {
-						return new Pair<>(AxolotlCapability.MISSING_KEYS, jid);
-					} else {
-						return new Pair<>(AxolotlCapability.MISSING_PRESENCE, jid);
-					}
-				}
-			}
-			if (jids.size() > 0) {
-				return new Pair<>(AxolotlCapability.FULL, null);
-			} else {
-				return new Pair<>(AxolotlCapability.NO_MEMBERS, null);
-			}
-		} else {
-			return new Pair<>(AxolotlCapability.WRONG_CONFIGURATION, null);
-		}
-	}
-
 	public List<Jid> getCryptoTargets(Conversation conversation) {
 		final List<Jid> jids;
 		if (conversation.getMode() == Conversation.MODE_SINGLE) {

src/main/java/eu/siacs/conversations/entities/Conversation.java 🔗

@@ -60,9 +60,10 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
 	private static final String ATTRIBUTE_NEXT_MESSAGE_TIMESTAMP = "next_message_timestamp";
 	private static final String ATTRIBUTE_CRYPTO_TARGETS = "crypto_targets";
 	private static final String ATTRIBUTE_NEXT_ENCRYPTION = "next_encryption";
-	public static final String ATTRIBUTE_MEMBERS_ONLY = "members_only";
-	public static final String ATTRIBUTE_MODERATED = "moderated";
-	public static final String ATTRIBUTE_NON_ANONYMOUS = "non_anonymous";
+	static final String ATTRIBUTE_MEMBERS_ONLY = "members_only";
+	static final String ATTRIBUTE_MODERATED = "moderated";
+	static final String ATTRIBUTE_NON_ANONYMOUS = "non_anonymous";
+	public static final String ATTRIBUTE_FORMERLY_PRIVATE_NON_ANONYMOUS = "formerly_private_non_anonymous";
 	protected final ArrayList<Message> messages = new ArrayList<>();
 	public AtomicBoolean messagesLoaded = new AtomicBoolean(true);
 	protected Account account = null;
@@ -74,7 +75,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
 	private int status;
 	private long created;
 	private int mode;
-	private JSONObject attributes = new JSONObject();
+	private JSONObject attributes;
 	private Jid nextCounterpart;
 	private transient MucOptions mucOptions = null;
 	private boolean messagesLeftOnServer = true;
@@ -653,12 +654,11 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
 		if (Config.OMEMO_EXCEPTIONS.CONTACT_DOMAINS.contains(contact) || Config.OMEMO_EXCEPTIONS.ACCOUNT_DOMAINS.contains(account)) {
 			return false;
 		}
-		final AxolotlService axolotlService = conversation.getAccount().getAxolotlService();
-		return axolotlService != null && axolotlService.isConversationAxolotlCapable(conversation);
+		return conversation.isSingleOrPrivateAndNonAnonymous() || conversation.getBooleanAttribute(ATTRIBUTE_FORMERLY_PRIVATE_NON_ANONYMOUS, false);
 	}
 
-	public void setNextEncryption(int encryption) {
-		this.setAttribute(ATTRIBUTE_NEXT_ENCRYPTION, String.valueOf(encryption));
+	public boolean setNextEncryption(int encryption) {
+		return this.setAttribute(ATTRIBUTE_NEXT_ENCRYPTION, encryption);
 	}
 
 	public String getNextMessage() {
@@ -772,15 +772,17 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
 	}
 
 	public boolean setAttribute(String key, boolean value) {
-		boolean prev = getBooleanAttribute(key,false);
-		setAttribute(key,Boolean.toString(value));
-		return prev != value;
+		return setAttribute(key, String.valueOf(value));
 	}
 
 	private boolean setAttribute(String key, long value) {
 		return setAttribute(key, Long.toString(value));
 	}
 
+	private boolean setAttribute(String key, int value) {
+		return setAttribute(key, String.valueOf(value));
+	}
+
 	public boolean setAttribute(String key, String value) {
 		synchronized (this.attributes) {
 			try {

src/main/java/eu/siacs/conversations/entities/MucOptions.java 🔗

@@ -511,7 +511,7 @@ public class MucOptions {
         return users;
     }
 
-    public String createNameFromParticipants() {
+    String createNameFromParticipants() {
         List<User> users = getUsersRelevantForNameAndAvatar();
         if (users.size() >= 2) {
             StringBuilder builder = new StringBuilder();

src/main/java/eu/siacs/conversations/services/XmppConnectionService.java 🔗

@@ -1310,6 +1310,12 @@ public class XmppConnectionService extends Service {
         boolean saveInDb = addToConversation;
         message.setStatus(Message.STATUS_WAITING);
 
+        if (message.getEncryption() != Message.ENCRYPTION_NONE && conversation.getMode() == Conversation.MODE_MULTI && conversation.isPrivateAndNonAnonymous()) {
+            if (conversation.setAttribute(Conversation.ATTRIBUTE_FORMERLY_PRIVATE_NON_ANONYMOUS, true)) {
+                databaseBackend.updateConversation(conversation);
+            }
+        }
+
         if (account.isOnlineAndConnected()) {
             switch (message.getEncryption()) {
                 case Message.ENCRYPTION_NONE:

src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java 🔗

@@ -356,6 +356,8 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         getMenuInflater().inflate(R.menu.muc_details, menu);
+        final MenuItem share = menu.findItem(R.id.action_share);
+        share.setVisible(mConversation != null && !mConversation.isPrivateAndNonAnonymous());
         AccountUtils.showHideMenuItems(menu);
         return super.onCreateOptionsMenu(menu);
     }

src/main/java/eu/siacs/conversations/ui/ConversationFragment.java 🔗

@@ -1254,34 +1254,39 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
         if (conversation == null) {
             return;
         }
+        final boolean updated;
         switch (item.getItemId()) {
             case R.id.encryption_choice_none:
-                conversation.setNextEncryption(Message.ENCRYPTION_NONE);
+                updated = conversation.setNextEncryption(Message.ENCRYPTION_NONE);
                 item.setChecked(true);
                 break;
             case R.id.encryption_choice_pgp:
                 if (activity.hasPgp()) {
                     if (conversation.getAccount().getPgpSignature() != null) {
-                        conversation.setNextEncryption(Message.ENCRYPTION_PGP);
+                        updated = conversation.setNextEncryption(Message.ENCRYPTION_PGP);
                         item.setChecked(true);
                     } else {
+                        updated = false;
                         activity.announcePgp(conversation.getAccount(), conversation, null, activity.onOpenPGPKeyPublished);
                     }
                 } else {
                     activity.showInstallPgpDialog();
+                    updated = false;
                 }
                 break;
             case R.id.encryption_choice_axolotl:
                 Log.d(Config.LOGTAG, AxolotlService.getLogprefix(conversation.getAccount())
                         + "Enabled axolotl for Contact " + conversation.getContact().getJid());
-                conversation.setNextEncryption(Message.ENCRYPTION_AXOLOTL);
+                updated = conversation.setNextEncryption(Message.ENCRYPTION_AXOLOTL);
                 item.setChecked(true);
                 break;
             default:
-                conversation.setNextEncryption(Message.ENCRYPTION_NONE);
+                updated = conversation.setNextEncryption(Message.ENCRYPTION_NONE);
                 break;
         }
-        activity.xmppConnectionService.updateConversation(conversation);
+        if (updated) {
+            activity.xmppConnectionService.updateConversation(conversation);
+        }
         updateChatMsgHint();
         getActivity().invalidateOptionsMenu();
         activity.refreshUi();

src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java 🔗

@@ -479,7 +479,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
 		EnterJidDialog dialog = EnterJidDialog.newInstance(
 				mActivatedAccounts,
 				getString(R.string.add_contact),
-				getString(R.string.create),
+				getString(R.string.add),
 				prefilledJid,
 				null,
 				invite == null || !invite.hasFingerprints()

src/main/java/eu/siacs/conversations/ui/util/ConversationMenuConfigurator.java 🔗

@@ -83,11 +83,17 @@ public class ConversationMenuConfigurator {
 		final MenuItem pgp = menu.findItem(R.id.encryption_choice_pgp);
 		final MenuItem axolotl = menu.findItem(R.id.encryption_choice_axolotl);
 
+		final int next = conversation.getNextEncryption();
+
 		boolean visible;
 		if (OmemoSetting.isAlways()) {
 			visible = false;
 		} else if (conversation.getMode() == Conversation.MODE_MULTI) {
-			visible = (Config.supportOpenPgp() || Config.supportOmemo()) && Config.multipleEncryptionChoices();
+			if (next == Message.ENCRYPTION_NONE && !conversation.isPrivateAndNonAnonymous() && !conversation.getBooleanAttribute(Conversation.ATTRIBUTE_FORMERLY_PRIVATE_NON_ANONYMOUS, false)) {
+				visible = false;
+			} else {
+				visible = (Config.supportOpenPgp() || Config.supportOmemo()) && Config.multipleEncryptionChoices();
+			}
 		} else {
 			visible = Config.multipleEncryptionChoices();
 		}
@@ -105,10 +111,6 @@ public class ConversationMenuConfigurator {
 		pgp.setVisible(Config.supportOpenPgp());
 		none.setVisible(Config.supportUnencrypted() || conversation.getMode() == Conversation.MODE_MULTI);
 		axolotl.setVisible(Config.supportOmemo());
-		final AxolotlService axolotlService = conversation.getAccount().getAxolotlService();
-		if (axolotlService == null || !axolotlService.isConversationAxolotlCapable(conversation)) {
-			axolotl.setEnabled(false);
-		}
 		switch (conversation.getNextEncryption()) {
 			case Message.ENCRYPTION_NONE:
 				none.setChecked(true);

src/main/java/eu/siacs/conversations/ui/util/MucDetailsContextMenuHelper.java 🔗

@@ -63,7 +63,10 @@ public final class MucDetailsContextMenuHelper {
             MenuItem removeOwnerPrivileges = menu.findItem(R.id.revoke_owner_privileges);
             MenuItem removeAdminPrivileges = menu.findItem(R.id.remove_admin_privileges);
             MenuItem removeFromRoom = menu.findItem(R.id.remove_from_room);
+            MenuItem managePermisisons = menu.findItem(R.id.manage_permissions);
+            removeFromRoom.setTitle(isGroupChat ? R.string.remove_from_room : R.string.remove_from_channel);
             MenuItem banFromConference = menu.findItem(R.id.ban_from_conference);
+            banFromConference.setTitle(isGroupChat ? R.string.ban_from_conference : R.string.ban_from_channel);
             MenuItem invite = menu.findItem(R.id.invite);
             startConversation.setVisible(true);
             final Contact contact = user.getContact();
@@ -74,8 +77,10 @@ public final class MucDetailsContextMenuHelper {
             if ((activity instanceof ConferenceDetailsActivity || activity instanceof MucUsersActivity) && user.getRole() == MucOptions.Role.NONE) {
                 invite.setVisible(true);
             }
-            if (self.getAffiliation().ranks(MucOptions.Affiliation.ADMIN) && self.getAffiliation().outranks(user.getAffiliation())) {
+            boolean managePermissionsVisible = false;
+            if ((self.getAffiliation().ranks(MucOptions.Affiliation.ADMIN) && self.getAffiliation().outranks(user.getAffiliation())) || self.getAffiliation() == MucOptions.Affiliation.OWNER) {
                 if (advancedMode) {
+                    managePermissionsVisible = true;
                     if (!user.getAffiliation().ranks(MucOptions.Affiliation.MEMBER)) {
                         giveMembership.setVisible(true);
                     } else if (user.getAffiliation() == MucOptions.Affiliation.MEMBER) {
@@ -86,12 +91,14 @@ public final class MucDetailsContextMenuHelper {
                     }
                 } else {
                     if (!Config.DISABLE_BAN || conversation.getMucOptions().membersOnly()) {
+                        managePermissionsVisible = true;
                         removeFromRoom.setVisible(true);
                     }
                 }
             }
             if (self.getAffiliation().ranks(MucOptions.Affiliation.OWNER)) {
                 if (isGroupChat || advancedMode || user.getAffiliation() == MucOptions.Affiliation.OWNER) {
+                    managePermissionsVisible = true;
                     if (!user.getAffiliation().ranks(MucOptions.Affiliation.OWNER)) {
                         giveOwnerPrivileges.setVisible(true);
                     } else if (user.getAffiliation() == MucOptions.Affiliation.OWNER){
@@ -99,6 +106,7 @@ public final class MucDetailsContextMenuHelper {
                     }
                 }
                 if (!isGroupChat || advancedMode || user.getAffiliation() == MucOptions.Affiliation.ADMIN) {
+                    managePermissionsVisible = true;
                     if (!user.getAffiliation().ranks(MucOptions.Affiliation.ADMIN)) {
                         giveAdminPrivileges.setVisible(true);
                     } else if (user.getAffiliation() == MucOptions.Affiliation.ADMIN) {
@@ -106,6 +114,7 @@ public final class MucDetailsContextMenuHelper {
                     }
                 }
             }
+            managePermisisons.setVisible(managePermissionsVisible);
             sendPrivateMessage.setVisible(!isGroupChat && mucOptions.allowPm() && user.getRole().ranks(MucOptions.Role.VISITOR));
         } else {
             sendPrivateMessage.setVisible(true);

src/main/res/layout/activity_muc_details.xml 🔗

@@ -156,7 +156,6 @@
                                 android:layout_alignParentStart="true"
                                 android:layout_centerVertical="true"
                                 android:layout_toStartOf="@+id/change_conference_button"
-                                android:text="@string/private_conference"
                                 android:textAppearance="@style/TextAppearance.Conversations.Body1"
                                 android:layout_alignParentLeft="true"
                                 android:layout_toLeftOf="@+id/change_conference_button" />

src/main/res/menu/muc_details_context.xml 🔗

@@ -16,36 +16,41 @@
         android:id="@+id/send_private_message"
         android:title="@string/send_private_message"
         android:visible="false"/>
-    <item
-        android:id="@+id/give_membership"
-        android:title="@string/grant_membership"
-        android:visible="false"/>
-    <item
-        android:id="@+id/remove_membership"
-        android:title="@string/remove_membership"
-        android:visible="false"/>
-    <item
-        android:id="@+id/give_admin_privileges"
-        android:title="@string/grant_admin_privileges"
-        android:visible="false"/>
-    <item
-        android:id="@+id/remove_admin_privileges"
-        android:title="@string/remove_admin_privileges"
-        android:visible="false"/>
-    <item
-        android:id="@+id/give_owner_privileges"
-        android:title="@string/grant_owner_privileges"
-        android:visible="false"/>
-    <item
-        android:id="@+id/revoke_owner_privileges"
-        android:title="@string/remove_owner_privileges"
-        android:visible="false"/>
-    <item
-        android:id="@+id/ban_from_conference"
-        android:title="@string/ban_from_conference"
-        android:visible="false"/>
-    <item
-        android:id="@+id/remove_from_room"
-        android:title="@string/remove_from_room"
-        android:visible="false"/>
+    <item android:id="@+id/manage_permissions"
+        android:title="@string/manage_permission">
+        <menu>
+            <item
+                android:id="@+id/give_membership"
+                android:title="@string/grant_membership"
+                android:visible="false"/>
+            <item
+                android:id="@+id/remove_membership"
+                android:title="@string/remove_membership"
+                android:visible="false"/>
+            <item
+                android:id="@+id/give_admin_privileges"
+                android:title="@string/grant_admin_privileges"
+                android:visible="false"/>
+            <item
+                android:id="@+id/remove_admin_privileges"
+                android:title="@string/remove_admin_privileges"
+                android:visible="false"/>
+            <item
+                android:id="@+id/give_owner_privileges"
+                android:title="@string/grant_owner_privileges"
+                android:visible="false"/>
+            <item
+                android:id="@+id/revoke_owner_privileges"
+                android:title="@string/remove_owner_privileges"
+                android:visible="false"/>
+            <item
+                android:id="@+id/ban_from_conference"
+                android:title="@string/ban_from_conference"
+                android:visible="false"/>
+            <item
+                android:id="@+id/remove_from_room"
+                android:title="@string/remove_from_room"
+                android:visible="false"/>
+        </menu>
+    </item>
 </menu>

src/main/res/values/strings.xml 🔗

@@ -371,13 +371,13 @@
     <string name="grant_owner_privileges">Grant owner privileges</string>
     <string name="remove_owner_privileges">Revoke owner privileges</string>
     <string name="remove_from_room">Remove from group chat</string>
+    <string name="remove_from_channel">Remove from channel</string>
     <string name="could_not_change_affiliation">Could not change affiliation of %s</string>
     <string name="ban_from_conference">Ban from group chat</string>
+    <string name="ban_from_channel">Ban from channel</string>
     <string name="removing_from_public_conference">You are trying to remove %s from a public group chat. The only way to do that is to ban that user for ever.</string>
     <string name="ban_now">Ban now</string>
     <string name="could_not_change_role">Could not change role of %s</string>
-    <string name="public_conference">Publicly accessible group chat</string>
-    <string name="private_conference">Private, members only group chat</string>
     <string name="conference_options">Private group chat configuration</string>
     <string name="channel_options">Public channel configuration</string>
     <string name="members_only">Private, members only</string>
@@ -846,6 +846,7 @@
     <string name="anyone_can_invite_others">Anyone can invite others.</string>
     <string name="jabber_ids_are_visible_to_admins">Jabber IDs are visible to admins.</string>
     <string name="jabber_ids_are_visible_to_anyone">Jabber IDs are visible to anyone.</string>
-    <string name="no_users_hint_channel">This public channel has no participants.</string>
+    <string name="no_users_hint_channel">This public channel has no participants. Invite your contacts or use the share button to distribute its XMPP address.</string>
     <string name="no_users_hint_group_chat">This private group chat has no participants.</string>
+    <string name="manage_permission">Manage permissions</string>
 </resources>