provide hint on why conference can not be encrypted

Daniel Gultsch created

Change summary

README.md                                                               |  3 
src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java | 28 
src/main/java/eu/siacs/conversations/ui/ConversationActivity.java       | 41 
src/main/res/values/strings.xml                                         |  4 
4 files changed, 74 insertions(+), 2 deletions(-)

Detailed changes

README.md 🔗

@@ -308,6 +308,9 @@ The owner of a conference can make a public conference private by going into the
 details and hit the settings button (the one with the gears) and select both *private* and
 *members only*.
 
+If OMEMO is grayed out long pressing the lock icon will reveal some quick hints on why OMEMO
+is disabled.
+
 ##### OpenPGP
 
 Every participant has to announce their OpenPGP key (see answer above).

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

@@ -647,14 +647,38 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
 		});
 	}
 
+	public enum AxolotlCapability {
+		FULL,
+		MISSING_PRESENCE,
+		MISSING_KEYS,
+		WRONG_CONFIGURATION,
+		NO_MEMBERS
+	}
+
 	public boolean isConversationAxolotlCapable(Conversation conversation) {
+		return isConversationAxolotlCapableDetailed(conversation).first == AxolotlCapability.FULL;
+	}
+
+	public Pair<AxolotlCapability,Jid> isConversationAxolotlCapableDetailed(Conversation conversation) {
 		final List<Jid> jids = getCryptoTargets(conversation);
 		for(Jid jid : jids) {
 			if (!hasAny(jid) && (!deviceIds.containsKey(jid) || deviceIds.get(jid).isEmpty())) {
-				return false;
+				if (conversation.getAccount().getRoster().getContact(jid).trusted()) {
+					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 {
+			if (conversation.getMucOptions().membersOnly() && conversation.getMucOptions().nonanonymous()) {
+				return new Pair<>(AxolotlCapability.NO_MEMBERS, null);
+			} else {
+				return new Pair<>(AxolotlCapability.WRONG_CONFIGURATION, null);
 			}
 		}
-		return jids.size() > 0;
 	}
 
 	public List<Jid> getCryptoTargets(Conversation conversation) {

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

@@ -15,6 +15,7 @@ import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Handler;
 import android.provider.MediaStore;
 import android.provider.Settings;
 import android.support.v4.widget.SlidingPaneLayout;
@@ -66,6 +67,8 @@ import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
 import eu.siacs.conversations.xmpp.jid.InvalidJidException;
 import eu.siacs.conversations.xmpp.jid.Jid;
 
+import static eu.siacs.conversations.crypto.axolotl.AxolotlService.AxolotlCapability.MISSING_PRESENCE;
+
 public class ConversationActivity extends XmppActivity
 	implements OnAccountUpdate, OnConversationUpdate, OnRosterUpdate, OnUpdateBlocklist, XmppConnectionService.OnShowErrorToast {
 
@@ -421,9 +424,47 @@ public class ConversationActivity extends XmppActivity
 				}
 			}
 		}
+		if (Config.supportOmemo()) {
+			new Handler().post(new Runnable() {
+				@Override
+				public void run() {
+					View view = findViewById(R.id.action_security);
+					if (view != null) {
+						view.setOnLongClickListener(new View.OnLongClickListener() {
+							@Override
+							public boolean onLongClick(View v) {
+								return quickOmemoDebugger(getSelectedConversation());
+							}
+						});
+					}
+				}
+			});
+		}
 		return super.onCreateOptionsMenu(menu);
 	}
 
+	private boolean quickOmemoDebugger(Conversation c) {
+		if (c != null) {
+			AxolotlService axolotlService = c.getAccount().getAxolotlService();
+			Pair<AxolotlService.AxolotlCapability,Jid> capabilityJidPair = axolotlService.isConversationAxolotlCapableDetailed(c);
+			switch (capabilityJidPair.first) {
+				case MISSING_PRESENCE:
+					Toast.makeText(ConversationActivity.this,getString(R.string.missing_presence_subscription_with_x,capabilityJidPair.second.toBareJid().toString()),Toast.LENGTH_SHORT).show();
+					return true;
+				case MISSING_KEYS:
+					Toast.makeText(ConversationActivity.this,getString(R.string.missing_keys_from_x,capabilityJidPair.second.toBareJid().toString()),Toast.LENGTH_SHORT).show();
+					return true;
+				case WRONG_CONFIGURATION:
+					Toast.makeText(ConversationActivity.this,R.string.wrong_conference_configuration, Toast.LENGTH_SHORT).show();
+					return true;
+				case NO_MEMBERS:
+					Toast.makeText(ConversationActivity.this,R.string.this_conference_has_no_members, Toast.LENGTH_SHORT).show();
+					return true;
+			}
+		}
+		return false;
+	}
+
 	protected void selectPresenceToAttachFile(final int attachmentChoice, final int encryption) {
 		final Conversation conversation = getSelectedConversation();
 		final Account account = conversation.getAccount();

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

@@ -682,4 +682,8 @@
 	<string name="no_permission_to_access_x">No permission to access %s</string>
 	<string name="remote_server_not_found">Remote server not found</string>
 	<string name="unable_to_update_account">Unable to update account</string>
+	<string name="missing_presence_subscription_with_x">Missing presence subscription with %s.</string>
+	<string name="missing_keys_from_x">Missing OMEMO keys from %s.</string>
+	<string name="wrong_conference_configuration">This is not a private, non-anonymous conference.</string>
+	<string name="this_conference_has_no_members">There are no members in this conference.</string>
 </resources>