Fixes FS#147: Disable OMEMO

This commit is contained in:
steckbrief 2016-03-02 16:54:18 +01:00
parent c26f907263
commit 5d21abac1d
20 changed files with 1331 additions and 981 deletions

View file

@ -14,6 +14,10 @@ public class ConversationsPlusPreferences extends Settings {
private static ConversationsPlusPreferences instance;
private final SharedPreferences sharedPreferences;
public static boolean omemoEnabled() {
return getBoolean("omemo_enabled", false);
}
public static String imgTransferFolder() {
return getString("img_transfer_folder", getString("app_name", "Conversations+"));
}

View file

@ -2,6 +2,7 @@ package eu.siacs.conversations;
import android.graphics.Bitmap;
import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
import eu.siacs.conversations.xmpp.chatstate.ChatState;
public final class Config {
@ -31,7 +32,7 @@ public final class Config {
}
public static boolean supportOmemo() {
return (ENCRYPTION_MASK & OMEMO) != 0;
return ConversationsPlusPreferences.omemoEnabled();
}
public static boolean multipleEncryptionChoices() {

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,196 @@
package eu.siacs.conversations.crypto.axolotl;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.whispersystems.libaxolotl.AxolotlAddress;
import org.whispersystems.libaxolotl.IdentityKey;
import org.whispersystems.libaxolotl.state.PreKeyRecord;
import org.whispersystems.libaxolotl.state.SignedPreKeyRecord;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.Set;
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.xmpp.jid.Jid;
/**
* Axolotl Service Stub implementation to avoid axolotl usage.
*/
public class AxolotlServiceStub implements AxolotlService {
@Override
public boolean fetchMapHasErrors(Contact contact) {
return false;
}
@Override
public String getOwnFingerprint() {
return null;
}
@Override
public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust) {
return Collections.emptySet();
}
@Override
public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust, Contact contact) {
return Collections.emptySet();
}
@Override
public long getNumTrustedKeys(Contact contact) {
return 0;
}
@Override
public Set<String> getFingerprintsForOwnSessions() {
return Collections.emptySet();
}
@Override
public Set<String> getFingerprintsForContact(Contact contact) {
return Collections.emptySet();
}
@Override
public boolean isPepBroken() {
return true;
}
@Override
public void regenerateKeys(boolean wipeOther) {
}
@Override
public int getOwnDeviceId() {
return 0;
}
@Override
public Set<Integer> getOwnDeviceIds() {
return Collections.emptySet();
}
@Override
public void registerDevices(Jid jid, @NonNull Set<Integer> deviceIds) {
}
@Override
public void wipeOtherPepDevices() {
}
@Override
public void purgeKey(String fingerprint) {
}
@Override
public void publishOwnDeviceIdIfNeeded() {
}
@Override
public void publishOwnDeviceId(Set<Integer> deviceIds) {
}
@Override
public void publishDeviceVerificationAndBundle(SignedPreKeyRecord signedPreKeyRecord, Set<PreKeyRecord> preKeyRecords, boolean announceAfter, boolean wipe) {
}
@Override
public void publishBundlesIfNeeded(boolean announce, boolean wipe) {
}
@Override
public boolean isContactAxolotlCapable(Contact contact) {
return false;
}
@Override
public XmppAxolotlSession.Trust getFingerprintTrust(String fingerprint) {
return XmppAxolotlSession.Trust.TRUSTED;
}
@Override
public X509Certificate getFingerprintCertificate(String fingerprint) {
return null;
}
@Override
public void setFingerprintTrust(String fingerprint, XmppAxolotlSession.Trust trust) {
}
@Override
public Set<AxolotlAddress> findDevicesWithoutSession(Conversation conversation) {
return Collections.emptySet();
}
@Override
public Set<AxolotlAddress> findDevicesWithoutSession(Jid contactJid) {
return Collections.emptySet();
}
@Override
public boolean createSessionsIfNeeded(Conversation conversation) {
return false;
}
@Override
public boolean trustedSessionVerified(Conversation conversation) {
return false;
}
@Override
public boolean hasPendingKeyFetches(Account account, Contact contact) {
return false;
}
@Nullable
@Override
public XmppAxolotlMessage encrypt(Message message) {
return null;
}
@Override
public void preparePayloadMessage(Message message, boolean delay) {
}
@Override
public void prepareKeyTransportMessage(Contact contact, OnMessageCreatedCallback onMessageCreatedCallback) {
}
@Override
public XmppAxolotlMessage fetchAxolotlMessageFromCache(Message message) {
return null;
}
@Override
public XmppAxolotlMessage.XmppAxolotlPlaintextMessage processReceivingPayloadMessage(XmppAxolotlMessage message) {
return null;
}
@Override
public XmppAxolotlMessage.XmppAxolotlKeyTransportMessage processReceivingKeyTransportMessage(XmppAxolotlMessage message) {
return null;
}
@Override
public void onAdvancedStreamFeaturesAvailable(Account account) {
}
}

View file

@ -77,7 +77,7 @@ public class SQLiteAxolotlStore implements AxolotlStore {
this.localRegistrationId = loadRegistrationId();
this.currentPreKeyId = loadCurrentPreKeyId();
for (SignedPreKeyRecord record : loadSignedPreKeys()) {
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Got Axolotl signed prekey record:" + record.getId());
Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Got Axolotl signed prekey record:" + record.getId());
}
}
@ -95,7 +95,7 @@ public class SQLiteAxolotlStore implements AxolotlStore {
if (ownKey != null) {
return ownKey;
} else {
Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Could not retrieve own IdentityKeyPair");
Log.i(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Could not retrieve own IdentityKeyPair");
ownKey = generateIdentityKeyPair();
mXmppConnectionService.databaseBackend.storeOwnIdentityKeyPair(account, ownKey);
}
@ -112,13 +112,13 @@ public class SQLiteAxolotlStore implements AxolotlStore {
if (!regenerate && regIdString != null) {
reg_id = Integer.valueOf(regIdString);
} else {
Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Could not retrieve axolotl registration id for account " + account.getJid());
Log.i(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Could not retrieve axolotl registration id for account " + account.getJid());
reg_id = generateRegistrationId();
boolean success = this.account.setKey(JSONKEY_REGISTRATION_ID, Integer.toString(reg_id));
if (success) {
mXmppConnectionService.databaseBackend.updateAccount(account);
} else {
Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Failed to write new key to the database!");
Log.e(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Failed to write new key to the database!");
}
}
return reg_id;
@ -130,7 +130,7 @@ public class SQLiteAxolotlStore implements AxolotlStore {
if (regIdString != null) {
reg_id = Integer.valueOf(regIdString);
} else {
Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Could not retrieve current prekey id for account " + account.getJid());
Log.w(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Could not retrieve current prekey id for account " + account.getJid());
reg_id = 0;
}
return reg_id;
@ -344,7 +344,7 @@ public class SQLiteAxolotlStore implements AxolotlStore {
if (success) {
mXmppConnectionService.databaseBackend.updateAccount(account);
} else {
Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Failed to write new prekey id to the database!");
Log.e(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Failed to write new prekey id to the database!");
}
}

View file

@ -168,10 +168,10 @@ public class XmppAxolotlSession {
try {
try {
PreKeyWhisperMessage message = new PreKeyWhisperMessage(encryptedKey);
Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "PreKeyWhisperMessage received, new session ID:" + message.getSignedPreKeyId() + "/" + message.getPreKeyId());
Log.i(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "PreKeyWhisperMessage received, new session ID:" + message.getSignedPreKeyId() + "/" + message.getPreKeyId());
IdentityKey msgIdentityKey = message.getIdentityKey();
if (this.identityKey != null && !this.identityKey.equals(msgIdentityKey)) {
Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Had session with fingerprint " + this.getFingerprint() + ", received message with fingerprint " + msgIdentityKey.getFingerprint());
Log.e(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Had session with fingerprint " + this.getFingerprint() + ", received message with fingerprint " + msgIdentityKey.getFingerprint());
} else {
this.identityKey = msgIdentityKey;
plaintext = cipher.decrypt(message);
@ -180,14 +180,14 @@ public class XmppAxolotlSession {
}
}
} catch (InvalidMessageException | InvalidVersionException e) {
Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "WhisperMessage received");
Log.i(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "WhisperMessage received");
WhisperMessage message = new WhisperMessage(encryptedKey);
plaintext = cipher.decrypt(message);
} catch (InvalidKeyException | InvalidKeyIdException | UntrustedIdentityException e) {
Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage());
Log.w(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage());
}
} catch (LegacyMessageException | InvalidMessageException | DuplicateMessageException | NoSessionException e) {
Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage());
Log.w(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage());
}
if (plaintext != null) {

View file

@ -19,11 +19,14 @@ import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.OtrService;
import eu.siacs.conversations.crypto.PgpDecryptionService;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.crypto.axolotl.AxolotlServiceImpl;
import eu.siacs.conversations.crypto.axolotl.AxolotlServiceStub;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
@ -352,10 +355,15 @@ public class Account extends AbstractEntity {
public void initAccountServices(final XmppConnectionService context) {
this.mOtrService = new OtrService(context, this);
this.axolotlService = new AxolotlService(this, context);
if (xmppConnection != null) {
xmppConnection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService);
}
if (ConversationsPlusPreferences.omemoEnabled()) {
this.axolotlService = new AxolotlServiceImpl(this, context);
if (xmppConnection != null) {
xmppConnection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService);
}
} else {
this.axolotlService = new AxolotlServiceStub();
}
this.pgpDecryptionService = new PgpDecryptionService(context);
}

View file

@ -13,13 +13,8 @@ import java.util.Locale;
import java.util.TimeZone;
import de.thedevstack.conversationsplus.ConversationsPlusApplication;
import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
import de.tzur.conversations.Settings;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.PhoneHelper;
import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
public abstract class AbstractGenerator {
private final String[] FEATURES = {

View file

@ -25,7 +25,6 @@ import eu.siacs.conversations.services.MessageArchiveService;
import eu.siacs.conversations.utils.Xmlns;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.forms.Data;
import eu.siacs.conversations.xmpp.forms.Field;
import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;

View file

@ -157,7 +157,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
try {
return Base64.decode(signedPreKeySignature.getContent(), Base64.DEFAULT);
} catch (Throwable e) {
Log.e(Config.LOGTAG,AxolotlService.LOGPREFIX+" : Invalid base64 in signedPreKeySignature");
Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX+" : Invalid base64 in signedPreKeySignature");
return null;
}
}
@ -171,7 +171,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
try {
identityKey = new IdentityKey(Base64.decode(identityKeyElement.getContent(), Base64.DEFAULT), 0);
} catch (Throwable e) {
Log.e(Config.LOGTAG,AxolotlService.LOGPREFIX+" : "+"Invalid identityKey in PEP: "+e.getMessage());
Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX+" : "+"Invalid identityKey in PEP: "+e.getMessage());
}
return identityKey;
}
@ -211,7 +211,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
public Pair<X509Certificate[],byte[]> verification(final IqPacket packet) {
Element item = getItem(packet);
Element verification = item != null ? item.findChild("verification",AxolotlService.PEP_PREFIX) : null;
Element verification = item != null ? item.findChild("verification", AxolotlService.PEP_PREFIX) : null;
Element chain = verification != null ? verification.findChild("chain") : null;
Element signature = verification != null ? verification.findChild("signature") : null;
if (chain != null && signature != null) {

View file

@ -10,14 +10,14 @@ import net.java.otr4j.session.SessionStatus;
import java.util.ArrayList;
import java.util.Set;
import java.util.UUID;
import de.thedevstack.android.logcat.Logging;
import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
import de.thedevstack.conversationsplus.utils.AvatarUtil;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.crypto.PgpDecryptionService;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.crypto.axolotl.AxolotlServiceImpl;
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Bookmark;
@ -118,7 +118,7 @@ public class MessageParser extends AbstractParser implements
if(plaintextMessage != null) {
finishedMessage = new Message(conversation, plaintextMessage.getPlaintext(), Message.ENCRYPTION_AXOLOTL, status);
finishedMessage.setAxolotlFingerprint(plaintextMessage.getFingerprint());
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(finishedMessage.getConversation().getAccount())+" Received Message with session fingerprint: "+plaintextMessage.getFingerprint());
Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(finishedMessage.getConversation().getAccount())+" Received Message with session fingerprint: "+plaintextMessage.getFingerprint());
}
return finishedMessage;
@ -211,8 +211,8 @@ public class MessageParser extends AbstractParser implements
mXmppConnectionService.updateConversationUi();
mXmppConnectionService.updateAccountUi();
}
} else if (AxolotlService.PEP_DEVICE_LIST.equals(node)) {
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Received PEP device list update from "+ from + ", processing...");
} else if (ConversationsPlusPreferences.omemoEnabled() && AxolotlService.PEP_DEVICE_LIST.equals(node)) {
Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account)+"Received PEP device list update from "+ from + ", processing...");
Element item = items.findChild("item");
Set<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item);
AxolotlService axolotlService = account.getAxolotlService();
@ -297,8 +297,6 @@ public class MessageParser extends AbstractParser implements
final String body = packet.getBody();
final Element mucUserElement = packet.findChild("x", "http://jabber.org/protocol/muc#user");
final String pgpEncrypted = packet.findChildContent("x", "jabber:x:encrypted");
final Element replaceElement = packet.findChild("replace","urn:xmpp:message-correct:0");
final String replacementId = replaceElement == null ? null : replaceElement.getAttribute("id");
final Element axolotlEncrypted = packet.findChild(XmppAxolotlMessage.CONTAINERTAG, AxolotlService.PEP_PREFIX);
int status;
final Jid counterpart;

View file

@ -35,7 +35,7 @@ import org.json.JSONException;
import de.thedevstack.android.logcat.Logging;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.crypto.axolotl.AxolotlServiceImpl;
import eu.siacs.conversations.crypto.axolotl.SQLiteAxolotlStore;
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
import eu.siacs.conversations.entities.Account;
@ -940,7 +940,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
try {
identityKeyPair = new IdentityKeyPair(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT));
} catch (InvalidKeyException e) {
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Encountered invalid IdentityKey in database for account" + account.getJid().toBareJid() + ", address: " + name);
Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Encountered invalid IdentityKey in database for account" + account.getJid().toBareJid() + ", address: " + name);
}
}
cursor.close();
@ -965,7 +965,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
try {
identityKeys.add(new IdentityKey(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT), 0));
} catch (InvalidKeyException e) {
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Encountered invalid IdentityKey in database for account" + account.getJid().toBareJid() + ", address: " + name);
Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Encountered invalid IdentityKey in database for account" + account.getJid().toBareJid() + ", address: " + name);
}
}
cursor.close();
@ -1095,7 +1095,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
}
public void recreateAxolotlDb(SQLiteDatabase db) {
Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + ">>> (RE)CREATING AXOLOTL DATABASE <<<");
Log.d(Config.LOGTAG, AxolotlServiceImpl.LOGPREFIX + " : " + ">>> (RE)CREATING AXOLOTL DATABASE <<<");
db.execSQL("DROP TABLE IF EXISTS " + SQLiteAxolotlStore.SESSION_TABLENAME);
db.execSQL(CREATE_SESSIONS_STATEMENT);
db.execSQL("DROP TABLE IF EXISTS " + SQLiteAxolotlStore.PREKEY_TABLENAME);
@ -1108,7 +1108,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
public void wipeAxolotlDb(Account account) {
String accountName = account.getUuid();
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ">>> WIPING AXOLOTL DATABASE FOR ACCOUNT " + accountName + " <<<");
Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + ">>> WIPING AXOLOTL DATABASE FOR ACCOUNT " + accountName + " <<<");
SQLiteDatabase db = this.getWritableDatabase();
String[] deleteArgs = {
accountName

View file

@ -50,6 +50,7 @@ import de.timroes.android.listview.EnhancedListView;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.crypto.axolotl.AxolotlServiceImpl;
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Blockable;
@ -835,7 +836,7 @@ public class ConversationActivity extends XmppActivity
}
break;
case R.id.encryption_choice_axolotl:
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(conversation.getAccount())
Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(conversation.getAccount())
+ "Enabled axolotl for Contact " + conversation.getContact().getJid());
conversation.setNextEncryption(Message.ENCRYPTION_AXOLOTL);
item.setChecked(true);

