diff options
author | lookshe <github@lookshe.org> | 2016-03-08 21:13:24 +0100 |
---|---|---|
committer | lookshe <github@lookshe.org> | 2016-03-08 21:13:24 +0100 |
commit | 74d6d10cb76b7440f57db193a3c38e476206969c (patch) | |
tree | 665b7787c0f7bcb6d759e849972fa9658e84423f /src | |
parent | 7be757de90fb71042b79b88a76cd36fc07b2ee87 (diff) | |
parent | f051dd0bcdbd35d101420fb1f75f33a565a1e0f8 (diff) |
Merge branch 'trz/rebase' into trz/rename
Diffstat (limited to 'src')
69 files changed, 1193 insertions, 608 deletions
diff --git a/src/main/java/de/thedevstack/conversationsplus/Config.java b/src/main/java/de/thedevstack/conversationsplus/Config.java index 71227be0..2f35ad6e 100644 --- a/src/main/java/de/thedevstack/conversationsplus/Config.java +++ b/src/main/java/de/thedevstack/conversationsplus/Config.java @@ -22,10 +22,6 @@ public final class Config { return (ENCRYPTION_MASK & OPENPGP) != 0; } - public static boolean supportOpenPgpOnly() { - return supportOpenPgp() && !multipleEncryptionChoices(); - } - public static boolean supportOtr() { return (ENCRYPTION_MASK & OTR) != 0; } @@ -45,7 +41,7 @@ public final class Config { public static final String CONFERENCE_DOMAIN_LOCK = null; //only allow conference creation for this domain public static final boolean LOCK_DOMAINS_IN_CONVERSATIONS = false; //only add contacts and conferences for own domains - public static final boolean SINGLE_ACCOUNT = false; //set to true to allow only one account + public static final boolean LOCK_SETTINGS = false; //set to true to disallow account and settings editing public static final boolean DISALLOW_REGISTRATION_IN_UI = false; //hide the register checkbox public static final boolean ALLOW_NON_TLS_CONNECTIONS = false; //very dangerous. you should have a good reason to set this to true diff --git a/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlService.java b/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlService.java index 987cf940..49cd6033 100644 --- a/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlService.java +++ b/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlService.java @@ -9,6 +9,7 @@ import org.whispersystems.libaxolotl.state.PreKeyRecord; import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; import java.security.cert.X509Certificate; +import java.util.List; import java.util.Set; import de.thedevstack.conversationsplus.entities.Account; @@ -30,6 +31,8 @@ public interface AxolotlService extends OnAdvancedStreamFeaturesLoaded { String PEP_BUNDLES = PEP_PREFIX + ".bundles"; String PEP_VERIFICATION = PEP_PREFIX + ".verification"; + int NUM_KEYS_TO_PUBLISH = 100; + enum FetchStatus { PENDING, SUCCESS, @@ -38,15 +41,10 @@ public interface AxolotlService extends OnAdvancedStreamFeaturesLoaded { ERROR } - boolean fetchMapHasErrors(Contact contact); - String getOwnFingerprint(); Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust); - Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust, Contact contact); - - long getNumTrustedKeys(Contact contact); Set<String> getFingerprintsForOwnSessions(); @@ -77,8 +75,6 @@ public interface AxolotlService extends OnAdvancedStreamFeaturesLoaded { void publishBundlesIfNeeded(boolean announce, boolean wipe); - boolean isContactAxolotlCapable(Contact contact); - XmppAxolotlSession.Trust getFingerprintTrust(String fingerprint); X509Certificate getFingerprintCertificate(String fingerprint); @@ -87,24 +83,36 @@ public interface AxolotlService extends OnAdvancedStreamFeaturesLoaded { Set<AxolotlAddress> findDevicesWithoutSession(Conversation conversation); - Set<AxolotlAddress> findDevicesWithoutSession(Jid contactJid); - boolean createSessionsIfNeeded(Conversation conversation); boolean trustedSessionVerified(Conversation conversation); - boolean hasPendingKeyFetches(Account account, Contact contact); - @Nullable XmppAxolotlMessage encrypt(Message message); void preparePayloadMessage(Message message, boolean delay); - void prepareKeyTransportMessage(Contact contact, OnMessageCreatedCallback onMessageCreatedCallback); - XmppAxolotlMessage fetchAxolotlMessageFromCache(Message message); XmppAxolotlMessage.XmppAxolotlPlaintextMessage processReceivingPayloadMessage(XmppAxolotlMessage message); XmppAxolotlMessage.XmppAxolotlKeyTransportMessage processReceivingKeyTransportMessage(XmppAxolotlMessage message); + + boolean fetchMapHasErrors(List<Jid> jids); + + Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust, Jid jid); + + Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust, List<Jid> jids); + + long getNumTrustedKeys(Jid jid); + + boolean anyTargetHasNoTrustedKeys(List<Jid> jids); + + boolean isConversationAxolotlCapable(Conversation conversation); + + List<Jid> getCryptoTargets(Conversation conversation); + + boolean hasPendingKeyFetches(Account account, List<Jid> jids); + + void prepareKeyTransportMessage(Conversation conversation, OnMessageCreatedCallback onMessageCreatedCallback); } diff --git a/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceImpl.java b/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceImpl.java index 5a05b34c..2463a795 100644 --- a/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceImpl.java +++ b/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceImpl.java @@ -50,7 +50,6 @@ import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket; public class AxolotlServiceImpl implements AxolotlService { - public static final int NUM_KEYS_TO_PUBLISH = 100; public static final int publishTriesThreshold = 3; private final Account account; @@ -74,13 +73,14 @@ public class AxolotlServiceImpl implements AxolotlService { } @Override - public boolean fetchMapHasErrors(Contact contact) { - Jid jid = contact.getJid().toBareJid(); - if (deviceIds.get(jid) != null) { - for (Integer foreignId : this.deviceIds.get(jid)) { - AxolotlAddress address = new AxolotlAddress(jid.toString(), foreignId); - if (fetchStatusMap.getAll(address).containsValue(FetchStatus.ERROR)) { - return true; + public boolean fetchMapHasErrors(List<Jid> jids) { + for(Jid jid : jids) { + if (deviceIds.get(jid) != null) { + for (Integer foreignId : this.deviceIds.get(jid)) { + AxolotlAddress address = new AxolotlAddress(jid.toString(), foreignId); + if (fetchStatusMap.getAll(address).containsValue(FetchStatus.ERROR)) { + return true; + } } } } @@ -219,24 +219,41 @@ public class AxolotlServiceImpl implements AxolotlService { this.executor = new SerialSingleThreadExecutor(); } - @Override public String getOwnFingerprint() { return axolotlStore.getIdentityKeyPair().getPublicKey().getFingerprint().replaceAll("\\s", ""); } - @Override public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust) { return axolotlStore.getContactKeysWithTrust(account.getJid().toBareJid().toString(), trust); } @Override - public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust, Contact contact) { - return axolotlStore.getContactKeysWithTrust(contact.getJid().toBareJid().toString(), trust); + public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust, Jid jid) { + return axolotlStore.getContactKeysWithTrust(jid.toBareJid().toString(), trust); + } + + @Override + public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust, List<Jid> jids) { + Set<IdentityKey> keys = new HashSet<>(); + for(Jid jid : jids) { + keys.addAll(axolotlStore.getContactKeysWithTrust(jid.toString(), trust)); + } + return keys; + } + + @Override + public long getNumTrustedKeys(Jid jid) { + return axolotlStore.getContactNumTrustedKeys(jid.toBareJid().toString()); } @Override - public long getNumTrustedKeys(Contact contact) { - return axolotlStore.getContactNumTrustedKeys(contact.getJid().toBareJid().toString()); + public boolean anyTargetHasNoTrustedKeys(List<Jid> jids) { + for(Jid jid : jids) { + if (axolotlStore.getContactNumTrustedKeys(jid.toBareJid().toString()) == 0) { + return true; + } + } + return false; } private AxolotlAddress getAddressForJid(Jid jid) { @@ -248,12 +265,19 @@ public class AxolotlServiceImpl implements AxolotlService { return new HashSet<>(this.sessions.getAll(ownAddress).values()); } - private Set<XmppAxolotlSession> findSessionsforContact(Contact contact) { + private Set<XmppAxolotlSession> findSessionsForContact(Contact contact) { AxolotlAddress contactAddress = getAddressForJid(contact.getJid()); return new HashSet<>(this.sessions.getAll(contactAddress).values()); } - @Override + private Set<XmppAxolotlSession> findSessionsForConversation(Conversation conversation) { + HashSet<XmppAxolotlSession> sessions = new HashSet<>(); + for(Jid jid : conversation.getAcceptedCryptoTargets()) { + sessions.addAll(this.sessions.getAll(getAddressForJid(jid)).values()); + } + return sessions; + } + public Set<String> getFingerprintsForOwnSessions() { Set<String> fingerprints = new HashSet<>(); for (XmppAxolotlSession session : findOwnSessions()) { @@ -262,26 +286,22 @@ public class AxolotlServiceImpl implements AxolotlService { return fingerprints; } - @Override public Set<String> getFingerprintsForContact(final Contact contact) { Set<String> fingerprints = new HashSet<>(); - for (XmppAxolotlSession session : findSessionsforContact(contact)) { + for (XmppAxolotlSession session : findSessionsForContact(contact)) { fingerprints.add(session.getFingerprint()); } return fingerprints; } - private boolean hasAny(Contact contact) { - AxolotlAddress contactAddress = getAddressForJid(contact.getJid()); - return sessions.hasAny(contactAddress); + private boolean hasAny(Jid jid) { + return sessions.hasAny(getAddressForJid(jid)); } - @Override public boolean isPepBroken() { return this.pepBroken; } - @Override public void regenerateKeys(boolean wipeOther) { axolotlStore.regenerate(); sessions.clear(); @@ -289,19 +309,17 @@ public class AxolotlServiceImpl implements AxolotlService { publishBundlesIfNeeded(true, wipeOther); } - @Override public int getOwnDeviceId() { return axolotlStore.getLocalRegistrationId(); } - @Override public Set<Integer> getOwnDeviceIds() { return this.deviceIds.get(account.getJid().toBareJid()); } private void setTrustOnSessions(final Jid jid, @NonNull final Set<Integer> deviceIds, - final XmppAxolotlSession.Trust from, - final XmppAxolotlSession.Trust to) { + final XmppAxolotlSession.Trust from, + final XmppAxolotlSession.Trust to) { for (Integer deviceId : deviceIds) { AxolotlAddress address = new AxolotlAddress(jid.toBareJid().toString(), deviceId); XmppAxolotlSession session = sessions.get(address); @@ -312,7 +330,6 @@ public class AxolotlServiceImpl implements AxolotlService { } } - @Override public void registerDevices(final Jid jid, @NonNull final Set<Integer> deviceIds) { if (jid.toBareJid().equals(account.getJid().toBareJid())) { if (!deviceIds.isEmpty()) { @@ -355,7 +372,6 @@ public class AxolotlServiceImpl implements AxolotlService { mXmppConnectionService.keyStatusUpdated(null); } - @Override public void wipeOtherPepDevices() { if (pepBroken) { Log.d(Config.LOGTAG, getLogprefix(account) + "wipeOtherPepDevices called, but PEP is broken. Ignoring... "); @@ -373,12 +389,10 @@ public class AxolotlServiceImpl implements AxolotlService { }); } - @Override public void purgeKey(final String fingerprint) { axolotlStore.setFingerprintTrust(fingerprint.replaceAll("\\s", ""), XmppAxolotlSession.Trust.COMPROMISED); } - @Override public void publishOwnDeviceIdIfNeeded() { if (pepBroken) { Log.d(Config.LOGTAG, getLogprefix(account) + "publishOwnDeviceIdIfNeeded called, but PEP is broken. Ignoring... "); @@ -401,7 +415,6 @@ public class AxolotlServiceImpl implements AxolotlService { }); } - @Override public void publishOwnDeviceId(Set<Integer> deviceIds) { Set<Integer> deviceIdsCopy = new HashSet<>(deviceIds); if (!deviceIdsCopy.contains(getOwnDeviceId())) { @@ -431,7 +444,6 @@ public class AxolotlServiceImpl implements AxolotlService { } } - @Override public void publishDeviceVerificationAndBundle(final SignedPreKeyRecord signedPreKeyRecord, final Set<PreKeyRecord> preKeyRecords, final boolean announceAfter, @@ -457,7 +469,6 @@ public class AxolotlServiceImpl implements AxolotlService { } } - @Override public void publishBundlesIfNeeded(final boolean announce, final boolean wipe) { if (pepBroken) { Log.d(Config.LOGTAG, getLogprefix(account) + "publishBundlesIfNeeded called, but PEP is broken. Ignoring... "); @@ -597,23 +608,36 @@ public class AxolotlServiceImpl implements AxolotlService { } @Override - public boolean isContactAxolotlCapable(Contact contact) { - Jid jid = contact.getJid().toBareJid(); - return hasAny(contact) || - (deviceIds.containsKey(jid) && !deviceIds.get(jid).isEmpty()); + public boolean isConversationAxolotlCapable(Conversation conversation) { + final List<Jid> jids = getCryptoTargets(conversation); + for(Jid jid : jids) { + if (!hasAny(jid) && (!deviceIds.containsKey(jid) || deviceIds.get(jid).isEmpty())) { + return false; + } + } + return jids.size() > 0; } @Override + public List<Jid> getCryptoTargets(Conversation conversation) { + final List<Jid> jids; + if (conversation.getMode() == Conversation.MODE_SINGLE) { + jids = Arrays.asList(conversation.getJid().toBareJid()); + } else { + jids = conversation.getMucOptions().getMembers(); + jids.remove(account.getJid().toBareJid()); + } + return jids; + } + public XmppAxolotlSession.Trust getFingerprintTrust(String fingerprint) { return axolotlStore.getFingerprintTrust(fingerprint); } - @Override public X509Certificate getFingerprintCertificate(String fingerprint) { return axolotlStore.getFingerprintCertificate(fingerprint); } - @Override public void setFingerprintTrust(String fingerprint, XmppAxolotlSession.Trust trust) { axolotlStore.setFingerprintTrust(fingerprint, trust); } @@ -758,36 +782,32 @@ public class AxolotlServiceImpl implements AxolotlService { } } - @Override public Set<AxolotlAddress> findDevicesWithoutSession(final Conversation conversation) { - return findDevicesWithoutSession(conversation.getContact().getJid().toBareJid()); - } - - @Override - public Set<AxolotlAddress> findDevicesWithoutSession(final Jid contactJid) { - Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Finding devices without session for " + contactJid); Set<AxolotlAddress> addresses = new HashSet<>(); - if (deviceIds.get(contactJid) != null) { - for (Integer foreignId : this.deviceIds.get(contactJid)) { - AxolotlAddress address = new AxolotlAddress(contactJid.toString(), foreignId); - if (sessions.get(address) == null) { - IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey(); - if (identityKey != null) { - Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Already have session for " + address.toString() + ", adding to cache..."); - XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, identityKey); - sessions.put(address, session); - } else { - Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Found device " + contactJid + ":" + foreignId); - if (fetchStatusMap.get(address) != FetchStatus.ERROR) { - addresses.add(address); + for(Jid jid : getCryptoTargets(conversation)) { + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Finding devices without session for " + jid); + if (deviceIds.get(jid) != null) { + for (Integer foreignId : this.deviceIds.get(jid)) { + AxolotlAddress address = new AxolotlAddress(jid.toString(), foreignId); + if (sessions.get(address) == null) { + IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey(); + if (identityKey != null) { + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Already have session for " + address.toString() + ", adding to cache..."); + XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, identityKey); + sessions.put(address, session); } else { - Log.d(Config.LOGTAG,getLogprefix(account)+"skipping over "+address+" because it's broken"); + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Found device " + jid + ":" + foreignId); + if (fetchStatusMap.get(address) != FetchStatus.ERROR) { + addresses.add(address); + } else { + Log.d(Config.LOGTAG, getLogprefix(account) + "skipping over " + address + " because it's broken"); + } } } } + } else { + Log.w(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Have no target devices in PEP!"); } - } else { - Log.w(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Have no target devices in PEP!"); } if (deviceIds.get(account.getJid().toBareJid()) != null) { for (Integer ownId : this.deviceIds.get(account.getJid().toBareJid())) { @@ -813,7 +833,6 @@ public class AxolotlServiceImpl implements AxolotlService { return addresses; } - @Override public boolean createSessionsIfNeeded(final Conversation conversation) { Log.i(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Creating axolotl sessions if needed..."); boolean newSessions = false; @@ -835,9 +854,8 @@ public class AxolotlServiceImpl implements AxolotlService { return newSessions; } - @Override public boolean trustedSessionVerified(final Conversation conversation) { - Set<XmppAxolotlSession> sessions = findSessionsforContact(conversation.getContact()); + Set<XmppAxolotlSession> sessions = findSessionsForConversation(conversation); sessions.addAll(findOwnSessions()); boolean verified = false; for(XmppAxolotlSession session : sessions) { @@ -853,26 +871,32 @@ public class AxolotlServiceImpl implements AxolotlService { } @Override - public boolean hasPendingKeyFetches(Account account, Contact contact) { + public boolean hasPendingKeyFetches(Account account, List<Jid> jids) { AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toString(), 0); - AxolotlAddress foreignAddress = new AxolotlAddress(contact.getJid().toBareJid().toString(), 0); - return fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING) - || fetchStatusMap.getAll(foreignAddress).containsValue(FetchStatus.PENDING); - + if (fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING)) { + return true; + } + for(Jid jid : jids) { + AxolotlAddress foreignAddress = new AxolotlAddress(jid.toBareJid().toString(), 0); + if (fetchStatusMap.getAll(foreignAddress).containsValue(FetchStatus.PENDING)) { + return true; + } + } + return false; } @Nullable - private XmppAxolotlMessage buildHeader(Contact contact) { + private XmppAxolotlMessage buildHeader(Conversation conversation) { final XmppAxolotlMessage axolotlMessage = new XmppAxolotlMessage( - contact.getJid().toBareJid(), getOwnDeviceId()); + account.getJid().toBareJid(), getOwnDeviceId()); - Set<XmppAxolotlSession> contactSessions = findSessionsforContact(contact); + Set<XmppAxolotlSession> remoteSessions = findSessionsForConversation(conversation); Set<XmppAxolotlSession> ownSessions = findOwnSessions(); - if (contactSessions.isEmpty()) { + if (remoteSessions.isEmpty()) { return null; } Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Building axolotl foreign keyElements..."); - for (XmppAxolotlSession session : contactSessions) { + for (XmppAxolotlSession session : remoteSessions) { Log.v(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + session.getRemoteAddress().toString()); axolotlMessage.addDevice(session); } @@ -885,10 +909,9 @@ public class AxolotlServiceImpl implements AxolotlService { return axolotlMessage; } - @Override @Nullable public XmppAxolotlMessage encrypt(Message message) { - XmppAxolotlMessage axolotlMessage = buildHeader(message.getContact()); + XmppAxolotlMessage axolotlMessage = buildHeader(message.getConversation()); if (axolotlMessage != null) { final String content; @@ -908,7 +931,6 @@ public class AxolotlServiceImpl implements AxolotlService { return axolotlMessage; } - @Override public void preparePayloadMessage(final Message message, final boolean delay) { executor.execute(new Runnable() { @Override @@ -927,17 +949,16 @@ public class AxolotlServiceImpl implements AxolotlService { } @Override - public void prepareKeyTransportMessage(final Contact contact, final OnMessageCreatedCallback onMessageCreatedCallback) { + public void prepareKeyTransportMessage(final Conversation conversation, final OnMessageCreatedCallback onMessageCreatedCallback) { executor.execute(new Runnable() { @Override public void run() { - XmppAxolotlMessage axolotlMessage = buildHeader(contact); + XmppAxolotlMessage axolotlMessage = buildHeader(conversation); onMessageCreatedCallback.run(axolotlMessage); } }); } - @Override public XmppAxolotlMessage fetchAxolotlMessageFromCache(Message message) { XmppAxolotlMessage axolotlMessage = messageCache.get(message.getUuid()); if (axolotlMessage != null) { @@ -970,7 +991,6 @@ public class AxolotlServiceImpl implements AxolotlService { return session; } - @Override public XmppAxolotlMessage.XmppAxolotlPlaintextMessage processReceivingPayloadMessage(XmppAxolotlMessage message) { XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage = null; @@ -993,7 +1013,6 @@ public class AxolotlServiceImpl implements AxolotlService { return plaintextMessage; } - @Override public XmppAxolotlMessage.XmppAxolotlKeyTransportMessage processReceivingKeyTransportMessage(XmppAxolotlMessage message) { XmppAxolotlMessage.XmppAxolotlKeyTransportMessage keyTransportMessage; @@ -1018,4 +1037,4 @@ public class AxolotlServiceImpl implements AxolotlService { } } } -} +}
\ No newline at end of file diff --git a/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceStub.java b/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceStub.java index a07905ab..858cdf08 100644 --- a/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceStub.java +++ b/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceStub.java @@ -10,6 +10,7 @@ import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; import java.security.cert.X509Certificate; import java.util.Collections; +import java.util.List; import java.util.Set; import de.thedevstack.conversationsplus.entities.Account; @@ -24,11 +25,6 @@ import de.thedevstack.conversationsplus.xmpp.jid.Jid; public class AxolotlServiceStub implements AxolotlService { @Override - public boolean fetchMapHasErrors(Contact contact) { - return false; - } - - @Override public String getOwnFingerprint() { return null; } @@ -39,16 +35,6 @@ public class AxolotlServiceStub implements AxolotlService { } @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(); } @@ -114,11 +100,6 @@ public class AxolotlServiceStub implements AxolotlService { } @Override - public boolean isContactAxolotlCapable(Contact contact) { - return false; - } - - @Override public XmppAxolotlSession.Trust getFingerprintTrust(String fingerprint) { return XmppAxolotlSession.Trust.TRUSTED; } @@ -139,11 +120,6 @@ public class AxolotlServiceStub implements AxolotlService { } @Override - public Set<AxolotlAddress> findDevicesWithoutSession(Jid contactJid) { - return Collections.emptySet(); - } - - @Override public boolean createSessionsIfNeeded(Conversation conversation) { return false; } @@ -153,11 +129,6 @@ public class AxolotlServiceStub implements AxolotlService { return false; } - @Override - public boolean hasPendingKeyFetches(Account account, Contact contact) { - return false; - } - @Nullable @Override public XmppAxolotlMessage encrypt(Message message) { @@ -170,11 +141,6 @@ public class AxolotlServiceStub implements AxolotlService { } @Override - public void prepareKeyTransportMessage(Contact contact, OnMessageCreatedCallback onMessageCreatedCallback) { - - } - - @Override public XmppAxolotlMessage fetchAxolotlMessageFromCache(Message message) { return null; } @@ -190,6 +156,51 @@ public class AxolotlServiceStub implements AxolotlService { } @Override + public boolean fetchMapHasErrors(List<Jid> jids) { + return false; + } + + @Override + public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust, Jid jid) { + return Collections.emptySet(); + } + + @Override + public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust, List<Jid> jids) { + return Collections.emptySet(); + } + + @Override + public long getNumTrustedKeys(Jid jid) { + return 0; + } + + @Override + public boolean anyTargetHasNoTrustedKeys(List<Jid> jids) { + return false; + } + + @Override + public boolean isConversationAxolotlCapable(Conversation conversation) { + return false; + } + + @Override + public List<Jid> getCryptoTargets(Conversation conversation) { + return Collections.emptyList(); + } + + @Override + public boolean hasPendingKeyFetches(Account account, List<Jid> jids) { + return false; + } + + @Override + public void prepareKeyTransportMessage(Conversation conversation, OnMessageCreatedCallback onMessageCreatedCallback) { + + } + + @Override public void onAdvancedStreamFeaturesAvailable(Account account) { } diff --git a/src/main/java/de/thedevstack/conversationsplus/entities/Conversation.java b/src/main/java/de/thedevstack/conversationsplus/entities/Conversation.java index 0b70d938..b822ef0e 100644 --- a/src/main/java/de/thedevstack/conversationsplus/entities/Conversation.java +++ b/src/main/java/de/thedevstack/conversationsplus/entities/Conversation.java @@ -9,11 +9,13 @@ import net.java.otr4j.session.SessionID; import net.java.otr4j.session.SessionImpl; import net.java.otr4j.session.SessionStatus; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.security.interfaces.DSAPublicKey; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; @@ -49,6 +51,7 @@ public class Conversation extends AbstractEntity implements Blockable { public static final String ATTRIBUTE_MUC_PASSWORD = "muc_password"; public static final String ATTRIBUTE_MUTED_TILL = "muted_till"; public static final String ATTRIBUTE_ALWAYS_NOTIFY = "always_notify"; + public static final String ATTRIBUTE_CRYPTO_TARGETS = "crypto_targets"; private String name; private String contactUuid; @@ -306,6 +309,18 @@ public class Conversation extends AbstractEntity implements Blockable { return this.mFirstMamReference; } + public List<Jid> getAcceptedCryptoTargets() { + if (mode == MODE_SINGLE) { + return Arrays.asList(getJid().toBareJid()); + } else { + return getJidListAttribute(ATTRIBUTE_CRYPTO_TARGETS); + } + } + + public void setAcceptedCryptoTargets(List<Jid> acceptedTargets) { + setAttribute(ATTRIBUTE_CRYPTO_TARGETS, acceptedTargets); + } + public void setCorrectingMessage(Message correctingMessage) { this.correctingMessage = correctingMessage; } @@ -652,8 +667,8 @@ public class Conversation extends AbstractEntity implements Blockable { final AxolotlService axolotlService = getAccount().getAxolotlService(); int next = this.getIntAttribute(ATTRIBUTE_NEXT_ENCRYPTION, -1); if (next == -1) { - if (Config.X509_VERIFICATION && mode == MODE_SINGLE) { - if (axolotlService != null && axolotlService.isContactAxolotlCapable(getContact())) { + if (Config.X509_VERIFICATION) { + if (axolotlService != null && axolotlService.isConversationAxolotlCapable(this)) { return Message.ENCRYPTION_AXOLOTL; } else { return Message.ENCRYPTION_NONE; @@ -666,16 +681,20 @@ public class Conversation extends AbstractEntity implements Blockable { next = outgoing; } } - if (!Config.supportUnencrypted() - && (mode == MODE_SINGLE || Config.supportOpenPgpOnly()) - && next <= 0) { - if (Config.supportOmemo() && (axolotlService != null && axolotlService.isContactAxolotlCapable(getContact()) || !Config.multipleEncryptionChoices())) { + + if (!Config.supportUnencrypted() && next <= 0) { + if (Config.supportOmemo() + && (axolotlService != null && axolotlService.isConversationAxolotlCapable(this) || !Config.multipleEncryptionChoices())) { return Message.ENCRYPTION_AXOLOTL; - } else if (Config.supportOtr()) { + } else if (Config.supportOtr() && mode == MODE_SINGLE) { return Message.ENCRYPTION_OTR; - } else if (Config.supportOpenPgp()) { + } else if (Config.supportOpenPgp() + && (mode == MODE_SINGLE) || !Config.multipleEncryptionChoices()) { return Message.ENCRYPTION_PGP; } + } else if (next == Message.ENCRYPTION_AXOLOTL + && (!Config.supportOmemo() || axolotlService == null || !axolotlService.isConversationAxolotlCapable(this))) { + next = Message.ENCRYPTION_NONE; } return next; } @@ -779,20 +798,59 @@ public class Conversation extends AbstractEntity implements Blockable { } public boolean setAttribute(String key, String value) { - try { - this.attributes.put(key, value); - return true; - } catch (JSONException e) { - return false; + synchronized (this.attributes) { + try { + this.attributes.put(key, value); + return true; + } catch (JSONException e) { + return false; + } + } + } + + public boolean setAttribute(String key, List<Jid> jids) { + JSONArray array = new JSONArray(); + for(Jid jid : jids) { + array.put(jid.toBareJid().toString()); + } + synchronized (this.attributes) { + try { + this.attributes.put(key, array); + return true; + } catch (JSONException e) { + e.printStackTrace(); + return false; + } } } public String getAttribute(String key) { - try { - return this.attributes.getString(key); - } catch (JSONException e) { - return null; + synchronized (this.attributes) { + try { + return this.attributes.getString(key); + } catch (JSONException e) { + return null; + } + } + } + + public List<Jid> getJidListAttribute(String key) { + ArrayList<Jid> list = new ArrayList<>(); + synchronized (this.attributes) { + try { + JSONArray array = this.attributes.getJSONArray(key); + for (int i = 0; i < array.length(); ++i) { + try { + list.add(Jid.fromString(array.getString(i))); + } catch (InvalidJidException e) { + //ignored + } + } + } catch (JSONException e) { + //ignored + } } + return list; } public int getIntAttribute(String key, int defaultValue) { diff --git a/src/main/java/de/thedevstack/conversationsplus/entities/Message.java b/src/main/java/de/thedevstack/conversationsplus/entities/Message.java index d03cab92..f1da885e 100644 --- a/src/main/java/de/thedevstack/conversationsplus/entities/Message.java +++ b/src/main/java/de/thedevstack/conversationsplus/entities/Message.java @@ -51,6 +51,7 @@ public class Message extends AbstractEntity { public static final String STATUS = "status"; public static final String TYPE = "type"; public static final String CARBON = "carbon"; + public static final String OOB = "oob"; public static final String EDITED = "edited"; public static final String REMOTE_MSG_ID = "remoteMsgId"; public static final String SERVER_MSG_ID = "serverMsgId"; @@ -71,6 +72,7 @@ public class Message extends AbstractEntity { protected int status; protected int type; protected boolean carbon = false; + protected boolean oob = false; protected String edited = null; protected String relativeFilePath; protected boolean read = true; @@ -106,7 +108,8 @@ public class Message extends AbstractEntity { null, null, true, - null); + null, + false); this.conversation = conversation; } @@ -115,7 +118,7 @@ public class Message extends AbstractEntity { final int encryption, final int status, final int type, final boolean carbon, final String remoteMsgId, final String relativeFilePath, final String serverMsgId, final String fingerprint, final boolean read, - final String edited) { + final String edited, final boolean oob) { this.uuid = uuid; this.conversationUuid = conversationUUid; this.counterpart = counterpart; @@ -132,6 +135,7 @@ public class Message extends AbstractEntity { this.axolotlFingerprint = fingerprint; this.read = read; this.edited = edited; + this.oob = oob; } public static Message fromCursor(Cursor cursor) { @@ -172,7 +176,8 @@ public class Message extends AbstractEntity { cursor.getString(cursor.getColumnIndex(SERVER_MSG_ID)), cursor.getString(cursor.getColumnIndex(FINGERPRINT)), cursor.getInt(cursor.getColumnIndex(READ)) > 0, - cursor.getString(cursor.getColumnIndex(EDITED))); + cursor.getString(cursor.getColumnIndex(EDITED)), + cursor.getInt(cursor.getColumnIndex(OOB)) > 0); } public static Message createStatusMessage(Conversation conversation, String body) { @@ -210,6 +215,7 @@ public class Message extends AbstractEntity { values.put(FINGERPRINT, axolotlFingerprint); values.put(READ,read ? 1 : 0); values.put(EDITED, edited); + values.put(OOB, oob ? 1 : 0); return values; } @@ -543,6 +549,10 @@ public class Message extends AbstractEntity { return edited; } + public void setOob(boolean isOob) { + this.oob = isOob; + } + public enum Decision { MUST, SHOULD, @@ -565,7 +575,7 @@ public class Message extends AbstractEntity { if (dotPosition != -1) { String extension = filename.substring(dotPosition + 1); // we want the real file extension, not the crypto one - if (Arrays.asList(Transferable.VALID_CRYPTO_EXTENSIONS).contains(extension)) { + if (Transferable.VALID_CRYPTO_EXTENSIONS.contains(extension)) { return extractRelevantExtension(filename.substring(0,dotPosition)); } else { return extension; @@ -604,6 +614,8 @@ public class Message extends AbstractEntity { URL url = new URL(body); if (!url.getProtocol().equalsIgnoreCase("http") && !url.getProtocol().equalsIgnoreCase("https")) { return Decision.NEVER; + } else if (oob) { + return Decision.MUST; } String extension = extractRelevantExtension(url); if (extension == null) { @@ -618,8 +630,8 @@ public class Message extends AbstractEntity { } else { return Decision.NEVER; } - } else if (Arrays.asList(Transferable.VALID_IMAGE_EXTENSIONS).contains(extension) - || Arrays.asList(Transferable.WELL_KNOWN_EXTENSIONS).contains(extension)) { + } else if (Transferable.VALID_IMAGE_EXTENSIONS.contains(extension) + || Transferable.WELL_KNOWN_EXTENSIONS.contains(extension)) { return Decision.SHOULD; } else { return Decision.NEVER; diff --git a/src/main/java/de/thedevstack/conversationsplus/entities/MucOptions.java b/src/main/java/de/thedevstack/conversationsplus/entities/MucOptions.java index f0eb83de..128e5423 100644 --- a/src/main/java/de/thedevstack/conversationsplus/entities/MucOptions.java +++ b/src/main/java/de/thedevstack/conversationsplus/entities/MucOptions.java @@ -4,9 +4,11 @@ import android.annotation.SuppressLint; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import de.thedevstack.conversationsplus.R; import de.thedevstack.conversationsplus.xmpp.forms.Data; @@ -245,7 +247,8 @@ public class MucOptions { private Account account; private final Map<String, User> users = Collections.synchronizedMap(new LinkedHashMap<String, User>()); - private List<String> features = new ArrayList<>(); + private final Set<Jid> members = Collections.synchronizedSet(new HashSet<Jid>()); + private final List<String> features = new ArrayList<>(); private Data form = new Data(); private Conversation conversation; private boolean isOnline = false; @@ -501,4 +504,12 @@ public class MucOptions { public Conversation getConversation() { return this.conversation; } + + public void putMember(Jid jid) { + members.add(jid); + } + + public List<Jid> getMembers() { + return new ArrayList<>(members); + } } diff --git a/src/main/java/de/thedevstack/conversationsplus/entities/Roster.java b/src/main/java/de/thedevstack/conversationsplus/entities/Roster.java index b1cb78b4..ec1a9426 100644 --- a/src/main/java/de/thedevstack/conversationsplus/entities/Roster.java +++ b/src/main/java/de/thedevstack/conversationsplus/entities/Roster.java @@ -9,7 +9,7 @@ import de.thedevstack.conversationsplus.xmpp.jid.Jid; public class Roster { final Account account; - final HashMap<String, Contact> contacts = new HashMap<>(); + final HashMap<Jid, Contact> contacts = new HashMap<>(); private String version = null; public Roster(Account account) { @@ -21,7 +21,7 @@ public class Roster { return null; } synchronized (this.contacts) { - Contact contact = contacts.get(jid.toBareJid().toString()); + Contact contact = contacts.get(jid.toBareJid()); if (contact != null && contact.showInRoster()) { return contact; } else { @@ -32,15 +32,13 @@ public class Roster { public Contact getContact(final Jid jid) { synchronized (this.contacts) { - final Jid bareJid = jid.toBareJid(); - if (contacts.containsKey(bareJid.toString())) { - return contacts.get(bareJid.toString()); - } else { - Contact contact = new Contact(bareJid); + if (!contacts.containsKey(jid.toBareJid())) { + Contact contact = new Contact(jid.toBareJid()); contact.setAccount(account); - contacts.put(bareJid.toString(), contact); + contacts.put(contact.getJid().toBareJid(), contact); return contact; } + return contacts.get(jid.toBareJid()); } } @@ -80,7 +78,7 @@ public class Roster { contact.setAccount(account); contact.setOption(Contact.Options.IN_ROSTER); synchronized (this.contacts) { - contacts.put(contact.getJid().toBareJid().toString(), contact); + contacts.put(contact.getJid().toBareJid(), contact); } } diff --git a/src/main/java/de/thedevstack/conversationsplus/entities/Transferable.java b/src/main/java/de/thedevstack/conversationsplus/entities/Transferable.java index b03d0fe0..8e2ca20e 100644 --- a/src/main/java/de/thedevstack/conversationsplus/entities/Transferable.java +++ b/src/main/java/de/thedevstack/conversationsplus/entities/Transferable.java @@ -1,10 +1,13 @@ package de.thedevstack.conversationsplus.entities; +import java.util.Arrays; +import java.util.List; + public interface Transferable { - String[] VALID_IMAGE_EXTENSIONS = {"webp", "jpeg", "jpg", "png", "jpe"}; - String[] VALID_CRYPTO_EXTENSIONS = {"pgp", "gpg", "otr"}; - String[] WELL_KNOWN_EXTENSIONS = {"pdf","m4a","mp4"}; + List<String> VALID_IMAGE_EXTENSIONS = Arrays.asList("webp", "jpeg", "jpg", "png", "jpe"); + List<String> VALID_CRYPTO_EXTENSIONS = Arrays.asList("pgp", "gpg", "otr"); + List<String> WELL_KNOWN_EXTENSIONS = Arrays.asList("pdf","m4a","mp4","3gp","aac","amr","mp3"); int STATUS_UNKNOWN = 0x200; int STATUS_CHECKING = 0x201; diff --git a/src/main/java/de/thedevstack/conversationsplus/generator/IqGenerator.java b/src/main/java/de/thedevstack/conversationsplus/generator/IqGenerator.java index df46f2b7..9df0cc21 100644 --- a/src/main/java/de/thedevstack/conversationsplus/generator/IqGenerator.java +++ b/src/main/java/de/thedevstack/conversationsplus/generator/IqGenerator.java @@ -292,4 +292,11 @@ public class IqGenerator extends AbstractGenerator { enable.addChild(data); return packet; } + + public IqPacket queryAffiliation(Conversation conversation, String affiliation) { + IqPacket packet = new IqPacket(IqPacket.TYPE.GET); + packet.setTo(conversation.getJid().toBareJid()); + packet.query("http://jabber.org/protocol/muc#admin").addChild("item").setAttribute("affiliation",affiliation); + return packet; + } } diff --git a/src/main/java/de/thedevstack/conversationsplus/http/HttpDownloadConnection.java b/src/main/java/de/thedevstack/conversationsplus/http/HttpDownloadConnection.java index 0462defd..e7fbb5cd 100644 --- a/src/main/java/de/thedevstack/conversationsplus/http/HttpDownloadConnection.java +++ b/src/main/java/de/thedevstack/conversationsplus/http/HttpDownloadConnection.java @@ -3,13 +3,17 @@ package de.thedevstack.conversationsplus.http; import android.content.Intent; import android.net.Uri; import android.os.PowerManager; +import android.util.Log; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.MalformedURLException; +import java.net.Proxy; import java.net.URL; import java.util.Arrays; import java.util.concurrent.CancellationException; @@ -70,75 +74,76 @@ public class HttpDownloadConnection implements Transferable { } public void init(Message message, boolean interactive) { - this.message = message; - this.message.setTransferable(this); - try { - if (message.hasFileOnRemoteHost()) { - mUrl = message.getFileParams().url; - } else { - mUrl = new URL(message.getBody()); - } - String[] parts = mUrl.getPath().toLowerCase().split("\\."); - String lastPart = parts.length >= 1 ? parts[parts.length - 1] : null; - String secondToLast = parts.length >= 2 ? parts[parts.length -2] : null; - if ("pgp".equals(lastPart) || "gpg".equals(lastPart)) { - this.message.setEncryption(Message.ENCRYPTION_PGP); - } else if (message.getEncryption() != Message.ENCRYPTION_OTR - && message.getEncryption() != Message.ENCRYPTION_AXOLOTL) { - this.message.setEncryption(Message.ENCRYPTION_NONE); - } - String extension; - if (Arrays.asList(VALID_CRYPTO_EXTENSIONS).contains(lastPart)) { - extension = secondToLast; - } else { - extension = lastPart; - } - message.setRelativeFilePath(message.getUuid()+"."+extension); - this.file = FileBackend.getFile(message, false); - String reference = mUrl.getRef(); - if (reference != null && reference.length() == 96) { - this.file.setKeyAndIv(CryptoHelper.hexToBytes(reference)); - } + this.message = message; + this.message.setTransferable(this); + try { + if (message.hasFileOnRemoteHost()) { + mUrl = message.getFileParams().url; + } else { + mUrl = new URL(message.getBody()); + } + String[] parts = mUrl.getPath().toLowerCase().split("\\."); + String lastPart = parts.length >= 1 ? parts[parts.length - 1] : null; + String secondToLast = parts.length >= 2 ? parts[parts.length -2] : null; + if ("pgp".equals(lastPart) || "gpg".equals(lastPart)) { + this.message.setEncryption(Message.ENCRYPTION_PGP); + } else if (message.getEncryption() != Message.ENCRYPTION_OTR + && message.getEncryption() != Message.ENCRYPTION_AXOLOTL) { + this.message.setEncryption(Message.ENCRYPTION_NONE); + } + String extension; + if (VALID_CRYPTO_EXTENSIONS.contains(lastPart)) { + extension = secondToLast; + } else { + extension = lastPart; + } + message.setRelativeFilePath(message.getUuid()+"."+extension); + this.file = FileBackend.getFile(message, false); + String reference = mUrl.getRef(); + if (reference != null && reference.length() == 96) { + this.file.setKeyAndIv(CryptoHelper.hexToBytes(reference)); + } - if ((this.message.getEncryption() == Message.ENCRYPTION_OTR - || this.message.getEncryption() == Message.ENCRYPTION_AXOLOTL) - && this.file.getKey() == null) { - this.message.setEncryption(Message.ENCRYPTION_NONE); - } - checkFileSize(interactive); - } catch (MalformedURLException e) { - this.cancel(); - } + if ((this.message.getEncryption() == Message.ENCRYPTION_OTR + || this.message.getEncryption() == Message.ENCRYPTION_AXOLOTL) + && this.file.getKey() == null) { + this.message.setEncryption(Message.ENCRYPTION_NONE); + } + checkFileSize(interactive); + } catch (MalformedURLException e) { + this.cancel(); + } } private void checkFileSize(boolean interactive) { new Thread(new FileSizeChecker(interactive)).start(); } + @Override public void cancel() { - this.canceled = true; - mHttpConnectionManager.finishConnection(this); - if (message.isFileOrImage()) { - message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED)); - } else { - message.setTransferable(null); - } - mXmppConnectionService.updateConversationUi(); + this.canceled = true; + mHttpConnectionManager.finishConnection(this); + if (message.isFileOrImage()) { + message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED)); + } else { + message.setTransferable(null); + } + mXmppConnectionService.updateConversationUi(); } private void finish() { - Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); - intent.setData(Uri.fromFile(file)); - mXmppConnectionService.sendBroadcast(intent); - message.setTransferable(null); - mHttpConnectionManager.finishConnection(this); - if (message.getEncryption() == Message.ENCRYPTION_PGP) { - message.getConversation().getAccount().getPgpDecryptionService().add(message); - } - mXmppConnectionService.updateConversationUi(); - if (acceptedAutomatically) { - mXmppConnectionService.getNotificationService().push(message); - } + Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); + intent.setData(Uri.fromFile(file)); + mXmppConnectionService.sendBroadcast(intent); + message.setTransferable(null); + mHttpConnectionManager.finishConnection(this); + if (message.getEncryption() == Message.ENCRYPTION_PGP) { + message.getConversation().getAccount().getPgpDecryptionService().add(message); + } + mXmppConnectionService.updateConversationUi(); + if (acceptedAutomatically) { + mXmppConnectionService.getNotificationService().push(message); + } } private void changeStatus(int status) { @@ -152,7 +157,7 @@ public class HttpDownloadConnection implements Transferable { mXmppConnectionService.showErrorToastInUi(R.string.download_failed_server_not_found); } else if (e instanceof java.net.ConnectException) { mXmppConnectionService.showErrorToastInUi(R.string.download_failed_could_not_connect); - } else if (!(e instanceof CancellationException)) { + } else if (!(e instanceof CancellationException)) { mXmppConnectionService.showErrorToastInUi(R.string.download_failed_file_not_found); } } @@ -176,7 +181,7 @@ public class HttpDownloadConnection implements Transferable { HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message); return; } catch (IOException e) { - Logging.d(Config.LOGTAG, "io exception in http file size checker: " + e.getMessage()); + Log.d(Config.LOGTAG, "io exception in http file size checker: " + e.getMessage()); if (interactive) { showToastForException(e); } @@ -184,7 +189,8 @@ public class HttpDownloadConnection implements Transferable { return; } file.setExpectedSize(size); - if (size != -1 + if (mHttpConnectionManager.hasStoragePermission() + && size != -1 && size <= ConversationsPlusPreferences.autoAcceptFileSize() && mXmppConnectionService.isDownloadAllowedInConnection()) { HttpDownloadConnection.this.acceptedAutomatically = true; @@ -221,13 +227,14 @@ public class HttpDownloadConnection implements Transferable { return -1; } } + } private class FileDownloader implements Runnable { private boolean interactive = false; - private OutputStream os; + private OutputStream os; public FileDownloader(boolean interactive) { this.interactive = interactive; @@ -243,10 +250,10 @@ public class HttpDownloadConnection implements Transferable { } catch (SSLHandshakeException e) { changeStatus(STATUS_OFFER); } catch (Exception e) { - if (interactive) { - showToastForException(e); - } - cancel(); + if (interactive) { + showToastForException(e); + } + cancel(); } } @@ -312,11 +319,12 @@ public class HttpDownloadConnection implements Transferable { MessageUtil.updateFileParams(message, mUrl); mXmppConnectionService.updateMessage(message); } + } public void updateProgress(int i) { - this.mProgress = i; - mXmppConnectionService.updateConversationUi(); + this.mProgress = i; + mXmppConnectionService.updateConversationUi(); } @Override diff --git a/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java b/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java index ee6ef8b6..e64fe6a4 100644 --- a/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java +++ b/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java @@ -8,6 +8,7 @@ import de.tzur.conversations.Settings; import net.java.otr4j.session.Session; import net.java.otr4j.session.SessionStatus; +import java.net.URL; import java.util.ArrayList; import java.util.Set; @@ -109,7 +110,7 @@ public class MessageParser extends AbstractParser implements } } - private Message parseAxolotlChat(Element axolotlMessage, Jid from, String id, Conversation conversation, int status) { + private Message parseAxolotlChat(Element axolotlMessage, Jid from, Conversation conversation, int status) { Message finishedMessage = null; AxolotlService service = conversation.getAccount().getAxolotlService(); XmppAxolotlMessage xmppAxolotlMessage = XmppAxolotlMessage.fromElement(axolotlMessage, from.toBareJid()); @@ -117,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, AxolotlServiceImpl.getLogprefix(finishedMessage.getConversation().getAccount())+" Received Message with session fingerprint: "+plaintextMessage.getFingerprint()); + Logging.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(finishedMessage.getConversation().getAccount())+" Received Message with session fingerprint: "+plaintextMessage.getFingerprint()); } return finishedMessage; @@ -296,6 +297,8 @@ 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 oob = packet.findChild("x", "jabber:x:oob"); + final boolean isOob = oob!= null && body != null && body.equals(oob.findChildContent("url")); final Element axolotlEncrypted = packet.findChild(XmppAxolotlMessage.CONTAINERTAG, AxolotlService.PEP_PREFIX); int status; final Jid counterpart; @@ -361,7 +364,17 @@ public class MessageParser extends AbstractParser implements } else if (pgpEncrypted != null && Config.supportOpenPgp()) { message = new Message(conversation, pgpEncrypted, Message.ENCRYPTION_PGP, status); } else if (axolotlEncrypted != null && Config.supportOmemo()) { - message = parseAxolotlChat(axolotlEncrypted, from, remoteMsgId, conversation, status); + Jid origin; + if (conversation.getMode() == Conversation.MODE_MULTI) { + origin = conversation.getMucOptions().getTrueCounterpart(counterpart.getResourcepart()); + if (origin == null) { + Log.d(Config.LOGTAG,"axolotl message in non anonymous conference received"); + return; + } + } else { + origin = from; + } + message = parseAxolotlChat(axolotlEncrypted, origin, conversation, status); if (message == null) { return; } @@ -378,6 +391,7 @@ public class MessageParser extends AbstractParser implements message.setServerMsgId(serverMsgId); message.setCarbon(isCarbon); message.setTime(timestamp); + message.setOob(isOob); message.markable = packet.hasChild("markable", "urn:xmpp:chat-markers:0"); if (conversation.getMode() == Conversation.MODE_MULTI) { Jid trueCounterpart = conversation.getMucOptions().getTrueCounterpart(counterpart.getResourcepart()); diff --git a/src/main/java/de/thedevstack/conversationsplus/parser/PresenceParser.java b/src/main/java/de/thedevstack/conversationsplus/parser/PresenceParser.java index 79ffa9bf..f575aaac 100644 --- a/src/main/java/de/thedevstack/conversationsplus/parser/PresenceParser.java +++ b/src/main/java/de/thedevstack/conversationsplus/parser/PresenceParser.java @@ -66,10 +66,14 @@ public class PresenceParser extends AbstractParser implements Element item = x.findChild("item"); if (item != null && !from.isBareJid()) { mucOptions.setError(MucOptions.Error.NONE); - MucOptions.User user = new MucOptions.User(mucOptions,from); + MucOptions.User user = new MucOptions.User(mucOptions, from); user.setAffiliation(item.getAttribute("affiliation")); user.setRole(item.getAttribute("role")); - user.setJid(item.getAttributeAsJid("jid")); + Jid real = item.getAttributeAsJid("jid"); + if (real != null) { + user.setJid(real); + mucOptions.putMember(real.toBareJid()); + } if (codes.contains(MucOptions.STATUS_CODE_SELF_PRESENCE) || packet.getFrom().equals(mucOptions.getConversation().getJid())) { mucOptions.setOnline(); mucOptions.setSelf(user); diff --git a/src/main/java/de/thedevstack/conversationsplus/persistance/DatabaseBackend.java b/src/main/java/de/thedevstack/conversationsplus/persistance/DatabaseBackend.java index 52f60e88..b6accd1e 100644 --- a/src/main/java/de/thedevstack/conversationsplus/persistance/DatabaseBackend.java +++ b/src/main/java/de/thedevstack/conversationsplus/persistance/DatabaseBackend.java @@ -52,7 +52,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { private static DatabaseBackend instance = null; private static final String DATABASE_NAME = "history"; - private static final int DATABASE_VERSION = 24; + private static final int DATABASE_VERSION = 25; private static String CREATE_CONTATCS_STATEMENT = "create table " + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, " @@ -164,6 +164,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { + Message.CARBON + " INTEGER, " + Message.EDITED + " TEXT, " + Message.READ + " NUMBER DEFAULT 1, " + + Message.OOB + " INTEGER, " + Message.REMOTE_MSG_ID + " TEXT, FOREIGN KEY(" + Message.CONVERSATION + ") REFERENCES " + Conversation.TABLENAME + "(" + Conversation.UUID @@ -376,6 +377,10 @@ public class DatabaseBackend extends SQLiteOpenHelper { if (oldVersion < 24 && newVersion >= 24) { db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.EDITED + " TEXT"); } + + if (oldVersion < 25 && newVersion >= 25) { + db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.OOB + " INTEGER"); + } } public static synchronized DatabaseBackend getInstance(Context context) { diff --git a/src/main/java/de/thedevstack/conversationsplus/services/ExportLogsService.java b/src/main/java/de/thedevstack/conversationsplus/services/ExportLogsService.java index 0b898125..18930d1c 100644 --- a/src/main/java/de/thedevstack/conversationsplus/services/ExportLogsService.java +++ b/src/main/java/de/thedevstack/conversationsplus/services/ExportLogsService.java @@ -46,9 +46,9 @@ public class ExportLogsService extends Service { new Thread(new Runnable() { @Override public void run() { - running.set(false); export(); stopForeground(true); + running.set(false); stopSelf(); } }).start(); diff --git a/src/main/java/de/thedevstack/conversationsplus/services/XmppConnectionService.java b/src/main/java/de/thedevstack/conversationsplus/services/XmppConnectionService.java index df5f72f5..36c4892f 100644 --- a/src/main/java/de/thedevstack/conversationsplus/services/XmppConnectionService.java +++ b/src/main/java/de/thedevstack/conversationsplus/services/XmppConnectionService.java @@ -258,6 +258,14 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa @Override public void onBind(final Account account) { + synchronized (mInProgressAvatarFetches) { + for (Iterator<String> iterator = mInProgressAvatarFetches.iterator(); iterator.hasNext(); ) { + final String KEY = iterator.next(); + if (KEY.startsWith(account.getJid().toBareJid() + "_")) { + iterator.remove(); + } + } + } account.getRoster().clearPresences(); mJingleConnectionManager.cancelInTransmission(); fetchRosterFromServer(account); @@ -569,6 +577,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa return getPreferences().getBoolean("xa_on_silent_mode", false); } + private boolean treatVibrateAsSilent() { + return getPreferences().getBoolean("treat_vibrate_as_silent", false); + } + private boolean awayWhenScreenOff() { return getPreferences().getBoolean("away_when_screen_off", false); } @@ -599,7 +611,11 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa private boolean isPhoneSilenced() { AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); - return audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT; + if (treatVibrateAsSilent()) { + return audioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL; + } else { + return audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT; + } } private void resetAllAttemptCounts(boolean reallyAll) { @@ -1768,8 +1784,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa private void join(Conversation conversation) { Account account = conversation.getAccount(); - final String nick = conversation.getMucOptions().getProposedNick(); - final Jid joinJid = conversation.getMucOptions().createJoinJid(nick); + final MucOptions mucOptions = conversation.getMucOptions(); + final Jid joinJid = mucOptions.getSelf().getFullJid(); Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": joining conversation " + joinJid.toString()); PresencePacket packet = new PresencePacket(); packet.setFrom(conversation.getAccount().getJid()); @@ -1779,7 +1795,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa x.addChild("password").setContent(conversation.getMucOptions().getPassword()); } - if (conversation.getMucOptions().mamSupport()) { + if (mucOptions.mamSupport()) { // Use MAM instead of the limited muc history to get history x.addChild("history").setAttribute("maxchars", "0"); } else { @@ -1798,9 +1814,13 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa conversation.setContactJid(joinJid); databaseBackend.updateConversation(conversation); } - if (conversation.getMucOptions().mamSupport()) { + + if (mucOptions.mamSupport()) { getMessageArchiveService().catchupMUC(conversation); } + if (mucOptions.membersOnly() && mucOptions.nonanonymous()) { + fetchConferenceMembers(conversation); + } sendUnsentMessages(conversation); } @@ -1824,6 +1844,37 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } + private void fetchConferenceMembers(final Conversation conversation) { + final Account account = conversation.getAccount(); + final String[] affiliations = {"member","admin","owner"}; + OnIqPacketReceived callback = new OnIqPacketReceived() { + + private int i = 0; + + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + Element query = packet.query("http://jabber.org/protocol/muc#admin"); + if (packet.getType() == IqPacket.TYPE.RESULT && query != null) { + for(Element child : query.getChildren()) { + if ("item".equals(child.getName())) { + conversation.getMucOptions().putMember(child.getAttributeAsJid("jid")); + } + } + } else { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not request affiliation "+affiliations[i]+" in "+conversation.getJid().toBareJid()); + } + ++i; + if (i >= affiliations.length) { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": retrieved members for "+conversation.getJid().toBareJid()+": "+conversation.getMucOptions().getMembers()); + } + } + }; + for(String affiliation : affiliations) { + sendIqPacket(account, mIqGenerator.queryAffiliation(conversation, affiliation), callback); + } + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": fetching members for "+conversation.getName()); + } + public void providePasswordForMuc(Conversation conversation, String password) { if (conversation.getMode() == Conversation.MODE_MULTI) { conversation.getMucOptions().setPassword(password); @@ -1897,7 +1948,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa account.pendingConferenceLeaves.remove(conversation); if (account.getStatus() == Account.State.ONLINE || now) { PresencePacket packet = new PresencePacket(); - packet.setTo(conversation.getJid()); + packet.setTo(conversation.getMucOptions().getSelf().getFullJid()); packet.setFrom(conversation.getAccount().getJid()); packet.setAttribute("type", "unavailable"); sendPresencePacket(conversation.getAccount(), packet); @@ -1997,9 +2048,9 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa sendIqPacket(conversation.getAccount(), request, new OnIqPacketReceived() { @Override public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.RESULT) { + Element query = packet.findChild("query","http://jabber.org/protocol/disco#info"); + if (packet.getType() == IqPacket.TYPE.RESULT && query != null) { ArrayList<String> features = new ArrayList<>(); - Element query = packet.query(); for (Element child : query.getChildren()) { if (child != null && child.getName().equals("feature")) { String var = child.getAttribute("var"); @@ -2016,6 +2067,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa if (callback != null) { callback.onConferenceConfigurationFetched(conversation); } + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": fetched muc configuration for "+conversation.getJid().toBareJid()+" - "+features.toString()); updateConversationUi(); } else if (packet.getType() == IqPacket.TYPE.ERROR) { if (callback != null) { @@ -2280,22 +2332,13 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa private void reconnectAccount(final Account account, final boolean force, final boolean interactive) { synchronized (account) { XmppConnection connection = account.getXmppConnection(); - if (connection != null) { - disconnect(account, force); - } else { + if (connection == null) { connection = createConnection(account); account.setXmppConnection(connection); } if (!account.isOptionSet(Account.OPTION_DISABLED)) { - synchronized (this.mInProgressAvatarFetches) { - for (Iterator<String> iterator = this.mInProgressAvatarFetches.iterator(); iterator.hasNext(); ) { - final String KEY = iterator.next(); - if (KEY.startsWith(account.getJid().toBareJid() + "_")) { - iterator.remove(); - } - } - } if (!force) { + disconnect(account, false); try { Logging.d(Config.LOGTAG, "wait for disconnect"); Thread.sleep(500); //sleep wait for disconnect @@ -2308,6 +2351,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa thread.start(); scheduleWakeUpCall(Config.CONNECT_DISCO_TIMEOUT, account.getUuid().hashCode()); } else { + disconnect(account, force); account.getRoster().clearPresences(); connection.resetEverything(); } diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/ConferenceDetailsActivity.java b/src/main/java/de/thedevstack/conversationsplus/ui/ConferenceDetailsActivity.java index c1b5ccc6..12ff2a52 100644 --- a/src/main/java/de/thedevstack/conversationsplus/ui/ConferenceDetailsActivity.java +++ b/src/main/java/de/thedevstack/conversationsplus/ui/ConferenceDetailsActivity.java @@ -334,7 +334,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.muc_details, menu); - return true; + return super.onCreateOptionsMenu(menu); } @Override diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/ContactDetailsActivity.java b/src/main/java/de/thedevstack/conversationsplus/ui/ContactDetailsActivity.java index aaa19fc5..a18fd848 100644 --- a/src/main/java/de/thedevstack/conversationsplus/ui/ContactDetailsActivity.java +++ b/src/main/java/de/thedevstack/conversationsplus/ui/ContactDetailsActivity.java @@ -297,7 +297,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd edit.setVisible(false); delete.setVisible(false); } - return true; + return super.onCreateOptionsMenu(menu); } private void populateView() { diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/ConversationActivity.java b/src/main/java/de/thedevstack/conversationsplus/ui/ConversationActivity.java index 1a4ccb8c..1546de27 100644 --- a/src/main/java/de/thedevstack/conversationsplus/ui/ConversationActivity.java +++ b/src/main/java/de/thedevstack/conversationsplus/ui/ConversationActivity.java @@ -415,7 +415,7 @@ public class ConversationActivity extends XmppActivity menuContactDetails.setVisible(false); menuAttach.setVisible(getSelectedConversation().getAccount().httpUploadAvailable() && getSelectedConversation().getMucOptions().participating()); menuInviteContact.setVisible(getSelectedConversation().getMucOptions().canInvite()); - menuSecure.setVisible(Config.supportOpenPgp() && Config.multipleEncryptionChoices()); //only if pgp is supported we have a choice + menuSecure.setVisible((Config.supportOpenPgp() || Config.supportOmemo()) && Config.multipleEncryptionChoices()); //only if pgp is supported we have a choice } else { menuMucDetails.setVisible(false); menuSecure.setVisible(Config.multipleEncryptionChoices()); @@ -427,7 +427,7 @@ public class ConversationActivity extends XmppActivity } } } - return true; + return super.onCreateOptionsMenu(menu); } protected void selectPresenceToAttachFile(final int attachmentChoice, final int encryption) { @@ -863,8 +863,8 @@ public class ConversationActivity extends XmppActivity axolotl.setVisible(Config.supportOmemo()); if (conversation.getMode() == Conversation.MODE_MULTI) { otr.setVisible(false); - axolotl.setVisible(false); - } else if (!conversation.getAccount().getAxolotlService().isContactAxolotlCapable(conversation.getContact())) { + } + if (!conversation.getAccount().getAxolotlService().isConversationAxolotlCapable(conversation)) { axolotl.setEnabled(false); } switch (conversation.getNextEncryption()) { @@ -1343,49 +1343,64 @@ public class ConversationActivity extends XmppActivity } } else { - mPendingImageUris.clear(); - mPendingFileUris.clear(); - if (requestCode == ConversationActivity.REQUEST_DECRYPT_PGP) { - mConversationFragment.onActivityResult(requestCode, resultCode, data); - } - if (requestCode == REQUEST_BATTERY_OP) { - setNeverAskForBatteryOptimizationsAgain(); - } - } - } - - private void setNeverAskForBatteryOptimizationsAgain() { - getPreferences().edit().putBoolean("show_battery_optimization", false).commit(); - } - - private void openBatteryOptimizationDialogIfNeeded() { - if (showBatteryOptimizationWarning() && getPreferences().getBoolean("show_battery_optimization", true)) { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.battery_optimizations_enabled); - builder.setMessage(R.string.battery_optimizations_enabled_dialog); - builder.setPositiveButton(R.string.next, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); - Uri uri = Uri.parse("package:" + getPackageName()); - intent.setData(uri); - startActivityForResult(intent, REQUEST_BATTERY_OP); - } - }); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - builder.setOnDismissListener(new DialogInterface.OnDismissListener() { - @Override - public void onDismiss(DialogInterface dialog) { - setNeverAskForBatteryOptimizationsAgain(); - } - }); - } - builder.create().show(); - } - } + mPendingImageUris.clear(); + mPendingFileUris.clear(); + if (requestCode == ConversationActivity.REQUEST_DECRYPT_PGP) { + mConversationFragment.onActivityResult(requestCode, resultCode, data); + } + if (requestCode == REQUEST_BATTERY_OP) { + setNeverAskForBatteryOptimizationsAgain(); + } + } + } + + private void setNeverAskForBatteryOptimizationsAgain() { + getPreferences().edit().putBoolean("show_battery_optimization", false).commit(); + } + + private void openBatteryOptimizationDialogIfNeeded() { + if (hasAccountWithoutPush() + && isOptimizingBattery() + && getPreferences().getBoolean("show_battery_optimization", true)) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.battery_optimizations_enabled); + builder.setMessage(R.string.battery_optimizations_enabled_dialog); + builder.setPositiveButton(R.string.next, new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); + Uri uri = Uri.parse("package:" + getPackageName()); + intent.setData(uri); + startActivityForResult(intent, REQUEST_BATTERY_OP); + } + }); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + builder.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + setNeverAskForBatteryOptimizationsAgain(); + } + }); + } + builder.create().show(); + } + } + + private boolean hasAccountWithoutPush() { + for(Account account : xmppConnectionService.getAccounts()) { + if (account.getStatus() != Account.State.DISABLED + && !xmppConnectionService.getPushManagementService().available(account)) { + return true; + } + } + return false; + } private void attachLocationToConversation(Conversation conversation, Uri uri) { - xmppConnectionService.attachLocationToConversation(conversation, uri, new UiCallback<Message>() { + if (conversation == null) { + return; + } + xmppConnectionService.attachLocationToConversation(conversation,uri, new UiCallback<Message>() { @Override public void success(Message message) { @@ -1502,18 +1517,23 @@ public class ConversationActivity extends XmppActivity protected boolean trustKeysIfNeeded(int requestCode, int attachmentChoice) { AxolotlService axolotlService = mSelectedConversation.getAccount().getAxolotlService(); - Contact contact = mSelectedConversation.getContact(); + final List<Jid> targets = axolotlService.getCryptoTargets(mSelectedConversation); + boolean hasUnaccepted = !mSelectedConversation.getAcceptedCryptoTargets().containsAll(targets); boolean hasUndecidedOwn = !axolotlService.getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED).isEmpty(); - boolean hasUndecidedContact = !axolotlService.getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED,contact).isEmpty(); + boolean hasUndecidedContacts = !axolotlService.getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED, targets).isEmpty(); boolean hasPendingKeys = !axolotlService.findDevicesWithoutSession(mSelectedConversation).isEmpty(); - boolean hasNoTrustedKeys = axolotlService.getNumTrustedKeys(mSelectedConversation.getContact()) == 0; - if(hasUndecidedOwn || hasUndecidedContact || hasPendingKeys || hasNoTrustedKeys) { + boolean hasNoTrustedKeys = axolotlService.anyTargetHasNoTrustedKeys(targets); + if(hasUndecidedOwn || hasUndecidedContacts || hasPendingKeys || hasNoTrustedKeys || hasUnaccepted) { axolotlService.createSessionsIfNeeded(mSelectedConversation); Intent intent = new Intent(getApplicationContext(), TrustKeysActivity.class); - intent.putExtra("contact", mSelectedConversation.getContact().getJid().toBareJid().toString()); + String[] contacts = new String[targets.size()]; + for(int i = 0; i < contacts.length; ++i) { + contacts[i] = targets.get(i).toString(); + } + intent.putExtra("contacts", contacts); intent.putExtra(EXTRA_ACCOUNT, mSelectedConversation.getAccount().getJid().toBareJid().toString()); intent.putExtra("choice", attachmentChoice); - intent.putExtra("has_no_trusted", hasNoTrustedKeys); + intent.putExtra("conversation",mSelectedConversation.getUuid()); startActivityForResult(intent, requestCode); return true; } else { diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/ConversationFragment.java b/src/main/java/de/thedevstack/conversationsplus/ui/ConversationFragment.java index 2828e27e..44132ce3 100644 --- a/src/main/java/de/thedevstack/conversationsplus/ui/ConversationFragment.java +++ b/src/main/java/de/thedevstack/conversationsplus/ui/ConversationFragment.java @@ -8,6 +8,7 @@ import android.content.ActivityNotFoundException; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.IntentSender; import android.content.IntentSender.SendIntentException; import android.os.Bundle; import android.support.annotation.Nullable; @@ -23,6 +24,8 @@ import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; +import android.widget.AbsListView; +import android.widget.AbsListView.OnScrollListener; import android.widget.AdapterView; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.ImageButton; @@ -43,6 +46,7 @@ import java.util.Collections; import java.util.List; import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.http.HttpDownloadConnection; import de.thedevstack.conversationsplus.ui.dialogs.MessageDetailsDialog; import de.thedevstack.conversationsplus.Config; import de.thedevstack.conversationsplus.R; @@ -521,6 +525,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa private void populateContextMenu(ContextMenu menu) { final Message m = this.selectedMessage; + final Transferable t = m.getTransferable(); if (m.getType() != Message.TYPE_STATUS) { activity.getMenuInflater().inflate(R.menu.message_context, menu); menu.setHeaderTitle(R.string.message_options); @@ -532,19 +537,18 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa MenuItem downloadFile = menu.findItem(R.id.download_file); MenuItem cancelTransmission = menu.findItem(R.id.cancel_transmission); if ((m.getType() == Message.TYPE_TEXT || m.getType() == Message.TYPE_PRIVATE) - && m.getTransferable() == null + && t == null && !GeoHelper.isGeoUri(m.getBody()) && m.treatAsDownloadable() != Message.Decision.MUST) { copyText.setVisible(true); } - if (m.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) { retryDecryption.setVisible(true); } if ((m.getType() != Message.TYPE_TEXT && m.getType() != Message.TYPE_PRIVATE - && m.getTransferable() == null) + && t == null) || (GeoHelper.isGeoUri(m.getBody()))) { shareWith.setVisible(true); } @@ -553,15 +557,16 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa } if (m.hasFileOnRemoteHost() || GeoHelper.isGeoUri(m.getBody()) - || m.treatAsDownloadable() == Message.Decision.MUST) { + || m.treatAsDownloadable() == Message.Decision.MUST + || (t != null && t instanceof HttpDownloadConnection)) { copyUrl.setVisible(true); } - if ((m.getType() == Message.TYPE_TEXT && m.getTransferable() == null && m.treatAsDownloadable() != Message.Decision.NEVER) - || (m.isFileOrImage() && m.getTransferable() instanceof TransferablePlaceholder && m.hasFileOnRemoteHost())){ + if ((m.getType() == Message.TYPE_TEXT && t == null && m.treatAsDownloadable() != Message.Decision.NEVER) + || (m.isFileOrImage() && t instanceof TransferablePlaceholder && m.hasFileOnRemoteHost())){ downloadFile.setVisible(true); downloadFile.setTitle(activity.getString(R.string.download_x_file,UIHelper.getFileDescriptionString(activity, m))); } - if ((m.getTransferable() != null && !(m.getTransferable() instanceof TransferablePlaceholder)) + if ((t != null && !(t instanceof TransferablePlaceholder)) || (m.isFileOrImage() && (m.getStatus() == Message.STATUS_WAITING || m.getStatus() == Message.STATUS_OFFERED))) { cancelTransmission.setVisible(true); @@ -757,6 +762,17 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa swipeLayout.setRefreshing(false); } + private OnClickListener mEnableAccountListener = new OnClickListener() { + @Override + public void onClick(View v) { + final Account account = conversation == null ? null : conversation.getAccount(); + if (account != null) { + account.setOption(Account.OPTION_DISABLED, false); + activity.xmppConnectionService.updateAccount(account); + } + } + }; + private OnClickListener mUnblockClickListener = new OnClickListener() { @Override public void onClick(final View v) { @@ -802,7 +818,9 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa final Account account = conversation.getAccount(); final Contact contact = conversation.getContact(); final int mode = conversation.getMode(); - if (conversation.isBlocked()) { + if (account.getStatus() == Account.State.DISABLED) { + showSnackbar(R.string.this_account_is_disabled, R.string.enable, this.mEnableAccountListener); + } else if (conversation.isBlocked()) { showSnackbar(R.string.contact_blocked, R.string.unblock, this.mUnblockClickListener); } else if (!contact.showInRoster() && contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) { showSnackbar(R.string.contact_added_you, R.string.add_back, this.mAddBackClickListener); diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/EditAccountActivity.java b/src/main/java/de/thedevstack/conversationsplus/ui/EditAccountActivity.java index 7da2e889..ac06544a 100644 --- a/src/main/java/de/thedevstack/conversationsplus/ui/EditAccountActivity.java +++ b/src/main/java/de/thedevstack/conversationsplus/ui/EditAccountActivity.java @@ -501,7 +501,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate clearDevices.setVisible(false); mamPrefs.setVisible(false); } - return true; + return super.onCreateOptionsMenu(menu); } @Override @@ -621,6 +621,13 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate this.mNamePort.setVisibility(mShowOptions ? View.VISIBLE : View.GONE); } + mPassword.setEnabled(!Config.LOCK_SETTINGS); + mAccountJid.setEnabled(!Config.LOCK_SETTINGS); + mHostname.setEnabled(!Config.LOCK_SETTINGS); + mPort.setEnabled(!Config.LOCK_SETTINGS); + mPasswordConfirm.setEnabled(!Config.LOCK_SETTINGS); + mRegisterNew.setEnabled(!Config.LOCK_SETTINGS); + if (!mInitMode) { this.mAvatar.setVisibility(View.VISIBLE); this.mAvatar.setImageBitmap(AvatarService.getInstance().get(this.mAccount, getPixel(72))); @@ -643,11 +650,12 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate } else { detailsAccountJid.setText(this.mAccount.getJid().toBareJid().toString()); } + Features features = this.mAccount.getXmppConnection().getFeatures(); this.mStats.setVisibility(View.VISIBLE); - this.mBatteryOptimizations.setVisibility(showBatteryOptimizationWarning() ? View.VISIBLE : View.GONE); + boolean showOptimizingWarning = !xmppConnectionService.getPushManagementService().available(mAccount) && isOptimizingBattery(); + this.mBatteryOptimizations.setVisibility(showOptimizingWarning ? View.VISIBLE : View.GONE); this.mSessionEst.setText(UIHelper.readableTimeDifferenceFull(this, this.mAccount.getXmppConnection() .getLastSessionEstablished())); - Features features = this.mAccount.getXmppConnection().getFeatures(); if (features.rosterVersioning()) { this.mServerInfoRosterVersion.setText(R.string.server_info_available); } else { diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/ExportLogsPreference.java b/src/main/java/de/thedevstack/conversationsplus/ui/ExportLogsPreference.java index eabb7518..54fe3910 100644 --- a/src/main/java/de/thedevstack/conversationsplus/ui/ExportLogsPreference.java +++ b/src/main/java/de/thedevstack/conversationsplus/ui/ExportLogsPreference.java @@ -1,7 +1,10 @@ package de.thedevstack.conversationsplus.ui; +import android.Manifest; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Build; import android.preference.Preference; import android.util.AttributeSet; @@ -22,6 +25,10 @@ public class ExportLogsPreference extends Preference { } protected void onClick() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M + && getContext().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + return; + } final Intent startIntent = new Intent(getContext(), ExportLogsService.class); getContext().startService(startIntent); super.onClick(); diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/ManageAccountActivity.java b/src/main/java/de/thedevstack/conversationsplus/ui/ManageAccountActivity.java index 431111f5..e376b6b3 100644 --- a/src/main/java/de/thedevstack/conversationsplus/ui/ManageAccountActivity.java +++ b/src/main/java/de/thedevstack/conversationsplus/ui/ManageAccountActivity.java @@ -155,9 +155,9 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda addAccount.setVisible(false); addAccountWithCertificate.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); } else { - addAccount.setVisible(!Config.SINGLE_ACCOUNT); + addAccount.setVisible(!Config.LOCK_SETTINGS); } - addAccountWithCertificate.setVisible(!Config.SINGLE_ACCOUNT); + addAccountWithCertificate.setVisible(!Config.LOCK_SETTINGS); if (!accountsLeftToEnable()) { enableAll.setVisible(false); diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/PublishProfilePictureActivity.java b/src/main/java/de/thedevstack/conversationsplus/ui/PublishProfilePictureActivity.java index 9b827861..6bf8c590 100644 --- a/src/main/java/de/thedevstack/conversationsplus/ui/PublishProfilePictureActivity.java +++ b/src/main/java/de/thedevstack/conversationsplus/ui/PublishProfilePictureActivity.java @@ -7,6 +7,8 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; @@ -32,7 +34,8 @@ import de.thedevstack.conversationsplus.xmpp.pep.Avatar; public class PublishProfilePictureActivity extends XmppActivity { - private static final int REQUEST_CHOOSE_FILE = 0xac23; + private static final int REQUEST_CHOOSE_FILE_AND_CROP = 0xac23; + private static final int REQUEST_CHOOSE_FILE = 0xac24; private ImageView avatar; private TextView accountTextView; private TextView hintOrWarning; @@ -137,7 +140,7 @@ public class PublishProfilePictureActivity extends XmppActivity { @Override public void onClick(View v) { if (hasStoragePermission(REQUEST_CHOOSE_FILE)) { - chooseAvatar(); + chooseAvatar(false); } } @@ -145,20 +148,22 @@ public class PublishProfilePictureActivity extends XmppActivity { this.defaultUri = PhoneHelper.getSefliUri(getApplicationContext()); } - private void chooseAvatar() { + private void chooseAvatar(boolean crop) { Intent attachFileIntent = new Intent(); attachFileIntent.setType("image/*"); attachFileIntent.setAction(Intent.ACTION_GET_CONTENT); Intent chooser = Intent.createChooser(attachFileIntent, getString(R.string.attach_file)); - startActivityForResult(chooser, REQUEST_CHOOSE_FILE); + startActivityForResult(chooser, crop ? REQUEST_CHOOSE_FILE_AND_CROP : REQUEST_CHOOSE_FILE); } @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { if (grantResults.length > 0) if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { - if (requestCode == REQUEST_CHOOSE_FILE) { - chooseAvatar(); + if (requestCode == REQUEST_CHOOSE_FILE_AND_CROP) { + chooseAvatar(true); + } else if (requestCode == REQUEST_CHOOSE_FILE) { + chooseAvatar(false); } } else { Toast.makeText(this, R.string.no_storage_permission, Toast.LENGTH_SHORT).show(); @@ -166,11 +171,29 @@ public class PublishProfilePictureActivity extends XmppActivity { } @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.publish_avatar, menu); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(final MenuItem item) { + if (item.getItemId() == R.id.action_crop_image) { + if (hasStoragePermission(REQUEST_CHOOSE_FILE_AND_CROP)) { + chooseAvatar(true); + } + return true; + } else { + return onOptionsItemSelected(item); + } + } + + @Override protected void onActivityResult(int requestCode, int resultCode, final Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { switch (requestCode) { - case REQUEST_CHOOSE_FILE: + case REQUEST_CHOOSE_FILE_AND_CROP: Uri source = data.getData(); String original = FileUtils.getPath(this, source); if (original != null) { @@ -180,9 +203,17 @@ public class PublishProfilePictureActivity extends XmppActivity { final int size = getPixel(192); Crop.of(source, destination).asSquare().withMaxSize(size, size).start(this); break; + case REQUEST_CHOOSE_FILE: + this.avatarUri = data.getData(); + if (xmppConnectionServiceBound) { + loadImageIntoPreview(this.avatarUri); + } + break; case Crop.REQUEST_CROP: this.avatarUri = Uri.fromFile(new File(getCacheDir(), "croppedAvatar")); - loadImageIntoPreview(this.avatarUri); + if (xmppConnectionServiceBound) { + loadImageIntoPreview(this.avatarUri); + } break; } } else { @@ -247,21 +278,10 @@ public class PublishProfilePictureActivity extends XmppActivity { } } - private Bitmap loadScaledBitmap(Uri uri, int reqSize) throws FileNotFoundException { - final BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), null, options); - int rotation = ExifHelper.getOrientation(getContentResolver().openInputStream(uri)); - options.inSampleSize = ImageUtil.calcSampleSize(options, reqSize); - options.inJustDecodeBounds = false; - Bitmap bm = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), null, options); - return ImageUtil.rotate(bm,rotation); - } - protected void loadImageIntoPreview(Uri uri) { Bitmap bm = null; try { - bm = loadScaledBitmap(uri, getPixel(192)); + bm = ImageUtil.cropCenterSquare(uri, getPixel(192)); } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/SettingsActivity.java b/src/main/java/de/thedevstack/conversationsplus/ui/SettingsActivity.java index e8c9587b..ff7e11d1 100644 --- a/src/main/java/de/thedevstack/conversationsplus/ui/SettingsActivity.java +++ b/src/main/java/de/thedevstack/conversationsplus/ui/SettingsActivity.java @@ -3,23 +3,30 @@ package de.thedevstack.conversationsplus.ui; import android.app.AlertDialog; import android.app.FragmentManager; import android.content.DialogInterface; +import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; +import android.preference.CheckBoxPreference; import android.preference.ListPreference; import android.preference.Preference; import android.preference.PreferenceCategory; import android.preference.PreferenceManager; +import android.preference.PreferenceScreen; +import android.util.Log; import android.widget.Toast; import java.security.KeyStoreException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.List; import java.util.Locale; import de.duenndns.ssl.MemorizingTrustManager; +import de.thedevstack.conversationsplus.services.ExportLogsService; import de.tzur.conversations.Settings; import de.thedevstack.conversationsplus.R; import de.thedevstack.conversationsplus.entities.Account; @@ -28,6 +35,8 @@ import github.ankushsachdeva.emojicon.EmojiconHandler; public class SettingsActivity extends XmppActivity implements OnSharedPreferenceChangeListener { + + public static final int REQUEST_WRITE_LOGS = 0xbf8701; private SettingsFragment mSettingsFragment; @Override @@ -62,34 +71,34 @@ public class SettingsActivity extends XmppActivity implements final Preference removeCertsPreference = mSettingsFragment.findPreference("remove_trusted_certificates"); removeCertsPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - final MemorizingTrustManager mtm = xmppConnectionService.getMemorizingTrustManager(); - final ArrayList<String> aliases = Collections.list(mtm.getCertificates()); - if (aliases.size() == 0) { - displayToast(getString(R.string.toast_no_trusted_certs)); - return true; - } - final ArrayList selectedItems = new ArrayList<Integer>(); - final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(SettingsActivity.this); - dialogBuilder.setTitle(getResources().getString(R.string.dialog_manage_certs_title)); - dialogBuilder.setMultiChoiceItems(aliases.toArray(new CharSequence[aliases.size()]), null, - new DialogInterface.OnMultiChoiceClickListener() { - @Override - public void onClick(DialogInterface dialog, int indexSelected, - boolean isChecked) { - if (isChecked) { - selectedItems.add(indexSelected); - } else if (selectedItems.contains(indexSelected)) { - selectedItems.remove(Integer.valueOf(indexSelected)); - } - if (selectedItems.size() > 0) - ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true); - else { - ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false); - } - } - }); + @Override + public boolean onPreferenceClick(Preference preference) { + final MemorizingTrustManager mtm = xmppConnectionService.getMemorizingTrustManager(); + final ArrayList<String> aliases = Collections.list(mtm.getCertificates()); + if (aliases.size() == 0) { + displayToast(getString(R.string.toast_no_trusted_certs)); + return true; + } + final ArrayList selectedItems = new ArrayList<Integer>(); + final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(SettingsActivity.this); + dialogBuilder.setTitle(getResources().getString(R.string.dialog_manage_certs_title)); + dialogBuilder.setMultiChoiceItems(aliases.toArray(new CharSequence[aliases.size()]), null, + new DialogInterface.OnMultiChoiceClickListener() { + @Override + public void onClick(DialogInterface dialog, int indexSelected, + boolean isChecked) { + if (isChecked) { + selectedItems.add(indexSelected); + } else if (selectedItems.contains(indexSelected)) { + selectedItems.remove(Integer.valueOf(indexSelected)); + } + if (selectedItems.size() > 0) + ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true); + else { + ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false); + } + } + }); dialogBuilder.setPositiveButton( getResources().getString(R.string.dialog_manage_certs_positivebutton), new DialogInterface.OnClickListener() { @@ -137,8 +146,12 @@ public class SettingsActivity extends XmppActivity implements } @Override - public void onSharedPreferenceChanged(SharedPreferences preferences, - String name) { + public void onSharedPreferenceChanged(SharedPreferences preferences, String name) { + final List<String> resendPresence = Arrays.asList( + "confirm_messages_list", + "xa_on_silent_mode", + "away_when_screen_off", + "treat_vibrate_as_silent"); // need to synchronize the settings class first Settings.synchronizeSettingsClassWithPreferences(preferences, name); if (name.equals("resource")) { @@ -159,9 +172,7 @@ public class SettingsActivity extends XmppActivity implements } } else if (name.equals("keep_foreground_service")) { xmppConnectionService.toggleForegroundService(); - } else if (name.equals("confirm_messages_list") - || name.equals("xa_on_silent_mode") - || name.equals("away_when_screen_off")) { + } else if (resendPresence.contains(name)) { if (xmppConnectionServiceBound) { if (name.equals("away_when_screen_off")) { xmppConnectionService.toggleScreenEventReceiver(); @@ -177,6 +188,18 @@ public class SettingsActivity extends XmppActivity implements } + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + if (grantResults.length > 0) + if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { + if (requestCode == REQUEST_WRITE_LOGS) { + getApplicationContext().startService(new Intent(getApplicationContext(), ExportLogsService.class)); + } + } else { + Toast.makeText(this, R.string.no_storage_permission, Toast.LENGTH_SHORT).show(); + } + } + private void displayToast(final String msg) { runOnUiThread(new Runnable() { @Override diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/StartConversationActivity.java b/src/main/java/de/thedevstack/conversationsplus/ui/StartConversationActivity.java index f8c624be..d3086d66 100644 --- a/src/main/java/de/thedevstack/conversationsplus/ui/StartConversationActivity.java +++ b/src/main/java/de/thedevstack/conversationsplus/ui/StartConversationActivity.java @@ -534,7 +534,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU mSearchEditText.append(mInitialJid); filter(mInitialJid); } - return true; + return super.onCreateOptionsMenu(menu); } @Override @@ -742,6 +742,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU Presence.Status s = p == null ? Presence.Status.OFFLINE : p.getStatus(); if (contact.showInRoster() && contact.match(needle) && (!this.mHideOfflineContacts + || (needle != null && !needle.trim().isEmpty()) || s.compareTo(Presence.Status.OFFLINE) < 0)) { this.contacts.add(contact); } diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/TrustKeysActivity.java b/src/main/java/de/thedevstack/conversationsplus/ui/TrustKeysActivity.java index 167b207f..a3f674be 100644 --- a/src/main/java/de/thedevstack/conversationsplus/ui/TrustKeysActivity.java +++ b/src/main/java/de/thedevstack/conversationsplus/ui/TrustKeysActivity.java @@ -12,7 +12,9 @@ import android.widget.Toast; import org.whispersystems.libaxolotl.IdentityKey; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; @@ -21,31 +23,29 @@ import de.thedevstack.conversationsplus.crypto.axolotl.AxolotlService; import de.thedevstack.conversationsplus.crypto.axolotl.XmppAxolotlSession; import de.thedevstack.conversationsplus.entities.Account; import de.thedevstack.conversationsplus.entities.Contact; +import de.thedevstack.conversationsplus.entities.Conversation; import de.thedevstack.conversationsplus.xmpp.OnKeyStatusUpdated; import de.thedevstack.conversationsplus.xmpp.jid.InvalidJidException; import de.thedevstack.conversationsplus.xmpp.jid.Jid; public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdated { - private Jid accountJid; - private Jid contactJid; + private List<Jid> contactJids; - private Contact contact; private Account mAccount; + private Conversation mConversation; private TextView keyErrorMessage; private LinearLayout keyErrorMessageCard; private TextView ownKeysTitle; private LinearLayout ownKeys; private LinearLayout ownKeysCard; - private TextView foreignKeysTitle; private LinearLayout foreignKeys; - private LinearLayout foreignKeysCard; private Button mSaveButton; private Button mCancelButton; private AxolotlService.FetchStatus lastFetchReport = AxolotlService.FetchStatus.SUCCESS; private final Map<String, Boolean> ownKeysToTrust = new HashMap<>(); - private final Map<String, Boolean> foreignKeysToTrust = new HashMap<>(); + private final Map<Jid,Map<String, Boolean>> foreignKeysToTrust = new HashMap<>(); private final OnClickListener mSaveButtonListener = new OnClickListener() { @Override @@ -70,25 +70,16 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate } @Override - protected String getShareableUri() { - if (contact != null) { - return contact.getShareableUri(); - } else { - return ""; - } - } - - @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_trust_keys); - try { - this.accountJid = Jid.fromString(getIntent().getExtras().getString(EXTRA_ACCOUNT)); - } catch (final InvalidJidException ignored) { - } - try { - this.contactJid = Jid.fromString(getIntent().getExtras().getString("contact")); - } catch (final InvalidJidException ignored) { + this.contactJids = new ArrayList<>(); + for(String jid : getIntent().getStringArrayExtra("contacts")) { + try { + this.contactJids.add(Jid.fromString(jid)); + } catch (InvalidJidException e) { + e.printStackTrace(); + } } keyErrorMessageCard = (LinearLayout) findViewById(R.id.key_error_message_card); @@ -96,9 +87,7 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate ownKeysTitle = (TextView) findViewById(R.id.own_keys_title); ownKeys = (LinearLayout) findViewById(R.id.own_keys_details); ownKeysCard = (LinearLayout) findViewById(R.id.own_keys_card); - foreignKeysTitle = (TextView) findViewById(R.id.foreign_keys_title); - foreignKeys = (LinearLayout) findViewById(R.id.foreign_keys_details); - foreignKeysCard = (LinearLayout) findViewById(R.id.foreign_keys_card); + foreignKeys = (LinearLayout) findViewById(R.id.foreign_keys); mCancelButton = (Button) findViewById(R.id.cancel_button); mCancelButton.setOnClickListener(mCancelButtonListener); mSaveButton = (Button) findViewById(R.id.save_button); @@ -119,7 +108,7 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate boolean hasForeignKeys = false; for(final String fingerprint : ownKeysToTrust.keySet()) { hasOwnKeys = true; - addFingerprintRowWithListeners(ownKeys, contact.getAccount(), fingerprint, false, + addFingerprintRowWithListeners(ownKeys, mAccount, fingerprint, false, XmppAxolotlSession.Trust.fromBoolean(ownKeysToTrust.get(fingerprint)), false, new CompoundButton.OnCheckedChangeListener() { @Override @@ -132,30 +121,44 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate null ); } - for(final String fingerprint : foreignKeysToTrust.keySet()) { - hasForeignKeys = true; - addFingerprintRowWithListeners(foreignKeys, contact.getAccount(), fingerprint, false, - XmppAxolotlSession.Trust.fromBoolean(foreignKeysToTrust.get(fingerprint)), false, - new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - foreignKeysToTrust.put(fingerprint, isChecked); - lockOrUnlockAsNeeded(); - } - }, - null, - null - ); - } - if(hasOwnKeys) { - ownKeysTitle.setText(accountJid.toString()); - ownKeysCard.setVisibility(View.VISIBLE); - } - if(hasForeignKeys) { - foreignKeysTitle.setText(contactJid.toString()); - foreignKeysCard.setVisibility(View.VISIBLE); + synchronized (this.foreignKeysToTrust) { + for (Map.Entry<Jid, Map<String, Boolean>> entry : foreignKeysToTrust.entrySet()) { + hasForeignKeys = true; + final LinearLayout layout = (LinearLayout) getLayoutInflater().inflate(R.layout.keys_card, foreignKeys, false); + final Jid jid = entry.getKey(); + final TextView header = (TextView) layout.findViewById(R.id.foreign_keys_title); + final LinearLayout keysContainer = (LinearLayout) layout.findViewById(R.id.foreign_keys_details); + final TextView informNoKeys = (TextView) layout.findViewById(R.id.no_keys_to_accept); + header.setText(jid.toString()); + final Map<String, Boolean> fingerprints = entry.getValue(); + for (final String fingerprint : fingerprints.keySet()) { + addFingerprintRowWithListeners(keysContainer, mAccount, fingerprint, false, + XmppAxolotlSession.Trust.fromBoolean(fingerprints.get(fingerprint)), false, + new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + fingerprints.put(fingerprint, isChecked); + lockOrUnlockAsNeeded(); + } + }, + null, + null + ); + } + if (fingerprints.size() == 0) { + informNoKeys.setVisibility(View.VISIBLE); + informNoKeys.setText(getString(R.string.no_keys_just_confirm,mAccount.getRoster().getContact(jid).getDisplayName())); + } else { + informNoKeys.setVisibility(View.GONE); + } + foreignKeys.addView(layout); + } } + + ownKeysTitle.setText(mAccount.getJid().toBareJid().toString()); + ownKeysCard.setVisibility(hasOwnKeys ? View.VISIBLE : View.GONE); + foreignKeys.setVisibility(hasForeignKeys ? View.VISIBLE : View.GONE); if(hasPendingKeyFetches()) { setFetching(); lock(); @@ -163,13 +166,15 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate if (!hasForeignKeys && hasNoOtherTrustedKeys()) { keyErrorMessageCard.setVisibility(View.VISIBLE); if (lastFetchReport == AxolotlService.FetchStatus.ERROR - || contact.getAccount().getAxolotlService().fetchMapHasErrors(contact)) { + || mAccount.getAxolotlService().fetchMapHasErrors(contactJids)) { keyErrorMessage.setText(R.string.error_no_keys_to_trust_server_error); } else { keyErrorMessage.setText(R.string.error_no_keys_to_trust); } - ownKeys.removeAllViews(); ownKeysCard.setVisibility(View.GONE); - foreignKeys.removeAllViews(); foreignKeysCard.setVisibility(View.GONE); + ownKeys.removeAllViews(); + ownKeysCard.setVisibility(View.GONE); + foreignKeys.removeAllViews(); + foreignKeys.setVisibility(View.GONE); } lockOrUnlockAsNeeded(); setDone(); @@ -177,46 +182,58 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate } private boolean reloadFingerprints() { + List<Jid> acceptedTargets = mConversation == null ? new ArrayList<Jid>() : mConversation.getAcceptedCryptoTargets(); ownKeysToTrust.clear(); - foreignKeysToTrust.clear(); AxolotlService service = this.mAccount.getAxolotlService(); Set<IdentityKey> ownKeysSet = service.getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED); - Set<IdentityKey> foreignKeysSet = service.getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED, contact); - if (hasNoOtherTrustedKeys() && ownKeysSet.size() == 0) { - foreignKeysSet.addAll(service.getKeysWithTrust(XmppAxolotlSession.Trust.UNTRUSTED, contact)); - } for(final IdentityKey identityKey : ownKeysSet) { if(!ownKeysToTrust.containsKey(identityKey)) { ownKeysToTrust.put(identityKey.getFingerprint().replaceAll("\\s", ""), false); } } - for(final IdentityKey identityKey : foreignKeysSet) { - if(!foreignKeysToTrust.containsKey(identityKey)) { - foreignKeysToTrust.put(identityKey.getFingerprint().replaceAll("\\s", ""), false); + synchronized (this.foreignKeysToTrust) { + foreignKeysToTrust.clear(); + for (Jid jid : contactJids) { + Set<IdentityKey> foreignKeysSet = service.getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED, jid); + if (hasNoOtherTrustedKeys(jid) && ownKeysSet.size() == 0) { + foreignKeysSet.addAll(service.getKeysWithTrust(XmppAxolotlSession.Trust.UNTRUSTED, jid)); + } + Map<String, Boolean> foreignFingerprints = new HashMap<>(); + for (final IdentityKey identityKey : foreignKeysSet) { + if (!foreignFingerprints.containsKey(identityKey)) { + foreignFingerprints.put(identityKey.getFingerprint().replaceAll("\\s", ""), false); + } + } + if (foreignFingerprints.size() > 0 || !acceptedTargets.contains(jid)) { + foreignKeysToTrust.put(jid, foreignFingerprints); + } } } - return ownKeysSet.size() + foreignKeysSet.size() > 0; + return ownKeysSet.size() + foreignKeysToTrust.size() > 0; } @Override public void onBackendConnected() { - if ((accountJid != null) && (contactJid != null)) { - this.mAccount = xmppConnectionService.findAccountByJid(accountJid); - if (this.mAccount == null) { - return; - } - this.contact = this.mAccount.getRoster().getContact(contactJid); + Intent intent = getIntent(); + this.mAccount = extractAccount(intent); + if (this.mAccount != null && intent != null) { + String uuid = intent.getStringExtra("conversation"); + this.mConversation = xmppConnectionService.findConversationByUuid(uuid); reloadFingerprints(); populateView(); } } private boolean hasNoOtherTrustedKeys() { + return mAccount == null || mAccount.getAxolotlService().anyTargetHasNoTrustedKeys(contactJids); + } + + private boolean hasNoOtherTrustedKeys(Jid contact) { return mAccount == null || mAccount.getAxolotlService().getNumTrustedKeys(contact) == 0; } private boolean hasPendingKeyFetches() { - return mAccount != null && contact != null && mAccount.getAxolotlService().hasPendingKeyFetches(mAccount,contact); + return mAccount != null && mAccount.getAxolotlService().hasPendingKeyFetches(mAccount, contactJids); } @@ -262,14 +279,28 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate private void commitTrusts() { for(final String fingerprint :ownKeysToTrust.keySet()) { - contact.getAccount().getAxolotlService().setFingerprintTrust( + mAccount.getAxolotlService().setFingerprintTrust( fingerprint, XmppAxolotlSession.Trust.fromBoolean(ownKeysToTrust.get(fingerprint))); } - for(final String fingerprint:foreignKeysToTrust.keySet()) { - contact.getAccount().getAxolotlService().setFingerprintTrust( - fingerprint, - XmppAxolotlSession.Trust.fromBoolean(foreignKeysToTrust.get(fingerprint))); + List<Jid> acceptedTargets = mConversation == null ? new ArrayList<Jid>() : mConversation.getAcceptedCryptoTargets(); + synchronized (this.foreignKeysToTrust) { + for (Map.Entry<Jid, Map<String, Boolean>> entry : foreignKeysToTrust.entrySet()) { + Jid jid = entry.getKey(); + Map<String, Boolean> value = entry.getValue(); + if (!acceptedTargets.contains(jid)) { + acceptedTargets.add(jid); + } + for (final String fingerprint : value.keySet()) { + mAccount.getAxolotlService().setFingerprintTrust( + fingerprint, + XmppAxolotlSession.Trust.fromBoolean(value.get(fingerprint))); + } + } + } + if (mConversation != null && mConversation.getMode() == Conversation.MODE_MULTI) { + mConversation.setAcceptedCryptoTargets(acceptedTargets); + xmppConnectionService.updateConversation(mConversation); } } @@ -284,11 +315,17 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate } private void lockOrUnlockAsNeeded() { - if (hasNoOtherTrustedKeys() && !foreignKeysToTrust.values().contains(true)){ - lock(); - } else { - unlock(); + synchronized (this.foreignKeysToTrust) { + for (Jid jid : contactJids) { + Map<String, Boolean> fingerprints = foreignKeysToTrust.get(jid); + if (hasNoOtherTrustedKeys(jid) && (fingerprints == null || !fingerprints.values().contains(true))) { + lock(); + return; + } + } } + unlock(); + } private void setDone() { diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/XmppActivity.java b/src/main/java/de/thedevstack/conversationsplus/ui/XmppActivity.java index 1fbb3b1d..cf029ee3 100644 --- a/src/main/java/de/thedevstack/conversationsplus/ui/XmppActivity.java +++ b/src/main/java/de/thedevstack/conversationsplus/ui/XmppActivity.java @@ -41,6 +41,7 @@ import android.os.SystemClock; import android.preference.PreferenceManager; import android.text.InputType; import android.util.DisplayMetrics; +import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.inputmethod.InputMethodManager; @@ -378,7 +379,20 @@ public abstract class XmppActivity extends Activity { } } - protected boolean showBatteryOptimizationWarning() { + @Override + public boolean onCreateOptionsMenu(Menu menu) { + final MenuItem menuSettings = menu.findItem(R.id.action_settings); + final MenuItem menuManageAccounts = menu.findItem(R.id.action_accounts); + if (menuSettings != null) { + menuSettings.setVisible(!Config.LOCK_SETTINGS); + } + if (menuManageAccounts != null) { + menuManageAccounts.setVisible(!Config.LOCK_SETTINGS); + } + return super.onCreateOptionsMenu(menu); + } + + protected boolean isOptimizingBattery() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE); return !pm.isIgnoringBatteryOptimizations(getPackageName()); @@ -786,7 +800,7 @@ public abstract class XmppActivity extends Activity { + "\n\n" + CryptoHelper.prettifyFingerprint(fingerprint.substring(2)) + "\n\n" + getString(R.string.purge_key_desc_part2)); builder.setNegativeButton(getString(R.string.cancel), null); - builder.setPositiveButton(getString(R.string.accept), + builder.setPositiveButton(getString(R.string.purge_key), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/adapter/MessageAdapter.java b/src/main/java/de/thedevstack/conversationsplus/ui/adapter/MessageAdapter.java index 6a141f11..79f3412d 100644 --- a/src/main/java/de/thedevstack/conversationsplus/ui/adapter/MessageAdapter.java +++ b/src/main/java/de/thedevstack/conversationsplus/ui/adapter/MessageAdapter.java @@ -17,6 +17,7 @@ import android.text.Spanned; import android.text.style.ForegroundColorSpan; import android.text.style.RelativeSizeSpan; import android.text.style.StyleSpan; +import android.text.util.Linkify; import android.util.DisplayMetrics; import android.util.Patterns; import android.view.View; @@ -31,9 +32,11 @@ import android.widget.TextView; import android.widget.Toast; import java.lang.ref.WeakReference; +import java.net.URL; import java.util.List; import java.util.concurrent.RejectedExecutionException; import java.util.regex.Matcher; +import java.util.regex.Pattern; import de.thedevstack.conversationsplus.ConversationsPlusPreferences; import de.thedevstack.conversationsplus.R; @@ -57,6 +60,11 @@ public class MessageAdapter extends ArrayAdapter<Message> { private static final int RECEIVED = 1; private static final int STATUS = 2; private static final int NULL = 3; + private static final Pattern XMPP_PATTERN = Pattern + .compile("xmpp\\:(?:(?:[" + + Patterns.GOOD_IRI_CHAR + + "\\;\\/\\?\\@\\&\\=\\#\\~\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])" + + "|(?:\\%[a-fA-F0-9]{2}))+"); private ConversationActivity activity; @@ -322,12 +330,40 @@ public class MessageAdapter extends ArrayAdapter<Message> { } viewHolder.messageBody.setText(span); } - int urlCount = 0; - Matcher matcher = Patterns.WEB_URL.matcher(body); - while (matcher.find()) { - urlCount++; + int patternMatchCount = 0; + int oldAutoLinkMask = viewHolder.messageBody.getAutoLinkMask(); + Matcher matcher = null; + + // first check if we have a match on XMPP_PATTERN so we do not have to check for EMAIL_ADDRESSES + matcher = XMPP_PATTERN.matcher(body); + if ((Linkify.EMAIL_ADDRESSES & oldAutoLinkMask) != 0 && matcher.find()) { + oldAutoLinkMask -= Linkify.EMAIL_ADDRESSES; } - viewHolder.messageBody.setTextIsSelectable(urlCount <= 1); + + // count matches for all patterns + if ((Linkify.WEB_URLS & oldAutoLinkMask) != 0) { + matcher = Patterns.WEB_URL.matcher(body); + while (matcher.find()) { + patternMatchCount++; + } + } + if ((Linkify.EMAIL_ADDRESSES & oldAutoLinkMask) != 0) { + matcher = Patterns.EMAIL_ADDRESS.matcher(body); + while (matcher.find()) { + patternMatchCount++; + } + } + if ((Linkify.PHONE_NUMBERS & oldAutoLinkMask) != 0) { + matcher = Patterns.PHONE.matcher(body); + while (matcher.find()) { + patternMatchCount++; + } + } + + viewHolder.messageBody.setTextIsSelectable(patternMatchCount <= 1); + viewHolder.messageBody.setAutoLinkMask(0); + Linkify.addLinks(viewHolder.messageBody, XMPP_PATTERN, "xmpp"); + viewHolder.messageBody.setAutoLinkMask(oldAutoLinkMask); } else { viewHolder.messageBody.setText(""); viewHolder.messageBody.setTextIsSelectable(false); @@ -574,7 +610,19 @@ public class MessageAdapter extends ArrayAdapter<Message> { if (GeoHelper.isGeoUri(message.getBody())) { displayLocationMessage(viewHolder,message); } else if (message.treatAsDownloadable() == Message.Decision.MUST) { - displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize, UIHelper.getFileDescriptionString(activity, message))); + try { + URL url = new URL(message.getBody()); + displayDownloadableMessage(viewHolder, + message, + activity.getString(R.string.check_x_filesize_on_host, + UIHelper.getFileDescriptionString(activity, message), + url.getHost())); + } catch (Exception e) { + displayDownloadableMessage(viewHolder, + message, + activity.getString(R.string.check_x_filesize, + UIHelper.getFileDescriptionString(activity, message))); + } } else { displayTextMessage(viewHolder, message, darkBackground); } diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/UIHelper.java b/src/main/java/de/thedevstack/conversationsplus/utils/UIHelper.java index e1915e99..44dc4774 100644 --- a/src/main/java/de/thedevstack/conversationsplus/utils/UIHelper.java +++ b/src/main/java/de/thedevstack/conversationsplus/utils/UIHelper.java @@ -183,10 +183,13 @@ public class UIHelper { UIHelper.getMessageDisplayName(message) + " "), false); } else if (GeoHelper.isGeoUri(message.getBody())) { if (message.getStatus() == Message.STATUS_RECEIVED) { - return new Pair<>(context.getString(R.string.received_location),true); + return new Pair<>(context.getString(R.string.received_location), true); } else { return new Pair<>(context.getString(R.string.location), true); } + } else if (message.treatAsDownloadable() == Message.Decision.MUST) { + return new Pair<>(context.getString(R.string.x_file_offered_for_download, + getFileDescriptionString(context,message)),true); } else{ return new Pair<>(message.getBody(), false); } diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/jingle/JingleConnection.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/jingle/JingleConnection.java index de5f3ab1..b57653d4 100644 --- a/src/main/java/de/thedevstack/conversationsplus/xmpp/jingle/JingleConnection.java +++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/jingle/JingleConnection.java @@ -221,7 +221,7 @@ public class JingleConnection implements Transferable { public void init(final Message message) { if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) { Conversation conversation = message.getConversation(); - conversation.getAccount().getAxolotlService().prepareKeyTransportMessage(conversation.getContact(), new OnMessageCreatedCallback() { + conversation.getAccount().getAxolotlService().prepareKeyTransportMessage(conversation, new OnMessageCreatedCallback() { @Override public void run(XmppAxolotlMessage xmppAxolotlMessage) { if (xmppAxolotlMessage != null) { @@ -326,14 +326,14 @@ public class JingleConnection implements Transferable { String[] filename = fileNameElement.getContent() .toLowerCase(Locale.US).toLowerCase().split("\\."); String extension = filename[filename.length - 1]; - if (Arrays.asList(VALID_IMAGE_EXTENSIONS).contains(extension)) { + if (VALID_IMAGE_EXTENSIONS.contains(extension)) { message.setType(Message.TYPE_IMAGE); message.setRelativeFilePath(message.getUuid()+"."+extension); - } else if (Arrays.asList(VALID_CRYPTO_EXTENSIONS).contains( + } else if (VALID_CRYPTO_EXTENSIONS.contains( filename[filename.length - 1])) { if (filename.length == 3) { extension = filename[filename.length - 2]; - if (Arrays.asList(VALID_IMAGE_EXTENSIONS).contains(extension)) { + if (VALID_IMAGE_EXTENSIONS.contains(extension)) { message.setType(Message.TYPE_IMAGE); message.setRelativeFilePath(message.getUuid()+"."+extension); } else { diff --git a/src/main/res/drawable-hdpi/ic_crop_white_24dp.png b/src/main/res/drawable-hdpi/ic_crop_white_24dp.png Binary files differnew file mode 100644 index 00000000..6cbb87ce --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_crop_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_crop_white_24dp.png b/src/main/res/drawable-mdpi/ic_crop_white_24dp.png Binary files differnew file mode 100644 index 00000000..d606c072 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_crop_white_24dp.png diff --git a/src/main/res/drawable-xhdpi/ic_crop_white_24dp.png b/src/main/res/drawable-xhdpi/ic_crop_white_24dp.png Binary files differnew file mode 100644 index 00000000..983af77f --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_crop_white_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_crop_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_crop_white_24dp.png Binary files differnew file mode 100644 index 00000000..8c9eecb9 --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_crop_white_24dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_crop_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_crop_white_24dp.png Binary files differnew file mode 100644 index 00000000..35d5434a --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_crop_white_24dp.png diff --git a/src/main/res/layout/activity_trust_keys.xml b/src/main/res/layout/activity_trust_keys.xml index 6f8a1bc9..d7bb7628 100644 --- a/src/main/res/layout/activity_trust_keys.xml +++ b/src/main/res/layout/activity_trust_keys.xml @@ -80,34 +80,11 @@ </LinearLayout> <LinearLayout - android:id="@+id/foreign_keys_card" + android:id="@+id/foreign_keys" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:layout_marginLeft="@dimen/activity_horizontal_margin" - android:layout_marginRight="@dimen/activity_horizontal_margin" - android:layout_marginTop="@dimen/activity_vertical_margin" - android:layout_marginBottom="@dimen/activity_vertical_margin" - android:background="@drawable/infocard_border" - android:orientation="vertical" - android:padding="@dimen/infocard_padding" - android:visibility="gone"> - - <TextView - android:id="@+id/foreign_keys_title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textColor="@color/black87" - android:textSize="?attr/TextSizeHeadline" - android:textStyle="bold"/> - - <LinearLayout - android:id="@+id/foreign_keys_details" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:divider="?android:dividerHorizontal" - android:showDividers="middle" - android:orientation="vertical"> - </LinearLayout> + android:visibility="gone" + android:orientation="vertical"> </LinearLayout> diff --git a/src/main/res/layout/keys_card.xml b/src/main/res/layout/keys_card.xml new file mode 100644 index 00000000..d3271d1b --- /dev/null +++ b/src/main/res/layout/keys_card.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout android:id="@+id/foreign_keys_card" + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/activity_vertical_margin" + android:layout_marginLeft="@dimen/activity_horizontal_margin" + android:layout_marginRight="@dimen/activity_horizontal_margin" + android:layout_marginTop="@dimen/activity_vertical_margin" + android:background="@drawable/infocard_border" + android:orientation="vertical" + android:padding="@dimen/infocard_padding"> + + <TextView + android:id="@+id/foreign_keys_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textColor="@color/black87" + android:textSize="?attr/TextSizeHeadline" + android:textStyle="bold"/> + + <LinearLayout + android:id="@+id/foreign_keys_details" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:divider="?android:dividerHorizontal" + android:orientation="vertical" + android:showDividers="middle"> + </LinearLayout> + + <TextView + android:layout_marginTop="8dp" + android:id="@+id/no_keys_to_accept" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textColor="@color/black87" + android:text="@string/no_keys_just_confirm" + android:textSize="?attr/TextSizeBody"/> +</LinearLayout>
\ No newline at end of file diff --git a/src/main/res/menu/publish_avatar.xml b/src/main/res/menu/publish_avatar.xml new file mode 100644 index 00000000..1bffb296 --- /dev/null +++ b/src/main/res/menu/publish_avatar.xml @@ -0,0 +1,8 @@ +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + + <item + android:id="@+id/action_crop_image" + android:showAsAction="always" + android:icon="@drawable/ic_crop_white_24dp" + android:title="@string/select_image_and_crop"/> +</menu>
\ No newline at end of file diff --git a/src/main/res/values-ar/strings.xml b/src/main/res/values-ar/strings.xml index 2e59983c..e3a71f69 100644 --- a/src/main/res/values-ar/strings.xml +++ b/src/main/res/values-ar/strings.xml @@ -176,7 +176,6 @@ <string name="bookmark_already_exists">موجوده بالمفضلة سابقا</string> <string name="you">انت</string> <string name="action_edit_subject">تعديل موضوع الغرفة</string> - <string name="conference_not_found">الغرفة غير متاحه .. تأكد من عنوان الغرفة</string> <string name="leave">غادر</string> <string name="contact_added_you">جهة اتصال أضافتك </string> <string name="publish">نشر</string> diff --git a/src/main/res/values-bg/strings.xml b/src/main/res/values-bg/strings.xml index 62e390ab..0ff4b8af 100644 --- a/src/main/res/values-bg/strings.xml +++ b/src/main/res/values-bg/strings.xml @@ -96,7 +96,6 @@ <string name="contact_has_no_pgp_key">Conversations не може да шифрова съобщенията Ви, тъй като Вашият контакт не обявява публичния си ключ.\n\n<small>Моля, помолете го/я да инсталира и настрои OpenPGP.</small></string> <string name="no_pgp_keys">Не са открити OpenPGP ключове</string> <string name="contacts_have_no_pgp_keys">Conversations не може да шифрова съобщенията Ви, тъй като Вашите контакти не обявяват публичните си ключове.\n\n<small>Моля, помолете го да инсталират и настроят OpenPGP.</small></string> - <string name="encrypted_message_received"><i>Получено е шифровано съобщение. Докоснете, за да го дешифровате.</i></string> <string name="pref_general">Общи</string> <string name="pref_xmpp_resource">XMPP ресурс</string> <string name="pref_xmpp_resource_summary">Името, с което се определя този клиент</string> @@ -240,8 +239,7 @@ <string name="bookmark_already_exists">Вече съществува такава отметка</string> <string name="you">Вие</string> <string name="action_edit_subject">Редактиране на темата на беседата</string> - <string name="conference_not_found">Беседата не е открита</string> - <string name="conference_unknown_error">Беше получена непозната грешка</string> + <string name="joining_conference">Присъединяване към беседата…</string> <string name="leave">Напускане</string> <string name="contact_added_you">Контактът е добавен във Вашия списък от контакти</string> <string name="add_back">Добавяне обратно</string> @@ -308,10 +306,13 @@ <string name="conference_banned">Достъпът Ви до тази беседа беше забранен</string> <string name="conference_members_only">Тази беседа е само за членове</string> <string name="conference_kicked">Бяхте изритан от тази конференция</string> + <string name="conference_shutdown">Беседата приключи</string> + <string name="conference_unknown_error">Вече не участвате в тази беседа</string> <string name="using_account">използвайки профила %s</string> <string name="checking_x">Проверяване на %s на HTTP сървъра</string> <string name="not_connected_try_again">Не сте свързани. Опитайте отново по-късно</string> <string name="check_x_filesize">Проверете размера на %s</string> + <string name="check_x_filesize_on_host">Проверете размера на %1$s на %2$s</string> <string name="message_options">Настройки за съобщенята</string> <string name="copy_text">Копиране на текста</string> <string name="copy_original_url">Копиране на оригиналния адрес</string> @@ -490,6 +491,8 @@ <string name="username">Потребителско име</string> <string name="username_hint">Потребителско име</string> <string name="invalid_username">Това не е правилно потребителско име</string> + <string name="conference_name">Име на беседата</string> + <string name="invalid_conference_name">Това не е правилно име на беседа</string> <string name="download_failed_server_not_found">Неуспешно сваляне: Сървърът не е открит</string> <string name="download_failed_file_not_found">Неуспешно сваляне: Файлът не е открит</string> <string name="download_failed_could_not_connect">Неуспешно сваляне: Неуспешна връзка със сървъра</string> @@ -500,6 +503,8 @@ <string name="pref_away_when_screen_off_summary">Преминава в състояние „отсъстващ“ когато екранът бъде изключен</string> <string name="pref_xa_on_silent_mode">Недостъпен, в тих режим</string> <string name="pref_xa_on_silent_mode_summary">Преминава в състояние „недостъпен“ когато устройството е в тих режим</string> + <string name="pref_treat_vibrate_as_silent">Тих режим при режим на вибриране</string> + <string name="pref_treat_vibrate_as_silent_summary">Преминава в състояние „недостъпен“ когато устройството е в режим на вибриране.</string> <string name="pref_show_connection_options">Разширени настройки за връзката</string> <string name="pref_show_connection_options_summary">Показване на настройките за сървър и порт при установка на профил</string> <string name="hostname_example">xmpp.example.com</string> @@ -546,6 +551,8 @@ <string name="notify_only_when_highlighted">Известяване само на осветените</string> <string name="notify_never">Известията са изключени</string> <string name="notify_paused">Известията са спрени временно</string> + <string name="pref_picture_compression">Компресиране на снимките</string> + <string name="pref_picture_compression_summary">Преоразмеряване и компресиране на снимките</string> <string name="always">Винаги</string> <string name="automatically">Автоматично</string> <string name="battery_optimizations_enabled">Оптимизациите за използв. на батерията са вкл.</string> @@ -555,4 +562,7 @@ <string name="selection_too_large">Избраната област е твърде голяма</string> <string name="no_accounts">(Няма активирани профили)</string> <string name="this_field_is_required">Това поле е задължително</string> + <string name="no_keys_just_confirm">Вие вече имате доверие на този контакт. Избирайки „готово“, Вие просто потвърждавате, че %s е част от тази беседа.</string> + <string name="select_image_and_crop">Изберете изображение и изрежете</string> + <string name="this_account_is_disabled">Вие сте деактивирали този профил</string> </resources> diff --git a/src/main/res/values-ca/strings.xml b/src/main/res/values-ca/strings.xml index 6f48ab63..1c06d9ab 100644 --- a/src/main/res/values-ca/strings.xml +++ b/src/main/res/values-ca/strings.xml @@ -211,7 +211,6 @@ <string name="bookmark_already_exists">Aquesta llista de favorits ja existeix</string> <string name="you">Tu</string> <string name="action_edit_subject">Editar el tema de la sala</string> - <string name="conference_not_found">Sala no trobada</string> <string name="leave">Sortir</string> <string name="contact_added_you">Afegir un contacte a la llista de contactes</string> <string name="add_back">Afegir nou</string> diff --git a/src/main/res/values-cs/strings.xml b/src/main/res/values-cs/strings.xml index 91fa2464..88d0b049 100644 --- a/src/main/res/values-cs/strings.xml +++ b/src/main/res/values-cs/strings.xml @@ -96,7 +96,6 @@ <string name="contact_has_no_pgp_key">Není možné zašifrovat zprávu v aplikaci Konverzace, protože druhá strana neoznamuje svůj veřejný klíč.\n\n<small>Požádejte svůj kontakt ať si nastaví OpenPGP.</small></string> <string name="no_pgp_keys">Nebyly nalezeny žádné OpenPGP klíče</string> <string name="contacts_have_no_pgp_keys">Není možné zašifrovat zprávy v aplikaci Konverzace, protože kontakty neoznamují svůj veřejný klíč.\n\n<small>Požádejte své kontakty ať si nastaví OpenPGP.</small></string> - <string name="encrypted_message_received"><i>Obdržena šifrovaná zpráva. Dešifrovat dotykem.</i></string> <string name="pref_general">Obecné</string> <string name="pref_xmpp_resource">XMPP zdroj</string> <string name="pref_xmpp_resource_summary">Jméno se kterým se tento klient identifikuje</string> @@ -240,8 +239,7 @@ <string name="bookmark_already_exists">Tato záložka již existuje</string> <string name="you">Já</string> <string name="action_edit_subject">Upravit jméno konference</string> - <string name="conference_not_found">Konference nenalezena</string> - <string name="conference_unknown_error">Došlo k neznámé chybě</string> + <string name="joining_conference">Připojování ke konferenci...</string> <string name="leave">Odejít</string> <string name="contact_added_you">Kontakt přidán do seznamu</string> <string name="add_back">Opět přidat</string> @@ -308,6 +306,8 @@ <string name="conference_banned">Vstup do konference byl zakázán</string> <string name="conference_members_only">Tato konference je pouze pro členy</string> <string name="conference_kicked">Vykopli tě z této konference</string> + <string name="conference_shutdown">Konference byla ukončena</string> + <string name="conference_unknown_error">Již nejste v této konferenci</string> <string name="using_account">za použití účtu %s</string> <string name="checking_x">Ověřuji %s na HTTP hostiteli</string> <string name="not_connected_try_again">Bez připojení. Zkus znovu později</string> @@ -492,6 +492,8 @@ <string name="username">Uživatelské jméno</string> <string name="username_hint">Uživatelské jméno</string> <string name="invalid_username">Toto není platné uživatelské jméno</string> + <string name="conference_name">Jméno konferenc</string> + <string name="invalid_conference_name">Toto není platné jméno konference</string> <string name="download_failed_server_not_found">Stahování selhalo: Server nenalezen</string> <string name="download_failed_file_not_found">Stahování selhalo: Soubor nenalezen</string> <string name="download_failed_could_not_connect">Stahování selhalo: Nelze se připojit k hostu</string> diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index 87cacb45..aff8affd 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -96,7 +96,6 @@ <string name="contact_has_no_pgp_key">Conversations ist nicht in der Lage, deine Nachrichten zu verschlüsseln, weil dein Kontakt seinen oder ihren Schlüssel nicht preisgibt.\n\n<small>Bitte sag deinem Kontakt, er oder sie möge OpenPGP einrichten.</small></string> <string name="no_pgp_keys">Keine OpenPGP-Schlüssel gefunden</string> <string name="contacts_have_no_pgp_keys">Conversations ist nicht in der Lage, deine Nachrichten zu verschlüsseln, weil deine Kontakte ihre Schlüssel nicht preisgeben.\n\n<small>Bitte sage deinen Kontakten, sie mögen OpenPGP einrichten.</small></string> - <string name="encrypted_message_received"><i>Verschlüsselte Nachricht erhalten. Antippen zum Entschlüsseln.</i></string> <string name="pref_general">Allgemeines</string> <string name="pref_xmpp_resource">XMPP-Ressource</string> <string name="pref_xmpp_resource_summary">Der Name, mit dem sich der Client selbst identifiziert</string> @@ -117,7 +116,7 @@ <string name="pref_sound_summary">Benachrichtigungston wiedergeben</string> <string name="pref_notification_grace_period">Gnadenfrist</string> <string name="pref_notification_grace_period_summary">Deaktiviere Benachrichtigungen für eine kurze Zeit nach Erhalt einer Nachricht, die von einem anderen deiner Clients kommt.</string> - <string name="pref_advanced_options">Erweiterte Optionen</string> + <string name="pref_advanced_options">Erweitert</string> <string name="pref_never_send_crash">Niemals Absturzberichte senden</string> <string name="pref_never_send_crash_summary">Wenn du Absturzberichte einschickst, hilfst du Conversations stetig zu verbessern</string> <string name="pref_confirm_messages">Lese- und Empfangsbestätigung senden</string> @@ -171,7 +170,7 @@ <string name="mgmt_account_disable">Vorübergehend abschalten</string> <string name="mgmt_account_publish_avatar">Avatar veröffentlichen</string> <string name="mgmt_account_publish_pgp">Öffentlichen OpenPGP-Schlüssel veröffentlichen</string> - <string name="mgmt_account_enable">Anschalten</string> + <string name="mgmt_account_enable">Aktivieren </string> <string name="mgmt_account_are_you_sure">Bist du dir sicher?</string> <string name="mgmt_account_delete_confirm_text">Wenn du dein Konto löschst, gehen alle Gesprächsverläufe verloren</string> <string name="attach_record_voice">Sprache aufzeichnen</string> @@ -228,7 +227,7 @@ <string name="other_devices">Andere Geräte</string> <string name="trust_omemo_fingerprints">OMEMO-Fingerabdruck vertrauen</string> <string name="fetching_keys">Schlüssel werden abgerufen …</string> - <string name="done">Erledigt</string> + <string name="done">Fertig</string> <string name="verify">Überprüfen</string> <string name="decrypt">Entschlüsseln</string> <string name="conferences">Konferenzen</string> @@ -251,8 +250,7 @@ <string name="bookmark_already_exists">Die Konferenz befindet sich bereits auf deiner Kontaktliste</string> <string name="you">Du</string> <string name="action_edit_subject">Konferenz-Thema bearbeiten</string> - <string name="conference_not_found">Konferenz nicht gefunden</string> - <string name="conference_unknown_error">Unbekannter Fehler</string> + <string name="joining_conference">Konferenz wird betreten…</string> <string name="leave">Verlassen</string> <string name="contact_added_you">Der Kontakt hat dich zur Kontaktliste hinzugefügt</string> <string name="add_back">Auch hinzufügen</string> @@ -312,17 +310,20 @@ <string name="pref_expert_options_other">Sonstiges</string> <string name="pref_conference_name">Konferenz-Name</string> <string name="pref_conference_name_summary">Konferenz-Thema statt Raum-JID als Namen verwenden</string> - <string name="pref_autojoin">Unterhaltung automatisch beitreten</string> + <string name="pref_autojoin">Konferenzen automatisch beitreten</string> <string name="pref_autojoin_summary">Autojoin-Flag in Konferenzlesezeichen beachten</string> <string name="toast_message_otr_fingerprint">OTR-Fingerabdruck in die Zwischenablage kopiert!</string> <string name="toast_message_omemo_fingerprint">OMEMO-Fingerabdruck in die Zwischenablage kopiert!</string> <string name="conference_banned">Du wurdest von der Konferenz ausgeschlossen</string> <string name="conference_members_only">Die Konferenz ist nur für Mitglieder</string> <string name="conference_kicked">Du wurdest aus der Konferenz geworfen</string> + <string name="conference_shutdown">Konferenz wurde geschlossen</string> + <string name="conference_unknown_error">Du bist nicht länger in dieser Konferenz</string> <string name="using_account">Verwendetes Konto: %s</string> <string name="checking_x">%s auf HTTP-Host wird überprüft</string> <string name="not_connected_try_again">Nicht verbunden, bitte später versuchen</string> <string name="check_x_filesize">%s-Größe prüfen</string> + <string name="check_x_filesize_on_host">%1$s-Größe auf %2$s prüfen</string> <string name="message_options">Nachrichtenoptionen</string> <string name="copy_text">Text kopieren</string> <string name="copy_original_url">Original-URL kopieren</string> @@ -412,7 +413,7 @@ <string name="new_password">Neues Passwort</string> <string name="password_should_not_be_empty">Das Passwort darf nicht leer sein</string> <string name="password_should_not_contain_only_spaces">Das Passwort darf nicht nur aus Leerzeichen bestehen</string> - <string name="enable_all_accounts">Alle Konten anschalten</string> + <string name="enable_all_accounts">Alle Konten aktivieren</string> <string name="disable_all_accounts">Alle Konten abschalten</string> <string name="perform_action_with">Aktion durchführen mit</string> <string name="no_affiliation">Keine Zugehörigkeit</string> @@ -550,6 +551,8 @@ <string name="username">Benutzername</string> <string name="username_hint">Benutzername</string> <string name="invalid_username">Ungültiger Benutzername</string> + <string name="conference_name">Konferenz-Name</string> + <string name="invalid_conference_name">Dies ist kein gültiger Konferenz-Name</string> <string name="download_failed_server_not_found">Download fehlgeschlagen: Server nicht gefunden</string> <string name="download_failed_file_not_found">Download fehlgeschlagen: Datei nicht gefunden</string> <string name="download_failed_could_not_connect">Download fehlgeschlagen: keine Verbindung zum Host</string> @@ -559,7 +562,9 @@ <string name="pref_away_when_screen_off">Abwesend bei abgeschaltetem Bildschirm</string> <string name="pref_away_when_screen_off_summary">Setzt deinen Status auf \"abwesend\", solange dein Bildschirm abgeschaltet ist</string> <string name="pref_xa_on_silent_mode">Nicht verfügbar bei Stummschaltung</string> - <string name="pref_xa_on_silent_mode_summary">Setzt deinen Status auf \"nicht verfügbar\", solange dein Telefon lautlos ist</string> + <string name="pref_xa_on_silent_mode_summary">Setzt deinen Status auf \"nicht verfügbar\", solange dein Gerät lautlos ist</string> + <string name="pref_treat_vibrate_as_silent">Behandle Vibration als Stumm</string> + <string name="pref_treat_vibrate_as_silent_summary">Setze deinen Status auf \"nicht verfügbar\" solange dein Gerät auf \"vibrieren\" ist.</string> <string name="pref_show_connection_options">Erweiterte Verbindungs-Optionen</string> <string name="pref_show_connection_options_summary">Hostname- und Port-Optionen bei Kontoeinrichtung anzeigen</string> <string name="hostname_example">xmpp.domain.de</string> @@ -616,4 +621,7 @@ <string name="no_accounts">(Keine aktivierten Konten)</string> <string name="this_field_is_required">Dieses Feld ist erforderlich</string> <string name="retry_decryption">Entschlüsselung nochmal versuchen</string> + <string name="no_keys_just_confirm">Du vertraust diesem Kontakt bereits. In dem du \'Fertig\' auswählst bestätigst Du, dass %s Teil dieser Konferenz ist.</string> + <string name="select_image_and_crop">Bild auswählen und zuschneiden</string> + <string name="this_account_is_disabled">Du hast diesen Account deaktiviert</string> </resources> diff --git a/src/main/res/values-el/strings.xml b/src/main/res/values-el/strings.xml index 0bdc6a5e..804a2146 100644 --- a/src/main/res/values-el/strings.xml +++ b/src/main/res/values-el/strings.xml @@ -211,7 +211,6 @@ <string name="bookmark_already_exists">Αυτός ο σελιδοδείκτης υπάρχει ήδη</string> <string name="you">Εσείς</string> <string name="action_edit_subject">Επεξεργασία θέματος συνδιάσκεψης</string> - <string name="conference_not_found">Η συνδιάσκεψη δεν βρέθηκε</string> <string name="leave">Έξοδος</string> <string name="contact_added_you">Η επαφή σας πρόσθεσε στην λίστα επαφών</string> <string name="add_back">Προσθήκη επίσης</string> diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index 18d9218d..fb088efc 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -96,7 +96,6 @@ <string name="contact_has_no_pgp_key">Conversations no ha podido cifrar tus mensajes porque tu contacto no está anunciando su clave publica.\n\n<small>Por favor, pide a tu contacto que configure OpenPGP.</small></string> <string name="no_pgp_keys">Claves OpenPGP no encontradas</string> <string name="contacts_have_no_pgp_keys">Conversations no ha podido cifrar tus mensajes porque tus contactos no están anunciando su clave publica.\n\n<small>Por favor, pide a tus contactos que configuren OpenPGP.</small></string> - <string name="encrypted_message_received"><i>Mensaje cifrado recibido. Pulsa para descifrar.</i></string> <string name="pref_general">General</string> <string name="pref_xmpp_resource">Recurso</string> <string name="pref_xmpp_resource_summary">El nombre que identifica el cliente que estás utilizando</string> @@ -240,8 +239,7 @@ <string name="bookmark_already_exists">Este marcador ya existe</string> <string name="you">Tú</string> <string name="action_edit_subject">Editar asunto de la conversación</string> - <string name="conference_not_found">Conversación no encontrada</string> - <string name="conference_unknown_error">Error desconocido</string> + <string name="joining_conference">Uniéndose a conversación...</string> <string name="leave">Salir</string> <string name="contact_added_you">El contacto te ha añadido a su lista de contactos</string> <string name="add_back">Añadir contacto</string> @@ -308,10 +306,13 @@ <string name="conference_banned">Tu entrada a esta conversación ha sido prohibida</string> <string name="conference_members_only">Esta conversación es solo para miembros</string> <string name="conference_kicked">Has sido expulsado de esta conversación</string> + <string name="conference_shutdown">La conversación ha sido cerrada</string> + <string name="conference_unknown_error">Ya no estás dentro de esta conversación</string> <string name="using_account">Usando cuenta %s</string> <string name="checking_x">Comprobando %s en servidor HTTP</string> <string name="not_connected_try_again">No estás conectado. Inténtalo más tarde</string> <string name="check_x_filesize">Comprobar tamaño de %s</string> + <string name="check_x_filesize_on_host">Comprobar tamaño de %1$s en %2$s</string> <string name="message_options">Opciones de mensaje</string> <string name="copy_text">Copiar texto</string> <string name="copy_original_url">Copiar URL original</string> @@ -490,6 +491,8 @@ <string name="username">Usuario</string> <string name="username_hint">Usuario</string> <string name="invalid_username">Esto no es un usuario válido</string> + <string name="conference_name">Nombre de la conversación</string> + <string name="invalid_conference_name">Nombre de conversación no válido</string> <string name="download_failed_server_not_found">Error al descargar: Servidor no encontrado</string> <string name="download_failed_file_not_found">Error al descargar: Archivo no encontrado</string> <string name="download_failed_could_not_connect">Error al descargar: No se ha podido conectar con el servidor</string> @@ -500,6 +503,8 @@ <string name="pref_away_when_screen_off_summary">Cambia tu estado a ausente cuando la pantalla está apagada</string> <string name="pref_xa_on_silent_mode">No disponible en modo silencio</string> <string name="pref_xa_on_silent_mode_summary">Cambia tu estado a no disponible cuando el dispositivo está en modo silencio</string> + <string name="pref_treat_vibrate_as_silent">Modo vibración como modo silencio</string> + <string name="pref_treat_vibrate_as_silent_summary">Cambia tu estado a no disponible cuando el dispositivo está en modo vibración</string> <string name="pref_show_connection_options">Opciones de conexión</string> <string name="pref_show_connection_options_summary">Mostrar el hostname y el puerto cuando se está creando una cuenta</string> <string name="hostname_example">xmpp.ejemplo.com</string> @@ -555,4 +560,7 @@ <string name="selection_too_large">El área seleccionada es demasiado grande</string> <string name="no_accounts">(No hay cuentas activas)</string> <string name="this_field_is_required">Este campo es requerido</string> + <string name="no_keys_just_confirm">Ya confías en este contacto. Seleccionando \'hecho\', estás confirmando que %s es parte de esta conversación.</string> + <string name="select_image_and_crop">Seleccionar imagen y recortar</string> + <string name="this_account_is_disabled">Has deshabilitado esta cuenta</string> </resources> diff --git a/src/main/res/values-eu/strings.xml b/src/main/res/values-eu/strings.xml index d27c304c..f04fd284 100644 --- a/src/main/res/values-eu/strings.xml +++ b/src/main/res/values-eu/strings.xml @@ -96,7 +96,6 @@ <string name="contact_has_no_pgp_key">Conversations ez da zure mezuak enkriptatzeko gai zure kontaktua bere gako publikoa jakinarazten ez dagoelako.\n\n<small>Mesedez eskatu ezaiozu zure kontaktuari openPGP konfigura dezan.</small></string> <string name="no_pgp_keys">Ez da OpenPGP gakorik aurkitu</string> <string name="contacts_have_no_pgp_keys">Conversations ez da zure mezuak enkriptatzeko gai zure kontaktuak haien gako publikoa jakinarazten ez daudelako.\n\n<small>Mesedez eskatu ezaiezu zure kontakuei OpenPGP konfigura dezaten.</small></string> - <string name="encrypted_message_received"><i>Enkriptatutako mezua jaso da. Ukitu desenkriptatzeko.</i></string> <string name="pref_general">Orokorrak</string> <string name="pref_xmpp_resource">XMPP baliabidea</string> <string name="pref_xmpp_resource_summary">Bezero honek bere burua aurkezteko erabiltzen duen izena</string> @@ -240,8 +239,7 @@ <string name="bookmark_already_exists">Laster-marka hau existitzen da dagoeneko</string> <string name="you">Zu</string> <string name="action_edit_subject">Konferentziaren gaia editatu</string> - <string name="conference_not_found">Konferentzia ez da aurkitu</string> - <string name="conference_unknown_error">Akats ezezaguna jaso da</string> + <string name="joining_conference">Konferentziara batzen...</string> <string name="leave">Alde egin</string> <string name="contact_added_you">Kontaktuak bere zerrendara gehitu zaitu</string> <string name="add_back">Bera gehitu</string> @@ -308,6 +306,8 @@ <string name="conference_banned">Konferentzia honetara sartzea debekatuta duzu</string> <string name="conference_members_only">Konferentzia hau kideentzat da soilik</string> <string name="conference_kicked">Konferentzia honetatik kanporatua izan zara</string> + <string name="conference_shutdown">Konferentzia itzali egin da</string> + <string name="conference_unknown_error">Ez zaude dagoeneko konferentzia honetan</string> <string name="using_account">%s kontua erabiltzen</string> <string name="checking_x">%s egiaztatzen HTTP ostalarian</string> <string name="not_connected_try_again">Ez zaude konektatuta. Saiatu beranduago berriz</string> @@ -490,6 +490,8 @@ <string name="username">Erabiltzaile izena</string> <string name="username_hint">Erabiltzaile izena</string> <string name="invalid_username">Hau ez da erabiltzaile izen baliodun bat</string> + <string name="conference_name">Konferentziaren izena</string> + <string name="invalid_conference_name">Hau ez da konferentzia batentzako izen balioduna</string> <string name="download_failed_server_not_found">Deskargak huts egin du: zerbitzaria ez da aurkitu</string> <string name="download_failed_file_not_found">Deskargak huts egin du: fitxategia ez da aurkitu</string> <string name="download_failed_could_not_connect">Deskargak huts egin du: ezin izan da ostalarira konektatu</string> diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml index 2a689797..d93a0224 100644 --- a/src/main/res/values-fr/strings.xml +++ b/src/main/res/values-fr/strings.xml @@ -79,8 +79,8 @@ <string name="choose_presence">Choisir le status de présence</string> <string name="send_unencrypted_message">Envoyer un message non chiffré</string> <string name="send_otr_message">Envoyer un message chiffré avec OTR</string> - <string name="send_omemo_message">Envoyé un message chiffré avec OMEMO</string> - <string name="send_omemo_x509_message">Envoyé un message chiffré avec \\OMEMO</string> + <string name="send_omemo_message">Envoyer un message chiffré avec OMEMO</string> + <string name="send_omemo_x509_message">Envoyer un message chiffré avec \\OMEMO</string> <string name="send_pgp_message">Envoyer un message chiffré avec OpenPGP</string> <string name="your_nick_has_been_changed">Votre identifiant a été changé</string> <string name="send_unencrypted">Envoyer en clair</string> @@ -96,7 +96,6 @@ <string name="contact_has_no_pgp_key">Conversations ne peut pas chiffrer vos messages car votre contact n\'a pas communiqué sa clef publique.\n\n<small>Demandez-lui de configurer OpenPGP.</small></string> <string name="no_pgp_keys">Aucune clef OpenPGP n\'a été trouvée.</string> <string name="contacts_have_no_pgp_keys">Conversations ne peut pas chiffrer votre message car vos contacts ne communiquent pas leur clef publique.\n\n<small>Demandez-leur de configurer OpenPGP.</small></string> - <string name="encrypted_message_received"><i>Message chiffré reçu. Appuyez pour déchiffrer.</i></string> <string name="pref_general">Général</string> <string name="pref_xmpp_resource">Ressource XMPP</string> <string name="pref_xmpp_resource_summary">Nom utilisé par ce client pour s\'identifier</string> @@ -171,7 +170,7 @@ <string name="password">Mot de passe</string> <string name="confirm_password">Confirmer le mot de passe</string> <string name="passwords_do_not_match">Les deux mots de passe ne correspondent pas.</string> - <string name="invalid_jid">Cet identifiant n\'est pas valide.</string> + <string name="invalid_jid">Cet identifiant n\'est pas valide</string> <string name="error_out_of_memory">Plus de mémoire disponible. L\'image est trop volumineuse.</string> <string name="add_phone_book_text">Voulez-vous ajouter %s à votre carnet d\'adresses ?</string> <string name="contact_status_online">En ligne</string> @@ -240,8 +239,7 @@ <string name="bookmark_already_exists">Le favori existe déjà</string> <string name="you">Vous</string> <string name="action_edit_subject">Modifier le sujet de la conférence</string> - <string name="conference_not_found">Impossible de trouver la conférence</string> - <string name="conference_unknown_error">Erreur inconnue reçue</string> + <string name="joining_conference">Connexion à la conférence...</string> <string name="leave">Partir</string> <string name="contact_added_you">Votre correspondant vous a ajouté dans sa liste de contacts</string> <string name="add_back">Ré-ajouter</string> @@ -308,10 +306,13 @@ <string name="conference_banned">Vous êtes banni de cette conférence</string> <string name="conference_members_only">Cette conférence est réservée aux membres</string> <string name="conference_kicked">Vous avez été éjecté de cette conférence</string> + <string name="conference_shutdown">La conférence a été fermée</string> + <string name="conference_unknown_error">Vous n\'êtes plus dans cette conférence</string> <string name="using_account">avec le compte %s</string> <string name="checking_x">Vérification de %s sur l\'hôte HTTP</string> <string name="not_connected_try_again">Vous n\'êtes pas connecté. Essayez plus tard.</string> <string name="check_x_filesize">Vérification de la taille de %s</string> + <string name="check_x_filesize_on_host">Vérification de la taille de %1$s sur %2$s</string> <string name="message_options">Options du message</string> <string name="copy_text">Copier le texte</string> <string name="copy_original_url">Copier l\'URL</string> @@ -489,7 +490,9 @@ <string name="user_has_left_conference">%s a quitté la conférence !</string> <string name="username">Identifiant</string> <string name="username_hint">Identifiant</string> - <string name="invalid_username">Ce n\'est pas un identifiant valide</string> + <string name="invalid_username">Cet identifiant n\'est pas valide</string> + <string name="conference_name">Nom de la conférence </string> + <string name="invalid_conference_name">Ce nom de conférence n\'est pas valide</string> <string name="download_failed_server_not_found">Échec du téléchargement : impossible de trouver le serveur</string> <string name="download_failed_file_not_found">Échec du téléchargement : impossible de trouver le fichier</string> <string name="download_failed_could_not_connect">Échec du téléchargement : impossible de se connecter à l\'hôte</string> @@ -500,6 +503,8 @@ <string name="pref_away_when_screen_off_summary">Marquer cette ressource comme absente quand l\'écran est éteint.</string> <string name="pref_xa_on_silent_mode">Indisponible en mode silencieux</string> <string name="pref_xa_on_silent_mode_summary">Marque cette ressource comme indisponible quand l\'appareil est en mode silencieux</string> + <string name="pref_treat_vibrate_as_silent">Indisponible en mode vibreur</string> + <string name="pref_treat_vibrate_as_silent_summary">Marque cette ressource comme indisponible quand l\'appareil est en mode vibreur</string> <string name="pref_show_connection_options">Paramètres de connexioin avancés</string> <string name="pref_show_connection_options_summary">Montrer le nom d\'hôte et le port lors du paramétrage d\'un compte</string> <string name="hostname_example">xmpp.example.com</string> @@ -555,4 +560,7 @@ <string name="selection_too_large">La zone sélectionnée est trop grande</string> <string name="no_accounts">(Aucun compte activé)</string> <string name="this_field_is_required">Ce champ est requis</string> + <string name="no_keys_just_confirm">Vous faites déjà confiance à ce contact. En sélectionnant « Terminé » vous confirmerez seulement que %s est membre de cette conférence.</string> + <string name="select_image_and_crop">Sélectionner et découper une image</string> + <string name="this_account_is_disabled">Vous avez désactivé ce compte</string> </resources> diff --git a/src/main/res/values-id/strings.xml b/src/main/res/values-id/strings.xml index eae96d30..3b5d10ff 100644 --- a/src/main/res/values-id/strings.xml +++ b/src/main/res/values-id/strings.xml @@ -27,7 +27,9 @@ <string name="minutes_ago">%d min lalu</string> <string name="unread_conversations">Percakapan belum dibaca</string> <string name="sending">mengirim...</string> - <string name="nick_in_use">Nick ini sudah digunakan</string> + <string name="message_decrypting">Mendekripsi pesan. Mohon tunggu…</string> + <string name="pgp_message">Pesan terenkripsi OpenPGP</string> + <string name="nick_in_use">Nickname ini sudah digunakan</string> <string name="admin">Administrator</string> <string name="owner">Pemilik</string> <string name="moderator">Moderator</string> @@ -73,6 +75,7 @@ <string name="clear_histor_msg">Apakah Anda ingin menghapus semua pesan dalam Percakapan ini\n\n<b>Peringatan:</b>ini tidak akan mempengaruhi pesan yang disimpan pada perangkat atau server lain.</string> <string name="delete_messages">Hapus pesan</string> <string name="choose_presence">Pilih kehadiran untuk kontak</string> + <string name="send_unencrypted_message">Kirim pesan tak-terenkripsi</string> <string name="send_otr_message">Kirim pesan terenskripsi OTR</string> <string name="send_pgp_message">Kirim pesan terenskripsi OpenPGP</string> <string name="your_nick_has_been_changed">Nick kamu telah dirubah</string> @@ -82,6 +85,7 @@ <string name="openkeychain_required_long">Conversations menggunakan app pihak ke-3 bernama <b>OpenKeychain</b> untuk mengenkripsi dan menerjemahkan pesan dan mengorganisir kunci anda.\n\nOpenKeychain berlisensi GPLv3 dan tersedia fi F-Droid dan Google play.\n\n<small>(Silahkan mulai ulang Conversations setelah menginstall.)</small></string> <string name="restart">Mulai ulang</string> <string name="install">Pasang</string> + <string name="openkeychain_not_installed">Harap install OpenKeychain</string> <string name="offering">menawarkan...</string> <string name="waiting">menunggu...</string> <string name="no_pgp_key">Tidak ada kunci OpenPGP ditemukan</string> @@ -93,6 +97,7 @@ <string name="pref_xmpp_resource_summary">Identifikasi nama klien ini dengan</string> <string name="pref_accept_files">Terima berkas</string> <string name="pref_accept_files_summary">Otomatis menerima berkas lebih kecil dari...</string> + <string name="pref_notification_settings">Notif</string> <string name="pref_notifications">Notifikasi</string> <string name="pref_notifications_summary">Notifikasikan jika pesan baru tiba</string> <string name="pref_vibrate">Getar</string> @@ -101,13 +106,15 @@ <string name="pref_sound_summary">mainkan suara saat menerima notifikasi</string> <string name="pref_notification_grace_period">Tenggang waktu pemberitahuan</string> <string name="pref_notification_grace_period_summary">Nonaktifkan pemberitahuan untuk waktu yang singkat setelah salinan diterima</string> + <string name="pref_advanced_options">Lanjutan</string> <string name="pref_never_send_crash">Jangan kirim laporan kerusakan</string> <string name="pref_never_send_crash_summary">Dengan mengirimkan kesalahan Anda membantu pengembangan Aplikasi Conversations</string> <string name="pref_confirm_messages">Konfirmasi Pesan</string> <string name="pref_confirm_messages_summary">Biarkan kontak Anda tahu kapan Anda telah menerima dan membaca pesan</string> + <string name="pref_ui_options">UI</string> <string name="openpgp_error">OpenKeychain melaporkan kesalahan</string> <string name="error_decrypting_file">I/O Error menerjemahkan berkas</string> - <string name="accept">Menerima</string> + <string name="accept">Terima</string> <string name="error">Sebuah kesalahan terjadi</string> <string name="pref_grant_presence_updates">Memberikan perubahan kehadiran</string> <string name="pref_grant_presence_updates_summary">Terlebih dahulu meminta dan berlangganan kehadiran untuk kontak Anda buat</string> @@ -141,6 +148,7 @@ <string name="account_status_incompatible_server">Server tidak cocok</string> <string name="encryption_choice_otr">OTR</string> <string name="encryption_choice_pgp">OpenPGP</string> + <string name="encryption_choice_omemo">OMEMO</string> <string name="mgmt_account_edit">Ubah akun</string> <string name="mgmt_account_delete">Hapus akun</string> <string name="mgmt_account_disable">Sementara dimatikan</string> @@ -213,7 +221,6 @@ <string name="bookmark_already_exists">Bookmark ini sudah ada</string> <string name="you">Anda</string> <string name="action_edit_subject">Ubah subjek conference</string> - <string name="conference_not_found">Conference tidak ditemukan</string> <string name="leave">Tinggalkan</string> <string name="contact_added_you">Kontak ditambahkan ke daftar anda</string> <string name="add_back">Tambah kembali</string> @@ -399,6 +406,7 @@ <string name="offering_x_file">Menawarkan %s</string> <string name="hide_offline">Sembunyikan Offline</string> <string name="disable_account">Nonaktifkan Akun</string> + <string name="contact_is_typing">%s sedang mengetik…</string> <string name="contact_has_stopped_typing">%s telah berhenti mengetik</string> <string name="pref_chat_states">Notifikasi ketik pesan</string> <string name="pref_chat_states_summary">Biarkan kontak Anda tahu ketika Anda sedang menulis pesan baru</string> @@ -434,7 +442,35 @@ <string name="username">Username</string> <string name="username_hint">Username</string> <string name="invalid_username">Username ini tidak valid</string> + <string name="conference_name">Nama Conference</string> <string name="download_failed_server_not_found">Unduhan gagal: Server tidak ditemukan</string> <string name="download_failed_file_not_found">Unduh gagal: Berkas tidak ditemukan</string> <string name="download_failed_could_not_connect">Unduhan gagal: Tidak dapat terhubung ke host</string> + <string name="pref_use_white_background">Gunakan latar putih</string> + <string name="account_status_tor_unavailable">Tor network tidak tersedia</string> + <string name="server_info_broken">Rusak</string> + <string name="pref_presence_settings">Kehadiran</string> + <string name="hostname_example">xmpp.example.com</string> + <string name="pref_connection_options">Koneksi</string> + <string name="pref_use_tor">Hubungkan via Tor</string> + <string name="account_settings_hostname">Hostname</string> + <string name="account_settings_port">Port</string> + <string name="certificate_information">Informasi Sertifikat</string> + <string name="certificate_subject">Subjek</string> + <string name="certificate_cn">Nama Umum</string> + <string name="certificate_o">Organisasi</string> + <string name="certificate_sha1">SHA-1</string> + <string name="certicate_info_not_available">(Tidak tersedia)</string> + <string name="certificate_not_found">Tidak ada sertifikat ditemukan</string> + <string name="pref_picture_compression">Kompres Gambar</string> + <string name="pref_picture_compression_summary">Rubah ukuran dan kompres gambar</string> + <string name="always">Selalu</string> + <string name="automatically">Secara otomatis</string> + <string name="battery_optimizations_enabled">Pengoptimalan baterai diaktifkan</string> + <string name="disable">Non-aktifkan</string> + <string name="selection_too_large">Area yang dipilih terlalu besar</string> + <string name="no_accounts">(Tidak ada akun aktif)</string> + <string name="this_field_is_required">Bagian ini wajib diisi</string> + <string name="correct_message">Perbaiki pesan</string> + <string name="send_corrected_message">Kirim perbaikan pesan</string> </resources> diff --git a/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml index 5fac85d0..b7b5f160 100644 --- a/src/main/res/values-it/strings.xml +++ b/src/main/res/values-it/strings.xml @@ -96,13 +96,12 @@ <string name="contact_has_no_pgp_key">Conversations non è in grado di cifrare i tuoi messaggi perché il contatto non sta annunciando la sua chiave pubblica.\n\n<small>Per favore chiedi al tuo contatto di configurare OpenPGP.</small></string> <string name="no_pgp_keys">Nessuna chiave OpenPGP trovata</string> <string name="contacts_have_no_pgp_keys">Conversations non è in grado di cifrare i tuoi messaggi perché i contatti non stanno annunciando la propria chiave pubblica.\n\n<small>Per favore chiedi ai tuoi contatti di configurare OpenPGP.</small></string> - <string name="encrypted_message_received"><i>Messaggio cifrato ricevuto. Tocca per decifrare.</i></string> <string name="pref_general">Generale</string> <string name="pref_xmpp_resource">Risorsa XMPP</string> <string name="pref_xmpp_resource_summary">Il nome con il quale questo client si identifica</string> <string name="pref_accept_files">Accetta i file</string> <string name="pref_accept_files_summary">Accetta automaticamente i file più piccoli di…</string> - <string name="pref_notification_settings">Impostazioni di Notifica</string> + <string name="pref_notification_settings">Notifiche</string> <string name="pref_notifications">Notifiche</string> <string name="pref_notifications_summary">Notifica quando arriva un nuovo messaggio</string> <string name="pref_vibrate">Vibra</string> @@ -111,12 +110,12 @@ <string name="pref_sound_summary">Riproduci una suoneria con la notifica</string> <string name="pref_notification_grace_period">Periodo tra notifiche</string> <string name="pref_notification_grace_period_summary">Disabilita le notifiche per un breve lasso di tempo dopo che un messaggio è stato ricevuto</string> - <string name="pref_advanced_options">Opzioni Avanzate</string> + <string name="pref_advanced_options">Avanzate</string> <string name="pref_never_send_crash">Non inviare mai segnalazioni di errore</string> <string name="pref_never_send_crash_summary">Se scegli di inviare una segnalazione dell’errore aiuterai lo sviluppo di Conversations</string> <string name="pref_confirm_messages">Conferma Messaggi</string> <string name="pref_confirm_messages_summary">Fai sapere ai tuoi contatti quando hai ricevuto il messaggio e l’hai letto</string> - <string name="pref_ui_options">Opzioni Interfaccia</string> + <string name="pref_ui_options">Interfaccia Utente</string> <string name="openpgp_error">OpenKeychain ha riportato un errore</string> <string name="error_decrypting_file">Errore di I/O nel decifrare il file</string> <string name="accept">Accetta</string> @@ -191,6 +190,7 @@ <string name="server_info_stream_management">XEP-0198: Stream Management</string> <string name="server_info_pep">XEP-0163: PEP (Avatars / OMEMO)</string> <string name="server_info_http_upload">XEP-0363: HTTP File Upload</string> + <string name="server_info_push">XEP-0357: Push</string> <string name="server_info_available">disponibile</string> <string name="server_info_unavailable">non disponibile</string> <string name="missing_public_keys">Annuncio chiave pubblica non effettuato</string> @@ -239,7 +239,7 @@ <string name="bookmark_already_exists">Questo segnalibro esiste già</string> <string name="you">Tu</string> <string name="action_edit_subject">Modifica soggetto conferenza</string> - <string name="conference_not_found">Conferenza non trovata</string> + <string name="joining_conference">Entro in conferenza...</string> <string name="leave">Abbandona</string> <string name="contact_added_you">Il contatto ti ha aggiunto alla sua lista contatti</string> <string name="add_back">Add back</string> @@ -274,12 +274,12 @@ <string name="sure_delete_fingerprint">Sei sicuro di voler eliminare questa impronta?</string> <string name="ignore">Ignora</string> <string name="without_mutual_presence_updates"><b>Attenzione:</b> Inviando questo messaggio senza aggiornamenti della presenza reciproci potrebbe causare problemi inaspettati.\n\n<small>Vai nei dettagli del contatto per verificare le tue sottoscrizioni alla presenza.</small></string> - <string name="pref_encryption_settings">Impostazioni di cifratura</string> + <string name="pref_security_settings">Sicurezza</string> <string name="pref_force_encryption">Forza cifratura end-to-end</string> <string name="pref_force_encryption_summary">Manda sempre messaggi cifrati (ad eccezione delle conferenze)</string> <string name="pref_dont_save_encrypted">Non salvare i messaggi cifrati</string> <string name="pref_dont_save_encrypted_summary">Attenzione: Questo potrebbe comportare la perdita di messaggi</string> - <string name="pref_expert_options">Opzioni da Esperto</string> + <string name="pref_expert_options">Impostazioni esperto</string> <string name="pref_expert_options_summary">Fai attenzione con queste impostazioni</string> <string name="title_activity_about">Info su Conversations</string> <string name="pref_about_conversations_summary">Informazioni sulla licenza</string> @@ -297,11 +297,15 @@ <string name="pref_expert_options_other">Altro</string> <string name="pref_conference_name">Nome della conferenza</string> <string name="pref_conference_name_summary">Usa il soggetto della stanza al posto del JID per identificare le conferenze</string> + <string name="pref_autojoin">Entra automaticamente in conferenza</string> + <string name="pref_autojoin_summary">Rispetta l\'opzione di entrata automatica nei segnalibri di conferenze</string> <string name="toast_message_otr_fingerprint">Impronta OTR copiata!</string> <string name="toast_message_omemo_fingerprint">Fingerprint OMEMO copiata negli appunti|</string> <string name="conference_banned">Sei stato bandito da questa conferenza</string> <string name="conference_members_only">Questa conferenza è solo per membri</string> <string name="conference_kicked">Sei stato buttato fuori dalla conferenza</string> + <string name="conference_shutdown">La conferenza è stata chiusa</string> + <string name="conference_unknown_error">Non sei più in questa conferenza</string> <string name="using_account">usando l’utente %s</string> <string name="checking_x">Controllo %s su host HTTP</string> <string name="not_connected_try_again">Non sei connesso. Riprova più tardi</string> @@ -427,7 +431,7 @@ <string name="two_hours">2 ore</string> <string name="eight_hours">8 ore</string> <string name="until_further_notice">Fino a nuovo avviso</string> - <string name="pref_input_options">Opzioni di ingresso</string> + <string name="pref_input_options">Input</string> <string name="pref_enter_is_send">Invio spedisce</string> <string name="pref_enter_is_send_summary">Il tasto invio spedisce il messaggio</string> <string name="pref_display_enter_key">Mostra il tasto invio</string> @@ -484,19 +488,28 @@ <string name="username">Utente</string> <string name="username_hint">Utente</string> <string name="invalid_username">Questo non è un nome utente valido</string> + <string name="conference_name">Nome conferenza</string> + <string name="invalid_conference_name">Questo non è un nome di conferenza valido</string> <string name="download_failed_server_not_found">Download fallito: Server non trovato</string> <string name="download_failed_file_not_found">Download fallito: File non trovato</string> <string name="download_failed_could_not_connect">Download fallito: Impossibile connettersi all\'host</string> <string name="account_status_tor_unavailable">Rete Tor non disponibile</string> <string name="server_info_broken">Rotto</string> - <string name="pref_presence_settings">Impostazioni presenza</string> + <string name="pref_presence_settings">Presenza</string> <string name="pref_away_when_screen_off">\"Non disponibile\" a schermo spento</string> <string name="pref_away_when_screen_off_summary">Imposta la tua risorsa come non disponibile quando lo schermo è spento</string> <string name="pref_xa_on_silent_mode">Non disponibile in modalità silenzioso</string> <string name="pref_xa_on_silent_mode_summary">Imposta la tua risorsa come non disponibile quando il dispositivo è in modalità silenziosa</string> + <string name="pref_show_connection_options">Impostazioni estese di connessione</string> + <string name="pref_show_connection_options_summary">Mostra nome host e impostazioni della porta quando configuri un account</string> + <string name="hostname_example">xmpp.esempio.it</string> <string name="action_add_account_with_certificate">Aggiungi account con certificato</string> <string name="unable_to_parse_certificate">Impossibile analizzare il certificato</string> <string name="authenticate_with_certificate">Lasciare vuoto per autenticarsi con certificato</string> + <string name="mam_prefs">Preferenze di archiviazione</string> + <string name="server_side_mam_prefs">Preferenze di archiviazione lato server</string> + <string name="fetching_mam_prefs">Raccolta preferenze di archiviazione. Attendere prego...</string> + <string name="unable_to_fetch_mam_prefs">Impossibile raccogliere le preferenze di archiviazione</string> <string name="captcha_ocr">Testo captcha</string> <string name="captcha_required">Captcha richiest</string> <string name="captcha_hint">inserire il testo dall\'immagine</string> @@ -506,7 +519,7 @@ <string name="error_fetching_omemo_key">Errore ricezione chiave OMEMO!</string> <string name="verified_omemo_key_with_certificate">Chiave OMEMO verificata con certificato!</string> <string name="device_does_not_support_certificates">Il tuo dispositivo non supporta la selezione di certificati utente!</string> - <string name="pref_connection_options">Opzioni di connessione</string> + <string name="pref_connection_options">Connessione</string> <string name="account_settings_hostname">Nome host</string> <string name="account_settings_port">Porta</string> <string name="not_a_valid_port">Questo non è un numero di porta valido</string> @@ -526,7 +539,7 @@ <string name="certificate_issuer">Emittente</string> <string name="certificate_cn">Nome comune</string> <string name="certificate_o">Organizzazione</string> - <string name="certificate_sha1">SHA1</string> + <string name="certificate_sha1">SHA-1</string> <string name="certicate_info_not_available">(Non disponibile)</string> <string name="certificate_not_found">Nessun certificato trovato</string> <string name="notify_on_all_messages">Notifica per tutti i messaggi</string> @@ -540,4 +553,6 @@ <string name="battery_optimizations_enabled_dialog">Il tuo dispositivo sta facendo delle ingenti ottimizzazioni della batteria per Conversations che potrebbero portare ritardi alle notifiche o anche perdita di messaggi.\n\nTi verrà ora chiesto di disabilitarle.</string> <string name="disable">Disabilita</string> <string name="selection_too_large">L\'area selezionata è troppo grande</string> + <string name="no_accounts">(Nessun account attivo)</string> + <string name="this_field_is_required">Questo campo è obbligatorio</string> </resources> diff --git a/src/main/res/values-iw/strings.xml b/src/main/res/values-iw/strings.xml index 3f52ad4c..e3732fc7 100644 --- a/src/main/res/values-iw/strings.xml +++ b/src/main/res/values-iw/strings.xml @@ -94,7 +94,6 @@ <string name="contact_has_no_pgp_key">Conversations אינה מסוגלת להצפין את הודעותיך משום שאיש הקשר שלך אינו מכריז על המפתח הפומבי שלו או שלה.\n\n<small>אנא בקש מאיש הקשר שלך להגדיר את OpenPGP.</small></string> <string name="no_pgp_keys">לא נמצאו מפתחות OpenPGP</string> <string name="contacts_have_no_pgp_keys">Conversations אינה מסוגלת להצפין את הודעותיך משום שאנשי הקשר שלך אינם מכריזים על המפתח הפומבי שלהם.\n\n<small>אנא בקש מאנשי הקשר שלך לארגן OpenPGP.</small></string> - <string name="encrypted_message_received"><i>הודעה מוצפנת תנקבלה. גע כדי לפענח צופן.</i></string> <string name="pref_general">כללי</string> <string name="pref_xmpp_resource">משאב XMPP</string> <string name="pref_xmpp_resource_summary">השם שבעזרתו לקוח זה מזהה את עצמו</string> @@ -228,7 +227,6 @@ <string name="bookmark_already_exists">סימנייה זו כבר קיימת</string> <string name="you">אני</string> <string name="action_edit_subject">ערוך נושא ועידה</string> - <string name="conference_not_found">ועידה לא נמצאה</string> <string name="leave">עזוב</string> <string name="contact_added_you">איש קשר הוסיף אותך אל רשימת קשר</string> <string name="add_back">הוסף בחזרה</string> diff --git a/src/main/res/values-ja/strings.xml b/src/main/res/values-ja/strings.xml index 57289f5a..c20bd099 100644 --- a/src/main/res/values-ja/strings.xml +++ b/src/main/res/values-ja/strings.xml @@ -96,7 +96,6 @@ <string name="contact_has_no_pgp_key">連絡先が公開鍵を通知しないため、Conversations はあなたのメッセージを暗号化することができません。\n\n<small>連絡先に OpenPGP をセットアップするように依頼してください。</small></string> <string name="no_pgp_keys">OpenPGP の鍵はありません</string> <string name="contacts_have_no_pgp_keys">連絡先が公開鍵を通知しないため、Conversations はあなたのメッセージを暗号化することができません。\n\n<small>連絡先に OpenPGP をセットアップするように依頼してください。</small></string> - <string name="encrypted_message_received"><i>暗号化されたメッセージを受信しました。タッチすると復号化します。</i></string> <string name="pref_general">全般</string> <string name="pref_xmpp_resource">XMPP リソース</string> <string name="pref_xmpp_resource_summary">自分自身を識別するこのクライアントの名前</string> @@ -240,8 +239,7 @@ <string name="bookmark_already_exists">このブックマークはすでに存在します</string> <string name="you">あなた</string> <string name="action_edit_subject">会議の件名を編集</string> - <string name="conference_not_found">会議が見つかりません</string> - <string name="conference_unknown_error">不明なエラーを受け取りました</string> + <string name="joining_conference">会議に参加中…</string> <string name="leave">退出</string> <string name="contact_added_you">連絡先があなたを連絡先リストに追加しました</string> <string name="add_back">戻りを追加</string> @@ -308,6 +306,8 @@ <string name="conference_banned">あなたはこの会議から禁止されています</string> <string name="conference_members_only">この会議はメンバーのみです</string> <string name="conference_kicked">あなたはこの会議からキックされました</string> + <string name="conference_shutdown">会議は停止しました</string> + <string name="conference_unknown_error">あなたはもうこの会議に参加していません</string> <string name="using_account">アカウント %s を使用</string> <string name="checking_x">HTTP ホストの %s を確認中</string> <string name="not_connected_try_again">接続されていません。後でもう一度お試しください</string> @@ -488,6 +488,8 @@ <string name="username">ユーザー名</string> <string name="username_hint">ユーザー名</string> <string name="invalid_username">これは有効なユーザー名ではありません</string> + <string name="conference_name">会議名</string> + <string name="invalid_conference_name">これは有効な会議名ではありません</string> <string name="download_failed_server_not_found">ダウンロードに失敗しました: サーバーが見つかりません</string> <string name="download_failed_file_not_found">ダウンロードに失敗しました: ファイルが見つかりません</string> <string name="download_failed_could_not_connect">ダウンロードに失敗しました: ホストに接続できませんでした</string> diff --git a/src/main/res/values-ko/strings.xml b/src/main/res/values-ko/strings.xml index ec730843..14f4493d 100644 --- a/src/main/res/values-ko/strings.xml +++ b/src/main/res/values-ko/strings.xml @@ -96,7 +96,6 @@ <string name="contact_has_no_pgp_key">당신의 연락처가 그들의 공개 키를 선언하지 않고 있기 때문에 Conversations는 당신의 메세지를 암호화할 수 없습니다. OpenPGP를 설정하도록 당신의 연락처에게 물어보세요. </string> <string name="no_pgp_keys">OpenPGP 키가 발견되지 않음 </string> <string name="contacts_have_no_pgp_keys">당신의 연락처가 그들의 공개 키를 선언하지 않고 있기 때문에 Conversations는 당신의 메세지를 암호화할 수 없습니다. OpenPGP를 설정하도록 당신의 연락처에게 물어보세요. </string> - <string name="encrypted_message_received"><i>암호화된 메세지가 도착했습니다. 터치하여 복호화하세요.</i></string> <string name="pref_general">일반 </string> <string name="pref_xmpp_resource">XMPP 자원 </string> <string name="pref_xmpp_resource_summary">이 클라이언트가 자신을 알아보는 이름</string> @@ -236,7 +235,6 @@ <string name="bookmark_already_exists">즐겨찾기가 이미 존재합니다 </string> <string name="you">당신 </string> <string name="action_edit_subject">회의 제목 편집 </string> - <string name="conference_not_found">회의를 찾을 수 없습니다 </string> <string name="leave">퇴장 </string> <string name="contact_added_you">연락처가 당신을 연락처 목록에 추가했습니다 </string> <string name="add_back">Add back</string> diff --git a/src/main/res/values-nb-rNO/strings.xml b/src/main/res/values-nb-rNO/strings.xml index 47d7334d..623013e1 100644 --- a/src/main/res/values-nb-rNO/strings.xml +++ b/src/main/res/values-nb-rNO/strings.xml @@ -96,7 +96,6 @@ <string name="contact_has_no_pgp_key">Conversations kan ikke kryptere din melding fordi din kontakt ikke annonserer sin offentlige nøkkel.\n\n<small>Spør din kontakt om å sette opp OpenPGP.</small></string> <string name="no_pgp_keys">Ingen OpenPGP-nøkler funnet</string> <string name="contacts_have_no_pgp_keys">Conversations kan ikke kryptere din melding fordi dine kontakter ikke annonserer sine offentlige nøkkler.\n\n<small>Spør din kontakt om å sette opp OpenPGP.</small></string> - <string name="encrypted_message_received"><i>Kryptert melding mottatt. Trykk for å dekryptere.</i></string> <string name="pref_general">Generelt</string> <string name="pref_xmpp_resource">XMPP-ressurs</string> <string name="pref_xmpp_resource_summary">Navnet denne klienten identifiserer seg med</string> @@ -239,7 +238,6 @@ <string name="bookmark_already_exists">Dette bokmerket finnes allerede</string> <string name="you">Deg</string> <string name="action_edit_subject">Rediger temaet for konferansen</string> - <string name="conference_not_found">Fant ikke konferansen</string> <string name="leave">Forlat</string> <string name="contact_added_you">Kontakt la deg til i sin liste</string> <string name="add_back">Gjengjeld tjenesten</string> diff --git a/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml index 33018c9d..fb154115 100644 --- a/src/main/res/values-nl/strings.xml +++ b/src/main/res/values-nl/strings.xml @@ -96,7 +96,6 @@ <string name="contact_has_no_pgp_key">Conversations kan je berichten niet versleutelen omdat je contact geen publieke sleutel heeft ingesteld.\n\n<small>Vraag je contact om OpenPGP te configureren.</small></string> <string name="no_pgp_keys">Geen OpenPGP-sleutels gevonden</string> <string name="contacts_have_no_pgp_keys">Conversations kan je berichten niet versleutelen omdat je contacten geen publieke sleutel hebben ingesteld.\n\n<small>Vraag je contacten om OpenPGP te configureren.</small></string> - <string name="encrypted_message_received"><i>Versleuteld bericht ontvangen. Raak aan om te ontsleutelen.</i></string> <string name="pref_general">Algemeen</string> <string name="pref_xmpp_resource">XMPP-bron</string> <string name="pref_xmpp_resource_summary">De naam waarmee deze cliënt zich identificeert</string> @@ -240,8 +239,7 @@ <string name="bookmark_already_exists">Deze bladwijzer bestaat al</string> <string name="you">Jij</string> <string name="action_edit_subject">Onderwerp van groepsgesprek bewerken</string> - <string name="conference_not_found">Groepsgesprek niet gevonden</string> - <string name="conference_unknown_error">Onbekende fout ontvangen</string> + <string name="joining_conference">Deelnemen aan groepsgesprek…</string> <string name="leave">Verlaten</string> <string name="contact_added_you">Contact heeft je toegevoegd aan zijn/haar contacten</string> <string name="add_back">Contact toevoegen aan eigen contacten</string> @@ -308,10 +306,13 @@ <string name="conference_banned">Je bent verbannen uit dit groepsgesprek</string> <string name="conference_members_only">Dit groepsgesprek is enkel voor leden</string> <string name="conference_kicked">Je bent uit dit groepsgesprek geschopt</string> + <string name="conference_shutdown">Het groepsgesprek is uitgeschakeld</string> + <string name="conference_unknown_error">Je neemt niet langer deel aan dit groepsgesprek</string> <string name="using_account">met account %s</string> <string name="checking_x">%s op HTTP-host nakijken</string> <string name="not_connected_try_again">Je bent niet verbonden. Probeer later opnieuw</string> <string name="check_x_filesize">Bestandsgrootte van %s controleren</string> + <string name="check_x_filesize_on_host">Bestandsgrootte van %1$s op %2$s controleren</string> <string name="message_options">Berichtopties</string> <string name="copy_text">Tekst kopiëren</string> <string name="copy_original_url">Oorspronkelijke URL kopiëren</string> @@ -490,6 +491,8 @@ <string name="username">Gebruikersnaam</string> <string name="username_hint">Gebruikersnaam</string> <string name="invalid_username">Dit is geen geldige gebruikersnaam</string> + <string name="conference_name">Groepsgespreksnaam</string> + <string name="invalid_conference_name">Dit is geen geldige groepsgespreksnaam</string> <string name="download_failed_server_not_found">Downloaden mislukt: server niet gevonden</string> <string name="download_failed_file_not_found">Downloaden mislukt: bestand niet gevonden</string> <string name="download_failed_could_not_connect">Downloaden mislukt: kon geen verbinding maken met host</string> @@ -500,6 +503,8 @@ <string name="pref_away_when_screen_off_summary">Stelt je bron in als even weg wanneer het scherm uitgeschakeld is</string> <string name="pref_xa_on_silent_mode">Niet beschikbaar in stille modus</string> <string name="pref_xa_on_silent_mode_summary">Stelt je bron in als niet beschikbaar wanneer je apparaat in stille modus staat</string> + <string name="pref_treat_vibrate_as_silent">Trillen behandelen als stille modus</string> + <string name="pref_treat_vibrate_as_silent_summary">Stelt je bron in als niet beschikbaar wanneer je apparaat in trilmodus staat</string> <string name="pref_show_connection_options">Uitgebreide verbindingsinstellingen</string> <string name="pref_show_connection_options_summary">Toon hostnaam- en poortinstellingen bij instellen van een account</string> <string name="hostname_example">xmpp.voorbeeld.be</string> @@ -555,4 +560,7 @@ <string name="selection_too_large">Het gekozen vlak is te groot</string> <string name="no_accounts">(Geen actieve accounts)</string> <string name="this_field_is_required">Dit veld is vereist</string> + <string name="no_keys_just_confirm">Je vertrouwt dit contact al. Door \'klaar\' te kiezen bevestig je enkel dat %s deel uitmaakt van dit groepsgesprek.</string> + <string name="select_image_and_crop">Afbeelding kiezen en bijsnijden</string> + <string name="this_account_is_disabled">Je hebt deze account uitgeschakeld</string> </resources> diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index 76d8db62..c5c25605 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -96,13 +96,12 @@ <string name="contact_has_no_pgp_key">Conversations nie może zaszyfrować wiadomości, ponieważ kontakt nie udostępnia klucza publicznego.\n\n<small>Zasugeruj rozmówcy instalację OpenPGP.</small></string> <string name="no_pgp_keys">Nie znaleziono kluczy OpenPGP</string> <string name="contacts_have_no_pgp_keys">Conversations nie może zaszyfrować wiadomości, ponieważ kontakty nie udostępniają kluczy publicznych.\n\n<small>Zasugeruj rozmówcom instalację OpenPGP.</small></string> - <string name="encrypted_message_received"><i>Odebrano zaszyfrowaną wiadomość. Dotknij by odszyfrować</i></string> <string name="pref_general">Główne</string> <string name="pref_xmpp_resource">Zasób XMPP</string> <string name="pref_xmpp_resource_summary">Nazwa identyfikująca urządzenie</string> <string name="pref_accept_files">Akceptuj pliki</string> <string name="pref_accept_files_summary">Automatycznie akceptuj pliki mniejsze niż...</string> - <string name="pref_notification_settings">Ustawienia powiadamiania</string> + <string name="pref_notification_settings">Powiadomienie</string> <string name="pref_notifications">Powiadomienia</string> <string name="pref_notifications_summary">Powiadamiaj, gdy nadejdzie wiadomość</string> <string name="pref_vibrate">Wibracje</string> @@ -111,12 +110,12 @@ <string name="pref_sound_summary">Odtwórz dźwięk z powiadomieniem</string> <string name="pref_notification_grace_period">Opóźnienie powiadomień</string> <string name="pref_notification_grace_period_summary">Wyłącz powiadomienia przez krótki czas po otrzymaniu kopii wiadomości</string> - <string name="pref_advanced_options">Opcje zaawansowane</string> + <string name="pref_advanced_options">Zaawansowane</string> <string name="pref_never_send_crash">Nie wysyłaj raportów awarii</string> <string name="pref_never_send_crash_summary">Wysyłając ślady stosu pomagasz rozwijać Conversations</string> <string name="pref_confirm_messages">Potwierdzenia wiadomości</string> <string name="pref_confirm_messages_summary">Powiadamiaj kontakty o otrzymaniu lub przeczytaniu wiadomości</string> - <string name="pref_ui_options">Ustawienia interfejsu</string> + <string name="pref_ui_options">UI</string> <string name="openpgp_error">Wystąpił błąd OpenKeychain</string> <string name="error_decrypting_file">Błąd podczas deszyfrowania pliku</string> <string name="accept">Akceptuj</string> @@ -191,6 +190,7 @@ <string name="server_info_stream_management">XEP-0198: Stream Management</string> <string name="server_info_pep">XEP-0163: PEP (Awatary / OMEMO)</string> <string name="server_info_http_upload">XEP-0363: Przesyłanie plików przez HTTP</string> + <string name="server_info_push">XEP-0357: Push</string> <string name="server_info_available">dostępny</string> <string name="server_info_unavailable">niedostępny</string> <string name="missing_public_keys">Brak informacji o kluczu publicznym</string> @@ -239,7 +239,7 @@ <string name="bookmark_already_exists">Zakładka już istnieje</string> <string name="you">Ty</string> <string name="action_edit_subject">Edytuj temat konferencji</string> - <string name="conference_not_found">Nie znaleziono konferencji</string> + <string name="joining_conference">Dołączenie do konferencji...</string> <string name="leave">Opuść pokój</string> <string name="contact_added_you">Kontakt został dodany do listy</string> <string name="add_back">Dodaj ponownie</string> @@ -274,7 +274,7 @@ <string name="sure_delete_fingerprint">Czy na pewno chcesz usunąć odcisk klucza?</string> <string name="ignore">Ignoruj</string> <string name="without_mutual_presence_updates"><b>Uwaga:</b> Wysyłanie bez obustronnych powiadomień o obecności może powodować nieoczekiwane problemy.\n\n<small>Sprawdź subskrypcję powiadomień w szczegółach kontaktu.</small></string> - <string name="pref_encryption_settings">Ustawienia szyfrowania</string> + <string name="pref_security_settings">Bezpieczeństwo</string> <string name="pref_force_encryption">Wymuszaj szyfrowanie typu end-to-end</string> <string name="pref_force_encryption_summary">Szyfruj wszystkie wiadomości (poza konferencjami)</string> <string name="pref_dont_save_encrypted">Nie zapisuj zaszyfrowanych wiadomości</string> @@ -297,11 +297,15 @@ <string name="pref_expert_options_other">Inne opcje</string> <string name="pref_conference_name">Nazwa konferencji</string> <string name="pref_conference_name_summary">Nazywaj konferencję tematem zamiast Jabber ID</string> + <string name="pref_autojoin">Automatycznie dołączaj do konferencji</string> + <string name="pref_autojoin_summary">Respektuj flagi autodołączania i zakładkach konferencji</string> <string name="toast_message_otr_fingerprint">Odcisk klucza OTR został skopiowany do schowka</string> <string name="toast_message_omemo_fingerprint">Odcisk klucza OMEMO został skopiowany do schowka!</string> <string name="conference_banned">Zbanowano cię w konferencji</string> <string name="conference_members_only">To jest zamknięty pokój</string> <string name="conference_kicked">Wyrzucono cię z konferencji</string> + <string name="conference_shutdown">Konferencję zamknięto</string> + <string name="conference_unknown_error">Nie uczestniczysz już w tej konferencji</string> <string name="using_account">używając konta %s</string> <string name="checking_x">Sprawdzanie %s na hoście HTTP</string> <string name="not_connected_try_again">Brak połączenia. Spróbuj ponownie później</string> @@ -427,7 +431,6 @@ <string name="two_hours">2 godziny</string> <string name="eight_hours">8 godzin</string> <string name="until_further_notice">Ręcznie</string> - <string name="pref_input_options">Ustawienia wprowadzania</string> <string name="pref_enter_is_send">Enter wysyła</string> <string name="pref_enter_is_send_summary">Używaj klawisza Enter do wysyłania wiadomości</string> <string name="pref_display_enter_key">Pokaż klawisz Enter</string> @@ -486,18 +489,28 @@ <string name="username">Nazwa użytkownika</string> <string name="username_hint">Nazwa użytkownika</string> <string name="invalid_username">Błędna nazwa użytkownika</string> + <string name="conference_name">Nazwa konferencji</string> + <string name="invalid_conference_name">Nie jest to poprawna nazwa konferencji</string> <string name="download_failed_server_not_found">Pobieranie nieudane: Nie odnaleziono serwera</string> <string name="download_failed_file_not_found">Pobieranie nieudane: Nie odnaleziono pliku</string> <string name="download_failed_could_not_connect">Pobieranie nieudane: Nie można połączyć z hostem</string> <string name="account_status_tor_unavailable">Sieć TOR jest niedostepna</string> <string name="server_info_broken">Zepsute</string> + <string name="pref_presence_settings">Obecność</string> <string name="pref_away_when_screen_off">Status \"Oddalony\" gdy wyświetlacz jest wyłączony</string> <string name="pref_away_when_screen_off_summary">Oznacza Twój zasób jako \"Oddalony\", gdy wyświetlacz jest wyłączony</string> <string name="pref_xa_on_silent_mode">Niedostepne w trybie cichym</string> <string name="pref_xa_on_silent_mode_summary">Oznacza Twój zasób jako \"Nieobecny\" gdy urządzenie jest w trybie cichym</string> + <string name="pref_show_connection_options">Rozszerzone ustawienia połączenia</string> + <string name="pref_show_connection_options_summary">Pokaż nazwę hosta i ustawienia portu przy dodawaniu konta</string> + <string name="hostname_example">xmpp.examle.com</string> <string name="action_add_account_with_certificate">Dodaj konto za pomocą certyfikatu</string> <string name="unable_to_parse_certificate">Nie mogę odczytać certyfikatu</string> <string name="authenticate_with_certificate">Pozostaw puste by autoryzować za pomocą certyfikatu</string> + <string name="mam_prefs">Preferencje archiwizacji</string> + <string name="server_side_mam_prefs">Preferencje archiwizacji po stronie serwera</string> + <string name="fetching_mam_prefs">Pobieranie preferencji archiwizacji. Proszę czekać...</string> + <string name="unable_to_fetch_mam_prefs">Nie można pobrać preferencji archiwizacji</string> <string name="captcha_ocr">Captcha</string> <string name="captcha_required">Captcha wymagana</string> <string name="captcha_hint">przepisz tekst z obrazka</string> @@ -507,6 +520,7 @@ <string name="error_fetching_omemo_key">Błąd pobierania klucza OMEMO!</string> <string name="verified_omemo_key_with_certificate">Zweryfikowano klucz OMEMO z certyfikatem</string> <string name="device_does_not_support_certificates">Twoje urządzenie nie wspiera wyboru certyfikatów klienckich</string> + <string name="pref_connection_options">Połączenie</string> <string name="account_settings_hostname">Nazwa hosta</string> <string name="account_settings_port">Port</string> <string name="not_a_valid_port">To nie jest prawidłowy numer portu</string> @@ -527,6 +541,7 @@ <string name="certificate_issuer">Wystawca</string> <string name="certificate_cn">Nazwa</string> <string name="certificate_o">Organizacja</string> + <string name="certificate_sha1">SHA-1</string> <string name="certicate_info_not_available">(Niedostępne)</string> <string name="certificate_not_found">Nie znaleziono certyfikatu</string> <string name="notify_on_all_messages">Powiadom o wszystkich wiadomościach</string> @@ -534,6 +549,7 @@ <string name="notify_never">Powiadomienia wyłączone</string> <string name="notify_paused">Powiadomienia wstrzymane</string> <string name="pref_picture_compression">Kompresuj obrazki</string> + <string name="pref_picture_compression_summary">Zmień rozmiar i kompresuj obrazki</string> <string name="always">Zawsze</string> <string name="automatically">Automatycznie</string> <string name="battery_optimizations_enabled">Optymalizacje zużycia baterii włączone</string> @@ -541,4 +557,6 @@ <string name="battery_optimizations_enabled_dialog">Twoje urządzenie wykonuje poważnie optymalizacje zużycia baterii przez Conversations, które mogą powodować opóźnienie powiadomień lub nawet utratę wiadomości.\nZostaniesz teraz poproszony o ich wyłączenie</string> <string name="disable">Wyłącz</string> <string name="selection_too_large">Zaznaczony obszar jest zbyt duży</string> + <string name="no_accounts">(Brak aktywynych kont)</string> + <string name="this_field_is_required">To pole jest wymagane</string> </resources> diff --git a/src/main/res/values-pt-rBR/strings.xml b/src/main/res/values-pt-rBR/strings.xml index 0b8b0080..7c4d40b1 100644 --- a/src/main/res/values-pt-rBR/strings.xml +++ b/src/main/res/values-pt-rBR/strings.xml @@ -96,7 +96,6 @@ <string name="contact_has_no_pgp_key">O Conversations não conseguiu criptografar suas mensagens porque o seu contato não está anunciando a chave pública dele(a).\n\n<small>Por favor, solicite ao seu contato para configurar o OpenPGP.</small></string> <string name="no_pgp_keys">Não foi encontrada nenhuma chave OpenPGP</string> <string name="contacts_have_no_pgp_keys">O Conversations não conseguiu criptografar suas mensagens porque os seus contatos não estão anunciando a chave pública deles(as).\n\n<small>Por favor, solicite aos seus contatos que configurem o OpenPGP.</small></string> - <string name="encrypted_message_received"><i>Foi recebida uma mensagem criptografada. Toque para descriptografar.</i></string> <string name="pref_general">Geral</string> <string name="pref_xmpp_resource">Recurso XMPP</string> <string name="pref_xmpp_resource_summary">O nome pelo qual esse cliente se identifica</string> @@ -240,8 +239,7 @@ <string name="bookmark_already_exists">Esse favorito já existe</string> <string name="you">Você</string> <string name="action_edit_subject">Editar o assunto da conferência</string> - <string name="conference_not_found">A conferência não foi encontrada</string> - <string name="conference_unknown_error">Foi recebido um erro desconhecido</string> + <string name="joining_conference">Entrando na conferência...</string> <string name="leave">Sair</string> <string name="contact_added_you">O contato foi adicionado à sua lista de contatos</string> <string name="add_back">Adicionar novamente</string> @@ -308,6 +306,8 @@ <string name="conference_banned">Você foi banido dessa conferência</string> <string name="conference_members_only">Essa conferência é restrita a seus membros</string> <string name="conference_kicked">Você foi expulso dessa conferência</string> + <string name="conference_shutdown">A conferência foi encerrada</string> + <string name="conference_unknown_error">Você não está mais participando dessa conferência</string> <string name="using_account">usando a conta %s</string> <string name="checking_x">Verificando %s no host HTTP</string> <string name="not_connected_try_again">Você não está conectado. Tente novamente mais tarde.</string> @@ -490,6 +490,8 @@ <string name="username">Nome de usuário</string> <string name="username_hint">Nome de usuário</string> <string name="invalid_username">Esse não é um nome de usuário válido</string> + <string name="conference_name">Nome da conferência</string> + <string name="invalid_conference_name">Esse nome de conferência não é válido</string> <string name="download_failed_server_not_found">Não foi possível fazer o download: servidor não encontrado</string> <string name="download_failed_file_not_found">Não foi possível fazer o download: arquivo não encontrado</string> <string name="download_failed_could_not_connect">Não foi possível fazer o download: não foi possível conectar ao host</string> diff --git a/src/main/res/values-pt/strings.xml b/src/main/res/values-pt/strings.xml index 30265948..4164ca71 100644 --- a/src/main/res/values-pt/strings.xml +++ b/src/main/res/values-pt/strings.xml @@ -195,7 +195,6 @@ <string name="bookmark_already_exists">O favorito já existe</string> <string name="you">Você</string> <string name="action_edit_subject">Editar o assunto da conferência</string> - <string name="conference_not_found">Conferência não encontrada</string> <string name="leave">Sair</string> <string name="contact_added_you">Contato adicionado à sua lista de contato</string> <string name="add_back">Adicionar novamente</string> diff --git a/src/main/res/values-ro-rRO/strings.xml b/src/main/res/values-ro-rRO/strings.xml index e55b21f2..202151fe 100644 --- a/src/main/res/values-ro-rRO/strings.xml +++ b/src/main/res/values-ro-rRO/strings.xml @@ -96,7 +96,6 @@ <string name="contact_has_no_pgp_key">Conversations nu a putut sa cripteze mesajele tale din cauza contactului care nu isi anunta cheia publica.\n\n<small>Roaga contactul sa isi configureze OpenPGP.</small></string> <string name="no_pgp_keys">Nu am gasit chei OpenPGP</string> <string name="contacts_have_no_pgp_keys">Conversations nu poate cripta mesajele tale pentru contactele tale care nu isi anunta cheia publica.\n\n<small>Te rog cere contactelor sa configureze OpenPGP.</small></string> - <string name="encrypted_message_received"><i>Ai primit mesaj criptat. Apasa aici pentru a-l decripta.</i></string> <string name="pref_general">General</string> <string name="pref_xmpp_resource">Nume client XMPP</string> <string name="pref_xmpp_resource_summary">Numele cu care acest client se identifica</string> @@ -240,8 +239,7 @@ <string name="bookmark_already_exists">Acest semn de carte exista</string> <string name="you">Tu</string> <string name="action_edit_subject">Editeaza titlul conferintei</string> - <string name="conference_not_found">Conferinta nu a fost gasita</string> - <string name="conference_unknown_error">A fost primita o eroare necunoscuta</string> + <string name="joining_conference">Alatura-te conferintei</string> <string name="leave">Paraseste</string> <string name="contact_added_you">Contactul a fost adaugat in lista </string> <string name="add_back">Adauga inapoi</string> @@ -308,10 +306,13 @@ <string name="conference_banned">Ti-a fost interzis accesul la aceasta conferinta</string> <string name="conference_members_only">Aceasta conferinta este rezervata membrilor</string> <string name="conference_kicked">Ai fost dat afara din conferinta</string> + <string name="conference_shutdown">Conferinta a fost inchisa</string> + <string name="conference_unknown_error">Nu mai participi la aceasta conferinta</string> <string name="using_account">folosind cont %s</string> <string name="checking_x">Verifica %s pe gazda HTTP</string> <string name="not_connected_try_again">Nu esti conectat. Incearca din nou mai tarziu.</string> <string name="check_x_filesize">Verifica marimea %s</string> + <string name="check_x_filesize_on_host">Verifica marimea %1$s pe %2$s</string> <string name="message_options">Optiuni mesaje</string> <string name="copy_text">Copiaza text</string> <string name="copy_original_url">Copiaza URL original</string> @@ -492,6 +493,8 @@ <string name="username">Nume utilizator</string> <string name="username_hint">Nume utilizator</string> <string name="invalid_username">Acesta nu este un nume de utilizator valabil</string> + <string name="conference_name">Titlu conferinta</string> + <string name="invalid_conference_name">Acesta nu este un nume de conferinta valabil</string> <string name="download_failed_server_not_found">Descarcarea a esuat: Serverul nu a fost gasit</string> <string name="download_failed_file_not_found">Descarcare esuata: Fisierul nu a fost gasit</string> <string name="download_failed_could_not_connect">Descarcarea a esuat. Nu s-a putut realiza conexiunea cu gazda.</string> @@ -499,9 +502,11 @@ <string name="server_info_broken">Deteriorat</string> <string name="pref_presence_settings">Setari de prezenta</string> <string name="pref_away_when_screen_off">Plecat cand ecranul este oprit</string> - <string name="pref_away_when_screen_off_summary">Marcheaza clientul drept plecat cand ecranul este oprit</string> + <string name="pref_away_when_screen_off_summary">Declara clientul drept plecat cand ecranul este oprit</string> <string name="pref_xa_on_silent_mode">Indisponibil in mod silentios</string> <string name="pref_xa_on_silent_mode_summary">Declara clientul drept indisponibil atunci cand dispozitivul este in mod silentios</string> + <string name="pref_treat_vibrate_as_silent">Trateaza modul vibratie ca silentios</string> + <string name="pref_treat_vibrate_as_silent_summary">Declara clientul drept indisponibil atunci cand dispozitivul este in mod vibratie</string> <string name="pref_show_connection_options">Optiuni avansate conexiune</string> <string name="pref_show_connection_options_summary">Arata optiunea de setare a numelui de gazda si a portului atunci cand se configureaza un cont</string> <string name="hostname_example">xmpp.example.com</string> @@ -559,4 +564,7 @@ Emitent</string> <string name="selection_too_large">Zona selectata este prea mare</string> <string name="no_accounts">(Nici un cont activat)</string> <string name="this_field_is_required">Acest camp este obligatoriu</string> + <string name="no_keys_just_confirm">Deja ai incredere in acest contact. Selectand \'gata\' doar confirmi ca %s ia parte la conferinta.</string> + <string name="select_image_and_crop">Selecteaza imaginea si decupeaza</string> + <string name="this_account_is_disabled">Ai dezactivat acest cont</string> </resources> diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml index e2ef9436..1640e051 100644 --- a/src/main/res/values-ru/strings.xml +++ b/src/main/res/values-ru/strings.xml @@ -96,7 +96,6 @@ <string name="contact_has_no_pgp_key">Conversations не может зашифровать сообщение, потому что удаленный пользователь не анонсирует свой открытый ключ.\n\n<small>Пожалуйста, попросите удаленного пользователя тоже установить OpenPGP.</small></string> <string name="no_pgp_keys">Нет OpenPGP ключей</string> <string name="contacts_have_no_pgp_keys">Conversations не может зашифровать сообщения, потому что удаленные пользователи не анонсируют свои открытые ключи.\n\n<small>Пожалуйста, попросите удаленных пользователей тоже установить OpenPGP.</small></string> - <string name="encrypted_message_received"><i>Получено зашифрованное сообщение. Нажмите, чтобы расшифровать.</i></string> <string name="pref_general">Общие</string> <string name="pref_xmpp_resource">Название ресурса</string> <string name="pref_xmpp_resource_summary">Имя которым Conversations идентифицирует себя</string> @@ -236,7 +235,6 @@ <string name="bookmark_already_exists">Такая закладка уже существует</string> <string name="you">Вы</string> <string name="action_edit_subject">Редактировать тему конференции</string> - <string name="conference_not_found">Конференция не найдена</string> <string name="leave">Покинуть</string> <string name="contact_added_you">Собеседник добавил вас в список контактов</string> <string name="add_back">Добавить в ответ</string> diff --git a/src/main/res/values-sk/strings.xml b/src/main/res/values-sk/strings.xml index ce99da94..414fdf69 100644 --- a/src/main/res/values-sk/strings.xml +++ b/src/main/res/values-sk/strings.xml @@ -224,7 +224,6 @@ <string name="bookmark_already_exists">Táto záložka už existuje</string> <string name="you">Ty</string> <string name="action_edit_subject">Upraviť meno skupinovej konverzácie</string> - <string name="conference_not_found">Skupinová konverzácia sa nenašla</string> <string name="leave">Odísť</string> <string name="contact_added_you">Kontakt pridaný do zoznamu</string> <string name="add_back">Znova pridať</string> diff --git a/src/main/res/values-sr/strings.xml b/src/main/res/values-sr/strings.xml index 92f5a933..ac056472 100644 --- a/src/main/res/values-sr/strings.xml +++ b/src/main/res/values-sr/strings.xml @@ -62,8 +62,8 @@ <string name="crash_report_message">Слањем контратрага помажете текући развој Конверзације\n<b>Упозорење:</b> Ово ће да искористи ваш ИксМПП налог за слање контратрага програмеру.</string> <string name="send_now">Пошаљи одмах</string> <string name="send_never">Не питај више</string> - <string name="problem_connecting_to_account">Не могу да се повежем са налогом</string> - <string name="problem_connecting_to_accounts">Не могу да се повежем са више налога</string> + <string name="problem_connecting_to_account">Не могох да се повежем са налогом</string> + <string name="problem_connecting_to_accounts">Не могох да се повежем са више налога</string> <string name="touch_to_fix">Тапните овде да бисте управљали вашим налозима</string> <string name="attach_file">Приложи фајл</string> <string name="not_in_roster">Контакт није на вашем списку контаката. Желите ли да га додате?</string> @@ -96,13 +96,12 @@ <string name="contact_has_no_pgp_key">Конверзација није могла да шифрује ваше поруке јер ваш контакт не објављује свој јавни кључ.\n\n<small>Замолите вашег контакта да постави ОпенПГП.</small></string> <string name="no_pgp_keys">Нема ОпенПГП кључева</string> <string name="contacts_have_no_pgp_keys">Конверзација није могла да шифрује ваше поруке јер ваши контакти не објављују свој јавни кључ.\n\n<small>Замолите ваше контакте да поставе ОпенПГП.</small></string> - <string name="encrypted_message_received"><i>Примљена је шифрована порука. Тапните за дешифровање.</i></string> <string name="pref_general">Опште</string> <string name="pref_xmpp_resource">ИксМПП ресурс</string> <string name="pref_xmpp_resource_summary">Име са којим се овај клијент идентификује</string> <string name="pref_accept_files">Прихватај фајлове</string> <string name="pref_accept_files_summary">Аутоматски прихватај фајлове мање од…</string> - <string name="pref_notification_settings">Поставке обавештења</string> + <string name="pref_notification_settings">Обавештење</string> <string name="pref_notifications">Обавештења</string> <string name="pref_notifications_summary">Обавести кад стигне нова порука</string> <string name="pref_vibrate">Вибрирај</string> @@ -111,12 +110,12 @@ <string name="pref_sound_summary">Звуци обавештења</string> <string name="pref_notification_grace_period">Период одгоде обавештења</string> <string name="pref_notification_grace_period_summary">Искључи обавештења на кратко по примању карбон копије</string> - <string name="pref_advanced_options">Напредне поставке</string> + <string name="pref_advanced_options">Напредно</string> <string name="pref_never_send_crash">Никад не шаљи извештаје о паду</string> <string name="pref_never_send_crash_summary">Слањем контратрага помажете текући развој Конверзације</string> <string name="pref_confirm_messages">Потврди поруке</string> <string name="pref_confirm_messages_summary">Обзнаните контакту кад примите и прочитате поруку</string> - <string name="pref_ui_options">Поставке сучеља</string> + <string name="pref_ui_options">Сучеље</string> <string name="openpgp_error">Отворени кључарник је пријавио грешку</string> <string name="error_decrypting_file">У/И грешка дешифровања фајла</string> <string name="accept">Прихвати</string> @@ -191,6 +190,7 @@ <string name="server_info_stream_management">XEP-0198: менаџмент тока</string> <string name="server_info_pep">XEP-0163: PEP (аватари/ОМЕМО)</string> <string name="server_info_http_upload">XEP-0363: ХТТП отпремање фајлова</string> + <string name="server_info_push">XEP-0357: „push“</string> <string name="server_info_available">доступан</string> <string name="server_info_unavailable">недоступан</string> <string name="missing_public_keys">Недостају објаве јавног кључа</string> @@ -239,8 +239,7 @@ <string name="bookmark_already_exists">Овај обележивач већ постоји</string> <string name="you">Ви</string> <string name="action_edit_subject">Уреди предмет групног ћаскања</string> - <string name="conference_not_found">Групно ћаскање није нађено</string> - <string name="conference_unknown_error">Примљена је непозната грешка</string> + <string name="joining_conference">Придружујем се групном ћаскању…</string> <string name="leave">Напусти</string> <string name="contact_added_you">Контакт вас је додао на списак контаката</string> <string name="add_back">Додај га</string> @@ -275,12 +274,12 @@ <string name="sure_delete_fingerprint">Желите ли заиста да обришете овај отисак?</string> <string name="ignore">Занемари</string> <string name="without_mutual_presence_updates"><b>Упозорење:</b> Слање овога без узајамних ажурирања присутности би могло да узрокује неочекиване проблеме.\n\n<small>Идите на детаље контакта да бисте проверили претплате на присутности.</small></string> - <string name="pref_encryption_settings">Поставке шифровања</string> + <string name="pref_security_settings">Безбедност</string> <string name="pref_force_encryption">Присили крај-на-крај шифровање</string> <string name="pref_force_encryption_summary">Увек шифруј поруке (осим за групна ћаскања)</string> <string name="pref_dont_save_encrypted">Не успремај шифроване поруке</string> <string name="pref_dont_save_encrypted_summary">Упозорење: Ово може да доведе до губитка порука</string> - <string name="pref_expert_options">Опције за стручњаке</string> + <string name="pref_expert_options">Поставке за стручњаке</string> <string name="pref_expert_options_summary">Будите пажљиви са овим</string> <string name="title_activity_about">О Конверзацији</string> <string name="pref_about_conversations_summary">Подаци о издању и лиценци</string> @@ -298,15 +297,20 @@ <string name="pref_expert_options_other">Остало</string> <string name="pref_conference_name">Назив групног ћаскања</string> <string name="pref_conference_name_summary">Предмет собе уместо ЈИД-а идентификује групно ћаскање</string> + <string name="pref_autojoin">Аутоматски ме придружуј групним ћаскањима</string> + <string name="pref_autojoin_summary">Поштовање опције аутоматског придруживања у обележивачу групног ћаскања</string> <string name="toast_message_otr_fingerprint">ОТР отисак копиран на клипборд!</string> <string name="toast_message_omemo_fingerprint">ОМЕМО отисак копиран на клипборд!</string> <string name="conference_banned">Забрањени сте на овом групном ћаскању</string> <string name="conference_members_only">Ово групно ћаскање је само за чланове</string> <string name="conference_kicked">Шутнути сте из овог групног ћаскања</string> + <string name="conference_shutdown">Групно ћаскање је угашено</string> + <string name="conference_unknown_error">Више нисте у овом групном ћаскању</string> <string name="using_account">преко налога %s</string> <string name="checking_x">Проверавам %s на ХТТП домаћину</string> <string name="not_connected_try_again">Нисте повезани. Покушајте поново касније</string> <string name="check_x_filesize">Провери величину %s</string> + <string name="check_x_filesize_on_host">Провери величину %1$s na %2$s</string> <string name="message_options">Опције поруке</string> <string name="copy_text">Копирај текст</string> <string name="copy_original_url">Копирај изворни УРЛ</string> @@ -428,7 +432,7 @@ <string name="two_hours">2 сата</string> <string name="eight_hours">8 сати</string> <string name="until_further_notice">до даљњег</string> - <string name="pref_input_options">Опције уноса</string> + <string name="pref_input_options">Унос</string> <string name="pref_enter_is_send">Ентер шаље</string> <string name="pref_enter_is_send_summary">Користи Ентер тастер за слање порука</string> <string name="pref_display_enter_key">Прикажи Ентер тастер</string> @@ -487,20 +491,30 @@ <string name="username">Корисничко име</string> <string name="username_hint">Корисничко име</string> <string name="invalid_username">Ово није исправно корисничко име</string> + <string name="conference_name">Назив групног ћаскања</string> + <string name="invalid_conference_name">Ово није исправан назив за групно ћаскање</string> <string name="download_failed_server_not_found">Преузимање није успело: сервер није нађен</string> <string name="download_failed_file_not_found">Преузимање није успело: фајл није нађен</string> <string name="download_failed_could_not_connect">Преузимање није успело: не могу да се повежем са домаћином</string> <string name="account_status_tor_unavailable">Тор мрежа недоступна</string> <string name="server_info_broken">Оштећен</string> - <string name="pref_presence_settings">Поставке присутности</string> + <string name="pref_presence_settings">Присутност</string> <string name="pref_away_when_screen_off">Одсутан кад је екран искључен</string> - <string name="pref_away_when_screen_off_summary">Означава ваш ресурс одсутним кад је екран искључен</string> - <string name="pref_xa_on_silent_mode">Недоступан у тихом режиму</string> - <string name="pref_xa_on_silent_mode_summary">Означава ваш ресурс недоступним кад је уређај у тихом режиму</string> + <string name="pref_away_when_screen_off_summary">Означавање вашег ресурса одсутним кад је екран искључен</string> + <string name="pref_xa_on_silent_mode">Недоступан у нечујном режиму</string> + <string name="pref_xa_on_silent_mode_summary">Означавање вашег ресурса недоступним кад је уређај у нечујном режиму</string> + <string name="pref_treat_vibrate_as_silent">Вибрација је нечујни режим</string> + <string name="pref_treat_vibrate_as_silent_summary">Означавање вашег ресурса недоступним кад је уређај у режиму вибрирања</string> + <string name="pref_show_connection_options">Проширене поставке повезивања</string> + <string name="pref_show_connection_options_summary">Приказ домаћина и порта у поставкама налога</string> <string name="hostname_example">xmpp.primer.com</string> <string name="action_add_account_with_certificate">Додај налог сертификатом</string> <string name="unable_to_parse_certificate">Не могу да рашчланим сертификат</string> <string name="authenticate_with_certificate">Оставите празно за аутентификацију сертификатом</string> + <string name="mam_prefs">Поставке архивисања</string> + <string name="server_side_mam_prefs">Серверске поставке архивисања</string> + <string name="fetching_mam_prefs">Добављам поставке архивисања, сачекајте…</string> + <string name="unable_to_fetch_mam_prefs">Не могох да добавим поставке архивисања</string> <string name="captcha_ocr">Текст стопке</string> <string name="captcha_required">Потребна стопка</string> <string name="captcha_hint">унесите текст са слике</string> @@ -510,7 +524,7 @@ <string name="error_fetching_omemo_key">Грешка добављања ОМЕМО кључа!</string> <string name="verified_omemo_key_with_certificate">Оверен ОМЕМО кључ помоћу сертификата!</string> <string name="device_does_not_support_certificates">Ваш уређај не подржава избор сертификата клијента!</string> - <string name="pref_connection_options">Опције повезивања</string> + <string name="pref_connection_options">Повезивање</string> <string name="account_settings_hostname">Име домаћина</string> <string name="account_settings_port">Порт</string> <string name="not_a_valid_port">Ово није исправан број порта</string> @@ -531,7 +545,7 @@ <string name="certificate_issuer">Издавач</string> <string name="certificate_cn">Заједничко име</string> <string name="certificate_o">Организација</string> - <string name="certificate_sha1">СХА1</string> + <string name="certificate_sha1">СХА-1</string> <string name="certicate_info_not_available">(није доступно)</string> <string name="certificate_not_found">Сертификат није нађен</string> <string name="notify_on_all_messages">Обавештења за све поруке</string> @@ -547,4 +561,7 @@ <string name="selection_too_large">Назначена површина је превелика</string> <string name="no_accounts">(Нема активираних налога)</string> <string name="this_field_is_required">Ово поље је захтевано</string> + <string name="no_keys_just_confirm">Већ се поуздате у овог контакта. Избором „Готово“ само потврђујете да је %s део овог групног ћаскања.</string> + <string name="select_image_and_crop">Изабери слику и опсеци</string> + <string name="this_account_is_disabled">Искључили сте овај налог</string> </resources> diff --git a/src/main/res/values-sv/strings.xml b/src/main/res/values-sv/strings.xml index 00db3f2e..4610e300 100644 --- a/src/main/res/values-sv/strings.xml +++ b/src/main/res/values-sv/strings.xml @@ -96,7 +96,6 @@ <string name="contact_has_no_pgp_key">Conversations kan inte kryptera ditt meddelande eftersom din kontakt inte annonserar sin publika nyckel.\n\n<small>Be din kontakt att sätta upp OpenPGP.</small></string> <string name="no_pgp_keys">Inga OpenPGP-nycklar funna</string> <string name="contacts_have_no_pgp_keys">Conversations kan inte kryptera ditt meddelande eftersom din kontakt inte annonserar sin publika nyckel.\n\n<small>Be din kontakt att sätta upp OpenPGP.</small></string> - <string name="encrypted_message_received"><i>Krypterat meddelande mottaget. Tryck för att avkryptera.</i></string> <string name="pref_general">Generellt</string> <string name="pref_xmpp_resource">XMPP-resurs</string> <string name="pref_xmpp_resource_summary">Namnet klienten identifierar sig med</string> @@ -240,8 +239,7 @@ <string name="bookmark_already_exists">Detta bokmärke finns redan</string> <string name="you">Du</string> <string name="action_edit_subject">Ändra konferensämne</string> - <string name="conference_not_found">Konferens hittades inte</string> - <string name="conference_unknown_error">Mottog okänt fel</string> + <string name="joining_conference">Går med i konferens...</string> <string name="leave">Lämna</string> <string name="contact_added_you">Kontakten lade till dig i sin kontaktlista</string> <string name="add_back">Addera tillbaka</string> @@ -308,10 +306,13 @@ <string name="conference_banned">Du är bannlyst från denna konferens</string> <string name="conference_members_only">Medlemsskap krävs för denna konferens</string> <string name="conference_kicked">Du har blivit utsparkad från denna konferens</string> + <string name="conference_shutdown">Konferensen stängdes ner</string> + <string name="conference_unknown_error">Du är inte längre med i denna konferens</string> <string name="using_account">använder konto %s</string> <string name="checking_x">Kontrollerar %s på webbserver</string> <string name="not_connected_try_again">Du är inte ansluten. Försök igen senare</string> - <string name="check_x_filesize">Kontrollera storleken på %s</string> + <string name="check_x_filesize">Kontrollera filstorleken på %s</string> + <string name="check_x_filesize_on_host">Kontrollera filstorlek för %1$s på %2$s</string> <string name="message_options">Meddelandealternativ</string> <string name="copy_text">Kopiera text</string> <string name="copy_original_url">Kopiera orginal-URL</string> @@ -490,6 +491,8 @@ <string name="username">Användarnamn</string> <string name="username_hint">Användarnamn</string> <string name="invalid_username">Inte ett giltigt användanamn</string> + <string name="conference_name">Konferensnamn</string> + <string name="invalid_conference_name">Detta är inte ett giltigt konferensnamn</string> <string name="download_failed_server_not_found">Nerladdning gick fel: Server hittades inte</string> <string name="download_failed_file_not_found">Nerladdning gick fel: Filen hittades inte</string> <string name="download_failed_could_not_connect">Nerladdningen gick fel: Kunder inte ansluta till server</string> @@ -500,6 +503,8 @@ <string name="pref_away_when_screen_off_summary">Sätter din tillgänglighet till borta när skrämen är av</string> <string name="pref_xa_on_silent_mode">Status ej tillgänglig i tyst läge</string> <string name="pref_xa_on_silent_mode_summary">Sätter din tillgänglighet till ej tillgänglig när enheten är i tyst läge</string> + <string name="pref_treat_vibrate_as_silent">Hantera vibrationsläge som tyst läge</string> + <string name="pref_treat_vibrate_as_silent_summary">Sätter din tillgänglighet till ej tillgänglig när enheten är i vibratorläge</string> <string name="pref_show_connection_options">Utökade anslutningsinställningar</string> <string name="pref_show_connection_options_summary">Visa val av servernamn och port vid inställning av konto</string> <string name="hostname_example">xmpp.example.com</string> @@ -555,4 +560,7 @@ <string name="selection_too_large">The valda området är för stort</string> <string name="no_accounts">(Inget konto aktiverat)</string> <string name="this_field_is_required">Detta fält måste fyllas i</string> + <string name="no_keys_just_confirm">Du litar redan på denna kontakt. Genom att välja \'klar\' bekräftar du att %s är med i denna konferens.</string> + <string name="select_image_and_crop">Välj bild och beskär</string> + <string name="this_account_is_disabled">Du har deaktiverat detta konto</string> </resources> diff --git a/src/main/res/values-tr-rTR/strings.xml b/src/main/res/values-tr-rTR/strings.xml index c96ab61c..e11e56e3 100644 --- a/src/main/res/values-tr-rTR/strings.xml +++ b/src/main/res/values-tr-rTR/strings.xml @@ -96,7 +96,6 @@ <string name="contact_has_no_pgp_key">Kişi ortak anahtarını yayınlamadığı için Conversations iletilerinizi şifreleyemiyor.\n\n<small>Lütfen kişiden OpenPGP’yi ayarlamasını isteyin.</small></string> <string name="no_pgp_keys">Herhangi bir OpenPGP anahtarı bulunamadı</string> <string name="contacts_have_no_pgp_keys">Kişiler ortak anahtarlarını yayınlamadığı için Conversations iletilerinizi şifreleyemiyor.\n\n<small>Lütfen kişilerden OpenPGP’yi ayarlamalarını isteyin.</small></string> - <string name="encrypted_message_received"><i>Şifreli ileti alındı. Deşifre etmek için dokunun.</i></string> <string name="pref_general">Genel</string> <string name="pref_xmpp_resource">XMPP kaynağı</string> <string name="pref_xmpp_resource_summary">İstemci kimliği</string> @@ -240,8 +239,7 @@ <string name="bookmark_already_exists">Bu yer imi zaten mevcut</string> <string name="you">Siz</string> <string name="action_edit_subject">Grup sohbet konusunu düzenle</string> - <string name="conference_not_found">Grup sohbet bulunamadı</string> - <string name="conference_unknown_error">Bilinmeyen hata alındı</string> + <string name="joining_conference">Grup sohbete katiliyor</string> <string name="leave">Ayrıl</string> <string name="contact_added_you">Kişi sizi listesine ekledi</string> <string name="add_back">Siz de ekleyin</string> @@ -308,10 +306,13 @@ <string name="conference_banned">Grup sohbetinden atıldınız</string> <string name="conference_members_only">Bu grup sohbet sadece üyelere açıktır</string> <string name="conference_kicked">Grup sohbetinden atıldınız</string> + <string name="conference_shutdown">Grup sohbet sona erdi</string> + <string name="conference_unknown_error">Artık bu grup sohbet içerisinde değilsiniz.</string> <string name="using_account">%s hesabını kullanarak</string> <string name="checking_x">HTTP sunucusundaki %s \'leri kontrol ediyor</string> <string name="not_connected_try_again">Bağlı değilsiniz. Daha sonra yeniden deneyin</string> <string name="check_x_filesize">%s boyutunu kontrol edin</string> + <string name="check_x_filesize_on_host">%2$s üzerindeki %1$s boyutunu kontrol edin</string> <string name="message_options">İleti seçenekleri</string> <string name="copy_text">Metni kopyala</string> <string name="copy_original_url">Orijinal URL\'i kopyala</string> @@ -488,6 +489,8 @@ <string name="username">Kullanıcı adı</string> <string name="username_hint">Kullanıcı adı</string> <string name="invalid_username">Kullanıcı adı geçerli değil</string> + <string name="conference_name">Grup sohbet ismi</string> + <string name="invalid_conference_name">Bu grup sohbet adı geçerli değil</string> <string name="download_failed_server_not_found">İndirme başarısız: Sunucu bulunamadı</string> <string name="download_failed_file_not_found">İndirme başarısız: Dosya bulunamadı</string> <string name="download_failed_could_not_connect">İndirme başarısız: Sunucuya bağlanılamadı</string> @@ -498,6 +501,8 @@ <string name="pref_away_when_screen_off_summary">Ekran kapandığında çevrimiçi durum bildiriminizi uzakta olarak değiştirir</string> <string name="pref_xa_on_silent_mode">Sessiz moddayken erişilemez</string> <string name="pref_xa_on_silent_mode_summary">Telefonunuz sessizdeyken, durum bildiriminizi müsait değil olarak değiştirir</string> + <string name="pref_treat_vibrate_as_silent">titreş modunu sessiz mod olarak değerlendir</string> + <string name="pref_treat_vibrate_as_silent_summary">cihazınız titreşim modundaysa durum bildiriminizi müsahit değil olarak değiştirir</string> <string name="pref_show_connection_options">Genişletilmiş bağlantı seçenekleri</string> <string name="pref_show_connection_options_summary">Hesap oluştururken sunucu adıyla port seçeneğini göster</string> <string name="hostname_example">xmpp.ornek.com</string> @@ -552,4 +557,7 @@ <string name="selection_too_large">Seçilen alan çok büyük</string> <string name="no_accounts">(Aktif hesap bulunmuyor)</string> <string name="this_field_is_required">Bu alan zorunludur</string> + <string name="no_keys_just_confirm">Bu kişiye zaten güveniyor durumdasınız. \"tamam\" seçeneğini işaretleyerek, sadece %s \'in bu grup sohbete katılabileceğini teyid ediyorsunuz.</string> + <string name="select_image_and_crop">Resmi seçip kırpın</string> + <string name="this_account_is_disabled">Bu hesabı devre dışı bıraktınız</string> </resources> diff --git a/src/main/res/values-vi/strings.xml b/src/main/res/values-vi/strings.xml index 2635498e..56ae3add 100644 --- a/src/main/res/values-vi/strings.xml +++ b/src/main/res/values-vi/strings.xml @@ -96,7 +96,6 @@ <string name="contact_has_no_pgp_key">Conversations không thể mã hoá tin nhắn vì liên hệ của bạn không thông báo khoá công cộng của anh/chị ấy.\n\n<small>Hãy yêu cầu liên hệ đó cài đặt OpenPGP.</small></string> <string name="no_pgp_keys">Không tìm thấy các khoá OpenPGP</string> <string name="contacts_have_no_pgp_keys">Conversations không thể mã hoá tin nhắn vì các liên hệ của bạn không thông báo khoá công cộng của họ.\n\n<small>Hãy yêu cầu họ cài đặt OpenPGP.</small></string> - <string name="encrypted_message_received"><i>Đã nhận tin nhắn được mã hoá. Chạm để giải mã.</i></string> <string name="pref_general">Tổng quan</string> <string name="pref_xmpp_resource">Ứng dụng XMPP</string> <string name="pref_xmpp_resource_summary">Tên của máy trạm này được tự đặt là</string> @@ -236,7 +235,6 @@ <string name="bookmark_already_exists">Đã có đánh dấu này rồi</string> <string name="you">Bạn</string> <string name="action_edit_subject">Chỉnh sửa tiêu đề diễn đàn</string> - <string name="conference_not_found">Không tìm thấy diễn đàn</string> <string name="leave">Rời khỏi</string> <string name="contact_added_you">Liên hệ đã thêm bạn vào danh bạ</string> <string name="add_back">Thêm họ vào</string> diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index 4f3c79ff..3cdf1650 100644 --- a/src/main/res/values-zh-rCN/strings.xml +++ b/src/main/res/values-zh-rCN/strings.xml @@ -96,7 +96,6 @@ <string name="contact_has_no_pgp_key">Conversations 无法加密信息,因为联系人未提供他/她的公钥。\n\n<small>请通知联系人设置 OpenPGP。</small></string> <string name="no_pgp_keys">未找到 OpenPGP 密钥</string> <string name="contacts_have_no_pgp_keys">因您的联系人未公布公钥,Conversations未能成功加密您的信息.\n\n<small>请通知联系人设置OpenPGP.</small></string> - <string name="encrypted_message_received"><i>接收到加密消息。轻触以解密。</i></string> <string name="pref_general">常规</string> <string name="pref_xmpp_resource">XMPP 资源</string> <string name="pref_xmpp_resource_summary">客户端标识名称</string> @@ -240,8 +239,7 @@ <string name="bookmark_already_exists">该书签已存在</string> <string name="you">你</string> <string name="action_edit_subject">编辑讨论组主题</string> - <string name="conference_not_found">讨论组未找到</string> - <string name="conference_unknown_error">收到未知错误</string> + <string name="joining_conference">加入讨论组…</string> <string name="leave">离开</string> <string name="contact_added_you">联系人已添加你到联系人列表</string> <string name="add_back">反向添加</string> @@ -308,10 +306,13 @@ <string name="conference_banned">你被此讨论组屏蔽</string> <string name="conference_members_only">此讨论组只允许成员加入</string> <string name="conference_kicked">你被从此讨论组踢出</string> + <string name="conference_shutdown">讨论组已被关闭</string> + <string name="conference_unknown_error">你已不属于此讨论组</string> <string name="using_account">用账户 %s</string> <string name="checking_x">正在 HTTP 服务器中检查 %s</string> <string name="not_connected_try_again">你没有连接。请稍后重试</string> <string name="check_x_filesize">检查 %s 大小</string> + <string name="check_x_filesize_on_host">在 %2$s 上检查 %1$s 的大小</string> <string name="message_options">消息选项</string> <string name="copy_text">拷贝文本</string> <string name="copy_original_url">拷贝原始URL</string> @@ -488,6 +489,8 @@ <string name="username">用户名</string> <string name="username_hint">用户名</string> <string name="invalid_username">该用户名无效</string> + <string name="conference_name">讨论组名称</string> + <string name="invalid_conference_name">该讨论组名称无效</string> <string name="download_failed_server_not_found">下载失败:未找到服务器</string> <string name="download_failed_file_not_found">下载失败:未找到文件</string> <string name="download_failed_could_not_connect">下载失败:无法连接到服务器</string> @@ -498,6 +501,8 @@ <string name="pref_away_when_screen_off_summary">当屏幕关闭时将标记您的资源为离开状态</string> <string name="pref_xa_on_silent_mode">静音模式时不可用</string> <string name="pref_xa_on_silent_mode_summary">当设备进入静音模式时把资源标识改为不可用</string> + <string name="pref_treat_vibrate_as_silent">静音模式开启振动</string> + <string name="pref_treat_vibrate_as_silent_summary">当设备进入振动模式时把资源标识改为不可用</string> <string name="pref_show_connection_options">高级边接设置</string> <string name="pref_show_connection_options_summary">注册账户时显示主机名和端口</string> <string name="hostname_example">xmpp.example.com</string> @@ -554,4 +559,7 @@ <string name="selection_too_large">选择区域过大</string> <string name="no_accounts">(没有激活的账户)</string> <string name="this_field_is_required">必填</string> + <string name="no_keys_just_confirm">你已信任此联系人。选择“完成”表示 %s 将成为此讨论组的一部分。</string> + <string name="select_image_and_crop">选择照片并裁剪</string> + <string name="this_account_is_disabled">你已经禁用了此账户</string> </resources> diff --git a/src/main/res/values-zh-rTW/strings.xml b/src/main/res/values-zh-rTW/strings.xml index 9a59cfe6..20202624 100644 --- a/src/main/res/values-zh-rTW/strings.xml +++ b/src/main/res/values-zh-rTW/strings.xml @@ -188,7 +188,6 @@ <string name="bookmark_already_exists">該書籤已存在</string> <string name="you">你</string> <string name="action_edit_subject">編輯群組主題</string> - <string name="conference_not_found">群組未找到</string> <string name="leave">離開</string> <string name="contact_added_you">聯絡人已新增你到聯絡人列表</string> <string name="add_back">新增為聯絡人</string> diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 15b7efc5..a34c81d5 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -358,6 +358,7 @@ <string name="checking_x">Checking %s on HTTP host</string> <string name="not_connected_try_again">You are not connected. Try again later</string> <string name="check_x_filesize">Check %s size</string> + <string name="check_x_filesize_on_host">Check %1$s size on %2$s</string> <string name="message_options">Message options</string> <string name="copy_text">Copy text</string> <string name="copy_original_url">Copy original URL</string> @@ -598,6 +599,8 @@ <string name="pref_away_when_screen_off_summary">Marks your resource as away when the screen is turned off</string> <string name="pref_xa_on_silent_mode">Not available in silent mode</string> <string name="pref_xa_on_silent_mode_summary">Marks your resource as not available when device is in silent mode</string> + <string name="pref_treat_vibrate_as_silent">Treat vibrate as silent mode</string> + <string name="pref_treat_vibrate_as_silent_summary">Marks your resource as not available when device is on vibrate</string> <string name="pref_show_connection_options">Extended connection settings</string> <string name="pref_show_connection_options_summary">Show hostname and port settings when setting up an account</string> <string name="hostname_example">xmpp.example.com</string> @@ -656,4 +659,7 @@ <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> + <string name="no_keys_just_confirm">You already trust this contact. By selecting \'done\' you are just confirming that %s is part of this conference.</string> + <string name="select_image_and_crop">Select image and crop</string> + <string name="this_account_is_disabled">You have disabled this account</string> </resources> diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml index 7dac445a..17c56bf7 100644 --- a/src/main/res/xml/preferences.xml +++ b/src/main/res/xml/preferences.xml @@ -219,6 +219,12 @@ android:key="xa_on_silent_mode" android:summary="@string/pref_xa_on_silent_mode_summary" android:title="@string/pref_xa_on_silent_mode"/> + <CheckBoxPreference + android:dependency="xa_on_silent_mode" + android:defaultValue="false" + android:key="treat_vibrate_as_silent" + android:title="@string/pref_treat_vibrate_as_silent" + android:summary="@string/pref_treat_vibrate_as_silent_summary"/> </PreferenceCategory> <PreferenceCategory android:key="other_expert_settings" android:title="@string/pref_expert_options_other"> <CheckBoxPreference |