View file

@ -7,6 +7,7 @@ import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Build;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceCategory;
@ -123,6 +124,12 @@ public class SettingsActivity extends XmppActivity implements
return true;
}
});
// Avoid appearence of setting to enable or disable omemo in screen
Preference omemoEnabledPreference = this.mSettingsFragment.findPreference("omemo_enabled");
PreferenceCategory otherExpertSettingsGroup = (PreferenceCategory) this.mSettingsFragment.findPreference("other_expert_settings");
if (null != omemoEnabledPreference && null != otherExpertSettingsGroup) {
otherExpertSettingsGroup.removePreference(omemoEnabledPreference);
}
}
@Override

View file

@ -504,7 +504,8 @@
android:id="@+id/axolotl_fingerprint_box"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginTop="32dp">
android:layout_marginTop="32dp"
android:visibility="gone">
<LinearLayout
android:layout_width="wrap_content"

View file

@ -7,7 +7,8 @@
android:title="@string/encryption_choice_unencrypted"/>
<item
android:id="@+id/encryption_choice_axolotl"
android:title="@string/encryption_choice_omemo"/>
android:title="@string/encryption_choice_omemo"
android:visible="@bool/omemo_enabled"/>
<item
android:id="@+id/encryption_choice_otr"
android:title="@string/encryption_choice_otr"/>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="omemo_enabled">false</bool>
</resources>

View file

@ -654,4 +654,6 @@
<string name="no_accounts">(No activated accounts)</string>
<string name="this_field_is_required">This field is required</string>
<string name="retry_decryption">Retry decryption</string>
<string name="pref_omemo_enabled_summary">Enable OMEMO?</string>
<string name="pref_omemo_enabled_title">Enable OMEMO</string>
</resources>

View file

@ -227,7 +227,7 @@
android:summary="@string/pref_xa_on_silent_mode_summary"
android:title="@string/pref_xa_on_silent_mode"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/pref_expert_options_other">
<PreferenceCategory android:key="other_expert_settings" android:title="@string/pref_expert_options_other">
<CheckBoxPreference
android:key="autojoin"
android:defaultValue="true"
@ -251,6 +251,11 @@
<de.thedevstack.conversationsplus.ui.preferences.LogInformationPreference
android:summary="@string/pref_show_logcat_summary"
android:title="@string/pref_show_logcat_title"/>
<CheckBoxPreference
android:defaultValue="@bool/omemo_enabled"
android:key="omemo_enabled"
android:summary="@string/pref_omemo_enabled_summary"
android:title="@string/pref_omemo_enabled_title"/>
</PreferenceCategory>
</PreferenceScreen>